Мне нужно преобразовать следующую временную метку, содержащую мс и часовой пояс, в формат гггг-мм-дд:
use Time::Piece;
my $created = "2024-07-02T11:20:11.000+0200";
my $test2 = Time::Piece->strptime($created,"%Y%m%dT%H:%M:%S.%z")->strftime("%Y-%m-%d");
но я получаю ошибку:
Error parsing time at /usr/local/lib/perl5/5.18.1/x86_64-linux/Time/Piece.pm line 469.
Кто-нибудь покажет мне правильный способ, как это сделать?
Между вашим форматом strptime и введенными вами данными есть несколько несоответствий. У вас есть %Y%m%d
в начале, что соответствует 20240702
, но в вашем вводе есть 2024-07-02
. У вас есть %S.%z
в конце, что соответствует 11.+0200
, но в вашем вводе есть 11.000+0200
.
Чтобы исправить первое, просто добавьте в формат дефисы.
Чтобы исправить второй, Time::Piece не понимает доли секунды. Если вы можете гарантировать, что полученная вами строка всегда будет длиться целую секунду, вы можете использовать формат %Y-%m-%dT%H:%M:%S.000%z
.
Если вы не можете гарантировать, что дробь всегда будет .000
, вы можете либо использовать более функциональный модуль (DateTime::Format::Strptime будет поддерживать %Y-%m-%dT%H:%M:%S.%3N%z
), либо просто признать, что в конечном итоге написанный вами код будет эквивалент substr $created, 0, 10
.
Для строк даты и времени в произвольных форматах часто бывает полезен DateTime::Format::Strptime.
use feature qw( say );
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => "%Y-%m-%dT%H:%M:%S.%3N%z",
strict => 1,
on_error => "croak",
);
my $dt_str = $ARGV[0];
say $format->parse_datetime( $dt_str )->strftime( "%Y-%m-%d" );
$ ./a.pl 2024-07-02T11:20:11.000+0200
2024-07-02
Ваш ввод очень близок к стандартному формату. Если бы в ваших входных данных использовалось +02:00
вместо +0200
, это соответствовало бы чрезвычайно широко используемым стандартам RFC3339 и ISO8601. Вы можете исправить ввод, а затем использовать инструменты, предназначенные для обработки этих форматов.
use feature qw( say );
use DateTime::Format::RFC3339 qw( );
my $dt_str = $ARGV[0];
$dt_str =~ s/[-+]\d\d\K(?=\d\d\z)/:/a;
say DateTime::Format::RFC3339->parse_datetime( $dt_str )->strftime( "%Y-%m-%d" );
$ ./b.pl 2024-07-02T11:20:11.000+0200
2024-07-02
use feature qw( say );
use DateTime::Format::ISO8601 qw( );
my $dt_str = $ARGV[0];
$dt_str =~ s/[-+]\d\d\K(?=\d\d\z)/:/a;
say DateTime::Format::ISO8601->parse_datetime( $dt_str )->strftime( "%Y-%m-%d" );
$ ./c.pl 2024-07-02T11:20:11.000+0200
2024-07-02
Если вам не нужна проверка, самое простое и быстрое решение вашей проблемы — выполнить манипуляцию со строками.
use feature qw( say );
my $dt_str = $ARGV[0];
$dt_str =~ s/T.*//s;
say $dt_str;
$ ./d.pl 2024-07-02T11:20:11.000+0200
2024-07-02
use feature qw( say );
my $dt_str = $ARGV[0];
substr( $dt_str, 10 ) = "";
say $dt_str;
$ ./e.pl 2024-07-02T11:20:11.000+0200
2024-07-02
Мне было интересно, когда же кто-нибудь покажет простые манипуляции со строками. Не адресовано @ikegami, а проблема в целом? Даже если вы хотите проверить метку времени, что вы будете делать, если проверка не удалась? На этой неделе я видел более нескольких проблем, когда люди хотели быть очень строгими (тип электронной почты!), но не учитывайте, как это просто взорвется где-то еще, если вы игнорируете имеющиеся данные, потому что вы думаете, что вам следует все остановить, потому что вы локально недовольны.
Первоначально проблема связана с библиотекой Time::Piece
, которая использует POSIX->strptime , это означает, что на основе документации здесь - https://www.unix.com/man-page/FreeBSD/3/strftime/ - strftime
не может справиться с миллисекундами.
1-е решение: Если вам нужно использовать Time::Piece, я предлагаю удалить миллисекунды перед их преобразованием.
use Time::Piece;
my $created = "2024-07-02T11:20:11+0200"; # milliseconds removed
my $test2 = Time::Piece->strptime($created,"%FT%T%z")->strftime("%Y-%m-%d"); # %F is equivalent to %Y-%m-%d and %T is equivalent to %H:%M:%S
2-е решение: Разберите и преобразуйте входные данные в эпоху и преобразуйте эпоху в желаемый формат.
use HTTP::Date;
use POSIX qw(strftime);
my $current_date = "2024-07-02T11:20:11.00+0200";
my $epoch = str2time($current_date);
my $yyyymmdd = strftime "%Y-%m-%d", localtime($epoch);
Я бы хотел, чтобы фраза «временная метка, содержащая часовой пояс», была лишней.