2

I'm attempting to parse a Datetime in revel from a json request. The request looks something like this:

{
  "startedAt": "2017-06-01 10:39",
  ...
}

The struct it's being decoded into looks like this:

type MyStruct struct {
  StartedAt time.Time `json:"startedAt" bson:"startedAt"`
  ...
}

The decode line looks like this:

json.NewDecoder(c.Request.Body).Decode(&MyStruct)

Revel returns this error:

interface conversion: error is *time.ParseError, not *errors.Error

According to revel's documentation here, https://revel.github.io/manual/parameters.html

The SQL standard date time formats of 2006-01-02, 2006-01-02 15:04 are built in.

In the same document, they also say you can append formats like this:

func init() {
    revel.TimeFormats = append(revel.TimeFormats, "01/02/2006")
}

To verify the format was in the array, I tried this:

func init() {
    revel.TimeFormats = append(revel.TimeFormats, "2016-06-01 12:12")
}

In a final act of desperation I tried submitting it in the same format that revel will return json times in:

{
  "startedAt": "2017-06-22T12:22:16.265618819-05:00",
  ...
}

At this point I'm not sure where to go with this. Has anyone been able to get revel to parse a Datetime?


Update: I tried RickyA's solution below, but now there is a parsing error.

parsing time ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon, 02 Jan 2006 15:04:05 -0700": cannot parse ""Mon, 02 Jan 2006 15:04:05 -0700"" as "Mon"

What's even stranger is that I implemented a bit of a hack to get this working in the interrum. I changed the request time field to a string, and gave it a ToX function which converted it. That function works, but when it's moved into the UnmarshalJSON function it fails.

Also, I can't tell if this is a bug or not:

func (t *AnyTime) UnmarshalJSON(b []byte) error {
  fmt.Println(time.RFC1123Z)
  fmt.Println(string(b))
  fmt.Println(reflect.TypeOf(time.RFC1123Z))
  fmt.Println(reflect.TypeOf(string(b)))
  ...

This outputs this:

Mon, 02 Jan 2006 15:04:05 -0700
"Mon, 02 Jan 2006 15:04:05 -0700"
string
string

Notice that only 1 of the string has double quotes. This is leading me to believe that some how the byte array passed to UnmarshalJSON has double quotes within its string.


Final Update: So I'm pretty sure the double quotes are intentional, and in a way it makes sense. The UnmarshalJSON is passing everything from the ':' through the ',' which includes the double quotes. I'm guessing this is done so that it can also support integers and booleans. I don't like the solution, but this is how I fixed it:

func (t *AnyTime) UnmarshalJSON(b []byte) error {
  var err error
  sTime := string(b)
  sTime = strings.TrimSuffix(sTime, "\"")
  sTime = strings.TrimPrefix(sTime, "\"")
  t.Time, err = time.Parse(time.RFC1123Z, sTime)
  ...
3
  • try revel.TimeFormats = append(revel.TimeFormats, "2006-02-01 15:04") instead: stackoverflow.com/questions/25845172/… Commented Jun 22, 2017 at 17:38
  • Thanks for the response, but I'm getting the same error Commented Jun 22, 2017 at 17:48
  • I realized after the fact, that I'm not using revel to parse the json. This is probably why adding the time format doesn't work. I've updated the post accordingly. Commented Jun 22, 2017 at 17:55

1 Answer 1

3

I had the same problem and ended up creating a custom type for time.Time

package genericfields

import (
    "encoding/json"
    "time"
    "strings"

    "gopkg.in/mgo.v2/bson"
)

// ANYTIME //

// AnyTime accepts any time format for its unmarshaling //
type AnyTime struct{ time.Time }

func (t AnyTime) MarshalJSON() ([]byte, error) {
    return json.Marshal(t.Time)
}

func (t *AnyTime) UnmarshalJSON(b []byte) error {
    err := json.Unmarshal(b, &t.Time)
    if err != nil { //assume non tz time input
        bstr := strings.Trim(string(b), `"`)
        t.Time, err = time.Parse("2006-01-02T15:04:05", bstr)
        if err != nil {
            return err //TODO add more formats to try
        }
    }
    return nil
}

func (t AnyTime) GetBSON() (interface{}, error) {
    return t.Time, nil
}

func (t *AnyTime) SetBSON(raw bson.Raw) error {
    var tm time.Time
    err := raw.Unmarshal(&tm)
    if err != nil {
        return err
    }
    t.Time = tm.UTC()
    return nil
}

func (t AnyTime) ToTime() time.Time {
    return t.Time
}

Usage:

type MyStruct struct {
  StartedAt genericfields.AnyTime `json:"startedAt" bson:"startedAt"`
  ...
}

You might need to tweak the input for time.Parse somewhat.

Sign up to request clarification or add additional context in comments.

3 Comments

I tried implementing your fix, but I'm still getting an error. I've edited the post with the new information.
Ah, I was wondering why quote trimming line was in my code so I deleted it when posting. Now we know why :( Updated the answer.
I'm still marking you answer as correct, because it lead me to the solution. I updated my post 1 more time to include the final fix. Thanks again for the response.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.