@@ -120,6 +120,104 @@ func NewDatetime(t time.Time) (*Datetime, error) {
120
120
return dt , nil
121
121
}
122
122
123
+ func intervalFromDatetime (dtime * Datetime ) (ival Interval ) {
124
+ ival .Year = int64 (dtime .time .Year ())
125
+ ival .Month = int64 (dtime .time .Month ())
126
+ ival .Day = int64 (dtime .time .Day ())
127
+ ival .Hour = int64 (dtime .time .Hour ())
128
+ ival .Min = int64 (dtime .time .Minute ())
129
+ ival .Sec = int64 (dtime .time .Second ())
130
+ ival .Nsec = int64 (dtime .time .Nanosecond ())
131
+ ival .Adjust = NoneAdjust
132
+
133
+ return ival
134
+ }
135
+
136
+ func daysInMonth (year int64 , month int64 ) int64 {
137
+ if month == 12 {
138
+ year ++
139
+ month = 1
140
+ } else {
141
+ month += 1
142
+ }
143
+
144
+ // We use the fact that time.Date accepts values outside their usual
145
+ // ranges - the values are normalized during the conversion.
146
+ //
147
+ // So we got a day (year, month - 1, last day of the month) before
148
+ // (year, month, 1) because we pass (year, month, 0).
149
+ return int64 (time .Date (int (year ), time .Month (month ), 0 , 0 , 0 , 0 , 0 , time .UTC ).Day ())
150
+ }
151
+
152
+ // C imlementation:
153
+ // https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.c#L74-L98
154
+ func addMonth (ival * Interval , delta int64 , adjust Adjust ) {
155
+ oldYear := ival .Year
156
+ oldMonth := ival .Month
157
+
158
+ ival .Month += delta
159
+ if ival .Month < 1 || ival .Month > 12 {
160
+ ival .Year += ival .Month / 12
161
+ ival .Month %= 12
162
+ if ival .Month < 1 {
163
+ ival .Year --
164
+ ival .Month += 12
165
+ }
166
+ }
167
+ if adjust == ExcessAdjust || ival .Day < 28 {
168
+ return
169
+ }
170
+
171
+ dim := daysInMonth (ival .Year , ival .Month )
172
+ if ival .Day > dim || (adjust == LastAdjust && ival .Day == daysInMonth (oldYear , oldMonth )) {
173
+ ival .Day = dim
174
+ }
175
+ }
176
+
177
+ func (dtime * Datetime ) add (ival Interval , positive bool ) (* Datetime , error ) {
178
+ newVal := intervalFromDatetime (dtime )
179
+
180
+ var direction int64
181
+ if positive {
182
+ direction = 1
183
+ } else {
184
+ direction = - 1
185
+ }
186
+
187
+ addMonth (& newVal , direction * ival .Year * 12 + direction * ival .Month , ival .Adjust )
188
+ newVal .Day += direction * 7 * ival .Week
189
+ newVal .Day += direction * ival .Day
190
+ newVal .Hour += direction * ival .Hour
191
+ newVal .Min += direction * ival .Min
192
+ newVal .Sec += direction * ival .Sec
193
+ newVal .Nsec += direction * ival .Nsec
194
+
195
+ tm := time .Date (int (newVal .Year ), time .Month (newVal .Month ),
196
+ int (newVal .Day ), int (newVal .Hour ), int (newVal .Min ),
197
+ int (newVal .Sec ), int (newVal .Nsec ), dtime .time .Location ())
198
+
199
+ return NewDatetime (tm )
200
+ }
201
+
202
+ // Add creates a new Datetime as addition of the Datetime and Interval. It may
203
+ // return an error if a new Datetime is out of supported range.
204
+ func (dtime * Datetime ) Add (ival Interval ) (* Datetime , error ) {
205
+ return dtime .add (ival , true )
206
+ }
207
+
208
+ // Sub creates a new Datetime as subtraction of the Datetime and Interval. It
209
+ // may return an error if a new Datetime is out of supported range.
210
+ func (dtime * Datetime ) Sub (ival Interval ) (* Datetime , error ) {
211
+ return dtime .add (ival , false )
212
+ }
213
+
214
+ // Interval returns an Interval value to a next Datetime value.
215
+ func (dtime * Datetime ) Interval (next * Datetime ) Interval {
216
+ curIval := intervalFromDatetime (dtime )
217
+ nextIval := intervalFromDatetime (next )
218
+ return nextIval .Sub (curIval )
219
+ }
220
+
123
221
// ToTime returns a time.Time that Datetime contains.
124
222
func (dtime * Datetime ) ToTime () time.Time {
125
223
return dtime .time
0 commit comments