2013-09-10 7 views
3

Я получаю даты в формате% dd.% Mm.% YYYY, и я пытаюсь вычитать один месяц, используя Perl.perl вычесть один месяц с даты

примеры: 12.07.2013 -> 12.06.2013, 30.09.2013 -> 31.08.2013

Должен ли я использовать Дата :: Calc? Есть идеи?

Благодаря

+7

Вычитая один месяц с 30 сентября и с 31 Август будет * необычным * для родового «отнимать один месяц» (хотя обратный сценарий, начинающийся месяц с 31 июля и заканчивающийся 30 июня, будет считаться нормальным). Вы уверены, что это то, что нужно? Пожалуйста, поставьте еще несколько примеров и любой код Perl, который вы уже пробовали. –

ответ

5

Вы можете использовать Time::Piece, который является основным модулем, так как Perl v5.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.

+1

'12.07.2013' минус' ONE_MONTH' равен 11.06.2013' этим способом. Очевидно, что многое зависит от определения OP * месяца *, но я ожидал бы от примеров до сих пор, мы говорим о расчетах календарного месяца, а не о фиксированном количестве дней (или секунд). –

+0

'Time :: Piece' имеет метод' add_months', который дает здесь 12.06.2013 (при использовании с '-1' в качестве аргумента). Тем не менее, все еще интересно, что ожидает OP, если «невозможными» датами станут результаты операции, например. 30.02.2013. –

+0

@SlavenRezic 'add_months', похоже, ничего не делает, он просто производит ту же дату. Возможно, это небольшая нерегулярная функция в модуле. – TLP

3

DateTime поддерживает привязку к концу месяца, которую вы ищете. Опция end_of_month задокументирована в разделе Adding a Duration to a Datetime. Я также предоставил решение для Date::Calc, которое показывает логику. Результат для обоих решений одинаковый.

DateTime:

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'); 
    } 
} 

Date::Calc:

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 
Смежные вопросы