4

I have two MongoDB servers. From one I receive data using mongo go driver. The received data has one date field which is always null. Then in my code I may or may not change it to some other date or leave it as null and put the received data to the other server.

The problem is that when I post the data, the time field turns to

Date(-62135596800000) instead of null.

I have tried to assign time.Time{} and the code below didn't solve the problem as well.

t, err := time.Parse("2006-01-02T15:04:05Z", "0001-01-01T00:00:01Z")
            if err != nil {
                fmt.Println(err)
            }
            retrieved[i].SessionEndedDateUTC = t

Everytime I get Date(-62135596800000) instead of null, even if I retrieve the data and upsert it without modification.

icza
  • 389,944
  • 63
  • 907
  • 827
SokIsKedu
  • 196
  • 1
  • 2
  • 17

2 Answers2

6

In Go time.Time is a struct which cannot have a nil value. It has a zero value (which is all struct fields having their zero values), but this zero value corresponds to MongoDB ISODate("0001-01-01T00:00:00Z") and not to the MongoDB null value.

If your field is of type time.Time, you cannot set any value to it to end up MongoDB null value.

The simplest solution is to use a field of type pointer to time.Time instead, that is *time.Time. If you leave or set this field to Go nil, it will end up as null in MongoDB.

If you can't or don't want to use *time.Time, there is still a "workaround": declare 2 fields, one is of your "regular" time.Time type, and use a struct tag to exclude this from MongoDB. And add another field of type *time.Time, and make this mapped to MongoDB. And write a custom marshaling / unmarshaling logic, which when marshaling would update this extra field based on the original, or would set the original based on the extra when unmarshaling.

This is an example how it could look like:

type User struct {
    CreatedAt  time.Time  `bson:"-"`
    PCreatedAt *time.Time `bson:"created"`
}

func (u *User) GetBSON() (interface{}, error) {
    if u.CreatedAt.IsZero() {
        u.PCreatedAt = nil
    } else {
        u.PCreatedAt = &u.CreatedAt
    }
    return u, nil
}

func (u *User) SetBSON(raw bson.Raw) (err error) {
    if err = raw.Unmarshal(u); err != nil {
        return
    }
    if u.PCreatedAt == nil {
        u.CreatedAt = time.Time{}
    } else {
        u.CreatedAt = *u.PCreatedAt
    }
    return
}

Explanation:

User.CreatedAt holds the time.Time value which you can work with (read / write). This field is excluded from MongoDB.

There is a pointer User.PCreatedAt field which is mapped to the created property in MongoDB.

When a User is marshaled (saved to MongoDB), GetBSON() is called. If CreatedAt is the zero value, sets PCreatedAt to nil which will end up as null in MongoDB. Else sets / uses the non-zero timestamp.

When a User is unmarshaled (loaded from MongoDB), SetBSON() is called. This checks if PCreatedAt is nil (which corresponds to MongoDB null), and if so, sets CreatedAt to its zero value. Else uses the timestamp retrieved from MongoDB.

Related / similar questions:

Set default date when inserting document with time.Time field

Accesing MongoDB from Go

Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827
  • Thanks so much. I won't be doing this exactly how you showed me, but your answer gave me a very good idea how to solve this. Thanks again! – SokIsKedu Apr 27 '17 at 09:40
0

mongodb doc

Behavior

Internally, Date objects are stored as a 64 bit integer representing the number of milliseconds since the Unix epoch (Jan 1, 1970), which results in a representable date range of about 290 millions years into the past and future

see https://docs.mongodb.com/manual/reference/method/Date/

lxz
  • 1
  • I have read the documentation before. It is not a problem with mongodb. The probloem was with the Go language, because it automatically sets a Date value o a null field. I had to work it out with pointers very similar how icza suggested. Still thanks – SokIsKedu Apr 28 '17 at 07:43