Преобразование временных меток в Perl, содержащих часовой пояс

Мне нужно преобразовать следующую временную метку, содержащую мс и часовой пояс, в формат гггг-мм-дд:

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.

Кто-нибудь покажет мне правильный способ, как это сделать?

Я бы хотел, чтобы фраза «временная метка, содержащая часовой пояс», была лишней.

William Pursell 10.07.2024 15:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
111
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Между вашим форматом 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, а проблема в целом? Даже если вы хотите проверить метку времени, что вы будете делать, если проверка не удалась? На этой неделе я видел более нескольких проблем, когда люди хотели быть очень строгими (тип электронной почты!), но не учитывайте, как это просто взорвется где-то еще, если вы игнорируете имеющиеся данные, потому что вы думаете, что вам следует все остановить, потому что вы локально недовольны.

brian d foy 11.07.2024 18:17

Первоначально проблема связана с библиотекой 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);

Другие вопросы по теме