date.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba *
4  * *
5  * This library is free software; you can redistribute it and/or modify it *
6  * under the terms of the GNU Affero General Public License as published *
7  * by the Free Software Foundation; either version 3 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This library is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU Affero General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU Affero General Public *
16  * License along with this program. *
17  * If not, see <http://www.gnu.org/licenses/>. *
18  * *
19  ***************************************************************************/
20 
21 #define FREPPLE_CORE
22 #include "frepple/utils.h"
23 #include <ctime>
24 #include <clocale>
25 
26 
27 namespace frepple
28 {
29 namespace utils
30 {
31 
32 DECLARE_EXPORT string Date::format("%Y-%m-%dT%H:%M:%S");
33 DECLARE_EXPORT string DateRange::separator = " / ";
34 DECLARE_EXPORT size_t DateRange::separatorlength = 3;
35 
36 /* This is the earliest date that we can represent. This not the
37  * traditional epoch start, but a year later. 1/1/1970 gave troubles
38  * when using a timezone with positive offset to GMT.
39  */
40 DECLARE_EXPORT const Date Date::infinitePast("1971-01-01T00:00:00",true);
41 
42 /* This is the latest date that we can represent. This is not the absolute
43  * limit of the internal representation, but more a convenient end date. */
44 DECLARE_EXPORT const Date Date::infiniteFuture("2030-12-31T00:00:00",true);
45 
46 DECLARE_EXPORT const TimePeriod TimePeriod::MAX(Date::infiniteFuture - Date::infinitePast);
47 DECLARE_EXPORT const TimePeriod TimePeriod::MIN(Date::infinitePast - Date::infiniteFuture);
48 
49 
50 DECLARE_EXPORT void Date::checkFinite(long long i)
51 {
52  if (i > infiniteFuture.lval) lval = infiniteFuture.lval;
53  else if (i < infinitePast.lval) lval = infinitePast.lval;
54  else lval = static_cast<long>(i);
55 }
56 
57 
58 DECLARE_EXPORT void TimePeriod::toCharBuffer(char* t) const
59 {
60  if (!lval)
61  {
62  sprintf(t,"P0D");
63  return;
64  }
65  long tmp = (lval>0 ? lval : -lval);
66  if (lval<0) *(t++) = '-';
67  *(t++) = 'P';
68  if (tmp >= 31536000L)
69  {
70  long y = tmp / 31536000L;
71  t += sprintf(t,"%liY", y);
72  tmp %= 31536000L;
73  }
74  if (tmp >= 86400L)
75  {
76  long d = tmp / 86400L;
77  t += sprintf(t,"%liD", d);
78  tmp %= 86400L;
79  }
80  if (tmp > 0L)
81  {
82  *(t++) = 'T';
83  if (tmp >= 3600L)
84  {
85  long h = tmp / 3600L;
86  t += sprintf(t,"%liH", h);
87  tmp %= 3600L;
88  }
89  if (tmp >= 60L)
90  {
91  long h = tmp / 60L;
92  t += sprintf(t,"%liM", h);
93  tmp %= 60L;
94  }
95  if (tmp > 0L)
96  sprintf(t,"%liS", tmp);
97  }
98 }
99 
100 
101 DECLARE_EXPORT DateRange::operator string() const
102 {
103  // Start date
104  char r[65];
105  char *pos = r + start.toCharBuffer(r);
106 
107  // Append the separator
108  strcat(pos, separator.c_str());
109  pos += separatorlength;
110 
111  // Append the end date
112  end.toCharBuffer(pos);
113  return r;
114 }
115 
116 
117 DECLARE_EXPORT void TimePeriod::parse (const char* s)
118 {
119  long totalvalue = 0;
120  long value = 0;
121  bool negative = false;
122  const char *c = s;
123 
124  // Optional minus sign
125  if (*c == '-')
126  {
127  negative = true;
128  ++c;
129  }
130 
131  // Compulsary 'P'
132  if (*c != 'P')
133  throw DataException("Invalid time string '" + string(s) + "'");
134  ++c;
135 
136  // Parse the date part
137  for ( ; *c && *c != 'T'; ++c)
138  {
139  switch (*c)
140  {
141  case '0': case '1': case '2': case '3': case '4':
142  case '5': case '6': case '7': case '8': case '9':
143  value = value * 10 + (*c - '0');
144  break;
145  case 'Y':
146  totalvalue += value * 31536000L;
147  value = 0;
148  break;
149  case 'M':
150  // 1 Month = 1 Year / 12 = 365 days / 12
151  totalvalue += value * 2628000L;
152  value = 0;
153  break;
154  case 'W':
155  totalvalue += value * 604800L;
156  value = 0;
157  break;
158  case 'D':
159  totalvalue += value * 86400L;
160  value = 0;
161  break;
162  default:
163  throw DataException("Invalid time string '" + string(s) + "'");
164  }
165  }
166 
167  // Parse the time part
168  if (*c == 'T')
169  {
170  for (++c ; *c; ++c)
171  {
172  switch (*c)
173  {
174  case '0': case '1': case '2': case '3': case '4':
175  case '5': case '6': case '7': case '8': case '9':
176  value = value * 10 + (*c - '0');
177  break;
178  case 'H':
179  totalvalue += value * 3600L;
180  value = 0;
181  break;
182  case 'M':
183  totalvalue += value * 60L;
184  value = 0;
185  break;
186  case 'S':
187  totalvalue += value;
188  value = 0;
189  break;
190  default:
191  throw DataException("Invalid time string '" + string(s) + "'");
192  }
193  }
194  }
195 
196  // Missing a time unit
197  if (value) throw DataException("Invalid time string '" + string(s) + "'");
198 
199  // If no exceptions where thrown we can now store the value
200  lval = negative ? -totalvalue : totalvalue;
201 }
202 
203 
204 DECLARE_EXPORT void Date::parse (const char* s, const string& fmt)
205 {
206  if (!s)
207  {
208  // Null string passed - default value is infinite past
209  lval = infinitePast.lval;
210  return;
211  }
212  struct tm p;
213  strptime(s, fmt.c_str(), &p);
214  // No clue whether daylight saving time is in effect...
215  p.tm_isdst = -1;
216  lval = mktime(&p);
217 }
218 
219 
221 (int year, int month, int day, int hr, int min, int sec)
222 {
223  struct tm p;
224  p.tm_isdst = -1;
225  p.tm_year = year - 1900;
226  p.tm_mon = month - 1;
227  p.tm_mday = day;
228  p.tm_hour = hr;
229  p.tm_min = min;
230  p.tm_sec = sec;
231  lval = mktime(&p);
232  checkFinite(lval);
233 }
234 
235 
236 // The next method is only compiled if the function strptime
237 // isn't available in your standard library.
238 #ifndef HAVE_STRPTIME
239 
240 DECLARE_EXPORT char* Date::strptime(const char *buf, const char *fmt, struct tm *tm)
241 {
242  struct dtconv
243  {
244  char *abbrev_month_names[12];
245  size_t len_abbrev_month_names[12];
246  char *month_names[12];
247  size_t len_month_names[12];
248  char *abbrev_weekday_names[7];
249  size_t len_abbrev_weekday_names[7];
250  char *weekday_names[7];
251  size_t len_weekday_names[7];
252  char *time_format;
253  char *sDate_format;
254  char *dtime_format;
255  char *am_string;
256  size_t len_am_string;
257  char *pm_string;
258  size_t len_pm_string;
259  char *lDate_format;
260  unsigned short numWeekdays;
261  unsigned short numMonths;
262  };
263 
264  // The "length" fields in this structure MUST match the values in the strings.
265  static struct dtconv En_US =
266  {
267  {
268  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
269  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
270  },
271  {
272  3, 3, 3, 3, 3, 3,
273  3, 3, 3, 3, 3, 3
274  },
275  {
276  "January", "February", "March", "April", "May", "June", "July", "August",
277  "September", "October", "November", "December"
278  },
279  {
280  8, 8, 5, 5, 3, 4, 4, 6,
281  9, 7, 8, 8
282  },
283  { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
284  { 3, 3, 3, 3, 3, 3, 3},
285  {
286  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
287  "Saturday"
288  },
289  {
290  6, 6, 7, 9, 8, 6,
291  8
292  },
293  "%H:%M:%S",
294  "%m/%d/%y",
295  "%a %b %e %T %Z %Y",
296  "AM",
297  2,
298  "PM",
299  2,
300  "%A, %B, %e, %Y",
301  7,
302  12
303  };
304 
305  char c, *ptr;
306  short i, len = 0;
307 
308  // No clue whether daylight saving time is in effect...
309  tm->tm_isdst = -1;
310 
311  ptr = (char*) fmt;
312  while (*ptr != 0)
313  {
314 
315  if (*buf == 0) break;
316  c = *ptr++;
317  if (c != '%')
318  {
319  if (isspace(c))
320  while (*buf != 0 && isspace(*buf)) buf++;
321  else if (c != *buf++) return 0;
322  continue;
323  }
324 
325  c = *ptr++;
326  switch (c)
327  {
328  case 0:
329  case '%':
330  if (*buf++ != '%') return 0;
331  break;
332 
333  case 'C':
334  buf = strptime(buf, En_US.lDate_format, tm);
335  if (buf == 0) return 0;
336  break;
337 
338  case 'c':
339  buf = strptime(buf, "%x %X", tm);
340  if (buf == 0) return 0;
341  break;
342 
343  case 'D':
344  buf = strptime(buf, "%m/%d/%y", tm);
345  if (buf == 0) return 0;
346  break;
347 
348  case 'R':
349  buf = strptime(buf, "%H:%M", tm);
350  if (buf == 0) return 0;
351  break;
352 
353  case 'r':
354  buf = strptime(buf, "%I:%M:%S %p", tm);
355  if (buf == 0) return 0;
356  break;
357 
358  case 'T':
359  buf = strptime(buf, "%H:%M:%S", tm);
360  if (buf == 0) return 0;
361  break;
362 
363  case 'X':
364  buf = strptime(buf, En_US.time_format, tm);
365  if (buf == 0) return 0;
366  break;
367 
368  case 'x':
369  buf = strptime(buf, En_US.sDate_format, tm);
370  if (buf == 0) return 0;
371  break;
372 
373  case 'j':
374  if (!isdigit(*buf)) return 0;
375  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
376  {
377  i *= 10;
378  i += *buf - '0';
379  }
380  if (i > 365) return 0;
381  tm->tm_yday = i;
382  break;
383 
384  case 'M':
385  case 'S':
386  if (*buf == 0 || isspace(*buf)) break;
387  if (!isdigit(*buf)) return 0;
388  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
389  {
390  i *= 10;
391  i += *buf - '0';
392  }
393  if (i > 59) return 0;
394  if (c == 'M')
395  tm->tm_min = i;
396  else
397  tm->tm_sec = i;
398  if (*buf != 0 && isspace(*buf))
399  while (*ptr != 0 && !isspace(*ptr)) ++ptr;
400  break;
401 
402  case 'H':
403  case 'I':
404  case 'k':
405  case 'l':
406  if (!isdigit(*buf)) return 0;
407  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
408  {
409  i *= 10;
410  i += *buf - '0';
411  }
412  if (c == 'H' || c == 'k')
413  {if (i > 23) return 0;}
414  else if (i > 11) return 0;
415  tm->tm_hour = i;
416  if (*buf != 0 && isspace(*buf))
417  while (*ptr != 0 && !isspace(*ptr)) ++ptr;
418  break;
419 
420  case 'p':
421  if (strncasecmp(buf, En_US.am_string, En_US.len_am_string) == 0)
422  {
423  if (tm->tm_hour > 12) return 0;
424  if (tm->tm_hour == 12) tm->tm_hour = 0;
425  buf += len;
426  break;
427  }
428  if (strncasecmp(buf, En_US.pm_string, En_US.len_pm_string) == 0)
429  {
430  if (tm->tm_hour > 12) return 0;
431  if (tm->tm_hour != 12) tm->tm_hour += 12;
432  buf += len;
433  break;
434  }
435  return 0;
436 
437  case 'A':
438  case 'a':
439  for (i = 0; i < En_US.numWeekdays; ++i)
440  {
441  if (strncasecmp(buf, En_US.weekday_names[i],
442  En_US.len_weekday_names[i]) == 0) break;
443  if (strncasecmp(buf, En_US.abbrev_weekday_names[i],
444  En_US.len_abbrev_weekday_names[i]) == 0) break;
445  }
446  if (i == En_US.numWeekdays) return 0;
447  tm->tm_wday = i;
448  buf += len;
449  break;
450 
451  case 'd':
452  case 'e':
453  if (!isdigit(*buf)) return 0;
454  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
455  {
456  i *= 10;
457  i += *buf - '0';
458  }
459  if (i > 31) return 0;
460  tm->tm_mday = i;
461  if (*buf != 0 && isspace(*buf))
462  while (*ptr != 0 && !isspace(*ptr)) ++ptr;
463  break;
464 
465  case 'B':
466  case 'b':
467  case 'h':
468  for (i = 0; i < En_US.numMonths; ++i)
469  {
470  if (strncasecmp(buf, En_US.month_names[i],
471  En_US.len_month_names[i]) == 0) break;
472  if (strncasecmp(buf, En_US.abbrev_month_names[i],
473  En_US.len_abbrev_month_names[i]) == 0) break;
474  }
475  if (i == En_US.numMonths) return 0;
476  tm->tm_mon = i;
477  buf += len;
478  break;
479 
480  case 'm':
481  if (!isdigit(*buf)) return 0;
482  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
483  {
484  i *= 10;
485  i += *buf - '0';
486  }
487  if (i < 1 || i > 12) return 0;
488  tm->tm_mon = i - 1;
489  if (*buf != 0 && isspace(*buf))
490  while (*ptr != 0 && !isspace(*ptr)) ++ptr;
491  break;
492 
493  case 'Y':
494  case 'y':
495  if (*buf == 0 || isspace(*buf)) break;
496  if (!isdigit(*buf)) return 0;
497  for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
498  {
499  i *= 10;
500  i += *buf - '0';
501  }
502  if (c == 'Y') i -= 1900;
503  if (i < 0) return 0;
504  tm->tm_year = i;
505  if (*buf != 0 && isspace(*buf))
506  while (*ptr != 0 && !isspace(*ptr)) ++ptr;
507  break;
508  }
509  }
510 
511  return const_cast<char*>(buf);
512 }
513 
514 #endif
515 
516 } // end namespace
517 } // end namespace
518