Perl日期时间处理模块之Time::Piece
2013-08-02 14:07:47

Time::Piece - Object Oriented time objects

功能与Datetime模块相同,可以做为其的替代者,且Time::Piece是核心模块,其稳定性是可以肯定的,借助于strftime函数可实现非常丰富的日期时间格式。

摘要
use Time::Piece;
my $t = localtime;
print "Time is $t\n";
print "Year is ", $t->ymd, "\n";

用法
$t->sec # also available as $t->second
$t->min # also available as $t->minute
$t->hour # 24 hour
$t->mday # also available as $t->day_of_month
$t->mon # 1 = January
$t->_mon # 0 = January
$t->monname # Feb
$t->month # same as $t->monname
$t->fullmonth # February
$t->year # based at 0 (year 0 AD is, of course 1 BC)
$t->_year # year minus 1900
$t->yy # 2 digit year
$t->wday # 1 = Sunday
$t->_wday # 0 = Sunday
$t->day_of_week # 0 = Sunday
$t->wdayname # Tue
$t->day # same as wdayname
$t->fullday # Tuesday
$t->yday # also available as $t->day_of_year, 0 = Jan 01
$t->isdst # also available as $t->daylight_savings
$t->hms # 12:34:56
$t->hms(".") # 12.34.56
$t->time # same as $t->hms
$t->ymd # 2000-02-29
$t->date # same as $t->ymd
$t->mdy # 02-29-2000
$t->mdy("/") # 02/29/2000
$t->dmy # 29-02-2000
$t->dmy(".") # 29.02.2000
$t->datetime # 2000-02-29T12:34:56 (ISO 8601)
$t->cdate # Tue Feb 29 12:34:56 2000
"$t" # same as $t->cdate
$t->epoch # seconds since the epoch
$t->tzoffset # timezone offset in a Time::Seconds object
$t->julian_day # number of days since Julian period began
$t->mjd # modified Julian date (JD-2400000.5 days)
$t->week # week number (ISO 8601)
$t->is_leap_year # true if it its
$t->month_last_day # 28-31
$t->time_separator($s)  # set the default separator (default ":")
$t->date_separator($s)  # set the default separator (default "-")
$t->day_list(@days)     # set the default weekdays
$t->mon_list(@days)     # set the default months
$t->strftime(FORMAT)    # same as POSIX::strftime (without the overhead of the full POSIX extension)
$t->strftime() # "Tue, 29 Feb 2000 12:34:56 GMT"
Time::Piece->strptime(STRING, FORMAT)
# see strptime man page. Creates a new Time::Piece object

日期计算
简单日期加减
use Time::Seconds;
my $seconds = $t1 - $t2;
$t1 += ONE_DAY; # add 1 day (constant from Time::Seconds)

下列的算是是有效的($t1 and $t2 是Time::Piece objects)
$t1 - $t2; # returns Time::Seconds object
$t1 - 42; # returns Time::Piece object
$t1 + 533; # returns Time::Piece object

然而 Time::Piece 相加会产生异常,相减返回Time::Seconds object,通过Time::Seconds API返回分钟、小时、天、星期、年。

除了能加秒, 还有两个API函数能加月,年:
$t->add_months(6);
$t->add_years(5);

月和年通过负数可以变为减。

日期比较操作
日期可以使用 "<", ">", "<=", ">=", "<=>", "==" and "!=" 进行比较

日期解析
Time::Piece通过strptime()函数(from FreeBSD),进行日期格式的转换。
my $t = Time::Piece->strptime("Sunday 3rd Nov, 1993", "%A %drd %b, %Y");
print $t->strftime("%a, %d %b %Y");

相关常量(需要参考Time::Seconds模块)
ONE_DAY
ONE_WEEK
ONE_HOUR
ONE_MINUTE
ONE_MONTH
ONE_YEAR
ONE_FINANCIAL_MONTH
LEAP_YEAR

NON_LEAP_YEAR

对时区问题的处理
当将日期时间传入时,用于返回unix时间戳(epoch)。由于不能指定传入时间的时区信息(time_zone),最后返回的时戳不是该时区的时间,而是gmt时区的时间,下面就讨论下可行的解决办法。

首先要保证系统及硬件时间是当前时区的正确时间,比如我的时间就是中国上海时间(gmt +8)。

与所有的日期时间处理函数相似,有'%Z'这个 Time zone name or abbreviation, or no bytes if no time zone information exists.

Also, "%z" seems to be Linux specific - does not work on Solaris either:

perl -MPOSIX -e 'print strftime "%Z (%z)\n",localtime'
CST (+0800)

向Time::Piece对象传入参数是通过strptime函数来实现的,传入后函数内部会将其按gmt时间来处理。处理代码如下:
use Time::Piece;

#想处理的日期时间(当地)
my $date = '2013-11-09 9:00';

# Parse the date using strptime(), which uses strftime() formats.将其按相关的格式传入strptime函数
my $time = Time::Piece->strptime($date, "%Y-%m-%d %k:%M");

# 打印出为gmt的日期时间
say $time->datetime;

# Get your local time zone offset.得到时区与gmt的关系后并处理为当地日期时间
$time -= $time->localtime->tzoffset;

# And here it is localized.现在得到的就是本地日期时间,如果取出时戳的话,就是当地时间的时戳
say $time->datetime;

我们再来看下对本地时间的取得:
#$ENV{TZ} = 'Asia/Shanghai';
#$ENV{TZ} = 'CST';

my $gdt=localtime;
say 'current hour:'.$gdt->hour;
say 'current epoch:'.$gdt->epoch;
say 'current full dt:'.$gdt->strftime("%c",$gdt);
say 'current tzone:'.$gdt->strftime('%Z',$gdt);
say 'current datetime:'.$gdt->datetime;
say 'localtime:'.$gdt->localtime;
say 'localtime tzoffset:'.$gdt->localtime->tzoffset;

输出如下:
current hour:11
current epoch:1384054601
current full dt:2013年11月10日 星期日 11时36分41秒
current tzone:CST
current datetime:2013-11-10T11:36:41
localtime:Sun Nov 10 11:36:41 2013
localtime tzoffset:28800

个性化的本地提示
让日期的输出带有本地化的字符,像DateTime::Locale::zh_CN这类的模块,就可以有中文日期的返回。Time::Piece模块中却可以定制,下面以一周名为例:
my @days=('一','二','三','四','五','六','日');
say $time->day(@days);
say Time::Piece::day_list(@days);
say $time->day_list(@days);


常用的日期时间处理参考

对日期时间进行多种格式化

$time;           # Thu Jan  9 21:21:38 2014
$time->datetime; # 2014-01-09T21:21:38
$time->date;     # 2014-01-09
$time->mdy;      # 01-09-2014
$time->fullday;  # Thursday
$time->hms;      # 21:21:38
$time->epoch;    # 1389320498 (Unix time)

如果想得到自定义的格式,Time::Piece 提供了 “strftime” 可以实现,下面是一些示例:

my $time = Time::Piece->new;
$time->strftime('%y/%m/%d %H:%M'); # 14/01/09 21:21
$time->strftime('%y_%m_%d');       # 14_01_09
$time->strftime('%s');             # 1389320496 (Unix time)
$time->strftime('%Y %y %G %g');    # 2014 14 2014 14 (4 different years,really)

读入不同格式的日期时间

Time::Piece 提供了strptime函数来解析传入的日期时间字符串,将其初始后返回该对象。如果日期或时间缺失,将会用当前的日期或时间补充之,以下是一些示例:

my $yesterday    = Time::Piece->strptime('01-08-2016', '%m-%d-%Y');
my $yesterdayDMY = Time::Piece->strptime('08-01-16', '%d-%m-%y');
my $lunchhour24  = Time::Piece->strptime('12:30', '%H:%M');
my $bedtime      = Time::Piece->strptime('12:30 AM', '%l:%M %p');

#处理时区也很容易
my $utc_datetime = Time::Piece->strptime('Mon, 19 Jan 2015 14:56:20 +0000','%a, %d %b %Y %H:%M:%S %z');
my $eastern_datetime = Time::Piece->strptime('2015-10-05T09:34:19 -0400','%Y-%m-%dT%T %z');
my $pacific_datetime = Time::Piece->strptime('2015-10-05T09:34:19 -0700','%Y-%m-%dT%T %z');

注意:如果时区中带有分号的话,处理会失败。在传入处理前应将它去掉。

my $datetime = '2015-10-05T09:34:19 -04:00';
$datetime    =~ s/([+\-]\d\d):(\d\d)/$1$2;
my $dt       = Time::Piece->strptime($datetime, "%Y-%m-%dT%H:%M:%S %z");

strptime、strftime这两个函数在参数的使用是一致的。

参考文档

Time::Piece

该文章最后由 阿炯 于 2016-09-25 19:34:08 更新,目前是第 2 版。