Marshaling et dé-marshalling des formats de date personnalisés dans Go

Je pense que beaucoup conviendront que travailler avec des dates dans presque tous les langages de programmation est un casse-tête incroyable. La date et l'heure ne sont pas décimales, les fuseaux horaires - oh mon Dieu, pourquoi ils ne peuvent pas être uniquement des entiers - et bien sûr, d'innombrables formats de date et d'heure. Vous pouvez, bien sûr, affirmer qu'ils disent qu'il existe ISO 8601 et pourquoi avez-vous besoin d'un imbécile, mais avouons-le, disons - à quelle fréquence avez-vous rencontré la conformité à cette norme dans les API tierces? Je ne sais pas comment ça se passe à l'étranger avec ça, j'espère qu'ils me le diront dans les commentaires, mais dans les étendues post-soviétiques, la situation est d'étreindre et de pleurer. Chacun utilise son propre format d'heure et de date, ce qui n'est pratique que pour lui, et gère-le comme tu veux.


Je parlerai de ma propre expérience et de la solution trouvée.


Le problème auquel je faisais face ne semblait pas si mal, le format de date dans l'API que je devais traiter ressemblait à ceci: AAAA-MM-JJ hh: mm: ss . Ça a l'air normal, non?


Non.


Absolument anormal.


Ceux qui connaissent le format ISO 8601 ont déjà réalisé le hic. Pour plus de clarté:


package main

import (
    "encoding/json"
    "log"
    "time"
)

type Dated struct {
    DateTime time.Time
}

func main() {
    input := []byte("{\"datetime\": \"1900-01-01 12:00:04\"}")
    var d Dated
    err := json.Unmarshal(input, &d)
    if err != nil {
        log.Fatal(err)
    }
}

À la suite de quoi? C'est vrai, l'erreur est:


parsing time ""1900-01-01 12:00:04"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 12:00:04"" as "T"

Ce qui est logique, car Go dispose dès le départ d'un nombre limité de formats d'heure et de date
qu'il peut analyser:


ANSIC       = "Mon Jan _2 15:04:05 2006"
UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
RFC822      = "02 Jan 06 15:04 MST"
RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339     = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen     = "3:04PM"
// Handy time stamps.
Stamp      = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano  = "Jan _2 15:04:05.000000000"

À partir d'ici: https://golang.org/src/time/format.go


Comme vous pouvez le voir, le format YYYY-MM-DD hh:mm:ssn'est pas là. D'accord, le problème est clair, mais que faire alors?


, , Go json.Unmarshall. — , JSON, , , UnmasrhallJSON,
.


, ,
, Unmarshaler, UnmasrhallJSON:


type CustomDate struct {
    time.Time
}

func (c *CustomDate) UnmarshalJSON(b []byte) (err error) {
    layout := "2006-01-02 15:04:05"

    s := strings.Trim(string(b), "\"") // remove quotes
    if s == "null" {
        return
    }
    c.Time, err = time.Parse(layout, s)
    return
}

type Dated struct {
    DateTime CustomDate
}

, , API, , , . :


package main

import (
    "encoding/json"
    "fmt"
    "log"
    "strings"
    "time"
)

type CustomDate struct {
    time.Time
}

const layout = "2006-01-02 15:04:05"

func (c *CustomDate) UnmarshalJSON(b []byte) (err error) {
    s := strings.Trim(string(b), `"`) // remove quotes
    if s == "null" {
        return
    }
    c.Time, err = time.Parse(layout, s)
    return
}

func (c CustomDate) MarshalJSON() ([]byte, error) {
    if c.Time.IsZero() {
        return nil, nil
    }
    return []byte(fmt.Sprintf(`"%s"`, c.Time.Format(layout))), nil
}

type Dated struct {
    DateTime CustomDate
}

func main() {
    input := []byte("{\"datetime\": \"1900-01-01 12:00:04\"}")
    var d Dated
    err := json.Unmarshal(input, &d)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Unmarshal:")
    log.Println(d.DateTime)

    b, err := json.Marshal(d)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Marshal:")
    log.Println(string(b))
}

Marshal/Unmarshal. — MarchalJSON , UnmarshalJSON —

:


Unmarshal:
1900-01-01 12:00:04 +0000 UTC
Marshal:
{"DateTime":"1900-01-01T12:00:04Z"}

, , .



Go — , , /, , ; — .


Si vous êtes prêt à proposer d'autres moyens intéressants pour résoudre ce problème - je serai heureux de les voir dans les commentaires. Eh bien, en conclusion, je tiens à vous souhaiter moins de traiter des formats non standard de quoi que ce soit.


Merci à tous.


All Articles