PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/grace/dates.c

https://github.com/mariusal/grace
C | 358 lines | 239 code | 48 blank | 71 comment | 100 complexity | 4c908feae3944b2d845d90edf6302954 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
  1. /*
  2. * Grace - GRaphing, Advanced Computation and Exploration of data
  3. *
  4. * Home page: http://plasma-gate.weizmann.ac.il/Grace/
  5. *
  6. * Copyright (c) 1999-2006 Grace Development Team
  7. *
  8. * Maintained by Evgeny Stambulchik
  9. *
  10. *
  11. * All Rights Reserved
  12. *
  13. * This program is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation; either version 2 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program; if not, write to the Free Software
  25. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26. */
  27. #include <ctype.h>
  28. #include "grace/graceP.h"
  29. /*
  30. * expand years according to the following rules :
  31. * [wy ; 99] -> [ wrap_year ; 100*(1 + wrap_year/100) - 1 ]
  32. * [00 ; wy-1] -> [ 100*(1 + wrap_year/100) ; wrap_year + 99]
  33. */
  34. static int expanded_year(const Project *pr, Int_token y)
  35. {
  36. if (pr->two_digits_years) {
  37. int century = 100*(1 + pr->wrap_year/100);
  38. int wy = pr->wrap_year - (century - 100);
  39. if (y.value >= 0 && y.value < wy && y.digits <= 2) {
  40. return century + y.value;
  41. } else if (y.value >= wy && y.value < 100 && y.digits <= 2) {
  42. return century - 100 + y.value;
  43. } else {
  44. return y.value;
  45. }
  46. } else {
  47. return y.value;
  48. }
  49. }
  50. /*
  51. * check the existence of given calendar elements
  52. * this includes either number of day in the month
  53. * and calendars pecularities (year 0 and October 1582)
  54. */
  55. static int check_date(const Project *pr,
  56. Int_token y, Int_token m, Int_token d, long *jul)
  57. {
  58. int y_expand, y_check, m_check, d_check;
  59. y_expand = expanded_year(pr, y);
  60. if (m.digits > 2 || d.digits > 2) {
  61. /* this should be the year instead of either the month or the day */
  62. return RETURN_FAILURE;
  63. }
  64. *jul = cal_to_jul(y_expand, m.value, d.value);
  65. jul_to_cal(*jul, &y_check, &m_check, &d_check);
  66. if (y_expand != y_check || m.value != m_check || d.value != d_check) {
  67. return RETURN_FAILURE;
  68. } else {
  69. return RETURN_SUCCESS;
  70. }
  71. }
  72. /*
  73. * lexical analyzer for float data. Knows about fortran exponent
  74. * markers. Store address of following data in *after, only in case of
  75. * success (thus it is allowed to have after == &s)
  76. */
  77. int parse_float(const char* s, double *value, const char **after)
  78. {
  79. int neg_mant, neg_exp, digits, dot_exp, raw_exp;
  80. const char *after_dot;
  81. /* we skip leading whitespace */
  82. while (isspace(*s)) {
  83. s++;
  84. }
  85. /* sign */
  86. if (*s == '-') {
  87. neg_mant = 1;
  88. s++;
  89. } else {
  90. neg_mant = 0;
  91. if (*s == '+') {
  92. s++;
  93. }
  94. }
  95. /* mantissa */
  96. digits = 0;
  97. *value = 0.0;
  98. while (isdigit(*s)) {
  99. *value = *value*10.0 + (*s++ - '0');
  100. digits++;
  101. }
  102. if (*s == '.') {
  103. after_dot = ++s;
  104. while (isdigit(*s)) {
  105. *value = *value*10.0 + (*s++ - '0');
  106. digits++;
  107. }
  108. dot_exp = after_dot - s;
  109. } else {
  110. dot_exp = 0;
  111. }
  112. if (digits == 0) {
  113. /* there should be at least one digit (either before or after dot) */
  114. return RETURN_FAILURE;
  115. }
  116. /* exponent (d and D are fortran exponent markers) */
  117. raw_exp = 0;
  118. if (*s == 'e' || *s == 'E' || *s == 'd' || *s == 'D') {
  119. s++;
  120. if (*s == '-') {
  121. neg_exp = 1;
  122. s++;
  123. } else {
  124. neg_exp = 0;
  125. if (*s == '+') {
  126. s++;
  127. }
  128. }
  129. while (isdigit(*s)) {
  130. raw_exp = raw_exp*10 + (*s++ - '0');
  131. }
  132. if (neg_exp) {
  133. raw_exp = -raw_exp;
  134. }
  135. }
  136. /* read float */
  137. *value = (neg_mant ? -(*value) : (*value)) * pow (10.0, dot_exp + raw_exp);
  138. if (after != NULL) {
  139. /* the caller wants to know what follows the float number */
  140. *after = s;
  141. }
  142. return RETURN_SUCCESS;
  143. }
  144. /*
  145. * lexical analyzer for calendar dates
  146. * return the number of read elements, or -1 on failure
  147. */
  148. static int parse_calendar_date(const char* s, Int_token tab[5], double *sec)
  149. {
  150. int i, waiting_separator, negative;
  151. negative = 0;
  152. waiting_separator = 0;
  153. i = 0;
  154. while (i < 5) {
  155. /* loop from year to minute elements : all integers */
  156. switch (*s) {
  157. case '\0': /* end of string */
  158. return i;
  159. case ' ' : /* repeatable separator */
  160. s++;
  161. negative = 0;
  162. break;
  163. case '/' : case ':' : case '.' : case 'T' : /* non-repeatable separator */
  164. if (waiting_separator) {
  165. if ((*s == 'T') && (i != 3)) {
  166. /* the T separator is only allowed between date
  167. and time (mainly for iso8601) */
  168. return -1;
  169. }
  170. s++;
  171. negative = 0;
  172. waiting_separator = 0;
  173. } else {
  174. return -1;
  175. }
  176. break;
  177. case '-' : /* either separator or minus sign */
  178. s++;
  179. if (waiting_separator) {
  180. negative = 0;
  181. waiting_separator = 0;
  182. } else if ((*s >= '0') && (*s <= '9')) {
  183. negative = 1;
  184. } else {
  185. return -1;
  186. }
  187. break;
  188. case '0' : case '1' : case '2' : case '3' : case '4' :
  189. case '5' : case '6' : case '7' : case '8' : case '9' : /* digit */
  190. tab[i].value = ((int) *s) - '0';
  191. tab[i].digits = 1;
  192. while (isdigit(*++s)) {
  193. tab[i].value = tab[i].value*10 + (((int) *s) - '0');
  194. tab[i].digits++;
  195. }
  196. if (negative) {
  197. tab[i].value = -tab[i].value;
  198. }
  199. i++;
  200. negative = 0;
  201. waiting_separator = 1;
  202. break;
  203. default :
  204. return -1;
  205. }
  206. }
  207. while (isspace(*s)) {
  208. s++;
  209. }
  210. if (*s == '\0') {
  211. return 5;
  212. }
  213. if ((*s == '/') || (*s == ':') || (*s == '.') || (*s == '-')) {
  214. /* this was the seconds separator */
  215. s++;
  216. /* seconds are read in float format */
  217. if (parse_float(s, sec, &s) == RETURN_SUCCESS) {
  218. while (isspace(*s)) {
  219. s++;
  220. }
  221. if (*s == '\0') {
  222. return 6;
  223. }
  224. }
  225. }
  226. /* something is wrong */
  227. return -1;
  228. }
  229. /*
  230. * parse a date given either in calendar or numerical format
  231. */
  232. int parse_date(const Quark *q, const char* s, Dates_format preferred, int absolute,
  233. double *jul, Dates_format *recognized)
  234. {
  235. Project *pr = project_get_data(q);
  236. int i, n;
  237. int ky, km, kd;
  238. static Dates_format trials [] = {FMT_nohint, FMT_iso, FMT_european, FMT_us};
  239. Int_token tab [5];
  240. long j;
  241. double sec;
  242. /* first guess : is it a date in calendar format ? */
  243. n = parse_calendar_date(s, tab, &sec);
  244. switch (n) {
  245. /* we consider hours, minutes and seconds as optional items */
  246. case -1 : /* parse error */
  247. break;
  248. case 3 :
  249. tab[3].value = 0; /* adding hours */
  250. tab[3].digits = 1;
  251. case 4 :
  252. tab[4].value = 0; /* adding minutes */
  253. tab[4].digits = 1;
  254. case 5 :
  255. sec = 0.0; /* adding seconds */
  256. case 6 :
  257. /* we now have a complete date */
  258. /* try the user's choice first */
  259. trials[0] = preferred;
  260. for (i = 0; i < 4; i++) {
  261. if (trials[i] == FMT_iso) {
  262. /* YYYY-MM-DD */
  263. ky = 0;
  264. km = 1;
  265. kd = 2;
  266. } else if (trials[i] == FMT_european) {
  267. /* DD/MM/(YY)YY */
  268. ky = 2;
  269. km = 1;
  270. kd = 0;
  271. } else if (trials[i] == FMT_us) {
  272. /* MM/DD/(YY)YY */
  273. ky = 2;
  274. km = 0;
  275. kd = 1;
  276. } else {
  277. /* the user didn't choose a calendar format */
  278. continue;
  279. }
  280. if (check_date(pr, tab[ky], tab[km], tab[kd], &j)
  281. == RETURN_SUCCESS) {
  282. *jul =
  283. jul_and_time_to_jul(j, tab[3].value, tab[4].value, sec);
  284. if (!absolute) {
  285. *jul -= pr->ref_date;
  286. }
  287. *recognized = trials[i];
  288. return RETURN_SUCCESS;
  289. }
  290. }
  291. break;
  292. default :
  293. /* probably a julian date */
  294. break;
  295. }
  296. return RETURN_FAILURE;
  297. }
  298. int parse_date_or_number(const Quark *q, const char* s,
  299. int absolute, Dates_format hint, double *value)
  300. {
  301. Dates_format dummy;
  302. const char *sdummy;
  303. if (parse_date(q, s, hint, absolute, value, &dummy) == RETURN_SUCCESS) {
  304. return RETURN_SUCCESS;
  305. } else if (parse_float(s, value, &sdummy) == RETURN_SUCCESS) {
  306. return RETURN_SUCCESS;
  307. } else {
  308. return RETURN_FAILURE;
  309. }
  310. }