Мы в настоящее время используем третью сторону API, который обеспечивает дату и время в следующем формате:
Sat Mar 06 09:00:00 ICST 2010
Fri Feb 19 19:30:00 JDT 2010
Fri Feb 19 19:30:00 PST 2010
Однако мы хотим хранить эти объекты даты и времени в MySQL в стандартном поле даты и времени, которое требует следующего формата:
YYYY-MM-DD HH:MM:SS
Прямо сейчас мы используем следующий код, который сообщает об ошибках для определенных часовых поясов, таких как KDT, JDT и ICST:
use Date::Manip;
use DateTime;
use DateTime::Format::DateManip;
my $date = ParseDate($time);
$date = DateTime::Format::DateManip->parse_datetime($date);
eval{ $time = $date->strftime("%Y-%m-%d %H:%M:%S"); };
Можно ли рекомендовать лучшей реализации кода Perl выше преобразовать объекты даты и времени от API до соответствующего формата и время на нашем сервере, который будет вставлен в поле даты и времени MySQL?
Заранее спасибо за Вашу справку и совет!
Сохранять время внутри по Гринвичу. Все манипуляции делайте в GMT. Затем в последний момент, когда вы собираетесь отобразить результаты для пользователя, , затем конвертируются в местное время пользователя.
Я рекомендую использовать Date :: Parse , но вам придется увеличить смещение его часового пояса, потому что сейчас, например, нет Индокитайского летнего времени и Японского летнего времени.
#! /usr/bin/perl
use warnings;
use strict;
use Date::Format;
use Date::Parse;
# add timezone offsets
$Time::Zone::Zone{icst} = +7*3600;
$Time::Zone::Zone{jdt} = +9*3600;
while (<DATA>) {
chomp;
warn("$0: failed conversion for $_\n"), next
unless defined(my $time_t = str2time $_);
my @t = gmtime($time_t);
print $_, " => ", strftime("%Y-%m-%d %H:%M:%S", @t), "\n";
}
__DATA__
Sat Mar 06 09:00:00 ICST 2010
Fri Feb 19 19:30:00 JDT 2010
Fri Feb 19 19:30:00 PST 2010
Вывод:
Sat Mar 06 09:00:00 ICST 2010 => 2010-03-06 02:00:00 Fri Feb 19 19:30:00 JDT 2010 => 2010-02-19 10:30:00 Fri Feb 19 19:30:00 PST 2010 => 2010-02-20 03:30:00
Для поддержки желаемого запроса сохраните время в GMT плюс смещение ( то есть , от GMT до местного времени из API). Обратите внимание, что в приведенном ниже коде предполагается, что если str2time
может анализировать заданное время, то strptime
тоже может. Измените цикл на
my @dates;
while (<DATA>) {
chomp;
warn("$0: failed conversion for $_\n"), next
unless defined(my $time_t = str2time $_);
my $zone = (strptime $_)[-1];
my @t = gmtime($time_t);
push @dates => [ strftime("%Y-%m-%d %H:%M:%S", @t)
, sprintf("%+03d:%02d",
int($zone / 3600),
int($zone % 3600) / 60)
, $_
];
}
С собранным временем визуализируйте его как SQL:
print "DROP TABLE IF EXISTS dates;\n",
"CREATE TABLE dates (date DATETIME, offset CHAR(6));\n",
"INSERT INTO dates (date,offset) VALUES\n",
join(",\n\n" =>
map(" -- $_->[2]\n" .
" ('$_->[0]','$_->[1]')", @dates)),
";\n",
"SELECT CONVERT_TZ(date,'+00:00',offset) FROM dates;\n"
Результатом будет
DROP TABLE IF EXISTS dates;
CREATE TABLE dates (date DATETIME, offset CHAR(6));
INSERT INTO dates (date,offset) VALUES
-- Sat Mar 06 09:00:00 ICST 2010
('2010-03-06 02:00:00','+07:00'),
-- Fri Feb 19 19:30:00 JDT 2010
('2010-02-19 10:30:00','+09:00'),
-- Fri Feb 19 19:30:00 PST 2010
('2010-02-20 03:30:00','-08:00');
SELECT CONVERT_TZ(date,'+00:00',offset) FROM dates;
, и мы можем передать его по конвейеру mysql
:
$ ./prog.pl | mysql -u username -D dbname CONVERT_TZ(date,'+00:00',offset) 2010-03-06 09:00:00 2010-02-19 19:30:00 2010-02-19 19:30:00
Вероятно, вам придется явно указать конструктору DateTime, с каким(и) часовым(и) поясом(ами) вы имеете дело, поскольку трех- и четырехбуквенные коды не уникальны и не стандартизованы.
Я бы предложил начать с DateTime::TimeZone, и, возможно, создать новые типы для ваших часовых поясов, если вы не сможете найти то, что ищете в списке. Вы также можете найти форматтер DateTime, который соответствует вашему синтаксису (их существует огромное количество), но в остальном несложно просто использовать regex для извлечения полей даты и времени и передать их в конструктор DateTime.
После того, как вы разобрали свои строки на объекты DateTime с правильно установленным временем, не составит труда преобразовать их обратно в временные метки MySQL: просто используйте DateTime::Format::MySQL:
my $string = DateTime::Format::MySQL->format_datetime($dt);
При хранении дат, вы всегда должны хранить их в UTC. Таким образом, вы сможете получить их из базы данных и преобразовать в соответствующий часовой пояс, если это необходимо для отображения пользователю.
Для правильной работы с датами в Perl я от души рекомендую набор DateTime, в котором есть парсеры и форматеры для всевозможных форматов ввода и вывода.
Я не уверен, являются ли даты, перечисленные в вашем OP, стандартным форматом, но если нет, то будет довольно просто создать из них формат DateTime:
my $str = 'Sat Mar 06 09:00:00 ICST 2010';
my ( $month, $day, $hour, $min, $sec, $tz, $year ) = ( $str =~ m{\w{3}\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s(\w+)\s(\d{4})} );
my $dt = DateTime->new( year => $year, month => $month, day => $day,
hour => $hour, minute => $min, second => $sec,
time_zone => $tz );
Затем вы можете использовать DateTime::Format::MySQL для преобразования даты в совместимое с MySQL время.