操控日期和时间的Perl模块
2010-05-09 11:39:50 阿炯

能够操控日期和时间对于大多数编程语言来说是基本的能力,Perl也不例外;但是在碰到与时间相关的更加复杂的处理时,Perl自带的功能常常就显得力不从心了。在这种情况下,上Perl综合典藏网(CPAN)看看是一个好主意,上面有很多随时可以使用的Perl模块,让开发人员能够有效地进行日期和时间操作。花点时间浏览这个网站看看是否能够找到一些在下一个项目里用得上的东西是值得的。

Perl中处理时间的函数有如下几种:
1、time() 函数:返回从1970年1月1日起累计的秒数,格式是以从1970年1月1日(unix操作系统纪元一般都是该时间)距离现在的秒数表示的epoch
2、localtime() 函数:获取本地时区时间,返回给定时间的秒、分、时、日、月、年、周等9个部分的时间属性,参数为epoch时间格式,无参数则返回当前时间点对应的秒、分、时、日、月、年等属性
3、gmtime() 函数:获取格林威治时间(UTC)

当前时间和日期

localtime()函数在没有参数的情况下返回当前的时间和日期,在列表上下文返回的是各个时间部分,在标量上下文返回的是一个本地格式的时间值。以下 9 个符号代表不同的时间日期参数:
# perl -e '$fa=localtime;print $fa,"\n";'
Fri Nov  3 17:23:32 2023

以下是localtime在列表上下文返回的各个时间部分:
#  0    1    2     3     4    5     6     7     8
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

sec,# 秒,0 到 61
min,# 分钟,0 到 59
hour,# 小时,0 到 24
mday,# 天,1 到 31
mon,# 月,0 到 11
year,# 年,从 1900 开始
wday,# 星期几,0-6,0是周日,1是周一,6是周六
yday,# 一年中的第几天,0-364,365
isdst,# 如果夏令时有效,则为真

@months = qw( 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 );
@days = qw(星期天 星期一 星期二 星期三 星期四 星期五 星期六);

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
print "$mday $months[$mon] $days[$wday]\n";

以上实例执行输出结果为:
4 三月 星期六

如果直接调用 localtime() ,它返回系统当期设置时区的时间:
$datestring = localtime();
print "时间日期为:$datestring\n";

格林威治时间 (GMT)

函数 gmtime() 与 localtime() 类似,但它返回标准格林威治时间。

$local_datestring = localtime();
print "本地时间日期为:$local_datestring\n";
 
$gmt_datestring = gmtime();
print "GMT 时间日期为:$gmt_datestring\n";

以上实例执行输出结果为:
本地时间日期为:Sat Mar  4 13:26:32 2023
GMT 时间日期为:Sat Mar  4 05:26:32 2023

从实例中我们可以看出,中国的时间和格林威治时间相差了8小时。

格式化日期和时间

可以使用 localtime() 函数的 9 个时间元素来输出需要制定的格式时间。格式化输出使用 printf() 函数:
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
 
printf("格式化时间:HH:MM:SS\n");
printf("%02d:%02d:%02d", $hour, $min, $sec);

以上实例执行输出结果为:
格式化时间:HH:MM:SS
11:35:23

当然格式化最好的函数还数POSIX中strftime了。
use POSIX qw(strftime);
strftime("%F %T", localtime);


新纪元时间(Epoch Time)

可以使用 time() 函数来获取新纪元时间,该函数返回从1970年1月1日起累计的秒数:
$epoc = time();
print "从1970年1月1日起累计的秒数为:$epoc\n";

以上实例执行输出结果为:
从1970年1月1日起累计的秒数为:1677907757

后文列出了Perl CPAN上用来操控日期和时间的10个最有趣的模块,为您下一次转化时区和计算两个时间戳之间的秒数打下了基础,现在来看看它们。

注:可以根据下面提供的方法直接从Internet上安装CPAN模块。

名称        描述
Date::Manip
这个模块被称为日期操控模块的“瑞士军刀”。它提供的例程可以用来分析和比较日期,提取日期信息,确定日期信息,确定日期/时间偏量,使用重复日期和时间,以及在不同语言里使用日期等。但是它运行起来要比其他更加专业的模块慢。
当您需要一个通用的日期操控模块且不特别关心性能的时候使用这个模块。

DateTime
这个模块是Perl的DateTime项目的一部分,它提供了一个基类,用以“代表日期/时间的组合”。它是创建自定义日期(Date)对象的最方便的方法,并且包括了用于分析、格式化、计算持续时间和操控时区的模块。
一般来说,在需要本地化或者定义用于时间信息的自定义存储容器的时候,您要将这个模块用作创建与日期相关的新对象。

Time::Format
在需要重新格式化日期和时间值的时候,这个模块是无可匹敌的——它支持大量格式化代码,能够大幅改变日期和时间的外观。它同时接受DateTime对象和Date::Manip字符串,还为加入的控件提供了到POSIX的strftime()函数的接口。
当您需要格式化日期和时间值的时候(通常是在将它们显示给应用程序用户之前)使用这个模块。

Time::Interval
这个模块对于处理时间间隔十分有用。它提供的例程可以计算两个时间戳之间的日、小时、分钟和秒的总数。它还可以轻松地将时长转换成不同的时间单位,例如秒、小时,或者分钟。
当您需要计算两个日期值之间所流逝的时间时使用这个模块。

Date::Convert
这个模块提供的例程用于在不同的日期格式(阳历、儒略历、犹太历、伊斯兰历和绝对历)之间转换。它还提供挂钩用以轻松扩展到新的日历格式。
当您需要从一个日历格式转换到另外一个日历格式的时候使用这个模块。

Benchmark
这个模块允许您多次运行同一段代码,计算执行每次运行的时间并返回所取得的平均值。它还可以被用来查看一个代码块可以在固定的时间窗里运行多少次。
在进行性能基准测试,测量某个代码块运行的速度,以及收集精确的读数以指导您进行优化的时候使用这个模块。

Time::Normalize
这个模块提供的例程用来把任意日期和时间值格式化成一个统一的、标准的表示,它然后可以被用来进行计算或者保存。它会对输入的内容进行错误检查,然后以清楚的格式返回独立的时间和/或日期组件。
当您怀疑输入的日期有错误并需要在数据库或者应用程序里使用它之前“清理干净”的时候使用这个模块。

Regexp::Common::time
这个模块会创建可以用被用来分析日期和时间的正则表达式。它支持同时使用精确的规则和复杂的模糊逻辑从字符串值捕捉日期模式。
用这个模块从人们易于理解的字符串识别和提取日期信息,并将它转换成机器可读的格式(例如ISO 8601)。

MySQL::DateFormat
这个模块提供的例程可以把日期和数字在MySQL的YYYY-MM-DD格式和人们易于理解的字符串之间来回转换。在从/到MySQL数据库取回/添加含有日期信息的记录时这个模块十分有用。
为了以可读性更强的方式显示,或者在将它插入MySQL数据库之前修改用户提供的日期值,您可以使用这个模块来重新格式化MySQL的日期/时间字符串。

Net::Time
这个模块提供了一个从远程客户端取回日期和时间信息的客户端接口。如果您的应用程序要总是确保知道当前的日期和时间,以保证系统不被黑客窥探,这就显得尤其有用。

在通过TCP网络从远程主机上取回日期/时间字符串的时候要使用这个模块。

使用示例:

取得某一日期时间的信息
方法一
use POSIX qw(strftime);
localtime(time() - 24*60*60)
strftime "%Y%m%d%k%M%S",localtime $^T;

方法二
use 5.010;
use POSIX qw(strftime);
# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;

my $yesterday = localtime() - ONE_DAY;
say $yesterday->strftime('%b %d %Y');


方法三(单行用法)
perl -e 'use POSIX qw(strftime); print strftime("%Y-%m-%d", localtime(time()-3600*24*2));'

perl -MPOSIX -e 'print strftime("%Y-%m-%d %T\n", localtime(time()));'

perl -MTime::Piece -E '$t=localtime;say $t->datetime'

在Linux下使用time指令做简单的测试时,发现Time::Piece比POSIX要慢上5ms。Perl也跟Python一样,越升级越慢(功能多还是代码低效导致的?)。

方法四(列出指定目录的文件及其最后的访问与修改时间(日期))
die "Dir $ARGV[0] you gived is correct?" unless(-d $ARGV[0]);
chdir($ARGV[0]);

foreach my $f (glob('*')){
    next unless(-f $f);    #只对文件做检查
    my ($atime,$mtime)=map{
        my ($year,$month,$day)=(localtime($_))[5,4,3];
        $year+=1900;$month+=1;
        sprintf('%4d-%02d-%02d',$year,$month,$day);
    } (stat $f)[8,9];
    printf("%-39s Atime:%10s,Mtime:%10s\n",$f,$atime,$mtime);
}


#使用日期时间格式化函数来取代sprintf来对日期的处理

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

die "Dir $ARGV[0] you gived is correct?" unless(-d $ARGV[0]);
chdir($ARGV[0]);

foreach my $f (glob('*')){
    next unless(-f $f);
    my ($atime,$mtime)=map{
        strftime('%y-%m-%d',localtime($_));
    } (stat $f)[8,9];
    printf("%-39s Atime:%8s,Mtime:%8s\n",$f,$atime,$mtime);
}