Perl - 如何在不使用DateTime的情况下从给定日期获取上周三的日期

时间:2015-08-04 08:37:18

标签: perl date unix

所有

我想知道从给定日期开始的上周三的日期。

EG。我的日期是" 20150804"我需要" 20150729"。

DateTime不可用,我也无法安装。

我查看了一些示例,但他们使用的是DateTime。

请你重新定位我可以获得帮助的地方。感谢。

我打算编写类似下面的代码。

代码:

#!/opt/perl-5.8.0/bin/perl

use warnings;
use strict;

my $dt="20150804";
my $prevWednesday=getPrevWednesday($dt);

sub getPrevWednesday()
{
    my $givenDt=shift;
    ...
}

4 个答案:

答案 0 :(得分:5)

另一种蛮力方法,这次使用另一个核心模块Time::Local

#!/usr/bin/perl
use warnings;
use strict;

use Time::Local;

sub prev_wednesday {
    my $date = shift;
    my ($year, $month, $day) = $date =~ /(....)(..)(..)/;
    my $time = timelocal(0, 0, 12, $day, $month - 1, $year);
    do { $time -= 60 * 60 * 24 } until (localtime $time)[6] == 3; # <- Wednesday
    my ($y, $m, $d) = (localtime $time)[5, 4, 3];
    return sprintf "%4d%02d%02d\n", 1900 + $y, $m + 1, $d;
}

print $_, ' ', prev_wednesday($_), for qw( 20150804 20150805 20150806
                                           20150101 20000301 20010301 );

答案 1 :(得分:2)

总有蛮力的方法。

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use POSIX 'strftime';

my $ONE_DAY = 24 * 60 * 60;

# Get now
my $time = time;

# Subtract days until you get to a Wednesday
do {
  $time -= $ONE_DAY;
} until (localtime($time))[6] == 3;

# Format
say strftime '%Y%m%d', localtime $time;

但是如果您在Perl环境中工作,无法从CPAN安装模块,那么删除该限制总是值得的。现代Perl编程通常是将正确的CPAN模块系列连接在一起的情况。如果你无法访问CPAN,那么你的生活就会比你需要的更加努力。

如果你真的无法解除限制,那么找另一份工作。与那些施加这种毫无意义的限制的人打交道并不值得。

更新:刚刚注意到您还使用了Perl的史前版本。您需要删除use 5.010并将say替换为print。并刷新你的简历: - /

更新2: choroba的解决方案更好。它以正确的格式处理任何日期。我只是处理当前日期。关于修复工作环境的建议仍然存在。

答案 2 :(得分:2)

使用Time::Piece

use feature qw(say);
use strict;
use warnings;

use Time::Piece;
use Time::Seconds;
my $str = '20150804';
my $fmt = '%Y%m%d';
my $t = Time::Piece->strptime($str, $fmt);
do {
    $t = $t - ONE_DAY;
} until ( $t->day eq 'Wed');
say $t->strftime($fmt);

答案 3 :(得分:2)

这是一个更优雅的解决方案,不会做暴力。

use strict;
use warnings;
use Time::Local 'timelocal';
use POSIX 'strftime';

my $dt = "20150804";
say getPrevWednesday($dt);

# note you do not want () here, 
# see http://perldoc.perl.org/perlsub.html#Prototypes
sub getPrevWednesday {
    my $givenDt = shift;

    # parse the string into a unix timestamp
    my ( $year, $month, $day ) = $givenDt =~ /(....)(..)(..)/;
    my $timestamp = timelocal( 0, 0, 12, $day, $month - 1, $year );

    # get the day of week, ignore the rest
    my ( undef, undef, undef, undef, undef, undef, $wday ) =
        localtime $timestamp;

    # because we start the week with Sunday on day 0
    # and to get to the previous Wednesday from Sunday it's
    # 4 days (Wednesday is 3) we can add 4 to the
    # number of this day, divide by 7, take the leftover (modulo)
    # and then subtract that many days
    # (86_400 is one day in seconds)

    #       v- -6 ------
    #                           6 % 7 = 6
    #                   +4 -----v
    #                   v
    # 0 1 2 3 4 5 6 0 1 2 3 4 5 6
    # S M T W T F S S M T W T F S
    my $prev_wed = $timestamp - ( ( $wday + 4 ) % 7 * 86_400 );

    # go one week back if we got the same day
    $prev_wed -= ( 7 * 86_400 ) if $prev_wed == $timestamp;

    # debug output
    warn "in: " . localtime($timestamp) . "\n";
    warn "out: " . localtime($prev_wed) . "\n\n";

    # put it back into your format
    return strftime('%Y%m%d', localtime $timestamp);
}

输出:

# STDOUT
20150804

# STDERR
in: Tue Aug  4 12:00:00 2015
out: Wed Jul 29 12:00:00 2015