'gin/golang gin-gonic does not parse time.Time properly for unix json?

I cannot find the way to execute this code properly with UNIX:

package main

import (
  "time"
  "github.com/gin-gonic/gin"
  "net/http"
)

type Things struct {
  Name string `json:"name"`
  OneDay time.Time `json:"oneDay"`
}

type Example struct {
  Things []Things `json:"things"`
  Something int `json:"something"`
}

func TestGinGo(c *gin.Context) {
  var example Example
  c.BindJSON(&example)
  c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
  r := gin.Default()

  r.POST("/", TestGinGo)

  r.Run("0.0.0.0:8080")
}

I call this endpoint like this:

curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "things": [{
        "name": "bling",
        "oneDay": "2020-01-01T00:00:00Z"
    }],
    "something": 2
}'

The response is correct:

{
    "data": {
        "things": [
            {
                "name": "bling",
                "oneDay": "2020-01-01T00:00:00Z"
            }
        ],
        "something": 2
    }
}

Now I change slightly the code to work with UNIX like this:

package main

import (
  "time"
  "github.com/gin-gonic/gin"
  "net/http"
)

type Things struct {
  Name string `json:"name"`
  OneDay time.Time `json:"oneDay" time_format:"unix"`
}

type Example struct {
  Things []Things `json:"things"`
  Something int `json:"something"`
}

func TestGinGo(c *gin.Context) {
  var example Example
  c.BindJSON(&example)
  c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
  r := gin.Default()

  r.POST("/", TestGinGo)

  r.Run("0.0.0.0:8080")
}

And I call it like this:

curl --location --request POST 'localhost:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "things": [{
        "name": "bling",
        "oneDay": 1589898758007
    }],
    "something": 2
}'

And I get this error now (400 bad format):

{"data":{"things":[{"name":"bling","oneDay":"0001-01-01T00:00:00Z"}],"something":0}}

I get into the library... and I see that the code is there to use "unix":

https://github.com/gin-gonic/gin/blob/master/binding/form_mapping.go#L272

I would really like to use unix, because many languages don't need a library for using unix, and I don't want to force an specific format to be used by consumer... And I don't see where I am missing the ball here...



Solution 1:[1]

  1. time_format tag is only used in form binding, not json;
  2. You can use custom Marshal and Unmarshal functions (see https://pkg.go.dev/encoding/json#example-package-CustomMarshalJSON)
package main

import (
    "encoding/json"
    "github.com/gin-gonic/gin"
    "log"
    "net/http"
    "time"
)

type myTime time.Time

func (mt *myTime) UnmarshalJSON(bs []byte) error {
    var timestamp int64
    err := json.Unmarshal(bs, &timestamp)
    if err != nil {
        return err
    }

    *mt = myTime(time.Unix(timestamp/1000, timestamp%1000*1e6))
    return nil
}

func (mt myTime) MarshalJSON() ([]byte, error) {
    timestamp := time.Time(mt).UnixNano() / 1e6
    log.Println(time.Time(mt).UnixNano())
    return json.Marshal(timestamp)
}

type Timestamp struct {
    OneDay     myTime    `json:"oneDay" form:"oneDay"`
    AnotherDay time.Time `json:"anotherDay" form:"anotherDay" time_format:"unix"`
}

func parseTime(c *gin.Context) {
    var example Timestamp
    if err := c.Bind(&example); err != nil {
        log.Printf("bind timestamp error: %s", err)
    }
    c.JSON(http.StatusOK, gin.H{"data": example})
}

func main() {
    r := gin.Default()

    r.POST("/time", parseTime)

    r.Run("0.0.0.0:8080")
}
  1. send as json
curl --location --request POST 'localhost:8080/time' \
--header 'Content-Type: application/json' \
--data '{
  "oneDay": 1589898758007,
  "anotherDay": "1589898758"
}'

oneDay is right, anotherDay doesn't work

{"data":{"oneDay":1589898758007,"anotherDay":"0001-01-01T00:00:00Z"}}
  1. send as form
curl --location --request POST 'localhost:8080/time' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'oneDay=1589898758007&anotherDay=1589898758'

both are right

{"data":{"oneDay":1589898758007,"anotherDay":"2020-05-19T22:32:38+08:00"}}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1