Perl DateTime模块计算第一,第二,第三,第四,上周日,周一,......每个月的周六

rit*_*ITW 2 perl datetime date

我是用Perl工作的日期时间模块,我不知道我将如何计算 First,Second,Third,FourthLast Sunday's,Monday's,... Saturday's指定月份.

我的方法:

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

指定日期i到日期($ dt)并使用$ dt-> day_of_week()获取星期几.

使用计数器跟踪第一,第二,第三,第四,最后.

如果星期几匹配所需的日期并且计数器匹配所需的间隔,则打破循环.

你能建议比上面更好(或更短)的方法吗?任何帮助表示赞赏.

cjm*_*cjm 5

这是对我找到上周一(或一周中任何指定日期)的答案的直接修改.唯一的困难是弄清楚你开始的日期.

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);
}
Run Code Online (Sandbox Code Playgroud)

更新:这是一个稍微高效的版本.它使用相同的算法,但它将调用组合为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);
}
Run Code Online (Sandbox Code Playgroud)