Analizowanie ciągu daty w Go

138

Próbowałem przeanalizować ciąg daty "2014-09-12T11:45:26.371Z"w Go.

Kod

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

Mam ten błąd:

czas analizy „2014-11-12T11: 47: 39.489Z”: miesiąc poza zakresem

Jak mogę przeanalizować ten ciąg daty?

kannanrbk
źródło
Dla przyszłych czytelników napisałem kilka ćwiczeń do ćwiczenia analizowania dat github.com/soniah/date_practice
Sonia Hamilton
Twój układ powinien być dokładnie 2006-01-02T15:04:05.000Zspowodowany jakimś szalonym standardem
tharinduwijewardane

Odpowiedzi:

164

Użyj dokładnych numerów układów opisanych tutaj i ładnego posta na blogu tutaj .

więc:

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)

daje:

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

Wiem. Zadziwiające. Złapał mnie też pierwszy raz. Go po prostu nie używa abstrakcyjnej składni dla składników datetime ( YYYY-MM-DD), ale te dokładne liczby ( myślę, że czas pierwszego zatwierdzenia go nie , zgodnie z tym . Czy ktoś wie?).

RickyA
źródło
118
Numery układów? Co? Czemu? Argh!
Darth Egregious
29
Co oni sobie myśleli ! ? czy palisz?
Jishnu Prathap
14
Mogę się trochę spóźnić z komentarzami, ale nie bój się układu z liczbami, po prostu użyj stałych, a Twój kod będzie czysty :) np. Czas.RFC3339
Davyd Dzhahaiev
11
Dla tych, którzy nie mają numerów układu, przyznaję, że na pierwszy rzut oka jest to bardzo obce, ale kiedy już się do tego przyzwyczaisz, wydaje mi się, że ma to co najmniej tyle samo sensu co typowe urządzenia układu („Czy używam„ D ”, „d”, „dd”, „DD” itp.) i prawdopodobnie bardziej sensowne. Po prostu musisz najpierw o tym wiedzieć.
trójka
5
Ma to na celu mnemonik, to znaczy wystarczy zapamiętać 1, 2, 3, 4, 5, 6, 7 tych liter. Jest świetny artykuł omawiający to: medium.com/@simplyianm/…
amigcamel
85

Układ do stosowania jest rzeczywiście „ 2006-01-02T15:04:05.000Z” opisany w RickyA „s odpowiedź .
To nie jest „czas pierwszego zatwierdzenia go”, ale raczej mnemoniczny sposób zapamiętania wspomnianego układu.
Zobacz opakowanie / czas :

Czas odniesienia używany w układach to:

Mon Jan 2 15:04:05 MST 2006

czyli czas uniksowy 1136239445.
Ponieważ MST to GMT-0700, czas odniesienia można traktować jako

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

(1, 2, 3, 4, 5, 6, 7, pod warunkiem, że pamiętasz, że 1 oznacza miesiąc, a 2 dzień, co nie jest łatwe dla Europejczyka takiego jak ja, używanego do formatu dzień-miesiąc)

Jak pokazano w „ time.parse: dlaczego golang niepoprawnie analizuje czas? ”, Ten układ (używając 1,2,3,4,5,6,7) musi być dokładnie przestrzegany .

VonC
źródło
Tak, to też przyciąga Australijczyków! MM / DD po ​​prostu nie oblicza dla mnie i muszę na to patrzeć.
Simon Whitehead
3
@SimonWhitehead Zgadzam się. Przynajmniej jak to sprawdzę, wiem, co oznaczają RR, MM, DD, hh, mm, ss i mogę je łatwo zamówić ponownie. W Go, nawet po sprawdzeniu tego, muszę pamiętać, co oznacza 1, 2, 3, 4 ...
VonC
1
Pamiętam to następująco: Krok 1) „Właściwa” kolejność dat to rok> miesiąc> dzień> godzina> minuta> sekunda> itd. (Ponieważ mixed-endian byłby po prostu pozbawiony sensu arbitralny i niespójny, a dla dat duże- endian jest lepszy niż little-endian, ponieważ jest przyjazny dla sortowania.) To dałoby nam 1 (rok), 2 (miesiąc), 3 (dzień), [...] Krok 2) Go / Google są Amerykanami i Amerykanie stawiają rok pod koniec ich dat, więc zamiast tego jest 1 (miesiąc), 2 (dzień), [...], n (rok) Krok 3) Strefa czasowa obejmuje wszystko inne, ponieważ strefy czasowe są dodatkową warstwą abstrakcji.
mtraceur
@mtraceur Tak ... Tęsknię za web.archive.org/web/20180501100155/http://... (z github.com/bdotdub/fuckinggodateformat ). Mam na myśli strftimeFTW.
VonC
58

Odpowiedziałem, ale aby zaoszczędzić na wpisywaniu "2006-01-02T15:04:05.000Z"dla układu, możesz użyć stałej pakietu 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
źródło
1
Dodatkowo, jeśli spojrzysz na stałe pakietu (połączone w powyższej odpowiedzi), istnieje kilka innych popularnych formatów, których można użyć. Jeśli potrzebujesz czegoś nieco innego, użyj ich jako punktu wyjścia.
Hugh
Ta i kilka odpowiedzi poleca 2006-01-02T15:04:05.000Zi wspomina, że ​​Go time.RFC3339również będzie działać. Ale time.RFC3339 = "2006-01-02T15:04:05Z07:00". Czy te dwa formaty są dokładnie równoważne, jeśli chodzi o to, co time.Parsei time.ParseInLocationbędą robić?
Miles
1
Zgadza się @Miles, ten test potwierdza, że play.golang.org/p/T3dW1kTeAHl
robstarbuck
24

Zasugeruję użycie stałej time.RFC3339 z pakietu time. Możesz sprawdzić inne stałe z pakietu czasowego. 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); 
}
Nishant Rawat
źródło
Czy chodziło Ci o użycie średników?
ChristoKiwi
20

To dość późno na imprezę i tak naprawdę nie mówię niczego, co nie zostało już powiedziane w takiej czy innej formie, głównie za pośrednictwem powyższych linków, ale chciałem dać podsumowanie TL; DR tym, którzy mają mniejszą uwagę:

Data i godzina ciągu formatu go są bardzo ważne. W ten sposób Go wie, które pole jest które. Zazwyczaj są one od 1 do 9 od lewej do prawej w następujący sposób:

  • Styczeń / Sty / Styczeń / Sty / 01 / _1 (itd.) Dotyczą miesiąca
  • 02 / _2 oznacza dzień miesiąca
  • 15/03 / _3 / PM / P / pm / p dotyczą godziny i południka (15:00)
  • 04 / _4 oznacza minuty
  • 05 / _5 oznacza sekundy
  • 2006/06 są na rok
  • -0700 / 07:00 / MST dotyczą strefy czasowej
  • .999999999 / .000000000 itd. Dotyczą częściowych sekund (myślę, że rozróżnienie dotyczy usunięcia zera końcowego)
  • Poniedziałek / poniedziałek to dzień tygodnia (którym faktycznie był 01-02-2006),

Dlatego nie pisz „01-05-15” jako formatu daty, chyba że chcesz „Miesiąc-Druga-Godzina”

(... znowu, było to w zasadzie podsumowanie powyższego.)

Loren Osborn
źródło
5

To może być bardzo późne, ale to jest dla osób, które mogą natknąć się na ten problem i mogą chcieć użyć zewnętrznego pakietu do analizy ciągu daty.

Próbowałem poszukać bibliotek i znalazłem tę:

https://github.com/araddon/dateparse

Przykład z 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
źródło
2
Niestety istnieje niejednoznaczność w kolejności doby i miesiąca. Europejski format daty podaje pierwszy dzień i drugi miesiąc, a amerykański format czasu używa odwrotności. Data taka jak 3/5/2004 jest niejednoznaczna. Data obowiązuje w formacie amerykańskim i europejskim, ale 3 i 5 mogą oznaczać dzień i miesiąc lub odwrotnie. dateParse przyjmuje format amerykański.
chmike
-1

Jeśli pracowałeś z formatowaniem / analizowaniem czasu / daty w innych językach, być może zauważyłeś, że inne języki używają specjalnych symboli zastępczych do formatowania godziny / daty. Na przykład używa języka ruby

%d for day
%Y for year

itd. Golang, zamiast używać kodów takich jak powyżej, używa symboli zastępczych formatu daty i czasu, które wyglądają tylko jak data i godzina. Go używa czasu standardowego, czyli:

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

Więc jeśli zauważysz, że Go używa

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

Dlatego na przykład podczas analizowania 2020-01-29 ciąg układu powinien mieć postać 06-01-02 lub 2006-01-02.

Możesz zapoznać się z pełną tabelą układu zastępczego pod tym linkiem - https://golangbyexample.com/parse-time-in-golang/

user27111987
źródło