GRASS Programmer's Manual
6.4.2(2012)
|
00001 /* 00002 * Copyright (C) 1995. Bill Brown <brown@gis.uiuc.edu> & Michael Shapiro 00003 * 00004 * This program is free software under the GPL (>=v2) 00005 * Read the file GPL.TXT coming with GRASS for details. 00006 */ 00007 #include <grass/datetime.h> 00008 00009 static int _datetime_add_field(DateTime *, DateTime *, int); 00010 static int _datetime_subtract_field(DateTime *, DateTime *, int); 00011 00012 /*****************************************************************/ 00013 #if 0 /* unused */ 00014 static double _debug_decimal(DateTime * dt) 00015 { 00016 double dtdec = 0.0; 00017 00018 if (dt->mode == DATETIME_RELATIVE) { 00019 if (datetime_in_interval_year_month(dt->from)) { 00020 dtdec = dt->year + dt->month / 12.; 00021 } 00022 else { 00023 dtdec = dt->day / 365.25 + 00024 dt->hour / 8766. + dt->minute / 525960. 00025 + dt->second / 31557600.; 00026 } 00027 } 00028 if (dt->positive) 00029 return (dtdec); 00030 return (-dtdec); 00031 } 00032 #endif /* unused */ 00033 00034 /*****************************************************************/ 00035 00067 int datetime_increment(DateTime * src, DateTime * incr) 00068 { 00069 int i, relfrom; 00070 DateTime cpdt, *dt; 00071 00072 if (!datetime_is_valid_increment(src, incr)) 00073 return datetime_error_code(); 00074 00075 /* special case - incrementing a relative might try to increment 00076 or borrow from a "lower" field than src has, 00077 so we use a copy to change from */ 00078 00079 if (src->mode == DATETIME_RELATIVE) { 00080 datetime_copy(&cpdt, src); 00081 relfrom = datetime_in_interval_day_second(src->from) 00082 ? DATETIME_DAY : DATETIME_YEAR; 00083 datetime_change_from_to(&cpdt, relfrom, src->to, -1); /* min. from */ 00084 dt = &cpdt; 00085 } 00086 else 00087 dt = src; 00088 00089 /* need to call carry first? (just to make sure?) */ 00090 /* 00091 fprintf (stdout,"DEBUG: INCR %.12lf %.12lf = %.12lf\n", 00092 _debug_decimal(dt), _debug_decimal(incr), 00093 _debug_decimal(dt)+_debug_decimal(incr)); 00094 */ 00095 00096 /* no sign change, just add */ 00097 if ((dt->positive && incr->positive) || 00098 (dt->mode == DATETIME_RELATIVE && !dt->positive && !incr->positive)) { 00099 00100 for (i = incr->to; i >= incr->from; i--) { 00101 _datetime_add_field(dt, incr, i); 00102 } 00103 } 00104 00105 else if (!incr->positive || dt->mode == DATETIME_RELATIVE) { 00106 00107 for (i = incr->to; i >= incr->from; i--) { 00108 _datetime_subtract_field(dt, incr, i); 00109 } 00110 } 00111 00112 /* now only two special cases of bc ABSOLUTE left */ 00113 00114 else if (!incr->positive) { /* incr is negative, dt is positive */ 00115 00116 for (i = incr->to; i > DATETIME_YEAR; i--) { 00117 _datetime_subtract_field(dt, incr, i); 00118 } 00119 _datetime_add_field(dt, incr, DATETIME_YEAR); 00120 } 00121 else { /* incr is positive, dt is negative */ 00122 00123 for (i = incr->to; i > DATETIME_YEAR; i--) { 00124 _datetime_add_field(dt, incr, i); 00125 } 00126 _datetime_subtract_field(dt, incr, DATETIME_YEAR); 00127 } 00128 /* 00129 fprintf (stdout,"DEBUG: INCR RESULT = %.12lf\n", _debug_decimal(dt)); 00130 */ 00131 if (src->mode == DATETIME_RELATIVE) { 00132 datetime_change_from_to(dt, src->from, src->to, -1); 00133 00134 /* copy dt back into src to return */ 00135 datetime_copy(src, dt); 00136 } 00137 00138 return 0; 00139 } 00140 00141 00142 /*****************************************************************/ 00143 /* 00144 When calling, the field must be 00145 in the range of src, but this is not enforced here. 00146 00147 The only thing used from the "incr" DateTime is the value of 00148 the field being subtracted and the "from" & "to" 00149 00150 by the time we get here, if src is RELATIVE, src->from should 00151 already be minimized to allow borrowing from "lower" fields 00152 00153 */ 00154 00155 static int _datetime_subtract_field(DateTime * src, DateTime * incr, 00156 int field) 00157 { 00158 00159 if (src->mode == DATETIME_RELATIVE) { 00160 DateTime srcinc, tinc; 00161 int borrow = 0; 00162 00163 datetime_copy(&tinc, src); 00164 datetime_copy(&srcinc, incr); 00165 switch (field) { 00166 case DATETIME_SECOND: 00167 /* no "-1" here - remember seconds is floating point */ 00168 /* might result in over borrowing, so have to check */ 00169 if (src->second < incr->second) { 00170 if ((int)(incr->second - src->second) == (incr->second - src->second)) { /* diff is integer */ 00171 borrow = 1 + (incr->second - src->second - 1) / 60; 00172 } 00173 else 00174 borrow = 1 + (incr->second - src->second) / 60; 00175 src->second += borrow * 60; 00176 } 00177 src->second -= incr->second; 00178 if (borrow) { 00179 srcinc.minute = borrow; 00180 _datetime_subtract_field(src, &srcinc, DATETIME_MINUTE); 00181 } 00182 break; 00183 00184 case DATETIME_MINUTE: 00185 if (src->minute < incr->minute) { 00186 borrow = 1 + (incr->minute - src->minute - 1) / 60; 00187 src->minute += borrow * 60; 00188 } 00189 src->minute -= incr->minute; 00190 if (borrow) { 00191 srcinc.hour = borrow; 00192 _datetime_subtract_field(src, &srcinc, DATETIME_HOUR); 00193 } 00194 break; 00195 00196 case DATETIME_HOUR: 00197 if (src->hour < incr->hour) { 00198 borrow = 1 + (incr->hour - src->hour - 1) / 24; 00199 src->hour += borrow * 24; 00200 } 00201 src->hour -= incr->hour; 00202 if (borrow) { 00203 srcinc.day = borrow; 00204 _datetime_subtract_field(src, &srcinc, DATETIME_DAY); 00205 } 00206 break; 00207 00208 case DATETIME_DAY: 00209 if (src->day < incr->day) { /* SIGN CHANGE */ 00210 src->day = incr->day - src->day; 00211 datetime_invert_sign(src); 00212 tinc.day = 0; 00213 src->hour = 0; 00214 src->minute = 0; 00215 src->second = 0.0; 00216 datetime_increment(src, &tinc); /* no sign change */ 00217 } 00218 else 00219 src->day -= incr->day; 00220 break; 00221 00222 case DATETIME_MONTH: 00223 if (src->month < incr->month) { 00224 borrow = 1 + (incr->month - src->month - 1) / 12; 00225 src->month += borrow * 12; 00226 } 00227 src->month -= incr->month; 00228 if (borrow) { 00229 srcinc.year = borrow; 00230 _datetime_subtract_field(src, &srcinc, DATETIME_YEAR); 00231 } 00232 break; 00233 00234 case DATETIME_YEAR: 00235 if (src->year < incr->year) { /* SIGN CHANGE */ 00236 src->year = incr->year - src->year; 00237 datetime_invert_sign(src); 00238 tinc.year = 0; 00239 src->month = 0; 00240 datetime_increment(src, &tinc); /* no sign change */ 00241 } 00242 else 00243 src->year -= incr->year; 00244 break; 00245 } 00246 } 00247 00248 else if (src->mode == DATETIME_ABSOLUTE) { 00249 DateTime srcinc, tinc, cpsrc; 00250 int i, newdays, borrow = 0; 00251 00252 00253 datetime_copy(&srcinc, incr); /* makes srcinc valid incr */ 00254 switch (field) { 00255 case DATETIME_SECOND: 00256 if (src->second < incr->second) { 00257 borrow = 1 + (incr->second - src->second - 1) / 60; 00258 src->second += borrow * 60; 00259 } 00260 src->second -= incr->second; 00261 if (borrow) { 00262 srcinc.minute = borrow; 00263 _datetime_subtract_field(src, &srcinc, DATETIME_MINUTE); 00264 } 00265 break; 00266 00267 case DATETIME_MINUTE: 00268 if (src->minute < incr->minute) { 00269 borrow = 1 + (incr->minute - src->minute - 1) / 60; 00270 src->minute += borrow * 60; 00271 } 00272 src->minute -= incr->minute; 00273 if (borrow) { 00274 srcinc.hour = borrow; 00275 _datetime_subtract_field(src, &srcinc, DATETIME_HOUR); 00276 } 00277 break; 00278 00279 case DATETIME_HOUR: 00280 if (src->hour < incr->hour) { 00281 borrow = 1 + (incr->hour - src->hour - 1) / 24; 00282 src->hour += borrow * 24; 00283 } 00284 src->hour -= incr->hour; 00285 if (borrow) { 00286 srcinc.day = borrow; 00287 _datetime_subtract_field(src, &srcinc, DATETIME_DAY); 00288 } 00289 break; 00290 00291 case DATETIME_DAY: 00292 00293 if (src->day <= incr->day) { 00294 datetime_copy(&cpsrc, src); 00295 datetime_change_from_to(&cpsrc, DATETIME_YEAR, 00296 DATETIME_MONTH, -1); 00297 datetime_set_increment_type(&cpsrc, &tinc); 00298 tinc.month = 1; 00299 newdays = src->day; 00300 while (newdays <= incr->day) { 00301 _datetime_subtract_field(&cpsrc, &tinc, DATETIME_MONTH); 00302 newdays += 00303 datetime_days_in_month(cpsrc.year, cpsrc.month, 00304 cpsrc.positive); 00305 borrow++; 00306 } 00307 src->day = newdays; 00308 } 00309 src->day -= incr->day; 00310 if (borrow) { 00311 /* 00312 src->year = cpsrc.year; 00313 src->month = cpsrc.month; 00314 src->positive = cpsrc.positive; 00315 */ 00316 /* check here & below - srcinc may be a day-second interval - mess anything up? */ 00317 srcinc.month = borrow; 00318 _datetime_subtract_field(src, &srcinc, DATETIME_MONTH); 00319 } 00320 break; 00321 00322 case DATETIME_MONTH: 00323 if (src->month <= incr->month) { 00324 borrow = 1 + (incr->month - src->month) / 12; 00325 src->month += borrow * 12; 00326 } 00327 src->month -= incr->month; 00328 if (borrow) { 00329 srcinc.year = borrow; 00330 _datetime_subtract_field(src, &srcinc, DATETIME_YEAR); 00331 } 00332 break; 00333 00334 case DATETIME_YEAR: 00335 if (src->year <= incr->year) { /* SIGN CHANGE */ 00336 datetime_set_increment_type(src, &tinc); 00337 tinc.positive = src->positive; 00338 if (datetime_in_interval_year_month(tinc.to)) { 00339 tinc.month = src->month - 1; /* convert to REL */ 00340 src->year = incr->year - src->year + 1; 00341 /* +1 to skip 0 */ 00342 datetime_invert_sign(src); 00343 tinc.year = 0; 00344 src->month = 1; 00345 datetime_increment(src, &tinc); /* no sign change */ 00346 } 00347 else { /* have to convert to days */ 00348 tinc.day = src->day - 1; /* convert to REL */ 00349 for (i = src->month - 1; i > 0; i--) { 00350 tinc.day += 00351 datetime_days_in_month(src->year, i, 00352 src->positive); 00353 } 00354 tinc.hour = src->hour; 00355 tinc.minute = src->minute; 00356 tinc.second = src->second; 00357 src->year = incr->year - src->year + 1; 00358 /* +1 to skip 0 */ 00359 datetime_invert_sign(src); 00360 src->month = 1; 00361 src->day = 1; 00362 src->hour = src->minute = 0; 00363 src->second = 0; 00364 datetime_increment(src, &tinc); /* no sign change */ 00365 } 00366 } 00367 else 00368 src->year -= incr->year; 00369 break; 00370 } 00371 } 00372 00373 return 0; 00374 } 00375 00376 /*****************************************************************/ 00377 00378 /* When absolute is zero, all fields carry toward the future */ 00379 /* When absolute is one, sign of datetime is ignored */ 00380 static int _datetime_carry(DateTime * dt, int absolute) 00381 { 00382 int i, carry; 00383 00384 /* normalize day-sec (same for ABSOLUTE & RELATIVE) */ 00385 for (i = dt->to; i > dt->from && i > DATETIME_DAY; i--) { 00386 switch (i) { 00387 case DATETIME_SECOND: 00388 if (dt->second >= 60.) { 00389 carry = dt->second / 60.; 00390 dt->minute += carry; 00391 dt->second -= carry * 60; 00392 } 00393 break; 00394 case DATETIME_MINUTE: 00395 if (dt->minute >= 60) { 00396 carry = dt->minute / 60; 00397 dt->hour += carry; 00398 dt->minute -= carry * 60; 00399 } 00400 break; 00401 case DATETIME_HOUR: 00402 if (dt->hour >= 24) { 00403 carry = dt->hour / 24; 00404 dt->day += carry; 00405 dt->hour -= carry * 24; 00406 } 00407 break; 00408 } 00409 } 00410 00411 /* give year a SIGN, temporarily */ 00412 if (!absolute && !dt->positive && dt->mode == DATETIME_ABSOLUTE) { 00413 dt->year = -dt->year; 00414 } 00415 00416 if (dt->from == DATETIME_YEAR && dt->to >= DATETIME_MONTH) { 00417 00418 /* normalize yr-mo */ 00419 if (dt->mode == DATETIME_ABSOLUTE) { 00420 if (dt->month > 12) { /* month will never be zero */ 00421 carry = (dt->month - 1) / 12; /* no carry until 13 */ 00422 dt->year += carry; 00423 if (dt->year == 0) 00424 dt->year = 1; 00425 dt->month -= carry * 12; 00426 /* 00427 if(dt->month == 0) dt->month = 1; 00428 shouldn't happen */ 00429 } 00430 } 00431 else { 00432 if (dt->month >= 12) { 00433 carry = dt->month / 12; 00434 dt->year += carry; 00435 dt->month -= carry * 12; 00436 } 00437 } 00438 00439 } 00440 00441 /* normalize yr-day */ 00442 if (dt->mode == DATETIME_ABSOLUTE && dt->to > DATETIME_MONTH) { 00443 00444 while (dt->day > 00445 datetime_days_in_month(dt->year, dt->month, dt->positive)) { 00446 dt->day -= 00447 datetime_days_in_month(dt->year, dt->month, dt->positive); 00448 if (dt->month == 12) { /* carry to year */ 00449 dt->year++; 00450 if (dt->year == 0) 00451 dt->year = 1; 00452 dt->month = 1; 00453 } 00454 else /* no carry to year */ 00455 dt->month++; 00456 00457 } /* end while */ 00458 } /* end if */ 00459 00460 /* undo giving year a SIGN, temporarily */ 00461 if (!absolute && dt->mode == DATETIME_ABSOLUTE) { 00462 if (dt->year < 0) { 00463 dt->year = -dt->year; 00464 dt->positive = 0; 00465 } 00466 else 00467 dt->positive = 1; 00468 } 00469 00470 return 0; 00471 } 00472 00473 static int _datetime_add_field(DateTime * src, DateTime * incr, int field) 00474 { 00475 switch (field) { 00476 case DATETIME_SECOND: 00477 src->second += incr->second; 00478 break; 00479 case DATETIME_MINUTE: 00480 src->minute += incr->minute; 00481 break; 00482 case DATETIME_HOUR: 00483 src->hour += incr->hour; 00484 break; 00485 case DATETIME_DAY: 00486 src->day += incr->day; 00487 break; 00488 case DATETIME_MONTH: 00489 src->month += incr->month; 00490 break; 00491 case DATETIME_YEAR: 00492 src->year += incr->year; 00493 break; 00494 } 00495 if (src->mode == DATETIME_RELATIVE) 00496 _datetime_carry(src, 1); /* do carries using absolute values */ 00497 else 00498 _datetime_carry(src, 0); /* do carries toward future */ 00499 00500 return 0; 00501 }