Marcar y desmantelar formatos de fecha personalizados en Go

Creo que muchos estarán de acuerdo en que trabajar con fechas en casi cualquier lenguaje de programación es un dolor de cabeza increíble. La fecha y la hora no son decimales, zonas horarias, Dios mío, por qué no pueden ser enteros, y por supuesto, innumerables formatos de fecha y hora. Por supuesto, puede argumentar que dicen que hay ISO 8601 y para qué más necesita un tonto, pero seamos sinceros, digamos: ¿con qué frecuencia ha encontrado el cumplimiento de este estándar en API de terceros? No sé cómo van las cosas en el extranjero con esto, espero que me lo digan en los comentarios, pero en los espacios abiertos postsoviéticos la situación es abrazar y llorar. Cada uno usa su propio formato de hora y fecha, solo conveniente para él, y lo maneja como lo desee.


Hablaré sobre mi propia experiencia y sobre la solución encontrada.


El problema que enfrentaba no se veía tan mal, el formato de fecha en la API con el que tuve que lidiar se veía así: AAAA-MM-DD hh: mm: ss . Parece normal, ¿no?


No.


Absolutamente anormal.


Aquellos familiarizados con el formato ISO 8601 ya se han dado cuenta de la captura. Para mayor claridad:


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)
    }
}

¿Como resultado de qué? Así es, el error es:


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

Lo cual es lógico, porque Go tiene una cantidad limitada de formatos de fecha y hora
que puede analizar:


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"

Desde aquí: https://golang.org/src/time/format.go


Como puede ver, el formato YYYY-MM-DD hh:mm:ssno está allí. De acuerdo, el problema está claro, pero ¿qué hacer entonces?


, , 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 está listo para ofrecer otras formas interesantes de resolver este problema, me complacerá verlos en los comentarios. Bueno, en conclusión, quiero desearle menos que trate con formatos no estándar de cualquier cosa.


Gracias a todos.


All Articles