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