Perl DateTime模块计算每月第一个、第二个、第三个、第四个、最后一个星期日、星期一、...、星期六。

3

我正在使用 Perl 的 DateTime 模块,想知道如何计算指定月份的第一、第二、第三、第四和最后一个星期日、星期一、……星期六。

我的方法:

从 i=1 到 DateTime->last_day_of_month( ... ) 运行循环。

将第 i 天赋值给 date($dt),并使用 $dt->day_of_week() 获取星期几。

使用计数器来跟踪第一、第二、第三、第四和最后一个日期。

如果星期几与所需日期匹配且计数器与所需间隔匹配,则中断循环。

你能否提供比上述更好(或更短)的方法? 感谢任何帮助。

2个回答

6

这是寻找上一个星期一(或任何指定的一周中的某一天)我的答案的简单修改。唯一的困难在于弄清楚你从哪个日期开始。

use DateTime;

# Here $nth is 1, 2, 3... for first, second, third, etc.
# Or -1, -2, -3... for last, next-to-last, etc.
# $dow is 1-7 for Monday-Sunday
# $month is 1-12
sub nth_day_of_month {
  my ($nth, $dow, $year, $month) = @_;

  my $date = ($nth > 0
              # For 1st etc. we want the last day of that week
              # (i.e. 7, 14, 21, 28 ...).  We have to use add because
              # the last week may extend into next month.
              ? DateTime->new(year => $year, month => $month, day => 1)
                        ->add( days => $nth * 7 - 1)
              # For last etc. we want the last day of the month
              # (minus a week if next-to-last, etc)
              : DateTime->last_day_of_month(year => $year, month => $month)
                        ->add( weeks => $nth + 1)); # $nth is negative

  # Back up to the first $dow on or before $date
  $date->subtract(days => ($date->day_of_week - $dow) % 7);

  # If we're not in the right month, then that month doesn't have the
  # specified date (e.g. there's no 5th Tuesday in Sept. 2013).
  return (($date->month == $month) ? $date : undef);
}

更新:这里有一个稍微更高效的版本。它使用相同的算法,但将addsubtract的调用合并在一起,因此只需要进行一次日期计算。

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

  my ($date, $delta);
  if ($nth > 0) {
    # For 1st etc. we want the last day of that week (i.e. 7, 14, 21, 28, "35")
    $date  = DateTime->new(year => $year, month => $month, day => 1);
    $delta = $nth * 7 - 1;
  } else {
    # For last etc. we want the last day of the month
    # (minus a week if next-to-last, etc)
    $date  = DateTime->last_day_of_month(year => $year, month => $month);
    $delta = 7 * ($nth + 1); # $nth is negative
  }

  # Back up to the first $dow on or before $date + $delta
  $date->add(days => $delta - ($date->day_of_week + $delta - $dow) % 7);

  # If we're not in the right month, then that month doesn't have the
  # specified date (e.g. there's no 5th Tuesday in Sept. 2013).
  return (($date->month == $month) ? $date : undef);
}

非常感谢。+1给评论形式的解释。 - Ritesh Kumar Gupta

1
另一个选择是利用模块Date::Manip
use strict;
use warnings;
use Date::Manip::Base;

my $dmb = new Date::Manip::Base;

# First Tuesday in October 2013
my $year  = 2013;
my $Nth   = 1;      # 1 = first
my $dow   = 2;      # 2 = Tuesday (day of week)
my $month = 10;     # October

# $ymd is an array reference
my $ymd = $dmb->nth_day_of_week( $year, $Nth, $dow, $month );

print join '-', @$ymd; # prints 2013-10-1

要解析日期字符串,您可以执行以下操作:
use strict;
use warnings;
use Date::Manip::Date;

my $date = new Date::Manip::Date;

$date->parse('First Tuesday in October 2013');

print $date->printf('%Y-%m-%d'); # prints 2013-10-01

希望这能帮到你!

1
这是21世纪。Date::Manip永远不是正确的答案 :-) - Dave Cross
@DaveCross - 今天是星期五。在这种情况下要小心普遍量词,因为它们本质上是脆弱的。;-) - Kenosis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接