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


Автор: infarch 22.8.2019, 13:02
Здравствуйте!

Вот и наступило будущее. Вызов Date::Calc::Date_to_Time(2039, 3, 27, 2, 0, 0) упал с ошибкой date out of range.
В полном соответствии с документацией:

"A fatal "date out of range" error will occur if the given date cannot be expressed in terms of seconds since the epoch (this happens for instance when the date lies before the epoch, or if it is later than 19-Jan-2038 03:14:07 GMT..."

Вопрос: как жить дальше? В идеале хотелось бы исправить именно этот модуль чтоб не переписывать кучу кода. Если это невозможно, то какие альтернативы?
Никто не в курсе?

Автор: arto 23.8.2019, 07:32
в зависимости от пакета увеличте константу DateCalc_DAYS_TO_OVFLW или $DateCalc_DAYS_TO_OVFLW.
Ну и обратите внимание на архитектуру.

Код

# perl -Mblib -MDate::Calc -le 'print Date::Calc::Date_to_Time(2039, 3, 27, 2, 0, 0)'
2184804000
# date --date=@2184804000
Sun 27 Mar 2039 05:00:00 AM EEST

Автор: infarch 26.9.2019, 10:40
arto, Наконец дошли руки проверить ваш совет. Я нашел эту переменную в Date::Calc::PP

Код

my $DateCalc_DAYS_TO_EPOCH;
my $DateCalc_DAYS_TO_OVFLW;
my $DateCalc_SECS_TO_OVFLW;

## MacOS (Classic):                                            ##
## <695056.0>     = Fri  1-Jan-1904 00:00:00 (time=0x00000000) ##
## <744766.23295> = Mon  6-Feb-2040 06:28:15 (time=0xFFFFFFFF) ##

## Unix:                                                       ##
## <719163.0>     = Thu  1-Jan-1970 00:00:00 (time=0x00000000) ##
## <744018.11647> = Tue 19-Jan-2038 03:14:07 (time=0x7FFFFFFF) ##

if ($^O eq 'MacOS')
{
    $DateCalc_DAYS_TO_EPOCH = 695056;
    $DateCalc_DAYS_TO_OVFLW = 744766;
    $DateCalc_SECS_TO_OVFLW =  23295;
}
else
{
    $DateCalc_DAYS_TO_EPOCH = 719163;
    $DateCalc_DAYS_TO_OVFLW = 744018;
    $DateCalc_SECS_TO_OVFLW =  11647;




Но вот переопределить ее я не могу. Пробовал так:

Код

use strict;
use warnings;

use Date::Calc::PP;
use Date::Calc;

$Date::Calc::PP::DateCalc_DAYS_TO_OVFLW = 7447660;

print Date::Calc::Date_to_Time(2039, 3, 27, 2, 0, 0);


Не сработало:

Name "Date::Calc::DateCalc_DAYS_TO_OVFLW" used only once: possible typo at C:\bb_projekt\scripts\easy.pl line 7.
Date::Calc::Date_to_Time(): date out of range at C:\bb_projekt\scripts\easy.pl line 9.

Автор: arto 26.9.2019, 17:08
Она локальная для модуля. Делайте исправления в нём.

Я для себя поправил XS модуль.

Автор: infarch 1.10.2019, 10:09
Боюсь что такой трюк мне не одобрят. Да и я так вижу по коду что проблема не в одной этой константе, там попадаются еще проверки вроде: 

Код

        return 0 if
        (
            ($year  < 1970) or ($year  > 2038) or
            ($month <    1) or ($month >   12) or
            ($day   <    1) or ($day   >   31) or
            ($hour  <    0) or ($hour  >   23) or
            ($min   <    0) or ($min   >   59) or
            ($sec   <    0) or ($sec   >   59)
        );
        return 0 if
        (
            ($year == 2038) and ( ($month >  1) or
                                ( ($month == 1) and ( ($day >  19) or
                                                    ( ($day == 19) and ( ($hour >  3) or
                                                                       ( ($hour == 3) and ( ($min >  14) or
                                                                                          ( ($min == 14) and ($sec > 7) ) )))))))
        );


Интересно было бы узнать откуда такой лимит. А я пока думаю о рефакторинге и переезде на DateTime. Но там свои тараканы. Например там поддерживается вычитание дат, но из результата вычисления невозможно узнать дельту дней. Вместо вычитания надо использовать функцию delta_days, да еще и не просто так... Выходят конструкции такого типа:

Код

    my $dt1 = DateTime->new(
        year => 2000,
        month => 3,
        day => 5,
    );
    
    my $dt2 = DateTime->new(
        year => 2004,
        month => 7,
        day => 15,
    );
    my $delta = $dt2->delta_days($dt1)->delta_days();


Все это производит впечатление сырого и неряшливого кода, так что я не уверен что хочу тащить это в проект.

Автор: arto 6.10.2019, 13:08
это наследие 32-битности.

Автор: infarch 28.10.2019, 12:40
Решил эту проблему созданием модуля - хелпера. Заменил в коде все упоминания Date::Calc на Backbone::DateCalc без каких либо иных изменений. Внутри он переопределяет две функции из Date::Calc::PP. Аналогичный прием для XS не сработал, так что пришлось юзать чистоперл-реализацию. Модуль хорош тем что в принципе ничего и менять не надо, достаточно его заюзать и он применяет фикс. Однако я не исключаю дальнейших его доработок и полного отказа от дейт-калк.

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