diff.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 <stdlib.h>
  8. #include <grass/datetime.h>
  9. #include "math.h"
  10. /*************************************************************/
  11. /*
  12. This performs the formula: result = a - b;
  13. both a and b must be absolute.
  14. result will be relative
  15. If a is "earlier" than b, then result should be set negative.
  16. b must be no more "precise" than a.
  17. (a copy of b is "extended" to the precision of a)
  18. datetime_copy (tb, b)
  19. datetime_reset_from_to (tb, b.from, a.to, a.fracsec))
  20. If result.to == SECOND, then result.fracsec is a.fracsec
  21. result will have the following from/to based on a.to:
  22. result
  23. a.to from to
  24. YEAR YEAR YEAR
  25. MONTH YEAR MONTH
  26. DAY DAY DAY
  27. HOUR DAY HOUR
  28. MINUTE DAY MINUTE
  29. SECOND DAY SECOND
  30. If either 'a' or 'b' has a timezone, both must have a timezone.
  31. The difference will account for the differences in the time zones.
  32. */
  33. static int _datetime_ymd_to_ddays(const DateTime *, double *);
  34. static int _datetime_compare(const DateTime *, const DateTime *);
  35. /*!
  36. * \brief
  37. *
  38. *
  39. * This performs the formula: result = a - b;
  40. * <ul>
  41. <li> both a and b must be absolute.
  42. * </li>
  43. <li> result will be relative
  44. * </li>
  45. <li> If a is "earlier" than b, then result will be set negative.
  46. * </li>
  47. <li> b must be no more "precise" than a.
  48. * (a copy of b is "extended" to the precision of a)
  49. * </li>
  50. <li> If result.to == SECOND, then result.fracsec is a.fracsec
  51. * </li>
  52. <li> result will have the following from/to based
  53. * on a.to: result a.to from to YEAR YEAR YEAR MONTH YEAR
  54. * MONTH DAY DAY DAY HOUR DAY HOUR MINUTE DAY
  55. * MINUTE SECOND DAY SECOND [LAYOUT ??? - see HTML]
  56. * </li>
  57. <li> If either 'a' or 'b' has a timezone, both must have a timezone. The
  58. * difference will account for the differences in the time zones.
  59. </li></ul>
  60. *
  61. * \param a
  62. * \param b
  63. * \param result
  64. * \return int
  65. */
  66. int
  67. datetime_difference(const DateTime * a, const DateTime * b, DateTime * result)
  68. {
  69. DateTime tb, ta, *early, *late;
  70. int compare, tzmin;
  71. /* if not both absolute, return error */
  72. datetime_copy(&tb, b);
  73. datetime_change_from_to(&tb, DATETIME_YEAR, a->to, a->fracsec);
  74. datetime_copy(&ta, a);
  75. if (datetime_get_timezone(&ta, &tzmin) == 0 ||
  76. datetime_get_timezone(&tb, &tzmin) == 0) {
  77. if (datetime_get_timezone(&ta, &tzmin) == 0 &&
  78. datetime_get_timezone(&tb, &tzmin) == 0) {
  79. datetime_change_to_utc(&ta);
  80. datetime_change_to_utc(&tb);
  81. }
  82. else
  83. return datetime_error(-1,
  84. "only one opperand contains valid timezone");
  85. }
  86. /* initialize result */
  87. datetime_set_type(result, DATETIME_RELATIVE,
  88. ta.to < DATETIME_DAY ? DATETIME_YEAR : DATETIME_DAY,
  89. ta.to, ta.fracsec);
  90. compare = _datetime_compare(&ta, &tb);
  91. if (compare > 0) {
  92. early = &tb;
  93. late = &ta;
  94. result->positive = 1;
  95. }
  96. else if (compare < 0) {
  97. early = &ta;
  98. late = &tb;
  99. result->positive = 0;
  100. }
  101. else { /* equal */
  102. return (0);
  103. }
  104. /* now the work */
  105. if (datetime_in_interval_year_month(ta.to)) {
  106. int dm;
  107. if (ta.positive == tb.positive) {
  108. /* change if we use doubles! */
  109. result->year = abs(late->year - early->year);
  110. }
  111. else {
  112. result->year = late->year + early->year - 2;
  113. }
  114. dm = late->month - early->month;
  115. if (dm >= 0)
  116. result->month = dm;
  117. else {
  118. result->year -= 1;
  119. result->month = dm + 12;
  120. }
  121. }
  122. else {
  123. DateTime erel, lrel;
  124. double latedays, earlydays;
  125. datetime_set_increment_type(a, &erel);
  126. _datetime_ymd_to_ddays(early, &earlydays);
  127. /* copy day -> down */
  128. erel.day = earlydays;
  129. erel.hour = early->hour;
  130. erel.minute = early->minute;
  131. erel.second = early->second;
  132. datetime_set_increment_type(a, &lrel);
  133. _datetime_ymd_to_ddays(late, &latedays);
  134. /* copy day -> down */
  135. lrel.day = latedays;
  136. lrel.hour = late->hour;
  137. lrel.minute = late->minute;
  138. lrel.second = late->second;
  139. datetime_invert_sign(&erel);
  140. datetime_increment(&erel, &lrel);
  141. /* copy erel back to result */
  142. result->day = erel.day;
  143. result->hour = erel.hour;
  144. result->minute = erel.minute;
  145. result->second = erel.second;
  146. /* need carry? */
  147. }
  148. return (0);
  149. }
  150. /*************************************************************/
  151. /* returns 1 if a is later than b,
  152. -1 if a is earlier than a,
  153. 0 otherwise
  154. */
  155. /* only looks at from-to fields defined by a */
  156. static int _datetime_compare(const DateTime * a, const DateTime * b)
  157. {
  158. int i;
  159. if (a->positive && !b->positive)
  160. return (1);
  161. else if (b->positive && !a->positive)
  162. return (-1);
  163. /* same signs */
  164. for (i = a->from; i <= a->to; i++) {
  165. switch (i) {
  166. case DATETIME_SECOND:
  167. if (a->second > b->second)
  168. return (1);
  169. else if (a->second < b->second)
  170. return (-1);
  171. break;
  172. case DATETIME_MINUTE:
  173. if (a->minute > b->minute)
  174. return (1);
  175. else if (a->minute < b->minute)
  176. return (-1);
  177. break;
  178. case DATETIME_HOUR:
  179. if (a->hour > b->hour)
  180. return (1);
  181. else if (a->hour < b->hour)
  182. return (-1);
  183. break;
  184. case DATETIME_DAY:
  185. if (a->day > b->day)
  186. return (1);
  187. else if (a->day < b->day)
  188. return (-1);
  189. break;
  190. case DATETIME_MONTH:
  191. if (a->month > b->month)
  192. return (1);
  193. else if (a->month < b->month)
  194. return (-1);
  195. break;
  196. case DATETIME_YEAR: /* only place sign matters */
  197. if (a->positive) {
  198. if (a->year > b->year)
  199. return (1);
  200. else if (a->year < b->year)
  201. return (-1);
  202. }
  203. else {
  204. if (a->year < b->year)
  205. return (1);
  206. else if (a->year > b->year)
  207. return (-1);
  208. }
  209. break;
  210. }
  211. }
  212. return (0);
  213. }
  214. /*************************************************************/
  215. static int _datetime_ymd_to_ddays(const DateTime * dtymd, double *days)
  216. { /* note extra precision! */
  217. int yr, mo;
  218. *days = 0.0;
  219. if (dtymd->positive) {
  220. *days = dtymd->day - 1; /* start w/ days - 1 */
  221. for (mo = dtymd->month - 1; mo > 0; mo--) { /* add earlier months */
  222. *days += datetime_days_in_month(dtymd->year, mo, dtymd->positive);
  223. }
  224. for (yr = dtymd->year - 1; yr > 0; yr--) { /* add earlier years */
  225. *days += datetime_days_in_year(yr, dtymd->positive);
  226. }
  227. }
  228. else {
  229. for (yr = dtymd->year - 1; yr > 0; yr--) { /* add later years */
  230. *days += datetime_days_in_year(yr, dtymd->positive);
  231. }
  232. for (mo = 12; mo >= dtymd->month; mo--) { /*add current & later months */
  233. *days += datetime_days_in_month(dtymd->year, mo, dtymd->positive);
  234. }
  235. *days -= dtymd->day; /* subtract current days */
  236. }
  237. return 0;
  238. }
  239. /*************************************************************/
  240. /*************************************************************/