Как я могу элегантно напечатать дату в формате RFC822 в Perl?
Как элегантно напечатать дату в формате RFC822 в Perl?
Ответы (4)
Пакет 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 в now():
DateTime->now( time_zone => $ENV{'TZ'}, ... )
setlocale()
. Затем DateTime::Format::Mail может использовать неанглийские названия месяца и дня. Как правило, временное переключение локали в этом контексте не является потокобезопасным. Часто не актуально, иногда имеет значение.
- person Guido Flohr; 13.10.2018
Это можно сделать с помощью strftime
, но его %a
(день) и %b
(месяц) выражаются на языке текущей локали.
От man strftime
:
%a Сокращенное название дня недели в соответствии с текущей локалью.
%b Сокращенное название месяца в соответствии с текущей локалью.
Поле даты в почте должно использовать только эти имена (из rfc2822 DATE И ВРЕМЯ):
day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
"Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
Поэтому переносимый код должен переключиться на локаль C
:
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";
time()
внутри localtime()
не нужен (это аргумент по умолчанию).
- person neuhaus; 19.06.2017
Простое использование 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
, вы избегаете всех вышеупомянутых проблем, но при этом полностью соответствуете стандартам, не говоря уже о более быстром времени. Используйте это решение, когда производительность может быть для вас проблемой. Используйте первое решение, когда ваши пользователи жалуются на программное обеспечение, сообщающее о «неправильном» часовом поясе.
strftime('%a, %d %b %Y %H:%M:%S %z', localtime(time()));
дает Thu, 14 Nov 2013 09:46:00 E. Australia Standard Time
, что определенно неверно. Пожалуйста, обратитесь к RFC 2822 3.3. Спецификация даты и времени.
- person AlwaysLearning; 14.11.2013
$ perl -v
Это Perl 5, версия 16, Subversion 2 (v5.16.2), созданный для darwin-thread-multi-2level $ perl -e 'use POSIX qw(strftime); print strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time())) . "\n";'
Воскресенье, 24 ноября 2013 г., 20:02:53 -0500
- person njsf; 25.11.2013