@@ -94,6 +94,101 @@ 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
+ return int64 (time .Date (int (year ), time .Month (month ), 0 , 0 , 0 , 0 , 0 , time .UTC ).Day ())
118
+ }
119
+
120
+ // C imlementation:
121
+ // https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.c#L74-L98
122
+ func addMonth (ival * Interval , delta int64 , adjust Adjust ) {
123
+ oldYear := ival .Year
124
+ oldMonth := ival .Month
125
+
126
+ ival .Month += delta
127
+ if ival .Month < 1 || ival .Month > 12 {
128
+ ival .Year += ival .Month / 12
129
+ ival .Month %= 12
130
+ if ival .Month < 1 {
131
+ ival .Year --
132
+ ival .Month += 12
133
+ }
134
+ }
135
+ if adjust == ExcessAdjust || ival .Day < 28 {
136
+ return
137
+ }
138
+
139
+ dim := daysInMonth (ival .Year , ival .Month )
140
+ if ival .Day > dim ||
141
+ (adjust == LastAdjust && ival .Day == daysInMonth (oldYear , oldMonth )) {
142
+ ival .Day = dim
143
+ }
144
+ }
145
+
146
+ func (dtime * Datetime ) add (ival Interval , positive bool ) (* Datetime , error ) {
147
+ newVal := intervalFromDatetime (dtime )
148
+
149
+ var direction int64
150
+ if positive {
151
+ direction = 1
152
+ } else {
153
+ direction = - 1
154
+ }
155
+
156
+ addMonth (& newVal , direction * ival .Year * 12 + direction * ival .Month , ival .Adjust )
157
+ newVal .Day += direction * 7 * ival .Week
158
+ newVal .Day += direction * ival .Day
159
+ newVal .Hour += direction * ival .Hour
160
+ newVal .Min += direction * ival .Min
161
+ newVal .Sec += direction * ival .Sec
162
+ newVal .Nsec += direction * ival .Nsec
163
+
164
+ tm := time .Date (int (newVal .Year ), time .Month (newVal .Month ),
165
+ int (newVal .Day ), int (newVal .Hour ), int (newVal .Min ),
166
+ int (newVal .Sec ), int (newVal .Nsec ), dtime .time .Location ())
167
+
168
+ return NewDatetime (tm )
169
+ }
170
+
171
+ // Add creates a new Datetime as addition of the Datetime and Interval. It may
172
+ // return an error if a new Datetime is out of supported range,
173
+ // see [NewDatetime].
174
+ func (dtime * Datetime ) Add (ival Interval ) (* Datetime , error ) {
175
+ return dtime .add (ival , true )
176
+ }
177
+
178
+ // Add creates a new Datetime as subtraction of the Datetime and Interval. It
179
+ // may return an error if a new Datetime is out of supported range, see
180
+ // [NewDatetime].
181
+ func (dtime * Datetime ) Sub (ival Interval ) (* Datetime , error ) {
182
+ return dtime .add (ival , false )
183
+ }
184
+
185
+ // Interval returns an Interval between a next Datetime value.
186
+ func (dtime * Datetime ) Interval (next * Datetime ) Interval {
187
+ curIval := intervalFromDatetime (dtime )
188
+ nextIval := intervalFromDatetime (next )
189
+ return nextIval .Sub (curIval )
190
+ }
191
+
97
192
// ToTime returns a time.Time that Datetime contains.
98
193
func (dtime * Datetime ) ToTime () time.Time {
99
194
return dtime .time
0 commit comments