我怎么知道几周前的日期是什么?

时间:2011-07-19 17:33:38

标签: perl date

我试图根据我想要回去的周数来确定计算上一个日期的好方法。今天是2011年7月19日,所以如果我想回到5周,那么确定那个日期的最佳方法是什么?

9 个答案:

答案 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));