Преобразование формата даты %Y%m%d%H%M%S в формат unix epoch

Вопрос или проблема

Преобразование формата даты %Y%m%d%H%M%S в формат unix epoch

Я пробую все методы, но получаю эту ошибку:

Я хочу преобразовать этот формат в эпоху –>

date "+%Y%m%d%H%M%S"

пример –> 20240913235959
Я также пробовал другие форматы, такие как:

date "+%Y%m%d %H%M%S"
date "+%Y-%m%-d% H:%M:%S"

но каждый раз, когда я передаю время этой команде, возникает ошибка.
Что я хочу, так это преобразовать дату и время в эпоху, только дело в том, что другие форматы, такие как 2022-11-09T14:41:15.555641007Z, не могут быть использованы?

Существует стандартная функция UNIX strptime()¹ для разбора метки времени на основе спецификации формата, такой как %Y%m%d%H%M%S, и стандартная функция POSIX mktime() для преобразования результата в эпоху, и большинство языков программирования и реализаций утилиты dateзаметным исключением GNU date) и некоторых оболочек имеют интерфейс для этого или чего-то подобного.

Теперь метка времени, такая как 20231029012345, не несет информации о временной зоне. Таким образом, чтобы преобразовать ее из настенного времени, относительного часового пояса, в эпоху, которая является представлением точного момента времени независимо от временной зоны, вам нужно указать часовую зону.

Как выяснилось, в многих временных зонах Западной Европы было два момента времени, когда часы показывали 20231029012345. Например, в Шотландии, где я это пишу, это было как на эпохе Unix 1698539025 (до перехода на зимнее время), так и 1698542625 (после перехода):

$ export TZ=Europe/London
$ for t (1698539025 1698542625) strftime '%Y%m%d%H%M%S can be %s (actually %FT%T.%N%z)' $t
20231029012345 can be 1698539025 (actually 2023-10-29T01:23:45.000000000+0100)
20231029012345 can be 1698542625 (actually 2023-10-29T01:23:45.000000000+0000)

Так что если 20231029012345 должна быть представлением местного времени, то преобразовать это в эпоху секунд надежно невозможно. Реализации должны будут выбрать одну из тех меток времени, которые возникают дважды.

Общие языки программирования предоставляют вам некоторый контроль над тем, хотите ли вы интерпретировать метку времени как время UTC или в местном часовом поясе или в любом другом часовом поясе, остальные в общем случае интерпретируют это как местное время (с вышеупомянутым оговоркой), но вы можете установить переменную окружения $TZ на UTC0, чтобы местное время стало UTC.

Примеры ниже с глобально установленным $TZ как export TZ=Pacific/Auckland, так как это очень далеко от UTC.

Кроме уже упомянутого GNU awk от @EdMorton, есть

date утилиты

  • FreeBSD date-f добавлено в 1997 году), OpenBSD date (с 2019 года), NetBSD date (с 2022 года):

    $ date -jf %Y%m%d%H%M%S 20240913235959 +%s
    1726228799
    $ date -ujf %Y%m%d%H%M%S  20240913235959 +%s
    1726271999
    $ TZ=UTC0 date -jf %Y%m%d%H%M%S 20240913235959 +%s
    1726271999
    
  • ast-open date (или встроенный date ksh93, если включен; редко в наши дни), где опция -p/--parse была добавлена в 2004 году или ранее:

    $ date -p %Y%m%d%H%M%S -d 20240913235959 +%s
    1726228799
    $ date -up %Y%m%d%H%M%S -d 20240913235959 +%s
    1726271999
    $ TZ=UTC0 date -p %Y%m%d%H%M%S -d 20240913235959 +%s
    1726271999
    
  • busybox и toybox date с -D, добавленные соответственно в 2006 году и в 2015 году.

    $ date -D %Y%m%d%H%M%S -d 20240913235959 +%s
    1726228799
    $ date -uD %Y%m%d%H%M%S -d 20240913235959 +%s
    1726271999
    $ TZ=UTC0 date -uD %Y%m%d%H%M%S -d 20240913235959 +%s
    1726271999
    

    (остерегайтесь, поддержка часовых поясов может быть отключена во время сборки)

GNU date поддерживает разбор времени (предоставленное либо через -d <timespec>, либо -f <file-containing-timespecs>, или -f - для тех <timespec>, которые читаются из stdin, не имея отношения к опции -f <input-format> утилиты BSD date), но в текущих версиях только в фиксированном наборе входных форматов. Поэтому вам нужно преобразовать 20240913235959 в один из них, такой как стандартный формат ISO-8601 2024-09-13T23:59:59:

$ echo 20240913235959 | sed -E 's/(....)(..)(..)(..)(..)/\1-\2-\3T\4:\5:/' | date -f- +%s
1726228799
$ echo 20240913235959 | sed -E 's/(....)(..)(..)(..)(..)/\1-\2-\3T\4:\5:/' | date -uf- +%s
1726271999
$ echo 20240913235959 | sed -E 's/(....)(..)(..)(..)(..)/\1-\2-\3T\4:\5:/' | TZ=UTC0 date -f- +%s
1726271999

Оболочки

  • zsh с -r добавлено в его встроенный strftime в 2006 году:

    $ zmodload zsh/datetime
    $ strftime -r %Y%m%d%H%M%S 20240913235959
    1726228799
    $ TZ=UTC0 strftime -r %Y%m%d%H%M%S 20240913235959
    1726271999
    

    Смотрите также опцию -s var, чтобы вернуть результат в $var, а не выводить его на экран.

  • ksh93 (с %T добавлено в его встроенный printf в 1999 году). Это немного неудобно, так как список форматов для распознавания на входе должен храниться в файле, на который ссылается переменная $DATEMSK.

    $ echo %Y%m%d%H%M%S > ~/.datemsk
    $ export DATEMSK=~/.datemsk
    $ printf '%(%s)T\n' 20240913235959
    1726228799
    $ TZ=UTC0 printf '%(%s)T\n' 20240913235959
    1726271999
    

Языки программирования

(упомянем лишь некоторые)

  • TCL (-format опция clock scan добавлена в 2004 году)

    $ tclsh
    % clock scan 20240913235959 -format %Y%m%d%H%M%S
    1726228799
    % clock scan 20240913235959 -format %Y%m%d%H%M%S -gmt true
    1726271999
    
  • perl (strptime() добавлено в Time::Piece в 2001 году)

    $ perl -MTime::Piece -le 'print localtime->strptime($_, "%Y%m%d%H%M%S")->epoch for @ARGV' 20240913235959
    1726228799
    $ perl -MTime::Piece -le 'print Time::Piece->strptime($_, "%Y%m%d%H%M%S")->epoch for @ARGV' 20240913235959
    1726271999
    $ TZ=UTC0 perl -MTime::Piece -le 'print localtime->strptime($_, "%Y%m%d%H%M%S")->epoch for @ARGV' 20240913235959
    1726271999
    
  • ruby (с 2005 года):

    $ ruby -e 'require "time"; ARGV.each {|t| puts Time.strptime(t, "%Y%m%d%H%M%S").to_i}' 20240913235959
    1726228799
    $ TZ=UTC0 ruby -e 'require "time"; ARGV.each {|t| puts Time.strptime(t, "%Y%m%d%H%M%S").to_i}' 20240913235959
    1726271999
    
  • python3 (time.strptime добавлено в python в 1998 году):

    $ python3 -c 'import time, sys
    for arg in sys.argv[1::]:
      print(int(time.mktime(time.strptime(arg, "%Y%m%d%H%M%S"))))' 20240913235959
    1726228799
    $ TZ=UTC0 python3 -c 'import time, sys
    for arg in sys.argv[1::]:
      print(int(time.mktime(time.strptime(arg, "%Y%m%d%H%M%S"))))' 20240913235959
    1726271999
    

¹ возможно, происходящий из SunOS 4.1 в 1989 году.

Используя GNU awk для его time библиотеки:

$ echo '20240913235959' |
    awk -l time '{print strptime($0,"%Y%m%d%H%M%S")}'
1726289999

или, если библиотека gawk time недоступна на вашей системе и вы не можете ее установить, то также GNU awk для встроенной mktime():

$ echo '20240913235959' |
    awk '{gsub(/../,"& "); print mktime($1$2" "$3" "$4" "$5" "$6" "$7)}'
1726289999

Вам, возможно, захочется сделать что-то другое, чем просто передать ввод в awk; правильное решение в этом отношении зависит от того, как хранятся ваши входные данные.

С помощью GNU date, bash и его развертывания параметров:

t="20240913235959"
date -d "${t:0:4}/${t:4:2}/${t:6:2} ${t:8:2}:${t:10:2}:${t:12:2}" +%s

Результат:

1726264799

Мы можем обмануть и использовать sed, чтобы преобразовать ввод в то, что нравится date.

например

% date -d "$(echo 20240913235959 | sed 's!\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)!\1/\2/\3 \4:\5:\6!')"
Пт Сен 13 23:59:59 EDT 2024

Затем мы можем использовать +%s, чтобы преобразовать в секунды эпохи

% date -d "$(echo 20240913235959 | sed 's!\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)!\1/\2/\3 \4:\5:\6!')" +%s
1726286399

Если вы не доверяете библиотекам, вы можете выполнить преобразование самостоятельно; это Bash, но тот же расчет можно провести на любом языке:


D=20240921141157    # пример входа
tz=$(( 10*3600 ))   # ваш местный часовой пояс с учетом перехода на летнее/зимнее время; я в Брисбене (UTC+10)

# Сначала разобьем его. (В других языках это будет выглядеть совершенно иначе.)
# Если у вас уже есть отдельные компоненты, вы можете пропустить этот этап
(( y  = 10#${D:0:4},
   mo = 10#${D:4:2},
   d  = 10#${D:6:2},
   h  = 10#${D:8:2},
   mi = 10#${D:10:2},
   s  = 10#${D:12:2} ))

# Сделаем вид, что январь и февраль - это 13-й и 14-й месяцы предыдущего года (так, чтобы високосные дни находились в конце "года")
(( mo < 3 && (y--, mo += 12) ))

# Считаем месяцы и дни, начиная с 0
(( d--, mo -= 3 ))

# Считаем дни с 1 марта (самого последнего)
(( d += mo / 5 * 153, mo %= 5,
   d += mo / 2 * 61, mo %= 2,
   d += mo * 31 ))

# Считаем дни с 1 января 1970 года
(( d += y / 400 * 146097, y %= 400,
   d += y / 100 * 36524, y %= 100,
   d += y / 4 * 1461, y %= 4,
   d += y * 365,
   d -= 719468 ))

# Преобразуем дни, минуты, часы в секунды
(( h  += d * 24,
   mi += h * 60,
   s  += mi * 60 ))

# Корректируем для вашего местного часового пояса и перехода на летнее/зимнее время
(( s -= tz ))

# Проверяем результат. Я в Брисбене (UTC+10)
echo "Секунды эпохи = $s"

TZ=Australia/Brisbane \
date -d @$s

Ответ или решение

Чтобы конвертировать дату и время в формате %Y%m%d%H%M%S (например, 20240913235959) в формат Unix epoch, можно воспользоваться различными методами в зависимости от используемой операционной системы и доступных утилит. Я рассмотрю несколько подходов, которые могут помочь вам решить эту задачу.

1. Использование GNU date

Если вы используете команды в терминале Linux, одним из простых методов является использование утилиты date. Однако стандартная версия date может не поддерживать формат входных данных напрямую. Поэтому, можно разбить строку и создать из неё понятный для date формат:

t="20240913235959"
date -d "${t:0:4}/${t:4:2}/${t:6:2} ${t:8:2}:${t:10:2}:${t:12:2}" +%s

2. Использование awk

Если GNU awk доступен, можно воспользоваться его возможностями для парсинга даты:

echo '20240913235959' | awk '{gsub(/../,"& "); print mktime($1$2" "$3" "$4" "$5" "$6)}'

3. Использование sed для преобразования формата

Также возможно преобразование формата с помощью sed, чтобы сделать его понятным для утилиты date:

date -d "$(echo 20240913235959 | sed 's!\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)!\1/\2/\3 \4:\5:\6!')" +%s

4. Использование других языков программирования

Если у вас есть возможность использовать языки программирования, вот примеры для разных языков:

  • Python:
import time

date_string = '20240913235959'
epoch_time = int(time.mktime(time.strptime(date_string, "%Y%m%d%H%M%S")))
print(epoch_time)
  • Perl:
use Time::Piece;
my $date_string = '20240913235959';
my $epoch_time = Time::Piece->strptime($date_string, "%Y%m%d%H%M%S")->epoch;
print "$epoch_time\n";
  • Ruby:
require 'time'
date_string = '20240913235959'
epoch_time = Time.strptime(date_string, "%Y%m%d%H%M%S").to_i
puts epoch_time

В каждом из этих примеров вам нужно будет гарантировать, что ваш ввод рассматривается как локальное время или UTC в зависимости от ваших требований и настроек.

Обратите внимание на часовой пояс

Важно помнить, что формат %Y%m%d%H%M%S не содержит информации о часовом поясе. Поэтому, для точного преобразования времени в epoch, желательно точно указать, как вы хотите интерпретировать дату: как локальное время или как время по UTC. Это может повлиять на конечный результат, особенно если дата попадает в момент перехода на зимнее или летнее время.

Заключение

В зависимости от ваших предпочтений и окружения, вы можете выбрать любой из предложенных методов. Убедитесь, что у вас установлен необходимый инструментарий и правильно установлены временные зоны, если используете локальное время.

Оцените материал
Добавить комментарий

Капча загружается...