Как элегантно напечатать дату в формате RFC822 на Perl? - PullRequest
18 голосов
/ 05 октября 2008

Как элегантно напечатать дату в формате RFC822 на Perl?

Ответы [ 4 ]

30 голосов
/ 05 октября 2008
use POSIX qw(strftime);
print strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time())) . "\n";
15 голосов
/ 05 октября 2008

Набор DateTime предоставляет вам несколько различных способов, например ::

use DateTime;
print DateTime->now()->strftime("%a, %d %b %Y %H:%M:%S %z");

use DateTime::Format::Mail;
print DateTime::Format::Mail->format_datetime( DateTime->now() );

print DateTime->now( formatter => DateTime::Format::Mail->new() );

Обновление: чтобы дать время для определенного часового пояса, добавьте аргумент time_zone до сих пор ():

DateTime->now( time_zone => $ENV{'TZ'}, ... )
5 голосов
/ 20 октября 2016

Это можно сделать с помощью strftime, но его %a (день) и %b (месяц) выражены на языке текущей локали.

С man strftime:

% a Сокращенное название дня недели в соответствии с текущей локалью.
% b Сокращенное название месяца в соответствии с текущей локалью.

Поле даты в письме должно использовать только эти имена (от rfc2822 ДАТА И ВРЕМЯ ):

day         =  "Mon"  / "Tue" /  "Wed"  / "Thu" /  "Fri"  / "Sat" /  "Sun"

month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr" /  "May"  /  "Jun" /
               "Jul"  /  "Aug" /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"

Поэтому переносимый код должен переключиться на C locale:

use POSIX qw(strftime locale_h);

my $old_locale = setlocale(LC_TIME, "C");
my $date_rfc822 = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));
setlocale(LC_TIME, $old_locale);

print "$date_rfc822\n";
0 голосов
/ 13 октября 2018

Просто использование POSIX::strftime() имеет проблемы, которые уже были указаны в других ответах и ​​комментариях к ним:

  • Он не будет работать с MS-DOS, также называемой Windows, которая генерирует строки типа «Стандартное время Европы» вместо «+0200», как требуется RFC822 для спецификации преобразования %z.
  • Он напечатает сокращенные названия месяца и дня в текущей локали вместо английского, что опять-таки требуется для RFC822 .

Переключение локали на "POSIX" соотв. «C» решает последнюю проблему, но потенциально дорого, даже больше для хорошо работающего кода, который позже переключается обратно на предыдущую локаль.

Но это также не полностью потокобезопасно. Хотя временное переключение локали будет работать без проблем в потоках интерпретатора Perl, бывают гонки, когда сам интерпретатор Perl запускается внутри потока ядра. Это может быть в случае, когда интерпретатор Perl встроен в сервер (например, mod_perl , работающий в многопоточном Apache MPM).

Следующая версия не имеет таких ограничений, поскольку не использует никаких зависящих от локали функций:

sub rfc822_local {
    my ($epoch) = @_;

    my @time = localtime $epoch;

    use integer;

    my $tz_offset = (Time::Local::timegm(@time) - $now) / 60;
    my $tz = sprintf('%s%02u%02u',
                     $tz_offset < 0 ? '-' : '+',
                     $tz_offset / 60, $tz_offset % 60);

    my @month_names = qw(Jan Feb Mar Apr May Jun
                         Jul Aug Sep Oct Nov Dec);
    my @day_names = qw(Sun Mon Tue Wed Thu Fri Sat Sun);

    return sprintf('%s, %02u %s %04u %02u:%02u:%02u %s',
                   $day_names[$time[6]], $time[3], $month_names[$time[4]],
                   $time[5] + 1900, $time[2], $time[1], $time[0], $tz);
}

Но следует отметить, что преобразование из секунд с эпохи в нерабочее время и наоборот - довольно сложные и дорогостоящие операции, даже больше, когда речь идет не о GMT / UTC, а о местном времени. Последнее требует проверки данных zoneinfo, содержащих текущие и исторические настройки летнего времени и часового пояса для текущего часового пояса. Он также подвержен ошибкам, поскольку эти параметры зависят от политических решений, которые могут быть возвращены в будущем . Из-за этого код, основанный на данных zoneinfo, является хрупким и может сломаться, если система не обновляется регулярно.

Однако цель спецификаций даты и времени, соответствующих RFC822, состоит не в том, чтобы информировать другие серверы о настройках часового пояса «вашего» сервера, а в том, чтобы дать представление о текущей дате и времени независимым от часового пояса способом. Вы можете сохранить много циклов ЦП (их можно измерить в выбросе CO2) как на отправляющей, так и на принимающей стороне, просто используя UTC вместо местного времени:

sub rfc822_gm {
    my ($epoch) = @_;

    my @time = gmtime $epoch;

    my @month_names = qw(Jan Feb Mar Apr May Jun
                         Jul Aug Sep Oct Nov Dec);
    my @day_names = qw(Sun Mon Tue Wed Thu Fri Sat Sun);

    return sprintf('%s, %02u %s %04u %02u:%02u:%02u +0000',
                   $day_names[$time[6]], $time[3], $month_names[$time[4]],
                   $time[5] + 1900, $time[2], $time[1], $time[0]);
}

Путем жесткого кодирования часового пояса в +0000 вы избегаете всех вышеперечисленных проблем, оставаясь при этом полностью совместимыми со стандартами, оставаясь в покое быстрее. Используйте это решение, когда производительность может быть проблемой для вас. Используйте первое решение, когда ваши пользователи жалуются на то, что программное обеспечение сообщает о «неправильном» часовом поясе.

...