1 /*
2 * Copyright (C) 1995. Bill Brown <brown@gis.uiuc.edu> & Michael Shapiro
3 *
4 * This program is free software under the GPL (>=v2)
5 * Read the file GPL.TXT coming with GRASS for details.
6 */
7 #include <grass/datetime.h>
8
9 static int _datetime_add_field(DateTime *, DateTime *, int);
10 static int _datetime_subtract_field(DateTime *, DateTime *, int);
11
12 /*****************************************************************/
13 #if 0 /* unused */
14 static double _debug_decimal(DateTime * dt)
15 {
16 double dtdec = 0.0;
17
18 if (dt->mode == DATETIME_RELATIVE) {
19 if (datetime_in_interval_year_month(dt->from)) {
20 dtdec = dt->year + dt->month / 12.;
21 }
22 else {
23 dtdec = dt->day / 365.25 +
24 dt->hour / 8766. + dt->minute / 525960.
25 + dt->second / 31557600.;
26 }
27 }
28 if (dt->positive)
29 return (dtdec);
30 return (-dtdec);
31 }
32 #endif /* unused */
33
34 /*****************************************************************/
35
36 /*!
37 * \brief
38 *
39 * This function changes the 'src' date/time data based on the 'incr'
40 * The type (mode/from/to) of the 'src' can be anything.
41 * The mode of the 'incr' must be RELATIVE, and the type (mode/from/to) for
42 * 'incr' must be a valid increment for 'src'. See <b>datetime_is_valid_increment()</b>,
43 * <b>datetime_check_increment()</b>
44 * Returns:
45 * 0: OK
46 * -1: 'incr' is invalid increment for 'src'
47 * For src.mode ABSOLUTE,
48 * <ul>
49 <li> positive 'incr' moves into the future,
50 </li>
51 <li> negative 'incr' moves into the past.
52 </li>
53 <li> BC implies the year is negative, but all else is positive. Also, year==0
54 * is illegal: adding 1 year to 1[bc] gives 1[ad]
55 </li></ul>
56 * The 'fracsec' in 'src' is preserved.
57 * The 'from/to' of the 'src' is preserved.
58 * A timezone in 'src' is allowed - it's presence is ignored.
59 * NOTE: There is no datetime_decrement() To decrement, set the 'incr' negative.
60
61 *
62 * \param src
63 * \param incr
64 * \return int
65 */
66
datetime_increment(DateTime * src,DateTime * incr)67 int datetime_increment(DateTime * src, DateTime * incr)
68 {
69 int i, relfrom;
70 DateTime cpdt, *dt;
71
72 if (!datetime_is_valid_increment(src, incr))
73 return datetime_error_code();
74
75 /* special case - incrementing a relative might try to increment
76 or borrow from a "lower" field than src has,
77 so we use a copy to change from */
78
79 if (src->mode == DATETIME_RELATIVE) {
80 datetime_copy(&cpdt, src);
81 relfrom = datetime_in_interval_day_second(src->from)
82 ? DATETIME_DAY : DATETIME_YEAR;
83 datetime_change_from_to(&cpdt, relfrom, src->to, -1); /* min. from */
84 dt = &cpdt;
85 }
86 else
87 dt = src;
88
89 /* need to call carry first? (just to make sure?) */
90 /*
91 fprintf (stdout,"DEBUG: INCR %.12lf %.12lf = %.12lf\n",
92 _debug_decimal(dt), _debug_decimal(incr),
93 _debug_decimal(dt)+_debug_decimal(incr));
94 */
95
96 /* no sign change, just add */
97 if ((dt->positive && incr->positive) ||
98 (dt->mode == DATETIME_RELATIVE && !dt->positive && !incr->positive)) {
99
100 for (i = incr->to; i >= incr->from; i--) {
101 _datetime_add_field(dt, incr, i);
102 }
103 }
104
105 else if (!incr->positive || dt->mode == DATETIME_RELATIVE) {
106
107 for (i = incr->to; i >= incr->from; i--) {
108 _datetime_subtract_field(dt, incr, i);
109 }
110 }
111
112 /* now only two special cases of bc ABSOLUTE left */
113
114 else if (!incr->positive) { /* incr is negative, dt is positive */
115
116 for (i = incr->to; i > DATETIME_YEAR; i--) {
117 _datetime_subtract_field(dt, incr, i);
118 }
119 _datetime_add_field(dt, incr, DATETIME_YEAR);
120 }
121 else { /* incr is positive, dt is negative */
122
123 for (i = incr->to; i > DATETIME_YEAR; i--) {
124 _datetime_add_field(dt, incr, i);
125 }
126 _datetime_subtract_field(dt, incr, DATETIME_YEAR);
127 }
128 /*
129 fprintf (stdout,"DEBUG: INCR RESULT = %.12lf\n", _debug_decimal(dt));
130 */
131 if (src->mode == DATETIME_RELATIVE) {
132 datetime_change_from_to(dt, src->from, src->to, -1);
133
134 /* copy dt back into src to return */
135 datetime_copy(src, dt);
136 }
137
138 return 0;
139 }
140
141
142 /*****************************************************************/
143 /*
144 When calling, the field must be
145 in the range of src, but this is not enforced here.
146
147 The only thing used from the "incr" DateTime is the value of
148 the field being subtracted and the "from" & "to"
149
150 by the time we get here, if src is RELATIVE, src->from should
151 already be minimized to allow borrowing from "lower" fields
152
153 */
154
_datetime_subtract_field(DateTime * src,DateTime * incr,int field)155 static int _datetime_subtract_field(DateTime * src, DateTime * incr,
156 int field)
157 {
158
159 if (src->mode == DATETIME_RELATIVE) {
160 DateTime srcinc, tinc;
161 int borrow = 0;
162
163 datetime_copy(&tinc, src);
164 datetime_copy(&srcinc, incr);
165 switch (field) {
166 case DATETIME_SECOND:
167 /* no "-1" here - remember seconds is floating point */
168 /* might result in over borrowing, so have to check */
169 if (src->second < incr->second) {
170 if ((int)(incr->second - src->second) == (incr->second - src->second)) { /* diff is integer */
171 borrow = 1 + (incr->second - src->second - 1) / 60;
172 }
173 else
174 borrow = 1 + (incr->second - src->second) / 60;
175 src->second += borrow * 60;
176 }
177 src->second -= incr->second;
178 if (borrow) {
179 srcinc.minute = borrow;
180 _datetime_subtract_field(src, &srcinc, DATETIME_MINUTE);
181 }
182 break;
183
184 case DATETIME_MINUTE:
185 if (src->minute < incr->minute) {
186 borrow = 1 + (incr->minute - src->minute - 1) / 60;
187 src->minute += borrow * 60;
188 }
189 src->minute -= incr->minute;
190 if (borrow) {
191 srcinc.hour = borrow;
192 _datetime_subtract_field(src, &srcinc, DATETIME_HOUR);
193 }
194 break;
195
196 case DATETIME_HOUR:
197 if (src->hour < incr->hour) {
198 borrow = 1 + (incr->hour - src->hour - 1) / 24;
199 src->hour += borrow * 24;
200 }
201 src->hour -= incr->hour;
202 if (borrow) {
203 srcinc.day = borrow;
204 _datetime_subtract_field(src, &srcinc, DATETIME_DAY);
205 }
206 break;
207
208 case DATETIME_DAY:
209 if (src->day < incr->day) { /* SIGN CHANGE */
210 src->day = incr->day - src->day;
211 datetime_invert_sign(src);
212 tinc.day = 0;
213 src->hour = 0;
214 src->minute = 0;
215 src->second = 0.0;
216 datetime_increment(src, &tinc); /* no sign change */
217 }
218 else
219 src->day -= incr->day;
220 break;
221
222 case DATETIME_MONTH:
223 if (src->month < incr->month) {
224 borrow = 1 + (incr->month - src->month - 1) / 12;
225 src->month += borrow * 12;
226 }
227 src->month -= incr->month;
228 if (borrow) {
229 srcinc.year = borrow;
230 _datetime_subtract_field(src, &srcinc, DATETIME_YEAR);
231 }
232 break;
233
234 case DATETIME_YEAR:
235 if (src->year < incr->year) { /* SIGN CHANGE */
236 src->year = incr->year - src->year;
237 datetime_invert_sign(src);
238 tinc.year = 0;
239 src->month = 0;
240 datetime_increment(src, &tinc); /* no sign change */
241 }
242 else
243 src->year -= incr->year;
244 break;
245 }
246 }
247
248 else if (src->mode == DATETIME_ABSOLUTE) {
249 DateTime srcinc, tinc, cpsrc;
250 int i, newdays, borrow = 0;
251
252
253 datetime_copy(&srcinc, incr); /* makes srcinc valid incr */
254 switch (field) {
255 case DATETIME_SECOND:
256 if (src->second < incr->second) {
257 borrow = 1 + (incr->second - src->second - 1) / 60;
258 src->second += borrow * 60;
259 }
260 src->second -= incr->second;
261 if (borrow) {
262 srcinc.minute = borrow;
263 _datetime_subtract_field(src, &srcinc, DATETIME_MINUTE);
264 }
265 break;
266
267 case DATETIME_MINUTE:
268 if (src->minute < incr->minute) {
269 borrow = 1 + (incr->minute - src->minute - 1) / 60;
270 src->minute += borrow * 60;
271 }
272 src->minute -= incr->minute;
273 if (borrow) {
274 srcinc.hour = borrow;
275 _datetime_subtract_field(src, &srcinc, DATETIME_HOUR);
276 }
277 break;
278
279 case DATETIME_HOUR:
280 if (src->hour < incr->hour) {
281 borrow = 1 + (incr->hour - src->hour - 1) / 24;
282 src->hour += borrow * 24;
283 }
284 src->hour -= incr->hour;
285 if (borrow) {
286 srcinc.day = borrow;
287 _datetime_subtract_field(src, &srcinc, DATETIME_DAY);
288 }
289 break;
290
291 case DATETIME_DAY:
292
293 if (src->day <= incr->day) {
294 datetime_copy(&cpsrc, src);
295 datetime_change_from_to(&cpsrc, DATETIME_YEAR,
296 DATETIME_MONTH, -1);
297 datetime_set_increment_type(&cpsrc, &tinc);
298 tinc.month = 1;
299 newdays = src->day;
300 while (newdays <= incr->day) {
301 _datetime_subtract_field(&cpsrc, &tinc, DATETIME_MONTH);
302 newdays +=
303 datetime_days_in_month(cpsrc.year, cpsrc.month,
304 cpsrc.positive);
305 borrow++;
306 }
307 src->day = newdays;
308 }
309 src->day -= incr->day;
310 if (borrow) {
311 /*
312 src->year = cpsrc.year;
313 src->month = cpsrc.month;
314 src->positive = cpsrc.positive;
315 */
316 /* check here & below - srcinc may be a day-second interval - mess anything up? */
317 srcinc.month = borrow;
318 _datetime_subtract_field(src, &srcinc, DATETIME_MONTH);
319 }
320 break;
321
322 case DATETIME_MONTH:
323 if (src->month <= incr->month) {
324 borrow = 1 + (incr->month - src->month) / 12;
325 src->month += borrow * 12;
326 }
327 src->month -= incr->month;
328 if (borrow) {
329 srcinc.year = borrow;
330 _datetime_subtract_field(src, &srcinc, DATETIME_YEAR);
331 }
332 break;
333
334 case DATETIME_YEAR:
335 if (src->year <= incr->year) { /* SIGN CHANGE */
336 datetime_set_increment_type(src, &tinc);
337 tinc.positive = src->positive;
338 if (datetime_in_interval_year_month(tinc.to)) {
339 tinc.month = src->month - 1; /* convert to REL */
340 src->year = incr->year - src->year + 1;
341 /* +1 to skip 0 */
342 datetime_invert_sign(src);
343 tinc.year = 0;
344 src->month = 1;
345 datetime_increment(src, &tinc); /* no sign change */
346 }
347 else { /* have to convert to days */
348 tinc.day = src->day - 1; /* convert to REL */
349 for (i = src->month - 1; i > 0; i--) {
350 tinc.day +=
351 datetime_days_in_month(src->year, i,
352 src->positive);
353 }
354 tinc.hour = src->hour;
355 tinc.minute = src->minute;
356 tinc.second = src->second;
357 src->year = incr->year - src->year + 1;
358 /* +1 to skip 0 */
359 datetime_invert_sign(src);
360 src->month = 1;
361 src->day = 1;
362 src->hour = src->minute = 0;
363 src->second = 0;
364 datetime_increment(src, &tinc); /* no sign change */
365 }
366 }
367 else
368 src->year -= incr->year;
369 break;
370 }
371 }
372
373 return 0;
374 }
375
376 /*****************************************************************/
377
378 /* When absolute is zero, all fields carry toward the future */
379 /* When absolute is one, sign of datetime is ignored */
_datetime_carry(DateTime * dt,int absolute)380 static int _datetime_carry(DateTime * dt, int absolute)
381 {
382 int i, carry;
383
384 /* normalize day-sec (same for ABSOLUTE & RELATIVE) */
385 for (i = dt->to; i > dt->from && i > DATETIME_DAY; i--) {
386 switch (i) {
387 case DATETIME_SECOND:
388 if (dt->second >= 60.) {
389 carry = dt->second / 60.;
390 dt->minute += carry;
391 dt->second -= carry * 60;
392 }
393 break;
394 case DATETIME_MINUTE:
395 if (dt->minute >= 60) {
396 carry = dt->minute / 60;
397 dt->hour += carry;
398 dt->minute -= carry * 60;
399 }
400 break;
401 case DATETIME_HOUR:
402 if (dt->hour >= 24) {
403 carry = dt->hour / 24;
404 dt->day += carry;
405 dt->hour -= carry * 24;
406 }
407 break;
408 }
409 }
410
411 /* give year a SIGN, temporarily */
412 if (!absolute && !dt->positive && dt->mode == DATETIME_ABSOLUTE) {
413 dt->year = -dt->year;
414 }
415
416 if (dt->from == DATETIME_YEAR && dt->to >= DATETIME_MONTH) {
417
418 /* normalize yr-mo */
419 if (dt->mode == DATETIME_ABSOLUTE) {
420 if (dt->month > 12) { /* month will never be zero */
421 carry = (dt->month - 1) / 12; /* no carry until 13 */
422 dt->year += carry;
423 if (dt->year == 0)
424 dt->year = 1;
425 dt->month -= carry * 12;
426 /*
427 if(dt->month == 0) dt->month = 1;
428 shouldn't happen */
429 }
430 }
431 else {
432 if (dt->month >= 12) {
433 carry = dt->month / 12;
434 dt->year += carry;
435 dt->month -= carry * 12;
436 }
437 }
438
439 }
440
441 /* normalize yr-day */
442 if (dt->mode == DATETIME_ABSOLUTE && dt->to > DATETIME_MONTH) {
443
444 while (dt->day >
445 datetime_days_in_month(dt->year, dt->month, dt->positive)) {
446 dt->day -=
447 datetime_days_in_month(dt->year, dt->month, dt->positive);
448 if (dt->month == 12) { /* carry to year */
449 dt->year++;
450 if (dt->year == 0)
451 dt->year = 1;
452 dt->month = 1;
453 }
454 else /* no carry to year */
455 dt->month++;
456
457 } /* end while */
458 } /* end if */
459
460 /* undo giving year a SIGN, temporarily */
461 if (!absolute && dt->mode == DATETIME_ABSOLUTE) {
462 if (dt->year < 0) {
463 dt->year = -dt->year;
464 dt->positive = 0;
465 }
466 else
467 dt->positive = 1;
468 }
469
470 return 0;
471 }
472
_datetime_add_field(DateTime * src,DateTime * incr,int field)473 static int _datetime_add_field(DateTime * src, DateTime * incr, int field)
474 {
475 switch (field) {
476 case DATETIME_SECOND:
477 src->second += incr->second;
478 break;
479 case DATETIME_MINUTE:
480 src->minute += incr->minute;
481 break;
482 case DATETIME_HOUR:
483 src->hour += incr->hour;
484 break;
485 case DATETIME_DAY:
486 src->day += incr->day;
487 break;
488 case DATETIME_MONTH:
489 src->month += incr->month;
490 break;
491 case DATETIME_YEAR:
492 src->year += incr->year;
493 break;
494 }
495 if (src->mode == DATETIME_RELATIVE)
496 _datetime_carry(src, 1); /* do carries using absolute values */
497 else
498 _datetime_carry(src, 0); /* do carries toward future */
499
500 return 0;
501 }
502