Analyse de la chaîne de date dans Go


138

J'ai essayé d'analyser la chaîne de date "2014-09-12T11:45:26.371Z"dans Go.

Code

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

J'ai eu cette erreur:

heure d'analyse "2014-11-12T11: 47: 39.489Z": mois hors limites

Comment puis-je analyser cette chaîne de date?


Pour les futurs lecteurs, j'ai écrit quelques exercices pour pratiquer l'analyse des dates github.com/soniah/date_practice
Sonia Hamilton

Votre mise en page devrait exactement être 2006-01-02T15:04:05.000Zdue à un standard fou
tharinduwijewardane

Réponses:


165

Utilisez les numéros de mise en page exacts décrits ici et un joli article de blog ici .

alors:

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)

donne:

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

Je connais. Esprit ahurissant. M'a également attrapé la première fois. Go n'utilise simplement pas de syntaxe abstraite pour les composants datetime ( YYYY-MM-DD), mais ces nombres exacts ( je pense que l'heure du premier commit de go Nope, d'après cela . Est-ce que quelqu'un le sait?).


119
Numéros de mise en page? Quoi? Pourquoi? Argh!
Darth Egregious

30
A quoi pensaient-ils ! ? ou fumer?
Jishnu Prathap

14
Je suis peut-être un peu en retard avec les commentaires ici, mais n'ayez pas peur de la mise en page avec des nombres, utilisez simplement des constantes et votre code serait propre :) par exemple time.RFC3339
Davyd Dzhahaiev

11
Pour ceux qui n'obtiennent pas les numéros de mise en page, j'admets que c'est très étranger à première vue, mais une fois que vous vous y êtes habitué, je pense que cela a au moins autant de sens que les dispositifs de mise en page typiques ('Dois-je utiliser "D", "d", "dd", "DD", etc?), et probablement plus de sens. Vous devez juste le savoir d'abord.
threeve

5
C'est à des fins mnémotechniques, c'est-à-dire que vous devez simplement vous rappeler 1, 2, 3, 4, 5, 6, 7 de ces lettres. Il y a un excellent article sur ce sujet: medium.com/@simplyianm
...

87

La mise en page à utiliser est en effet " 2006-01-02T15:04:05.000Z" décrite dans la réponse de RickyA . Ce n'est pas "l'heure du premier commit de go", mais plutôt un moyen mnémotechnique de se souvenir de ladite mise en page. Voir pkg / heure :

L'heure de référence utilisée dans les mises en page est:

Mon Jan 2 15:04:05 MST 2006

qui est l'heure Unix 1136239445.
Puisque MST est GMT-0700, l'heure de référence peut être considérée comme

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

(1, 2, 3, 4, 5, 6, 7, à condition que vous vous souveniez que 1 est pour le mois, et 2 pour le jour, ce qui n'est pas facile pour un Européen comme moi, habitué au format de date jour-mois)

Comme illustré dans " time.parse: pourquoi golang analyse-t-il l'heure de manière incorrecte? ", Cette disposition (en utilisant 1,2,3,4,5,6,7) doit être respectée exactement .


Ouais, ça surprend aussi les Australiens! MM / DD ne calcule pas pour moi et je dois continuer à le regarder.
Simon Whitehead

3
@SimonWhitehead Je suis d'accord. Au moins, une fois que je la recherche, je sais ce que signifient AA, MM, JJ, hh, mm, ss et je peux les commander à nouveau facilement. Avec Go, même après l'avoir recherché, je dois me souvenir de ce que représentent 1, 2, 3, 4 ...
VonC le

1
La façon dont je me souviens est: Étape 1) La commande date « appropriée » est année> mois> jour> heure> minute> seconde> etc. (Parce que mixte endian serait juste non sensically arbitraire et incohérente et pour les dates Big- endian est préférable à little-endian car il est facile à trier.) Cela nous donnerait 1 (année), 2 (mois), 3 (jour), [...] Étape 2) Go / Google sont américains et les Américains mettent leur année à la fin de leurs dates, donc à la place c'est 1 (mois), 2 (jour), [...], n (année) Étape 3) Le fuseau horaire passe après tout le reste parce que les fuseaux horaires sont une couche d'abstraction supplémentaire.
mtraceur

@mtraceur Oui ... Je manque aussi web.archive.org/web/20180501100155/http://... (de github.com/bdotdub/fuckinggodateformat ). Je veux dire, strftimeFTW.
VonC

58

Comme réponse, mais pour économiser la saisie "2006-01-02T15:04:05.000Z"pour la mise en page, vous pouvez utiliser la constante RFC3339 du package .

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


1
De plus, si vous regardez les constantes de package (liées dans la réponse ci-dessus), il existe un tas d'autres formats courants fournis qui peuvent être utilisés. Si vous avez besoin de quelque chose de légèrement différent, utilisez-les comme point de départ.
Hugh

Ceci et plusieurs réponses recommandent 2006-01-02T15:04:05.000Zet mentionnent que Go time.RFC3339fonctionnerait également. Mais time.RFC3339 = "2006-01-02T15:04:05Z07:00". Ces deux formats sont-ils exactement équivalents dans la mesure où que feront time.Parseet time.ParseInLocationferont?
Miles

1
C'est vrai @Miles, ce test le confirme play.golang.org/p/T3dW1kTeAHl
robstarbuck

24

Je suggérerai d'utiliser la constante time.RFC3339 du package time. Vous pouvez vérifier d'autres constantes du package de temps. 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); 
}

Vouliez-vous utiliser des points-virgules?
ChristoKiwi

20

C'est plutôt tard pour la fête, et je ne dis pas vraiment quoi que ce soit qui n'ait déjà été dit sous une forme ou une autre, principalement via les liens ci-dessus, mais je voulais donner un récapitulatif TL; DR à ceux qui ont moins d'attention:

La date et l'heure de la chaîne de format aller sont très importantes. C'est ainsi que Go sait quel champ est quel champ. Ils sont généralement 1-9 de gauche à droite comme suit:

  • Janvier / janvier / janvier / janvier / 01 / _1 (etc) sont pour le mois
  • 02 / _2 sont pour le jour du mois
  • 15/03 / _3 / PM / P / pm / p sont pour l'heure et le méridien (15h)
  • 04 / _4 sont pour les minutes
  • 05 / _5 sont pour les secondes
  • 2006/06 sont pour l'année
  • -0700 / 07:00 / MST sont pour le fuseau horaire
  • .999999999 / .000000000 etc. sont pour des secondes partielles (je pense que la distinction est si les zéros de fin sont supprimés)
  • Lundi / Lundi sont le jour de la semaine (ce qui était en fait le 01-02-2006),

Donc, n'écrivez pas "01-05-15" comme format de date, sauf si vous voulez "Mois-Seconde-Heure"

(... encore une fois, c'était essentiellement un résumé de ce qui précède.)


5

Cela peut être très tard, mais c'est pour les personnes qui pourraient tomber sur ce problème et pourraient vouloir utiliser un package externe pour analyser la chaîne de date.

J'ai essayé de chercher une bibliothèque et j'ai trouvé celle-ci:

https://github.com/araddon/dateparse

Exemple du 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())
}

2
Malheureusement, il existe une ambiguïté dans l'ordre jour-mois. Le format de date européen donne le premier jour et le mois second, et le format d'heure américain utilise l'inverse. Une date comme le 05/03/2004 est ambiguë. La date est valable au format américain et européen, mais 3 et 5 peuvent correspondre au jour et au mois ou inversement. dateParse prend le format américain.
chmike

-1

Si vous avez travaillé avec le formatage / l'analyse de l'heure / date dans d'autres langues, vous avez peut-être remarqué que les autres langues utilisent des espaces réservés spéciaux pour le formatage de l'heure / date. Par exemple, le langage ruby ​​utilise

%d for day
%Y for year

etc. Golang, au lieu d'utiliser des codes comme ci-dessus, utilise des espaces réservés au format de date et d'heure qui ressemblent à la date et à l'heure uniquement. Go utilise l'heure standard, qui est:

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

Donc, si vous remarquez que Go utilise

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

Par conséquent, par exemple pour l'analyse du 29/01/2020, la chaîne de mise en page doit être 06-01-02 ou 2006-01-02.

Vous pouvez vous référer au tableau de disposition complet des espaces réservés à ce lien - https://golangbyexample.com/parse-time-in-golang/

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.