Uploaded image for project: 'Go Driver'
  1. Go Driver
  2. GODRIVER-564

bson.EC.Time does not treat the zero time.Time value the same as the encoder

    • Type: Icon: Bug Bug
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 0.0.16
    • Affects Version/s: None
    • Component/s: None
    • None

      When storing structs in a collection which have a time.Time field that is
      uninitialized and therefore zero value for time.Time we see:

      {"time" : ISODate("0001-01-01T00:00:00Z")} 
      

      Which is what we want. When stored this way they will decode back to the zero
      time so that the time.Time.IsZero() will still return True. However when trying
      to query out these values you would naturally think to do:

      var zeroTime time.Time
      query := bson.NewDocument(
          bson.EC.Time("time", zeroTime,
      )
      

      This unintuitively produces different BSON than the encoder:

      // query.String()
      // bson.Document{bson.Element{[UTC datetime]"time": 1754-08-30 17:43:41.129 -0500 EST}}
      

      If you try to use the DateTime constructor you of course get an element for the Unix epoch time:

      var zeroTime time.Time
      query := bson.NewDocument(
          bson.EC.DateTime("time", zeroTime.Unix()),
      )
      
      // query.String()
      // bson.Document{bson.Element{[UTC datetime]"time": 1968-01-12 15:06:43.2 -0500 EST}}
      

      Looking at the encoder code we find that this is because when given a time.Time
      there is additional changes being made to the time.Time from our struct:

      // bson/encode.go L534
      case time.Time:
          elems = append(elems, EC.DateTime(key, convertTimeToInt64(t)))
      

      Which points us to the function convertTimeToInt64:

      // bson/encode.go L125
      func convertTimeToInt64(t time.Time) int64 {
      	return t.Unix()*1000 + int64(t.Nanosecond()/1e6)
      }
      

      If we then apply this same math to our attempted query it now produces the
      expected BSON:

      var zeroTime time.Time
      query := bson.NewDocument(
          bson.EC.DateTime("completeddate", zeroTime.Unix()*1000+int64(zeroTime.Nanosecond()/1e6)),
      )
      
      // query.String()
      // bson.Document{bson.Element{[UTC datetime]"completeddate": 0000-12-31 19:00:00 -0500 EST}}
      

      From a user perspective this is unexpected and unintuitive. I would expect the
      ElementConstructor which takes a higher level type to automatically perform any
      necessary transformations and produce the same result as encoding a struct.

            Assignee:
            mathew.robinson@mongodb.com Mathew Robinson (Inactive)
            Reporter:
            mathew.robinson@mongodb.com Mathew Robinson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: