Я получаю даты в формате %dd.%mm.%YYYY и пытаюсь вычесть один месяц, используя Perl.
примеры: 12.07.2013 -> 12.06.2013 , 30.09.2013 -> 31.08.2013
Должен ли я использовать Date:: Calc? Любые идеи?
Спасибо
Я получаю даты в формате %dd.%mm.%YYYY и пытаюсь вычесть один месяц, используя Perl.
примеры: 12.07.2013 -> 12.06.2013 , 30.09.2013 -> 31.08.2013
Должен ли я использовать Date:: Calc? Любые идеи?
Спасибо
Вы можете использовать Time::Piece
, который является основным модулем, начиная с Perl версии 5.9.5.
use strict;
use warnings;
use Time::Piece;
use Time::Seconds;
my $t = Time::Piece->strptime(shift, "%d.%m.%Y");
$t -= ONE_MONTH;
print $t->strftime("%d.%m.%Y");
Учитывая аргументы 12.07.2013
и 30.09.2013
, этот код печатает 11.06.2013
и 30.08.2013
соответственно.
Функция strptime
анализирует строку в соответствии с шаблоном в объект Time::Piece
. Затем мы можем просто добавить/вычесть объект, чтобы управлять датой. Здесь я использую константу из модуля Time::Seconds
, соответствующую одному месяцу.
Все это взято из документации для Time::Piece
.
DateTime поддерживает привязку к поведению в конце месяца, которое вы ищете. Параметр end_of_month
описан в разделе Добавление длительности к дате и времени. Я также предоставил решение для Date::Calc, которое показывает логику. Результат одинаков для обоих решений.
use DateTime;
my @dates = qw(
01.01.2013
28.02.2013
12.07.2013
30.09.2013
);
foreach my $string (@dates) {
my %p; @p{qw(day month year)} = split /\./, $string;
my $dt = DateTime->new(%p);
for my $n (-1, 1) {
my $res = $dt->clone->add(months => $n, end_of_month => 'preserve');
printf "%s %+d month => %s\n", $string, $n, $res->strftime('%d.%m.%Y');
}
}
use Date::Calc qw[Days_in_Month Decode_Date_EU];
my @dates = qw(
01.01.2013
28.02.2013
12.07.2013
30.09.2013
);
sub Add_Months {
@_ == 4 || die q/Usage: Add_Months($year, $month, $day, $delta)/;
my ($y, $m, $d, $delta) = @_;
my $ultimo = ($d == Days_in_Month($y, $m));
use integer;
$m += $delta;
$y += $m / 12;
$m %= 12;
if ($m < 1) {
$y--, $m += 12;
}
my $dim = Days_in_Month($y, $m);
if ($d > $dim || $ultimo) {
$d = $dim;
}
return ($y, $m, $d);
}
foreach my $string (@dates) {
for my $n (-1, 1) {
printf "%s %+d month => %.2d.%.2d.%.4d\n",
$string, $n, reverse(Add_Months(Decode_Date_EU($string), $n));
}
}
Выход:
01.01.2013 -1 month => 01.12.2012
01.01.2013 +1 month => 01.02.2013
28.02.2013 -1 month => 31.01.2013
28.02.2013 +1 month => 31.03.2013
12.07.2013 -1 month => 12.06.2013
12.07.2013 +1 month => 12.08.2013
30.09.2013 -1 month => 31.08.2013
30.09.2013 +1 month => 31.10.2013
12.07.2013
минусONE_MONTH
равняется11.06.2013
этим методом. Очевидно, что многое зависит от определения одного месяца в OP, но я ожидаю, что из примеров до сих пор мы говорим о расчетах календарного месяца, а не о фиксированном количестве дней (или секунд). 10.09.2013Time::Piece
имеет методadd_months
, который даст здесь 12.06.2013 (при использовании с-1
в качестве аргумента). Тем не менее, все еще интересно, что ожидает ОП, если результатом операции будут невозможные даты, например. 30.02.2013. 10.09.2013add_months
, похоже, ничего не делает, просто выдает ту же дату. Возможно, это немного неправильная функция в модуле. 10.09.2013perl -MTime::Piece -e 'warn Time::Piece->new->add_months(-1)'
являетсяSat Aug 10 14:44:56 2013 at -e line 1.
. Сегодня 2013-09-10, так что здесь все в порядке; тот же результат с perl 5.10.1 и 5.18.1, вероятно, здесь установленTime::Piece
по умолчанию. 10.09.2013my $t2 = $t->add_months(-1)
. Ну это как минимум оригинально. Попробуйтеperl -MTime::Piece -lwe'$t = localtime; $t->add_months(-1); print $t;'
10.09.2013perl -MTime::Piece -wE'$d=shift//20190331; $dt = Time::Piece->strptime($d, "%Y%m%d"); say $dt; say $dt->add_months(-1)'
(и я также могу сделать так, чтобы он давал мне 02-01, в зависимости от того, как установлена дата). ИспользованиеONE_MONTH
намного хуже: добавление месяца к 05-01 таким образом дает мне 05-31:perl -MTime::Piece -MTime::Seconds -wE'$d=shift//20190501; $dt = Time::Piece->strptime($d, "%Y%m%d"); say $dt; say $dt + ONE_MONTH'
. Теперь я используюTime::Piece
, но он у меня завернут. 13.04.2019add_month(-1)
, первую строчку выше), и мы получим 07-01, но если вместо этого использовать- ONE_MONTH
, мы получим 06-30. Так что в данном случае именноONE_MONTH
дает разумное поведение. 13.04.2019