JohnWrensby JohnWrensby - 3 months ago 8
JSON Question

Parse .Net JSON date with Go

How can I parse this .Net JSON date with Go?
The value comes back unassigned.
It appears to parse up to the date field.

package main

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

type MyStruct struct {
FirstField string
SomeTime time.Time
LastField string
}

type MyStructSlice struct {
MyStructs []MyStruct
}

func main() {
var s MyStructSlice
str := `{"MyStructs":[{"FirstField":"123", "SomeTime":"\/Date(1432187580000-0500)\/", "LastField":"456"}]}`
json.Unmarshal([]byte(str), &s)
fmt.Println(s)
}


Go Playground

Answer

First you model is wrong, it doesn't model the data structure in your JSON. It should be:

type Data struct {
    Data []MyStruct `json:"data`
}
type MyStruct struct {
    SomeTime string
}

It works with this, try it on the Go Playground.

Problem is that we still have the time as string.

Now if you want SomeTime to be time.Time, you need to parse it yourself, you can do it by implementing json.Unmarshaler:

type Data struct {
    Data []MyStruct `json:"data`
}

type MyStruct struct {
    SomeTime time.Time
}

func (m *MyStruct) UnmarshalJSON(data []byte) error {
    // First unmashal it into a string:
    ms := struct{ SomeTime string }{}
    if err := json.Unmarshal(data, &ms); err != nil {
        return err
    }

    s := ms.SomeTime

    // s is of format: "/Date(1432187580000-0500)/"
    // extract millis and time zone offset hours
    i1 := strings.Index(s, "(")
    i3 := strings.Index(s, ")")
    i2 := strings.Index(s, "-")
    if i2 < 0 {
        i2 = strings.Index(s, "+")
    }
    if i1 < 0 || i2 < 0 || i3 < 0 {
        return errors.New("Invalid format")
    }

    millis, err := strconv.ParseInt(s[i1+1:i2], 10, 64)
    if err != nil {
        return err
    }
    m.SomeTime = time.Unix(0, millis*1000000)

    // Apply timezone:
    zoneHours, err := strconv.ParseInt(s[i2:i3], 10, 64)
    if err != nil {
        return err
    }
    zone := time.FixedZone("something", int(zoneHours)*3600)
    m.SomeTime = m.SomeTime.In(zone)
    return nil
}

Try it on the Go Playground.