Skip to content

Perl 日期和时间

Perl 提供了内置函数用于基本的日期和时间操作。对于更复杂的任务,强烈推荐使用强大的模块,如 Time::Piece(Perl 5.10 起的核心模块)以及 DateTime(来自 CPAN)。

localtime() 函数是获取当前本地日期和时间的主要方式。不带参数时,它使用当前的系统时间。

在列表上下文(list context)中,localtime() 返回一个包含 9 个元素的列表:

# 元素 描述 范围
# -------------------------------------------------------
# $sec 秒 0-60 (60 用于闰秒)
# $min 分 0-59
# $hour 小时 0-23
# $mday 月份中的日期 1-31
# $mon 月份 0-11 (0 表示一月)
# $year 自 1900 年起的年份 (例如,2023 年是 123)
# $wday 星期几 0-6 (0 表示星期日)
# $yday 年份中的日期 0-365 (365 用于闰年)
# $isdst 夏令时标志 (如果夏令时生效则为真)

在列表上下文(list context)中使用 localtime() 的示例:

#!/usr/bin/env perl
use strict;
use warnings;
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
my ($sec, $min, $hour, $mday, $mon, $year_offset, $wday, $yday, $isdst) =
localtime();
my $year = 1900 + $year_offset;
print "Date: $days[$wday], $months[$mon] $mday, $year\n";
print "Time: $hour:$min:$sec\n";
print "Day of year: $yday\n";
print "DST in effect: ", ($isdst ? "Yes" : "No"), "\n";

执行时,可能会产生如下输出(取决于当前的日期/时间):

Date: Sat, Feb 16, 2024
Time: 14:30:55
Day of year: 46
DST in effect: No

在标量上下文(scalar context)中,localtime() 返回一个人类可读的字符串:

#!/usr/bin/env perl
use strict;
use warnings;
my $datestring = localtime();
print "Current local date and time: $datestring\n";

输出:

Current local date and time: Sat Feb 16 14:30:55 2024

gmtime() 函数的功能与 localtime() 完全相同,但它返回的时间组件是格林威治平均时间 (GMT) 或协调世界时 (UTC) 时区本地化的。gmtime() 在列表上下文(list context)中的 $isdst 元素始终为 0,因为 GMT/UTC 不遵守夏令时。

#!/usr/bin/env perl
use strict;
use warnings;
my $gmt_datestring = gmtime();
print "Current GMT/UTC date and time: $gmt_datestring\n";
my (undef, undef, undef, $mday, $mon, $year_offset) = gmtime();
my $year = 1900 + $year_offset;
printf "GMT Date: %04d-%02d-%02d\n", $year, $mon + 1, $mday;

输出(假设本地时间为美国中部时间,比 UTC 晚 6 小时):

Current GMT/UTC date and time: Sat Feb 16 20:30:55 2024
GMT Date: 2024-02-16

您可以结合 printf 或 sprintf 使用 localtime() 或 gmtime() 返回的元素进行自定义格式化:

#!/usr/bin/env perl
use strict;
use warnings;
my ($sec, $min, $hour, $mday, $mon, $year_offset) = localtime();
my $year = 1900 + $year_offset;
$mon += 1; # 将月份调整为 1-12
printf("Formatted Time (HH:MM:SS): %02d:%02d:%02d\n", $hour, $min, $sec);
printf("Formatted Date (YYYY-MM-DD): %04d-%02d-%02d\n", $year, $mon, $mday);

输出:

Formatted Time (HH:MM:SS): 14:30:55
Formatted Date (YYYY-MM-DD): 2024-02-16

time() 函数返回自 Unix 纪元(UTC 1970 年 1 月 1 日 00:00:00)以来经过的秒数。这通常被称为 Unix 时间戳。

#!/usr/bin/env perl
use strict;
use warnings;
my $epoch_seconds = time();
print "Seconds since Jan 1, 1970 UTC: $epoch_seconds\n";
# 您可以将纪元时间戳传递给 localtime() 或 gmtime()
my $one_day_ago_epoch = $epoch_seconds - (24 * 60 * 60);
my $datestring_one_day_ago = localtime($one_day_ago_epoch);
print "One day ago: $datestring_one_day_ago\n";

输出:

Seconds since Jan 1, 1970 UTC: 1708093855
One day ago: Fri Feb 15 14:30:55 2024

为了更灵活的格式化,POSIX 模块提供了 strftime() 函数。它根据一个格式字符串格式化日期和时间,类似于同名的 C 标准库函数。

strftime(format, sec, min, hour, mday, mon, year, wday (可选), yday (可选), isdst (可选))

常用的 strftime() 格式说明符(与区域设置相关的用 * 标记):

说明符描述示例(针对 Sat Feb 16 14:30:55 2024 CST)
%a缩写星期名称 *Sat
%A完整星期名称 *Saturday
%b缩写月份名称 *Feb
%B完整月份名称 *February
%c区域设置对应的日期和时间表示 *Sat Feb 16 14:30:55 2024
%C世纪(年份除以 100 并截断为整数)20
%d月份中的日期,零填充 (01-31)16
%D简短的月/日/年格式,等同于 %m/%d/%y02/16/24
%e月份中的日期,空格填充 ( 1-31)16
%FISO 8601 日期格式 (YYYY-MM-DD),等同于 %Y-%m-%d2024-02-16
%g基于周的年份,最后两位数字 (00-99)07
%G基于周的年份 (例如,2024)2024
%h缩写月份名称 * (与 %b 相同)Feb
%H24 小时制的小时 (00-23)14
%I12 小时制的小时 (01-12)02
%j年份中的日期,零填充 (001-366)047
%m月份的十进制表示,零填充 (01-12)02
%M分钟,零填充 (00-59)30
%n换行符\n
%p12 小时制的 AM 或 PM 标记 *PM
%r12 小时制时间 * (例如,hh:mm:ss AM/PM)02:30:55 PM
%R24 小时制 HH:MM 时间,等同于 %H:%M14:30
%S秒,零填充 (00-60)55
%t水平制表符\t
%TISO 8601 时间格式 (HH:MM:SS),等同于 %H:%M:%S14:30:55
%uISO 8601 星期几编号 (星期一=1, 星期日=7)6
%U年份中的周数(以星期日作为一周的第一天)(00-53)07
%VISO 8601 年份中的周数(以星期一作为一周的第一天)(01-53)07
%w星期几的十进制表示 (星期日=0, 星期六=6)6
%W年份中的周数(以星期一作为一周的第一天)(00-53)07
%x区域设置对应的日期表示 *02/16/24
%X区域设置对应的时间表示 *14:30:55
%y年份,后两位数字 (00-99)24
%Y年份 (例如,2024)2024
%zISO 8601 时区与 UTC 的偏移量 (例如,+hhmm 或 -hhmm)-0600
%Z时区名称或缩写 *CST
%%字面量 % 字符%

使用 POSIX::strftime() 的示例:

#!/usr/bin/env perl
use strict;
use warnings;
use POSIX qw(strftime);
my @time_parts = localtime(); # 获取当前的本地时间组件
# 注意:strftime 期望年份为 (当前年份 - 1900)
# 并且月份为 0-11,localtime() 直接提供这些值。
my $formatted_string = strftime "%Y-%m-%d %H:%M:%S %Z (%A)", @time_parts;
print "Formatted local time: $formatted_string\n";
my @gmt_time_parts = gmtime();
my $gmt_formatted_string = strftime "%Y-%m-%dT%H:%M:%SZ (%A)", @gmt_time_parts;
print "Formatted GMT/UTC time: $gmt_formatted_string\n";

输出:

Formatted local time: 2024-02-16 14:30:55 CST (Saturday)
Formatted GMT/UTC time: 2024-02-16T20:30:55Z (Saturday)

自 Perl 5.10 起,Time::Piece 已成为一个核心模块,提供了一种面向对象的日期和时间操作方法,通常比原始的 localtime/gmtime 和 strftime 更易于使用。

#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
use Time::Seconds; # 用于像 ONE_DAY 这样的常量
my $t = localtime(); # 为当前本地时间创建一个 Time::Piece 对象
print "Current time: ", $t->datetime, "\n"; # YYYY-MM-DDTHH:MM:SS
print "Formatted: ", $t->strftime("%A, %B %d, %Y, %I:%M %p"), "\n";
print "Epoch seconds: ", $t->epoch, "\n";
my $tomorrow = $t + ONE_DAY;
print "Tomorrow: ", $tomorrow->date, "\n"; # YYYY-MM-DD
my $custom_time = Time::Piece->strptime("2023-07-15 10:00:00", "%Y-%m-%d %H:%M:%S");
print "Custom time: ", $custom_time->strftime("%c"), "\n";

对于更高级的日期/时间操作,包括复杂的时区处理、日期算术、解析各种格式以及国际化,来自 CPAN 的 DateTime 模块套件是 Perl 社区事实上的标准。它功能非常强大且全面。

#!/usr/bin/env perl
use strict;
use warnings;
use DateTime;
my $dt = DateTime->now(time_zone => 'local'); # 当前本地时间
print "DateTime now: ", $dt->iso8601, " (", $dt->time_zone_long_name, ")\n";
my $dt_ny = DateTime->now(time_zone => 'America/New_York');
print "New York time: ", $dt_ny->strftime('%Y-%m-%d %H:%M:%S %Z'), "\n";
$dt->add(days => 5, hours => 3);
print "After adding 5 days, 3 hours: ", $dt->datetime, "\n";
# 需要使用 'cpanm DateTime' 或类似命令进行安装。

建议:对于新项目,考虑到更高的清晰度、健壮性和更丰富的功能集,对于简单的任务优先使用 Time::Piece,对于更复杂的需求使用 DateTime,而非使用较旧的过程式函数。