time_codec.go raw
1 // Copyright (C) MongoDB, Inc. 2017-present.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may
4 // not use this file except in compliance with the License. You may obtain
5 // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7 package bsoncodec
8
9 import (
10 "fmt"
11 "reflect"
12 "time"
13
14 "go.mongodb.org/mongo-driver/bson/bsonoptions"
15 "go.mongodb.org/mongo-driver/bson/bsonrw"
16 "go.mongodb.org/mongo-driver/bson/bsontype"
17 "go.mongodb.org/mongo-driver/bson/primitive"
18 )
19
20 const (
21 timeFormatString = "2006-01-02T15:04:05.999Z07:00"
22 )
23
24 // TimeCodec is the Codec used for time.Time values.
25 //
26 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
27 // TimeCodec registered.
28 type TimeCodec struct {
29 // UseLocalTimeZone specifies if we should decode into the local time zone. Defaults to false.
30 //
31 // Deprecated: Use bson.Decoder.UseLocalTimeZone instead.
32 UseLocalTimeZone bool
33 }
34
35 var (
36 defaultTimeCodec = NewTimeCodec()
37
38 // Assert that defaultTimeCodec satisfies the typeDecoder interface, which allows it to be used
39 // by collection type decoders (e.g. map, slice, etc) to set individual values in a collection.
40 _ typeDecoder = defaultTimeCodec
41 )
42
43 // NewTimeCodec returns a TimeCodec with options opts.
44 //
45 // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
46 // TimeCodec registered.
47 func NewTimeCodec(opts ...*bsonoptions.TimeCodecOptions) *TimeCodec {
48 timeOpt := bsonoptions.MergeTimeCodecOptions(opts...)
49
50 codec := TimeCodec{}
51 if timeOpt.UseLocalTimeZone != nil {
52 codec.UseLocalTimeZone = *timeOpt.UseLocalTimeZone
53 }
54 return &codec
55 }
56
57 func (tc *TimeCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
58 if t != tTime {
59 return emptyValue, ValueDecoderError{
60 Name: "TimeDecodeValue",
61 Types: []reflect.Type{tTime},
62 Received: reflect.Zero(t),
63 }
64 }
65
66 var timeVal time.Time
67 switch vrType := vr.Type(); vrType {
68 case bsontype.DateTime:
69 dt, err := vr.ReadDateTime()
70 if err != nil {
71 return emptyValue, err
72 }
73 timeVal = time.Unix(dt/1000, dt%1000*1000000)
74 case bsontype.String:
75 // assume strings are in the isoTimeFormat
76 timeStr, err := vr.ReadString()
77 if err != nil {
78 return emptyValue, err
79 }
80 timeVal, err = time.Parse(timeFormatString, timeStr)
81 if err != nil {
82 return emptyValue, err
83 }
84 case bsontype.Int64:
85 i64, err := vr.ReadInt64()
86 if err != nil {
87 return emptyValue, err
88 }
89 timeVal = time.Unix(i64/1000, i64%1000*1000000)
90 case bsontype.Timestamp:
91 t, _, err := vr.ReadTimestamp()
92 if err != nil {
93 return emptyValue, err
94 }
95 timeVal = time.Unix(int64(t), 0)
96 case bsontype.Null:
97 if err := vr.ReadNull(); err != nil {
98 return emptyValue, err
99 }
100 case bsontype.Undefined:
101 if err := vr.ReadUndefined(); err != nil {
102 return emptyValue, err
103 }
104 default:
105 return emptyValue, fmt.Errorf("cannot decode %v into a time.Time", vrType)
106 }
107
108 if !tc.UseLocalTimeZone && !dc.useLocalTimeZone {
109 timeVal = timeVal.UTC()
110 }
111 return reflect.ValueOf(timeVal), nil
112 }
113
114 // DecodeValue is the ValueDecoderFunc for time.Time.
115 func (tc *TimeCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
116 if !val.CanSet() || val.Type() != tTime {
117 return ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: val}
118 }
119
120 elem, err := tc.decodeType(dc, vr, tTime)
121 if err != nil {
122 return err
123 }
124
125 val.Set(elem)
126 return nil
127 }
128
129 // EncodeValue is the ValueEncoderFunc for time.TIme.
130 func (tc *TimeCodec) EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
131 if !val.IsValid() || val.Type() != tTime {
132 return ValueEncoderError{Name: "TimeEncodeValue", Types: []reflect.Type{tTime}, Received: val}
133 }
134 tt := val.Interface().(time.Time)
135 dt := primitive.NewDateTimeFromTime(tt)
136 return vw.WriteDateTime(int64(dt))
137 }
138