Perl:获得本月的最后一个星期五

时间:2015-03-18 23:49:14

标签: perl date

我想要获得本月的最后一个星期五。我找到了可以完成这项工作的awtome awk脚本。我尝试将其移植到perl但面临一些问题。任何见解都会有很大的帮助。我不能使用除内置模块之外的任何perl模块,这就是为什么我必须完成构建这些东西。

感谢您的帮助。

AWK脚本:

BEGIN {
split("31,28,31,30,31,30,31,31,30,31,30,31",daynum_array,",") # days per month in non leap year
year = ARGV[1]
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {
  daynum_array[2] = 29
}
y = year - 1
k = 44 + y + int(y/4) + int(6*(y/100)) + int(y/400)
for (m=1; m<=12; m++) {
  k += daynum_array[m]
  d = daynum_array[m] - (k%7)
  printf("%04d-%02d-%02d\n",year,m,d)
}
exit(0)
}

我的Perl脚本:

my @time            = localtime;
my ($month, $year)  = @time[4, 5];
$year              += 1900;
@months             = qw( 31 28 31 30 31 30 31 31 30 31 30 31 );
$months[1]          = check_leap_year($year) ? 29 : 28;
$y  = $year - 1;
$k  = 44 + $y + int($y / 4) + int(6 * ($y / 100)) + int($y / 400);
$k += $months[$month];
$d  = $months[$month] - ($k % 7);
$month += 1;
printf "%04d-%02d-%02d\n", $year, $month, $d;

sub check_leap_year {
    my $year = shift;
    return 0 if $year % 4;
    return 1 if $year % 100;
    return 0 if $year % 400;
    return 1;
}

4 个答案:

答案 0 :(得分:6)

有几种方法可以做到这一点。使用Time::Piece并不是最简单的,它不是为日期数学设计的,但您不必安装其他软件。

use v5.10;
use strict;
use warnings;

use Time::Piece;

sub get_last_dow_in_month {
    my($year, $month, $dow) = @_;

    # Get a Time::Piece object at the last day of the month.
    my $first_of_the_month = Time::Piece->strptime("$year $month", "%Y %m");
    my $last_day = $first_of_the_month->month_last_day;
    my $last_of_the_month  = Time::Piece->strptime("$year $month $last_day", "%Y %m %d");

    # Figure out how many days you need to go back.
    my $days_offset = -(($last_of_the_month->day_of_week + (7 - $dow)) % 7);

    return $last_of_the_month->mday + $days_offset;
}

say get_last_dow_in_month(2014, 3, 5);

如果您需要进行更多日期处理,DateTime是最全面的。

答案 1 :(得分:5)

使用模块。 Calc last friday of month on PerlMonks包含一些示例。

E.g。

$ perl -MDate::Manip -E 'say UnixDate(ParseDate("last Friday in March 2015"),"Last Friday of the month is %B %E, %Y.")

Last Friday of the month is March 27th, 2015.

不是解决技术限制,而是需要解决妨碍工作技术方面的社会限制。

答案 2 :(得分:0)

这只是为了变化。正如@Schwern所说,&#34; cal是一个聪明的黑客,但它是一个拐杖,让OP避免学习使用一个好的约会库。日历很难,使用图书馆。&#34;

假设您的系统上有cal

#!/usr/bin/env perl

use strict;
use warnings;

local $ENV{LC_ALL} = 'C';

my @cal = `cal`;
my $last_friday;

for (my $i = $#cal; $i >= 0; $i -= 1) {
    my @dates = split ' ', $cal[$i];
    next unless @dates > 5;
    $last_friday = $dates[5];
    last;
}

print "$last_friday\n";

或者,更简洁,但效率更低:

#!/usr/bin/env perl

use strict;
use warnings;

local $ENV{LC_ALL} = 'C';

my ($last_friday) = grep defined, map +(split)[5], reverse `cal`;

print "$last_friday\n";

或者,甚至,

#!/usr/bin/env perl

use strict;
use warnings;

use List::Util qw( first );

local $ENV{LC_ALL} = 'C';

my $last_friday = first { defined } map +(split)[5], reverse `cal`;

print "$last_friday\n";

答案 3 :(得分:0)

如果仅限于使用核心模块,这是计算它的方法:

use strict;
use warnings;

use Time::Local qw[];

# Computes the last day in the given month which occurs on the given 
# day of the week. Returns the day of the month [22, 31].
# 1 <= $month <= 12
# 0 <= $dow <= 6 (0=Sunday)
sub last_dow_in_month {
    my ($year, $month, $dow) = @_;

    $year  += int($month / 12);
    $month %= 12;

    my $time = Time::Local::timegm(0, 0, 0, 1, $month, $year) - 86400;
    my ($mday, $wday) = (gmtime($time))[3,6];
    return $mday - ($wday - $dow) % 7;
}

my $year = 2015;
foreach my $month (1..12) {
    printf "%.4d-%.2d-%.2d\n", 
      $year, $month, last_dow_in_month($year, $month, 5);
}

输出:

2015-01-30
2015-02-27
2015-03-27
2015-04-24
2015-05-29
2015-06-26
2015-07-31
2015-08-28
2015-09-25
2015-10-30
2015-11-27
2015-12-25

使用DateTime代码变得更具可读性:

use DateTime;

# Computes the last day in the given month which occurs on the given 
# day of the week. Returns the day of the month [22, 31].
# 1 <= $month <= 12
# 1 <= $dow <= 7 (1=Monday)
sub last_dow_in_month {
    my ($year, $month, $dow) = @_;

    my $dt = DateTime->last_day_of_month(year => $year, month => $month);
       $dt->subtract(days => ($dt->day_of_week - $dow) % 7);
    return $dt->day_of_month;
}

如果性能至关重要,可以使用Time::Moment来计算它:

use Time::Moment qw[];

# Computes the last day in the given month which occurs on the given 
# day of the week. Returns the day of the month [22, 31].
# 1 <= $month <= 12
# 1 <= $dow <= 7 (1=Monday)
sub last_dow_in_month {
    my ($year, $month, $dow) = @_;

    my $tm = Time::Moment->new(year => $year, month => $month)
                         ->plus_months(1)
                         ->minus_days(1);
    return $tm->minus_days(($tm->day_of_week - $dow) % 7)
              ->day_of_month;
}

正确实施您的预期算法:

# Computes the last friday in the given month. Returns the day of the 
# month [22, 31].
# 1 <= $month <= 12
sub last_friday_in_month {
    my ($year, $month) = @_;

    my $days = (
        [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365],
        [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366],
    )[($year % 4) == 0 && ($year % 100 != 0 || $year % 400 == 0)];

    my $y = $year - 1;
    my $k = 44 + $y + int($y/4) + int(6 * ($y/100)) + int($y/400);
    $k += $days->[$month];
    return $days->[$month] - $days->[$month - 1] - ($k % 7);
}