我试图根据我想要回去的周数来确定计算上一个日期的好方法。今天是2011年7月19日,所以如果我想回到5周,那么确定那个日期的最佳方法是什么?
答案 0 :(得分:8)
DateTime::Duration
是你的朋友:
use strict;
use warnings;
use 5.010;
use DateTime;
my $now = DateTime->now(time_zone => 'local');
my $five_weeks = DateTime::Duration->new(weeks => 5);
my $five_weeks_ago = $now - $five_weeks;
say "Five weeks ago now it was $five_weeks_ago";
请注意,它允许您以问题为单位指定持续时间。
答案 1 :(得分:3)
Perl有一个叫做正则表达式的奇妙东西,几乎可以解决任何问题。
use strict;
use warnings;
my $date = shift || '7/19/2011';
my $days_ago = shift || 7*5;
$date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)\z#@{[sprintf"%.2d",$1]}/@{[sprintf"%.2d",$2]}/$3/$days_ago#;
until ( $date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)/0\z#@{[$1+0]}/@{[$2+0]}/$3# ) {
$date =~ s#([0-9]+)/([0-9]+)/([0-9]+)/([0-9]+)#@{[$2==1?sprintf"%.2d",$1-1||12:$1]}/@{[sprintf"%.2d",$2-1||31]}/@{[$1==1 && $2==1?$3-1:$3]}/@{[$4-1]}#;
$date =~ s#([0-9]+)\z#@{[$1+1]}# unless $date =~ m#^(?:0[1-9]|1[012])/(?:0[1-9]|1[0-9]|2[0-8]|(?<!0[2469]/|11/)31|(?<!02/)30|(?<!02/(?=...(?:..(?:[02468][1235679]|[13579][01345789])|(?:[02468][1235679]|[13579][01345789])00)))29)/#;
}
print $date, "\n";
(请不要这样做。)
答案 2 :(得分:1)
最好还是最简单?我总是发现strftime
的日期规范化对于这类事情很方便:
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw/strftime/;
my @date = localtime;
print strftime "today is %Y-%m-%d\n", @date;
$date[3] -= 5 * 7;
print strftime "five weeks ago was %Y-%m-%d\n", @date;
哪种解决方案最好取决于您在完成日期后要对日期做什么。以下是各种方法实现的基准:
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark;
use Date::Manip qw/UnixDate/;
use Date::Simple qw/today/;
use Date::Calc qw/Add_Delta_Days Today/;
use DateTime;
use POSIX qw/strftime/;
use Class::Date;
my %subs = (
cd => sub {
(Class::Date::now - [0, 0, 5 * 7])->strftime("%Y-%m-%d");
},
dc => sub {
sprintf "%d-%02s-%02d", Add_Delta_Days Today, -5 * 7;
},
dm => sub {
UnixDate("5 weeks ago", "%Y-%m-%d");
},
ds => sub {
(today() - 5 * 7)->strftime("%Y-%m-%d");
},
dt => sub {
my $now = DateTime->from_epoch(epoch => time, time_zone => "local");
my $five_weeks = DateTime::Duration->new(weeks => 5);
($now - $five_weeks)->ymd('-');
},
p => sub {
my @date = localtime;
$date[3] -= 5 * 7;
strftime "%Y-%m-%d", @date;
},
y => sub {
my ($d, $m, $y) = (localtime)[3..5];
my $date = join "/", $m+1, $d, $y+1900;
my $days_ago = 7*5;
$date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)\z#@{[sprintf"%.2d",$1]}/@{[sprintf"%.2d",$2]}/$3/$days_ago#;
until ( $date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)/0\z#@{[$1+0]}/@{[$2+0]}/$3# ) {
$date =~ s#([0-9]+)/([0-9]+)/([0-9]+)/([0-9]+)#@{[$2==1?sprintf"%.2d",$1-1||12:$1]}/@{[sprintf"%.2d",$2-1||31]}/@{[$1==1 && $2==1?$3-1:$3]}/@{[$4-1]}#;
$date =~ s#([0-9]+)\z#@{[$1+1]}# unless $date =~ m#^(?:0[1-9]|1[012])/(?:0[1-9]|1[0-9]|2[0-8]|(?<!0[2469]/|11/)31|(?<!02/)30|(?<!02/(?=...(?:..(?:[02468][1235679]|[13579][01345789])|(?:[02468][1235679]|[13579][01345789])00)))29)/#;
}
return $date;
},
);
print "$_: ", $subs{$_}(), "\n" for keys %subs;
Benchmark::cmpthese -1, \%subs;
以下是结果。 strftime
方法似乎是最快的,但它的功能也最少。
y: 6/14/2011
dm: 2011-06-14
p: 2011-06-14
dc: 2011-06-14
cd: 2011-06-14
dt: 2011-06-15
ds: 2011-06-14
Rate dt dm y ds cd dc p
dt 1345/s -- -5% -28% -77% -82% -96% -98%
dm 1408/s 5% -- -24% -75% -81% -96% -98%
y 1862/s 38% 32% -- -68% -75% -95% -97%
ds 5743/s 327% 308% 208% -- -24% -84% -90%
cd 7529/s 460% 435% 304% 31% -- -78% -87%
dc 34909/s 2495% 2378% 1775% 508% 364% -- -39%
p 56775/s 4121% 3931% 2949% 889% 654% 63% --
比基准测试更好的是测试它们如何处理DST(这个测试会在关于DateTime->now
返回的假设中发现错误。
#!/usr/bin/perl
use strict;
use warnings;
use Time::Mock;
use Date::Manip qw/UnixDate/;
use Date::Simple qw/today/;
use Date::Calc qw/Add_Delta_Days Today/;
use DateTime;
use POSIX qw/strftime mktime/;
use Class::Date;
sub target {
my @date = localtime;
$date[3] -= 5 * 7;
strftime "%Y-%m-%d", @date;
}
my %subs = (
cd => sub {
(Class::Date::now - [0, 0, 5 * 7])->strftime("%Y-%m-%d");
},
dc => sub { sprintf "%d-%02s-%02d", Add_Delta_Days Today, -5 * 7;
},
dm => sub {
UnixDate("5 weeks ago", "%Y-%m-%d");
},
ds => sub {
(today() - 5 * 7)->strftime("%Y-%m-%d");
},
dt => sub {
my $now = DateTime->from_epoch( epoch => time, time_zone => 'local' );
my $five_weeks = DateTime::Duration->new(weeks => 5);
($now - $five_weeks)->ymd('-');
},
y => sub {
my ($d, $m, $y) = (localtime)[3..5];
my $date = join "/", $m+1, $d, $y+1900;
my $days_ago = 7*5;
$date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)\z#@{[sprintf"%.2d",$1]}/@{[sprintf"%.2d",$2]}/$3/$days_ago#;
until ( $date =~ s#^([0-9]+)/([0-9]+)/([0-9]+)/0\z#@{[$1+0]}/@{[$2+0]}/$3# ) {
$date =~ s#([0-9]+)/([0-9]+)/([0-9]+)/([0-9]+)#@{[$2==1?sprintf"%.2d",$1-1||12:$1]}/@{[sprintf"%.2d",$2-1||31]}/@{[$1==1 && $2==1?$3-1:$3]}/@{[$4-1]}#;
$date =~ s#([0-9]+)\z#@{[$1+1]}# unless $date =~ m#^(?:0[1-9]|1[012])/(?:0[1-9]|1[0-9]|2[0-8]|(?<!0[2469]/|11/)31|(?<!02/)30|(?<!02/(?=...(?:..(?:[02468][1235679]|[13579][01345789])|(?:[02468][1235679]|[13579][01345789])00)))29)/#;
}
return join "-", map { sprintf "%02d", $_ }
(split "/", $date)[2,0,1];
},
);
my $time = mktime 0, 0, 0, 13, 2, 111; #2011-03-13 00:00:00, DST in US
for my $offset (map { $_ * 60 * 60 } 1 .. 24) {
print strftime "%Y-%m-%d %H:%M:%S\n", (localtime $time + $offset);
Time::Mock->set($time + $offset);
my $target = target;
for my $sub (sort keys %subs) {
my $result = $subs{$sub}();
if ($result ne $target) {
print "$sub disagrees: ",
"time $time target $target result $result\n";
}
}
}
答案 3 :(得分:1)
我喜欢Date::Calc
use strict;
use warnings;
use Date::Calc qw/Add_Delta_Days Today/;
my $offset_weeks = -5;
my $offset_days = $offset_weeks * 7;
# Year, Month, Day
my @delta_date = Add_Delta_Days(
Today( [ localtime ] ),
$offset_days
);
printf "%2d/%2d/%4d\n", @delta_date[1,2,0];
它旨在捕捉闰年等常见陷阱。
答案 4 :(得分:0)
使用Time::Piece:
use Time::Piece;
use Time::Seconds qw(ONE_DAY);
my $weeks_back = 5;
my $date_str = '7/19/2011';
my $dt = Time::Piece->strptime($date_str, '%m/%d/%Y');
# Avoid DST issues
$dt -= ONE_DAY() * ( 7 * $weeks_back - 0.5 )
my $past_str = $dt->strftime('%m/%d/%Y');
print "$past_str\n";
答案 5 :(得分:0)
这么简单问题的代码太多了!您只需要两个简单的界限:
my $five_weeks_ago = time - (5*7)*24*60*60;
print scalar localtime($five_weeks_ago), "\n";
我的解决方案对DST和闰年都是准确的。
答案 6 :(得分:0)
以下是获取5周日期的方法:
$ uname
HP-UX
$ date
Wed Nov 11 09:42:05 CST 2015
$ perl -e 'my ($d,$m,$y) = (localtime(time-60*60*24*(5*7)))[3,4,5]; printf("%d/%d/%d\n", $m+1, $d, $y+1900);'
10/7/2015
答案 7 :(得分:-1)
say POSIX::strftime(
'%m/%d/%Y' # format string -> mm/dd/YYYY
, 0 # no seconds
, 0 # no minutes
, 0 # no hours
, 19 - ( 5 * 7 ) # current day - numweeks * 7
, 7 - 1 # month - 1
, 2011 - 1900 # YYYY year - 1900
);
是,这一天是19 - 35 = -16
,是可行。
答案 8 :(得分:-2)
如果日期可用作unix时间戳,则可以使用简单的算术完成:
use POSIX qw/strftime/;
say strftime('%Y-%m-%d', localtime(time - 5 * 7 * 86400));