所以我想使用函数localtime()
,但我在上个月的第一天和最后一天遇到问题。现在我有工作功能,但我敢打赌有更好的方法可以解决这个问题。
use Time::Piece;
use Time::Seconds;
$start_of_month = localtime();
while($start_of_month->mday < 10) {
$start_of_month += ONE_DAY;
}
$start_of_month -= ONE_MONTH; # Subtract one month to get previous month. "ONE_MONTH" is defined by Time::Seconds
$end_of_month = $start_of_month; # Copy start_of_month to end_of_month as they both have same year and month.
# Subtract day from $start_of_month until mday is the first day of the month.
while($start_of_month->mday != 1) {
$start_of_month -= ONE_DAY;
}
# Silly workaround to bring $end_of_month to last day of the month as Time::Piece object does not have good way to change mday.
while($end_of_month->mday != $start_of_month->month_last_day) {
$end_of_month += ONE_DAY;
}
$period_start = $start_of_month->dmy("."); # End result has to be same!
有人能给我一个更好的处理方法吗?
答案 0 :(得分:6)
以下是我建议使用Time :: Piece和Time :: Seconds简化OP的日期操作。如果这对重载运算符起作用,它实际上可能按预期工作,但正如ikegami所指出的,这不能保证。所以我在下面测试过。
如果你想走数学路线,至少你可以简化一些事情:
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $date_start = localtime();
$date_start -= ONE_MONTH;
$date_start -= ($date_start->mday - 1) * ONE_DAY;
my $date_end = $date_start + ($date_start->month_last_day - 1) * ONE_DAY;
print $date_start , "\n"; # Prints Mar 1st (at least today it does)
print $date_end , "\n"; # Prints Mar 31st
我会考虑使用其他模块,例如Date::Calc
或DateTime
,但这可能适用于您的目的。
我创建了一个脚本,下面的脚本循环显示从1月15日开始的一年中的每个日期,显示上述代码无法按预期工作的每个范围。
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $t = 1389812400; # Wed 2014-Jan-15 7pm GMT. 11am PST
my $t_max = 1421348400; # Thu 2015-Jan-15 7pm GMT. 11am PST
my %prev_month = map {$_ => ($_ - 1) || 12} (1..12);
my $fail = '';
while ($t <= $t_max) {
$t += 60; # Increment by 1 minute
# Testing potentially overloaded math of Time::Piece & Time::Seconds
my $start = my $src = localtime($t);
$start -= ONE_MONTH;
$start -= ($start->mday - 1) * ONE_DAY;
if ($start->mon != $prev_month{$src->mon}) {
print "From ($t) $src -> $start\n" if !$fail;
$fail = " To ($t) $src -> $start\n\n";
} elsif ($fail) {
print $fail;
$fail = '';
}
}
以下是此脚本的输出,其中插入了注释以解释每个范围失败的原因:
# ONE_MONTH is exactly 2_629_744 seconds, or 30.437 days.
# ONE_MONTH is too short for January
From (1391193000) Fri Jan 31 10:30:00 2014 -> Wed Jan 1 00:00:56 2014
To (1391241540) Fri Jan 31 23:59:00 2014 -> Wed Jan 1 13:29:56 2014
# ONE_MONTH is too long for February
From (1393660800) Sat Mar 1 00:00:00 2014 -> Wed Jan 1 13:30:56 2014
To (1393871340) Mon Mar 3 10:29:00 2014 -> Wed Jan 1 23:59:56 2014
# ONE_MONTH is too short for March
From (1396290600) Mon Mar 31 11:30:00 2014 -> Sat Mar 1 00:00:56 2014
To (1396335540) Mon Mar 31 23:59:00 2014 -> Sat Mar 1 12:29:56 2014
# ONE_DAY is 86_400 seconds, or 24 hours.
# March 9th is only 23 hours long due to DST, ONE_DAY goes to far.
From (1397064600) Wed Apr 9 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397068140) Wed Apr 9 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397151000) Thu Apr 10 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397154540) Thu Apr 10 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397237400) Fri Apr 11 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397240940) Fri Apr 11 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397323800) Sat Apr 12 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397327340) Sat Apr 12 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397410200) Sun Apr 13 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397413740) Sun Apr 13 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397496600) Mon Apr 14 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397500140) Mon Apr 14 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397583000) Tue Apr 15 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397586540) Tue Apr 15 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397669400) Wed Apr 16 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397672940) Wed Apr 16 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397755800) Thu Apr 17 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397759340) Thu Apr 17 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397842200) Fri Apr 18 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397845740) Fri Apr 18 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1397928600) Sat Apr 19 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1397932140) Sat Apr 19 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398015000) Sun Apr 20 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398018540) Sun Apr 20 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398101400) Mon Apr 21 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398104940) Mon Apr 21 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398187800) Tue Apr 22 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398191340) Tue Apr 22 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398274200) Wed Apr 23 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398277740) Wed Apr 23 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398360600) Thu Apr 24 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398364140) Thu Apr 24 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398447000) Fri Apr 25 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398450540) Fri Apr 25 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398533400) Sat Apr 26 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398536940) Sat Apr 26 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398619800) Sun Apr 27 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398623340) Sun Apr 27 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398706200) Mon Apr 28 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398709740) Mon Apr 28 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398792600) Tue Apr 29 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398796140) Tue Apr 29 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
From (1398879000) Wed Apr 30 10:30:00 2014 -> Fri Feb 28 23:00:56 2014
To (1398882540) Wed Apr 30 11:29:00 2014 -> Fri Feb 28 23:59:56 2014
# ONE_MONTH is too long for April
From (1398927600) Thu May 1 00:00:00 2014 -> Sat Mar 1 12:30:56 2014
To (1398965340) Thu May 1 10:29:00 2014 -> Sat Mar 1 22:59:56 2014
# ONE_MONTH is too short for May
From (1401557400) Sat May 31 10:30:00 2014 -> Thu May 1 00:00:56 2014
To (1401605940) Sat May 31 23:59:00 2014 -> Thu May 1 13:29:56 2014
# ONE_MONTH is too long for June
From (1404198000) Tue Jul 1 00:00:00 2014 -> Thu May 1 13:30:56 2014
To (1404235740) Tue Jul 1 10:29:00 2014 -> Thu May 1 23:59:56 2014
# ONE_MONTH is too short for July
From (1406827800) Thu Jul 31 10:30:00 2014 -> Tue Jul 1 00:00:56 2014
To (1406876340) Thu Jul 31 23:59:00 2014 -> Tue Jul 1 13:29:56 2014
# ONE_MONTH is too short for August
From (1409506200) Sun Aug 31 10:30:00 2014 -> Fri Aug 1 00:00:56 2014
To (1409554740) Sun Aug 31 23:59:00 2014 -> Fri Aug 1 13:29:56 2014
# ONE_MONTH is too long for September
From (1412146800) Wed Oct 1 00:00:00 2014 -> Fri Aug 1 13:30:56 2014
To (1412184540) Wed Oct 1 10:29:00 2014 -> Fri Aug 1 23:59:56 2014
# ONE_MONTH is too short for October
From (1414776600) Fri Oct 31 10:30:00 2014 -> Wed Oct 1 00:00:56 2014
To (1414825140) Fri Oct 31 23:59:00 2014 -> Wed Oct 1 13:29:56 2014
# ONE_MONTH is too long for November
From (1417420800) Mon Dec 1 00:00:00 2014 -> Wed Oct 1 14:30:56 2014
To (1417454940) Mon Dec 1 09:29:00 2014 -> Wed Oct 1 23:59:56 2014
# ONE_MONTH is too short for December
From (1420050600) Wed Dec 31 10:30:00 2014 -> Mon Dec 1 00:00:56 2014
To (1420099140) Wed Dec 31 23:59:00 2014 -> Mon Dec 1 13:29:56 2014
Time::Piece
有两个函数,add_months
和add_years
用于日期计算。不幸的是,文档说明:
请注意,在月末添加和减去月份时会出现一些“奇怪”的行为。通常,当结果月份比起始月份短时,则添加重叠天数。例如,从2008-03-31减去一个月将不会导致2008-02-31,因为这是一个不可能的日期。相反,你会得到2008-03-02。这似乎与其他日期操作工具一致。
以下代码完全演示了PST时区的这种行为。
use strict;
use warnings;
use Time::Piece;
my $t = localtime(1420012800);
print $t->add_months($_),"\n" for (0..12)
输出:
Wed Dec 31 00:00:00 2014
Sat Jan 31 00:00:00 2015
Tue Mar 3 00:00:00 2015
Tue Mar 31 00:00:00 2015
Fri May 1 00:00:00 2015
Sun May 31 00:00:00 2015
Wed Jul 1 00:00:00 2015
Fri Jul 31 00:00:00 2015
Mon Aug 31 00:00:00 2015
Thu Oct 1 00:00:00 2015
Sat Oct 31 00:00:00 2015
Tue Dec 1 00:00:00 2015
Thu Dec 31 00:00:00 2015
现在,这个功能如何能够准确地循环回到一年后的同一天,这很好,但是在那之前有很多不利的月份。
Time::Seconds
中的常量就是常量。它们是用于表示时间段的精确秒数。没有运算符重载以促进奇特的日期操作。相反,这些值仅适用于数学比较。
要操纵特定日期,我建议您使用Date::Calc
,DateTime
或此问题中建议的任何其他模块。
答案 1 :(得分:3)
少OO,但替代解决方案:
use POSIX qw(mktime);
my @now = localtime;
# mday = 1, month = month - 1
my $date_start = mktime(@now[0 .. 2], 1, $now[4] - 1, @now[5 .. 8]);
# mday = 0, the 0th day of this month == last day of prior month
my $date_end = mktime(@now[0 .. 2], 0, @now[4 .. 8]);
print scalar localtime $date_start, "\n";
print scalar localtime $date_end, "\n";
这种夏令时的打嗝;如果你不关心时间,你可以将$now[2]
设置为中午12点,并希望没有国家宣布夜晚是白天和黑夜:
my @now = (0, 0, 12, (localtime)[3..8]);
答案 2 :(得分:1)
使用Time::Seconds
的{{1}}定义为您提供一年一天的错误解决方案。它是一个月的平均长度,以秒为单位,相当于约30.4天。例如,从3月1日开始减去1月下旬的日期。
但是,上个月的第一天只能根据ONE_MONTH
返回的月和日字段计算。将其转换为localtime
对象可让我们计算同月的最后一天。
Time::Piece
<强>输出强>
use strict;
use warnings;
use Time::Piece;
my ($m, $y) = (localtime)[4,5];
$y += 1900;
if ($m == 0) {
$m = 11;
$y -= 1;
}
my $period_start = sprintf '%02d.%02d.%04d', 1, $m, $y;
my $period_end = do {
my $tp = Time::Piece->strptime($period_start, '%d.%m.%Y');
sprintf '%02d.%02d.%04d', $tp->month_last_day, $m, $y;
};
print $period_start, "\n";
print $period_end, "\n";
答案 3 :(得分:1)
我不相信使用Time :: Piece的代码。它依赖于重载操作符,使其看起来像你做错了($time += ONE_DAY;
看起来像你正在添加一个常量)当你真正做到这一点时。也许。这很难说。你必须熟悉模块的内容,才能知道你是否正确行事。
DateTime解决方案:
my $dt = DateTime
->now( time_zone => 'local' )
->set_time_zone('floating') # Do this when you want to date arithmetic.
->truncate( to => 'day' );
$dt->set( day => 1 )->subtract( days => 1 );
my $last = $dt->ymd('-');
$dt->set( day => 1 );
my $first = $dt->ymd('-');
say "$first .. $last";
答案 4 :(得分:0)
很抱歉,如果我不理解财产,但是:DaysInMonth(检查冥想)
my @monthDays= qw( 31 28 31 30 31 30 31 31 30 31 30 31 );
sub MonthDays {
my $month= shift(@_);
my $year= @_ ? shift(@_) : 1900+(localtime())[5];
if( $year <= 1752 ) {
# Note: Although September 1752 only had 19 days,
# they were numbered 1,2,14..30!
return 19 if 1752 == $year && 9 == $month;
return 29 if 2 == $month && 0 == $year % 4;
} else {
return 29 if 2 == $month and
0 == $year%4 && 0 != $year%100 || 0 == $year%400;
}
return $monthDays[$month-1];
}
如果您想要简单的数据到秒,请查看Time::Local
$time = timelocal( $sec, $min, $hour, $mday, $mon, $year );