POSIX函数之strftime
2012-11-26 16:55:04 阿炯

本文介绍一下 Perl 下取时间大都使用'localtime'来取得当地日期时间和日期,这个函数如果在标量环境时,会以字符串的形式来传回目前的时间和日期 。默认的 localtime 的函数是以1970 到今天的秒来做整数计算的,默认这个程序会调用 time 的函数来给它提供一个值。

使用方法:
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)=localtime;
$sec:秒
$min:分
$hour:小时
$mday:日
$mon:月
$year:目前的年减去1990,不是仅将19xx年的19去掉,因此不会有Y2K的困扰.
$wday:每周的日期(如Sunday是0)
$yday:每年的日期(如Jan 1是0)
$isdst:如果日光节约时间使用则是正值,其它为0.

上面这个函数常用,可读性不太好,容易出错,所以推荐 strftime 这个时间函数。当然还有另一个模块 DataTime 也相当不错,不过 strftime 非常像 Linux 常用的 date 的命令。strftime 是 C 中 POSIX 的一个功能函数。

strftime is actually a C function from the POSIX 1003.1 standard. Luckily, the POSIX module which comes standard with Perl, and practically all modern operating systems implement the standard.

To use strftime, we supply a list of elements in the same format as the return from Perl's own localtime or gmtime functions.

使用起来很容易,示例如下:
use v5.12;
use POSIX qw(strftime);
print strftime("%Y-%m-%d %H:%M:%S\n", localtime(time));

会输出
2012-09-11 06:44:24

Strftime 时间域:(这个和 date 的命令的字符格式是一样的,另外不同版本的参数域(转换字符)是不一样的,使用前尽量查询较新的手册页)
%a根据当前语言环境的缩写的工作日名称,星期几的简称 Sun..Sat。
%A根据当前语言环境的完整工作日名称,星期几的全称 Sunday..Saturday。
%b根据当前语言环境的缩写月份名称,月的简称Jan..Dec。
%B根据当前语言环境的完整月份名称,星期几的全称 Sunday..Saturday。
%c当前语言环境的首选日期和时间表示形式,日期和时间 Mon Nov 8 14:12:46 CST 2009。
%C世纪数字(年/100)为2位整数(00到99)。
%d以十进制数字表示的月份中的日期(范围为01到31);%-d:则表示无前导的'0'(without leading zero)。
%D等效于%m/%d/%y。对于美国人。美国人应该注意,在其他国家,%d/%m/%y相当普遍。在国际范围内,这种格式是模棱两可的,不应使用。
%e%d一样,将月中的日期作为十进制数字,但是前导零被空格代替(1到31)。
%E修饰符:使用其他格式,请参见下文。
%F相当于%Y-%m-%d(YYYY-MM-DD)(在ISO 8601日期格式)。
%GISO 8601星期基准年(请参见“注释”),以世纪作为十进制数字。与ISO周编号相对应的4位数字的年份(请参见%V)。此转换的格式和值与%Y相同,不同的是,如果ISO周编号属于上一年或下一年,则使用该年份。
%g%G一样,但没有世纪,也就是说,具有两位数的年份(00-99)。
%h
等效于%b
%H使用24小时制的小时(十进制数)(范围为00到23)。
%I使用12小时制(范围为01至12)的小时作为十进制数字。
%j一年中的天,以十进制数字表示(范围为001至366)。
%k小时(24小时制)为十进制数字(范围为0到23);单个数字后跟一个空格。(另请参见%H。)
%l小时(12小时制)为十进制数字(范围为1到12);单个数字后跟一个空格。(另请参见%I。)
%m以十进制数字表示的月份(范围为01到12)。
%M分钟,十进制数字(范围为00到59)。
%n一个换行字符。
%O修饰符:使用其他格式,请参见下文。
%p根据给定的时间值,使用“ AM”或“ PM”,或者使用当前语言环境的相应字符串。中午被视为“ PM”,午夜被视为“ AM”。
%P与%p类似,但小写:“ am”或“ pm”或当前语言环境的相应字符串。
%r上午或下午表示法中的时间。时间hh:mm:ss AM或PM,12小时。在POSIX语言环境中,这等效于%I%M%S %p
%R以24小时制表示的时间(%H:%M)。(SU)有关包含秒数的版本,请参见下面的%T
%s自1970年1月1日00:00:00 +0000(UTC)以来的秒数。
%S第二个十进制数字(范围为00到60)。范围最大为60,以允许偶尔出现leap秒。
%t一个选项卡字符。
%TISO 8601,以24小时制(hh:mm:ss)表示的时间(%H:%M:%S)。
%uISO 8601,以十进制表示的星期几,范围为1到7,星期一为1。另请参见%w
%U当前年份的星期数,以十进制数表示,范围为00到53,从第一个星期日作为01周的第一天开始。另请参见%V%W
%V当年的ISO 8601周编号为十进制数字,范围为01至53,其中第1周是新年中至少有4天的第一周。另请参见%U%W
%w以十进制表示的星期几,范围为0到6,星期日为0。另请参见%u
%W本年的星期数(以十进制数表示),范围为00到53,从第一个星期一开始(第01周的第一天)开始。
%x当前语言环境的首选日期表示形式,没有时间。
%X当前区域设置的首选时间表示形式,不带日期。
%y年份,不带世纪的十进制数字(范围为00到99)。
%Y以十进制数字表示的年份,包括世纪。
%zISO 8601,+ hhmm或-hhmm数字时区(即距UTC的小时和分钟偏移量)。
%Z时区或名称或缩写,CST、CDT。
%+日期和时间,采用日期格式。
%%文字“”字符。


可以通过在转换说明符前面加上EO修饰符来修改某些转换规范,以指示应使用其他格式。如果当前语言环境不存在替代格式或规范,则其行为将类似于使用未修改的转换规范。单一UNIX规范(SU)提到%Ec%EC%Ex%EX%Ey%EY%Od%Oe%OH%OI%Om%OM%OS%Ou%OU%OV%Ow%OW%Oy,其中O修饰符的作用是使用替代数字符号(例如,罗马数字),而E修饰符的作用是使用语言环境依赖的替代表示。

细分的时间结构tm中定义。

Glibc为转换规范提供了一些扩展。在POSIX.1-2001中未指定这些扩展名,但是其他一些系统也提供了类似的功能。在'  '字符和转换说明符之间,可以指定可选的标志和字段宽度。它们在EO修饰符(如果有)之前。

允许使用以下标志字符:
_(underscore)

用空格填充数字结果字符串。
--(dash)

请勿填充数字结果字符串。
0(zero)

将数字结果字符串填充为零,即使转换说明符字符默认情况下使用空格填充也是如此。
^(carat)

将结果字符串中的字母字符转换为大写。
(hash)

交换结果字符串的大小写。

该标志仅适用于某些转换说明符,其中只有%Z才真正有用。

可选的十进制宽度说明符可以位于(可能不存在)标志之后。如果字段的自然大小小于此宽度,则将结果字符串填充(在左侧)为指定宽度。

use v5.28;
use utf8;
use Time::Piece;
say localtime(time())->strftime('%FT%T');
say 'WeekNo:'.localtime(time())->strftime('%U');

>2023-02-20T19:56:16
>WeekNo:08


Format strings
Your first step in learning more about reading any module in Perl should be to read its documentation, which we can access with perldoc POSIX. Unfortunately, the entry for strftime refers you to your operating system's man page for further details. This isn't very useful if your system doesn't have that documentation installed, and you don't have a copy of POSIX 1003.1 sitting on your desk.

Even if you do have the strftime documentation, it's worth being a little bit careful. Standards are great, because there are so many to choose from, and the interpretation of the POSIX standard certainly varies between operating systems.

In particular, any format that involves words (days of the week, or months, or abbreviations thereof) and some of the ``preferred date format'' strings should depend upon the user's locale, but some systems ignore the locale entirely.

Portable formats
Below we list the format options which are portable, and should be implemented on all modern systems:

%d
The day of the month as a decimal number (range 01 to 31).

%H
The hour as a decimal number using a 24-hour clock (range 00 to 23).

%I
The hour as a decimal number using a 12-hour clock (range 01 to 12).

%j
The day of the year as a decimal number (range 001 to 366).

%m
The month as a decimal number (range 01 to 12).

%M
The minute as a decimal number (range 00 to 59).

%S
The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds (and double leap seconds).)

%U
The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %W.

%w
The day of the week as a decimal, range 0 to 6, Sunday being 0.

%W
The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.

%y
The year as a decimal number without a century (range 00 to 99).

%Y
The year as a decimal number including the century.

%%
A literal `%' character.

Locale-dependent formats
The following formats may depend upon the user's locale. They're generally considered portable, but may return different strings for the same date, depending upon operating system and locale particulars:

%a
The abbreviated weekday name according to the current locale.

%A
The full weekday name according to the current locale.

%b
The abbreviated month name according to the current locale.

%B
The full month name according to the current locale.

%p
Either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as `pm' and midnight as `am'.

Not recommended formats.

The following formats are not recommended. Often the ``preferred locale'' is ambiguous, sometimes it's ignored, and often it's both. Presenting a user with a date such as ``02/03/09'' can be a great source of confusion and angst, whereas ``2009-03-02'' is unambiguously clear.

The %c, %x, %X and %Z formats, while part of the standard, are all notorious for producing these sorts of ambiguous results, and while they're part of the standard, they should be avoided.


另有GNU版的时间转换模块:POSIX::strftime::GNU

flag characters:

_
(underscore) Pad a numeric result string with spaces.

-
(dash) Do not pad a numeric result string.

0
Pad a numeric result string with zeros even if the conversion specifier character uses space-padding by default.

^
Convert alphabetic characters in result string to upper case.

#
Swap the case of the result string. (This flag only works with certain conversion specifier characters, and of these, it is only really useful with %Z.)


常用的实例

得到日期的全部
perl -MPOSIX -le 'print strftime "%c", localtime();'
Sat 21 Aug 2010 07:54:34 AM CST

得到普通的指定的日期
perl -MPOSIX -le 'print strftime "%a %d %b %Y %H:%M:%S %Z", localtime();'
Sat 21 Aug 2010 07:54:11 CST

得到一个小时以前的时间
perl -MPOSIX -le 'print strftime "%c", localtime(time()-3600);'
Sat 21 Aug 2010 06:55:54 AM CST

得到一天前的时间
perl -MPOSIX -le 'print strftime "%c", localtime(time()-86400);'

my $yesterday = strftime "%b %d, %Y", localtime(time-86400);
my $tomorrow  = strftime "%b %d, %Y", localtime(time+86400);

# Thu 26 Feb 2009 13:49:54 EST
print strftime("%a %d %b %Y %H:%M:%S %Z\n", localtime());

# The below is the same as above due to our locale setting
# Thu 26 Feb 2009 13:49:54 EST
print strftime("%c\n", localtime());

# Thursday 26 February 09, 01:49:54 PM (day 057)
print strftime("%A %d %B %y, %I:%M:%S %p (day %j)\n", localtime());

say strftime '%Y-%m-%d', gmtime(); # 2024-01-02
say strftime '%Y-%m-%dT%H-%M-%S', gmtime(); # 2024-01-02T13-29-57(比本地时间慢8小时)

use v5.16;
use POSIX qw(strftime);

my $utime = 1704202553;
my @gtime = gmtime($utime);
say strftime '%FT%T',@gtime;
say '-' x 13;
my @ltime=localtime($utime);
say strftime '%FT%T',@ltime;

>
2024-01-02T13:35:53
-------------
2024-01-02T21:35:53