Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Разность дат


Автор: meridian 2.2.2007, 13:00
Привет всем!
Есть вот такой модуль, вроде нормально работал до сих пор
но при следующих значениях ерунда получается
$data1=31.01.2007
$data2=02.02.2007
$diff получатся отриц. число

Код

$diff = diffDates( $date1, $date2, "s" );

sub diffDates
{
    my( $date1, $date2, $fmt )= @_;
    my $result;
    my( $day, $hour, $min, $sec);

    use POSIX;

    my( $year1, $month1, $days1, $hours1, $minutes1, $seconds1 ) = ( $date1 =~ /^(\d{4})\-(\d{2})\-(\d{2})\s+(\d{1,2})\:(\d{2})\:(\d{2})$/);    
    my( $year2, $month2, $days2, $hours2, $minutes2, $seconds2 ) = ( $date2 =~ /^(\d{4})\-(\d{2})\-(\d{2})\s+(\d{1,2})\:(\d{2})\:(\d{2})$/);    
    
    $year1 = ( $year1 - 2000 ) + 100;
    $year2 = ( $year2 - 2000 ) + 100;

    $time1 = POSIX::mktime ( $seconds1, $minutes1, $hours1, $days1, $month1, $year1 );
    $time2 = POSIX::mktime ( $seconds1, $minutes2, $hours2, $days2, $month2, $year2 );

    $day =    $time = POSIX::ceil( ( $time2 - $time1 ) / 3600 / 24 );
    $hour = POSIX::ceil( $time2 - $time1 );
    $min = $hour / 60;
    $sec = $min / 60;
    
    if( $fmt eq "s" ) # часы
    {
        $result = sprintf( "%.2f", $sec );
    }
    elsif( $fmt eq "m" ) # минуты
    {
        $result = sprintf( "%.2f", $min );
    }
    elsif( $fmt eq "h" ) # секунды
    {
        $result = sprintf( "%.2f", $hour );
    }
    else # дни
    {
        $result = sprintf( "%.2f", $day );
    }
    
    return $result;
}



Пасибо

Автор: amg 2.2.2007, 13:32
Если уж совсем не хочется пользоваться специализированными модулями для работы с датами (Date::Manip, HTTP::Date), то нужно хотя бы преобразовать даты к тому формату, который подразумевает подпрограмма diffDates
Код

s/(\d+)\.(\d+)\.(\d+)/$3-$2-$1 00:00:00/ for ($data1, $data2);
А число действительно получается отрицательное, но, наверно, так и должно быть, т.к. первая дата раньше (меньше) второй.

Автор: meridian 2.2.2007, 14:32
поправочка, даты в таком формате ,т.е. как обрабатывается в пп
31-01-2007 12:19:47  #data1
02-02-2007 11:19:47  #data2

а меньше непонятно почему , т.к. из date2 вычитаем date1 ( если даты одного месяца, то все нормально )

Автор: amg 2.2.2007, 14:49
Смотри внимательно подпрограмму: она требует формат "YYYY-MM-DD hh:mm:ss"
И если на неправильный формат даже не ругается, то не пользуйся такой подпрограммой! Есть же отличные модули для этого! Или, в крайнем случае, преобразуй дату:
Код

s/^(\d+)\.(\d+)\.(\d+)/$3-$2-$1/ for ($data1, $data2);

Автор: meridian 2.2.2007, 15:04
С правильным форматом тоже самое
2007-01-31 12:19:47  #data1
2007-02-02 11:19:47  #data2

Автор: meridian 2.2.2007, 15:19
Кстате, amq, если использовать Date::Manip,
достаточно будет при 
$date1 = "2007-01-31 12:19:47" 
$date2= "2007-02-02 11:19:47"

использовать $delta = &DateCalc($date1,$date2);

или нужно преобразоват даты ?

Автор: meridian 2.2.2007, 20:51
Вот так сделал проверку дат
получаю разность в секундах

Код


my $sec = cmc_CDEMDate( $date2 ) - cmc_CDEMDate( $date1 );

use Time::Local;
sub cmc_CDEMDate
{
       my( $datetime )= @_;
       my( $year, $month, $days, $hours, $minutes, $seconds ) = ( $datetime =~ /^(\d{4})\-(\d{2})\-(\d{2})\s+(\d{1,2})\:(\d{2})\:(\d{2})$/);
       return( timelocal( $seconds, $minutes, $hours, $days, $month, $year ) );
}
# end cmc_CDEMDate


Автор: Bulat 30.6.2007, 10:38
у меня обнаружился маленький косяк вот тут

Код

return( timelocal( $seconds, $minutes, $hours, $days, $month, $year ) );
 

если дата 22.12.2005 00:00:00

Month '12' out of range 0..11 

Как это понимать ??

Автор: nitr 30.6.2007, 10:40
Bulat, массивы с 0 начинаются ... 0..11 = 12

Автор: Bulat 30.6.2007, 11:44
nitr, я понимаю что массивы начинаются с нуля, и если дело только в этом, то как-то не красиво получается, если исчисления месяцов начниается с 1, а функция не понимает, что 12 месяц это декабрь.

Автор: nitr 30.6.2007, 11:48
return( timelocal( $seconds, $minutes, $hours, $days, $month-1, $year ) );

Добавлено через 45 секунд
я не видел вашей функции, но думаю это вам поможет ;)

Автор: Bulat 2.7.2007, 07:35
nitr, ну это-то понятно, просто на 31 число он так не реагирует же, вроде с днями все гуд, а вот с месяцами...

Автор: nitr 2.7.2007, 08:45
Bulat, немного запутали (т.к. тема в теме), покажите функцию и вводимые данные, а то ничего неясно (я про комментарии) ;)

Автор: amg 2.7.2007, 10:39
Bulat, в документации к модулю Time::Local (я полагаю, функция timelocal оттуда?) сказано: 
"The value for the day of the month is the actual day (ie 1..31), while the month is the number of months since January (0..11). This is consistent with the values returned from localtime() and gmtime()."
Т.е. совет nitr'а - правильный (кто бы сомневался smile )

С годом сложнее, но если не нужны даты из далекого прошлого/будущего, то обычный формат YYYY типа 2005 будет восприниматься правильно, как и год, возвращаемый перловскими функциями localtime и gmtime (YYYY-1900).

Автор: Bulat 2.7.2007, 14:43
amg, я ничего не говорю против совета nitr'а. И то что ты привел читал, но не совсем, лично мне понятно почему же месяцы от 0 отсчитываются, хотелось бы узнать причины, почему не с 1. Вот этого я не нашел.  smile 

Автор: fantome 2.7.2007, 14:58
Bulat, 
Цитата

лично мне понятно почему же месяцы от 0 отсчитываются, хотелось бы узнать причины, почему не с 1. Вот этого я не нашел.


это уже у разработчиков модуля надо спрашивать... Либо писать самому...свой модуль...  smile 

P.S: вообще то у программистов принято исчисление с 0, а не с 1. По тому как ноль - тоже число... Да и если на числовую ось посмотреть... Да и  на байт. 

Автор: amg 2.7.2007, 15:18
Почему так принято для timelocal - понятно: для совместимости с localtime. Собственно, так и сказано в документации: "This is consistent with the values returned from localtime() and gmtime()."
Код

$var == timelocal localtime $var # Тождество, было бы странно, если бы было иначе.

А почему в localtime так - тоже сказано в документации, и nitr о том же говорил:
"... $mon is the month itself, in the range 0..11 with 0 indicating January and 11 indicating December. This makes it easy to get a month name from a list:  
    my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );"

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)