Разбор строки даты в Go

138

Я попытался разобрать строку даты "2014-09-12T11:45:26.371Z"в Go.

Код

layout := "2014-09-12T11:45:26.371Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout , str)

Я получил эту ошибку:

время разбора "2014-11-12T11: 47: 39.489Z": месяц вне диапазона

Как я могу разобрать эту строку даты?

kannanrbk
источник
Для будущих читателей я написал несколько упражнений для анализа даты github.com/soniah/date_practice
Соня Гамильтон
Ваш макет должен быть точно 2006-01-02T15:04:05.000Zиз-за какого-то безумного стандарта
tharinduwijewardane

Ответы:

165

Используйте точные номера макетов, описанные здесь, и хороший пост в блоге здесь .

так:

layout := "2006-01-02T15:04:05.000Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

дает:

>> 2014-11-12 11:45:26.371 +0000 UTC

Я знаю. Сногсшибательно. Также поймал меня впервые. Go просто не использует абстрактный синтаксис для компонентов datetime ( YYYY-MM-DD), но эти точные числа ( я думаю, время первого коммита go Nope, в соответствии с этим . Кто-нибудь знает?).

RickyA
источник
119
Номера раскладок? Какой? Зачем? Argh!
Дарт Эгрегиус
30
О чем они думали! ? или курить?
Джишну Пратхап
14
Я мог бы немного опоздать с комментариями здесь, но не бойтесь компоновки с числами, просто используйте константы, и ваш код будет чистым :) например, time.RFC3339
Davyd Jhahaiev
11
Для тех, кто не получает номера макетов, я признаю, что на первый взгляд это очень чуждо, но как только вы к этому привыкнете, я думаю, что это имеет как минимум такой же смысл, как и типичные устройства макетов («Использую ли я« D », «д», «дд», «дд» и т. д.) и, возможно, больше смысла. Вы просто должны знать об этом в первую очередь.
три
5
Это для мнемонической цели, то есть вы просто должны запомнить 1, 2, 3, 4, 5, 6, 7 этих букв. Там отличная статья обсуждали это: medium.com/@simplyianm/...
amigcamel
87

Макет использования действительно « 2006-01-02T15:04:05.000Z» описано в RickyA «s ответ .
Это не "время первого коммита", а скорее мнемонический способ запоминания указанного макета.
Смотрите пкг / время :

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

Mon Jan 2 15:04:05 MST 2006

что время Unix 1136239445.
Поскольку MST - GMT-0700, отсчет времени можно рассматривать как

 01/02 03:04:05PM '06 -0700

(1,2,3,4,5,6,7, при условии, что вы помните, что 1 для месяца, а 2 для дня, что непросто для европейца, такого как я, в формате даты день-месяц)

Как показано в « time.parse: почему golang неправильно анализирует время? », Этот макет (с использованием 1,2,3,4,5,6,7) должен точно соблюдаться .

VonC
источник
Да, это тоже ловит австралийцев! ММ / ДД просто не вычисляет для меня, и я должен продолжать смотреть на это.
Саймон Уайтхед
3
@SimonWhitehead Я согласен. По крайней мере, как только я посмотрю это, я знаю, что обозначают YY, MM, DD, hh, mm, ss, и я могу легко переупорядочить их. Что касается Go, даже после поиска мне нужно вспомнить, что означает 1, 2, 3, 4 ...
VonC
1
Насколько я помню, это: Шаг 1) «Правильный» порядок даты - это год> месяц> день> час> минута> секунда и т. Д. (Потому что смешанный-порядковый номер будет просто бессмысленно произвольным и непоследовательным, а для дат, больших endian предпочтительнее, чем little-endian, потому что он удобен для сортировки.) Это даст нам 1 (год), 2 (месяц), 3 (день), [...] шаг 2) Go / Google - американцы, а американцы ставят свои год в конце их дат, поэтому вместо этого 1 (месяц), 2 (день), [...], n (год) Шаг 3) Часовой пояс идет после всего остального, потому что часовые пояса являются дополнительным уровнем абстракции.
mtraceur
@mtraceur Да ... Я тоже скучаю по web.archive.org/web/20180501100155/http://… (из github.com/bdotdub/fuckinggodateformat ). Я имею в виду, strftimeFTW.
VonC
58

Как и ответили, но чтобы сохранить ввод "2006-01-02T15:04:05.000Z"для макета, вы можете использовать константу пакета RFC3339 .

str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(time.RFC3339, str)

if err != nil {
    fmt.Println(err)
}
fmt.Println(t)

https://play.golang.org/p/Dgu2ZvHwTh

robstarbuck
источник
1
Кроме того, если вы посмотрите на константы пакета (ссылки указаны в ответе выше), есть множество других распространенных форматов, которые можно использовать. Если вам нужно что-то немного другое, используйте их в качестве отправной точки.
Хью
Этот и несколько ответов рекомендуют 2006-01-02T15:04:05.000Zи упоминают, что Go также time.RFC3339будет работать. Но time.RFC3339 = "2006-01-02T15:04:05Z07:00". Являются ли эти два формата абсолютно эквивалентными, если time.Parseи time.ParseInLocationчто делать?
Майлз
1
Правильно, @Miles, этот тест подтверждает это play.golang.org/p/T3dW1kTeAHl
robstarbuck
24

Я предлагаю использовать постоянную time.RFC3339 из пакета времени. Вы можете проверить другие константы из пакета времени. https://golang.org/pkg/time/#pkg-constants

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Time parsing");
    dateString := "2014-11-12T11:45:26.371Z"
    time1, err := time.Parse(time.RFC3339,dateString);
    if err!=nil {
    fmt.Println("Error while parsing date :", err);
    }
    fmt.Println(time1); 
}
Нишант Рават
источник
Вы хотели использовать точку с запятой?
ChristoKiwi
20

Это довольно поздно для вечеринки, и на самом деле ничего не сказано о том, что еще не было сказано в той или иной форме, в основном по ссылкам выше, но я хотел дать резюме TL; DR тем, у кого меньше внимания:

Дата и время строки формата go очень важны. Это как Го знает, какое поле какое. Они обычно 1-9 слева направо следующим образом:

  • Январь / январь / январь / январь / 01 / _1 (и т. Д.) За месяц
  • 02 / _2 для дня месяца
  • 15/03 / _3 / PM / P / pm / p для часа и меридиана (3 часа дня)
  • 04 / _4 минуты
  • 05 / _5 за секунды
  • 2006/06 год
  • -0700 / 07:00 / MST для часового пояса
  • .999999999 / .000000000 и т. Д. За неполные секунды (я думаю, что различие заключается в том, что удаляются завершающие нули)
  • Понедельник / понедельник - день недели (который на самом деле был 01-02-2006),

Поэтому не пишите «01-05-15» в качестве формата даты, если вы не хотите «Месяц-Второй-Час»

(... опять же, это было в основном резюме выше.)

Лорен Осборн
источник
5

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

Я попытался найти библиотеки, и я нашел это:

https://github.com/araddon/dateparse

Пример из README:

package main

import (
    "flag"
    "fmt"
    "time"

    "github.com/apcera/termtables"
    "github.com/araddon/dateparse"
)

var examples = []string{
    "May 8, 2009 5:57:51 PM",
    "Mon Jan  2 15:04:05 2006",
    "Mon Jan  2 15:04:05 MST 2006",
    "Mon Jan 02 15:04:05 -0700 2006",
    "Monday, 02-Jan-06 15:04:05 MST",
    "Mon, 02 Jan 2006 15:04:05 MST",
    "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
    "Mon, 02 Jan 2006 15:04:05 -0700",
    "Thu, 4 Jan 2018 17:53:36 +0000",
    "Mon Aug 10 15:44:11 UTC+0100 2015",
    "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
    "12 Feb 2006, 19:17",
    "12 Feb 2006 19:17",
    "03 February 2013",
    "2013-Feb-03",
    //   mm/dd/yy
    "3/31/2014",
    "03/31/2014",
    "08/21/71",
    "8/1/71",
    "4/8/2014 22:05",
    "04/08/2014 22:05",
    "4/8/14 22:05",
    "04/2/2014 03:00:51",
    "8/8/1965 12:00:00 AM",
    "8/8/1965 01:00:01 PM",
    "8/8/1965 01:00 PM",
    "8/8/1965 1:00 PM",
    "8/8/1965 12:00 AM",
    "4/02/2014 03:00:51",
    "03/19/2012 10:11:59",
    "03/19/2012 10:11:59.3186369",
    // yyyy/mm/dd
    "2014/3/31",
    "2014/03/31",
    "2014/4/8 22:05",
    "2014/04/08 22:05",
    "2014/04/2 03:00:51",
    "2014/4/02 03:00:51",
    "2012/03/19 10:11:59",
    "2012/03/19 10:11:59.3186369",
    // Chinese
    "2014年04月08日",
    //   yyyy-mm-ddThh
    "2006-01-02T15:04:05+0000",
    "2009-08-12T22:15:09-07:00",
    "2009-08-12T22:15:09",
    "2009-08-12T22:15:09Z",
    //   yyyy-mm-dd hh:mm:ss
    "2014-04-26 17:24:37.3186369",
    "2012-08-03 18:31:59.257000000",
    "2014-04-26 17:24:37.123",
    "2013-04-01 22:43",
    "2013-04-01 22:43:22",
    "2014-12-16 06:20:00 UTC",
    "2014-12-16 06:20:00 GMT",
    "2014-04-26 05:24:37 PM",
    "2014-04-26 13:13:43 +0800",
    "2014-04-26 13:13:44 +09:00",
    "2012-08-03 18:31:59.257000000 +0000 UTC",
    "2015-09-30 18:48:56.35272715 +0000 UTC",
    "2015-02-18 00:12:00 +0000 GMT",
    "2015-02-18 00:12:00 +0000 UTC",
    "2017-07-19 03:21:51+00:00",
    "2014-04-26",
    "2014-04",
    "2014",
    "2014-05-11 08:20:13,787",
    // mm.dd.yy
    "3.31.2014",
    "03.31.2014",
    "08.21.71",
    //  yyyymmdd and similar
    "20140601",
    // unix seconds, ms
    "1332151919",
    "1384216367189",
}

var (
    timezone = ""
)

func main() {
    flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
    flag.Parse()

    if timezone != "" {
        // NOTE:  This is very, very important to understand
        // time-parsing in go
        loc, err := time.LoadLocation(timezone)
        if err != nil {
            panic(err.Error())
        }
        time.Local = loc
    }

    table := termtables.CreateTable()

    table.AddHeaders("Input", "Parsed, and Output as %v")
    for _, dateExample := range examples {
        t, err := dateparse.ParseLocal(dateExample)
        if err != nil {
            panic(err.Error())
        }
        table.AddRow(dateExample, fmt.Sprintf("%v", t))
    }
    fmt.Println(table.Render())
}
stevenferrer
источник
2
К сожалению, в дневном месячном порядке есть неоднозначность. Европейский формат даты дает первый день и второй месяц, а американский формат времени использует обратное. Дата как 3/5/2004 неоднозначна. Дата действительна в американском и европейском формате, но 3 и 5 могут соответствовать дню и месяцу или наоборот. dateParse принимает формат США.
Чмике
-1

Если вы работали с форматированием времени / даты / анализом на других языках, вы могли заметить, что на других языках используются специальные заполнители для форматирования времени / даты. Например, для использования языка рубин

%d for day
%Y for year

и т. д. Golang вместо кодов, таких как выше, использует заполнители формата даты и времени, которые выглядят только как дата и время. Go использует стандартное время, которое:

Mon Jan 2 15:04:05 MST 2006  (MST is GMT-0700)
or 
01/02 03:04:05PM '06 -0700

Так что, если вы заметили, Go использует

01 for the day of the month,
02 for the month
03 for hours,
04 for minutes
05 for second
and so on

Поэтому, например, для синтаксического анализа 2020-01-29 строка макета должна быть 06-01-02 или 2006-01-02.

Вы можете обратиться к полной таблице макета заполнителя по этой ссылке - https://golangbyexample.com/parse-time-in-golang/

user27111987
источник