00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #define FREPPLE_CORE
00029 #include "frepple/utils.h"
00030 #include <ctime>
00031 #include <clocale>
00032
00033
00034 namespace frepple
00035 {
00036 namespace utils
00037 {
00038
00039 DECLARE_EXPORT string Date::format("%Y-%m-%dT%H:%M:%S");
00040 DECLARE_EXPORT string DateRange::separator = " / ";
00041 DECLARE_EXPORT size_t DateRange::separatorlength = 3;
00042
00043
00044
00045
00046
00047 DECLARE_EXPORT const Date Date::infinitePast("1971-01-01T00:00:00",true);
00048
00049
00050
00051 DECLARE_EXPORT const Date Date::infiniteFuture("2030-12-31T00:00:00",true);
00052
00053 DECLARE_EXPORT const TimePeriod TimePeriod::MAX(Date::infiniteFuture - Date::infinitePast);
00054 DECLARE_EXPORT const TimePeriod TimePeriod::MIN(Date::infinitePast - Date::infiniteFuture);
00055
00056
00057 DECLARE_EXPORT void Date::checkFinite(long long i)
00058 {
00059 if (i > infiniteFuture.lval) lval = infiniteFuture.lval;
00060 else if (i < infinitePast.lval) lval = infinitePast.lval;
00061 else lval = static_cast<long>(i);
00062 }
00063
00064
00065 DECLARE_EXPORT void TimePeriod::toCharBuffer(char* t) const
00066 {
00067 if (!lval)
00068 {
00069 sprintf(t,"P0D");
00070 return;
00071 }
00072 long tmp = (lval>0 ? lval : -lval);
00073 if (lval<0) *(t++) = '-';
00074 *(t++) = 'P';
00075 if (tmp >= 31536000L)
00076 {
00077 long y = tmp / 31536000L;
00078 t += sprintf(t,"%liY", y);
00079 tmp %= 31536000L;
00080 }
00081 if (tmp >= 86400L)
00082 {
00083 long d = tmp / 86400L;
00084 t += sprintf(t,"%liD", d);
00085 tmp %= 86400L;
00086 }
00087 if (tmp > 0L)
00088 {
00089 *(t++) = 'T';
00090 if (tmp >= 3600L)
00091 {
00092 long h = tmp / 3600L;
00093 t += sprintf(t,"%liH", h);
00094 tmp %= 3600L;
00095 }
00096 if (tmp >= 60L)
00097 {
00098 long h = tmp / 60L;
00099 t += sprintf(t,"%liM", h);
00100 tmp %= 60L;
00101 }
00102 if (tmp > 0L)
00103 sprintf(t,"%liS", tmp);
00104 }
00105 }
00106
00107
00108 DECLARE_EXPORT DateRange::operator string() const
00109 {
00110
00111 char r[65];
00112 char *pos = r + start.toCharBuffer(r);
00113
00114
00115 strcat(pos, separator.c_str());
00116 pos += separatorlength;
00117
00118
00119 end.toCharBuffer(pos);
00120 return r;
00121 }
00122
00123
00124 DECLARE_EXPORT size_t Date::toCharBuffer(char* str) const
00125 {
00126
00127
00128
00129
00130
00131
00132
00133 #ifdef HAVE_LOCALTIME_R
00134 struct tm timeStruct;
00135 localtime_r(&lval, &timeStruct);
00136 #else
00137 struct tm timeStruct = *localtime(&lval);
00138 #endif
00139 return strftime(str, 30, format.c_str(), &timeStruct);
00140 }
00141
00142
00143 DECLARE_EXPORT void TimePeriod::parse (const char* s)
00144 {
00145 long totalvalue = 0;
00146 long value = 0;
00147 bool negative = false;
00148 const char *c = s;
00149
00150
00151 if (*c == '-')
00152 {
00153 negative = true;
00154 ++c;
00155 }
00156
00157
00158 if (*c != 'P')
00159 throw DataException("Invalid time string '" + string(s) + "'");
00160 ++c;
00161
00162
00163 for ( ; *c && *c != 'T'; ++c)
00164 {
00165 switch (*c)
00166 {
00167 case '0': case '1': case '2': case '3': case '4':
00168 case '5': case '6': case '7': case '8': case '9':
00169 value = value * 10 + (*c - '0');
00170 break;
00171 case 'Y':
00172 totalvalue += value * 31536000L;
00173 value = 0;
00174 break;
00175 case 'M':
00176
00177 totalvalue += value * 2628000L;
00178 value = 0;
00179 break;
00180 case 'W':
00181 totalvalue += value * 604800L;
00182 value = 0;
00183 break;
00184 case 'D':
00185 totalvalue += value * 86400L;
00186 value = 0;
00187 break;
00188 default:
00189 throw DataException("Invalid time string '" + string(s) + "'");
00190 }
00191 }
00192
00193
00194 if (*c == 'T')
00195 {
00196 for (++c ; *c; ++c)
00197 {
00198 switch (*c)
00199 {
00200 case '0': case '1': case '2': case '3': case '4':
00201 case '5': case '6': case '7': case '8': case '9':
00202 value = value * 10 + (*c - '0');
00203 break;
00204 case 'H':
00205 totalvalue += value * 3600L;
00206 value = 0;
00207 break;
00208 case 'M':
00209 totalvalue += value * 60L;
00210 value = 0;
00211 break;
00212 case 'S':
00213 totalvalue += value;
00214 value = 0;
00215 break;
00216 default:
00217 throw DataException("Invalid time string '" + string(s) + "'");
00218 }
00219 }
00220 }
00221
00222
00223 if (value) throw DataException("Invalid time string '" + string(s) + "'");
00224
00225
00226 lval = negative ? -totalvalue : totalvalue;
00227 }
00228
00229
00230 DECLARE_EXPORT void Date::parse (const char* s, const string& fmt)
00231 {
00232 if (!s)
00233 {
00234
00235 lval = infinitePast.lval;
00236 return;
00237 }
00238 struct tm p;
00239 strptime(s, fmt.c_str(), &p);
00240
00241 p.tm_isdst = -1;
00242 lval = mktime(&p);
00243 }
00244
00245
00246 DECLARE_EXPORT Date::Date
00247 (int year, int month, int day, int hr, int min, int sec)
00248 {
00249 struct tm p;
00250 p.tm_isdst = -1;
00251 p.tm_year = year - 1900;
00252 p.tm_mon = month - 1;
00253 p.tm_mday = day;
00254 p.tm_hour = hr;
00255 p.tm_min = min;
00256 p.tm_sec = sec;
00257 lval = mktime(&p);
00258 checkFinite(lval);
00259 }
00260
00261
00262
00263
00264 #ifndef HAVE_STRPTIME
00265
00266 DECLARE_EXPORT char* Date::strptime(const char *buf, const char *fmt, struct tm *tm)
00267 {
00268 struct dtconv
00269 {
00270 char *abbrev_month_names[12];
00271 size_t len_abbrev_month_names[12];
00272 char *month_names[12];
00273 size_t len_month_names[12];
00274 char *abbrev_weekday_names[7];
00275 size_t len_abbrev_weekday_names[7];
00276 char *weekday_names[7];
00277 size_t len_weekday_names[7];
00278 char *time_format;
00279 char *sDate_format;
00280 char *dtime_format;
00281 char *am_string;
00282 size_t len_am_string;
00283 char *pm_string;
00284 size_t len_pm_string;
00285 char *lDate_format;
00286 unsigned short numWeekdays;
00287 unsigned short numMonths;
00288 };
00289
00290
00291 static struct dtconv En_US =
00292 {
00293 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00294 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00295 },
00296 { 3, 3, 3, 3, 3, 3,
00297 3, 3, 3, 3, 3, 3},
00298 { "January", "February", "March", "April", "May", "June", "July", "August",
00299 "September", "October", "November", "December" },
00300 { 8, 8, 5, 5, 3, 4, 4, 6,
00301 9, 7, 8, 8},
00302 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
00303 { 3, 3, 3, 3, 3, 3, 3},
00304 { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
00305 "Saturday" },
00306 { 6, 6, 7, 9, 8, 6,
00307 8},
00308 "%H:%M:%S",
00309 "%m/%d/%y",
00310 "%a %b %e %T %Z %Y",
00311 "AM",
00312 2,
00313 "PM",
00314 2,
00315 "%A, %B, %e, %Y",
00316 7,
00317 12
00318 };
00319
00320 char c, *ptr;
00321 short i, len = 0;
00322
00323
00324 tm->tm_isdst = -1;
00325
00326 ptr = (char*) fmt;
00327 while (*ptr != 0)
00328 {
00329
00330 if (*buf == 0) break;
00331 c = *ptr++;
00332 if (c != '%')
00333 {
00334 if (isspace(c))
00335 while (*buf != 0 && isspace(*buf)) buf++;
00336 else if (c != *buf++) return 0;
00337 continue;
00338 }
00339
00340 c = *ptr++;
00341 switch (c)
00342 {
00343 case 0:
00344 case '%':
00345 if (*buf++ != '%') return 0;
00346 break;
00347
00348 case 'C':
00349 buf = strptime(buf, En_US.lDate_format, tm);
00350 if (buf == 0) return 0;
00351 break;
00352
00353 case 'c':
00354 buf = strptime(buf, "%x %X", tm);
00355 if (buf == 0) return 0;
00356 break;
00357
00358 case 'D':
00359 buf = strptime(buf, "%m/%d/%y", tm);
00360 if (buf == 0) return 0;
00361 break;
00362
00363 case 'R':
00364 buf = strptime(buf, "%H:%M", tm);
00365 if (buf == 0) return 0;
00366 break;
00367
00368 case 'r':
00369 buf = strptime(buf, "%I:%M:%S %p", tm);
00370 if (buf == 0) return 0;
00371 break;
00372
00373 case 'T':
00374 buf = strptime(buf, "%H:%M:%S", tm);
00375 if (buf == 0) return 0;
00376 break;
00377
00378 case 'X':
00379 buf = strptime(buf, En_US.time_format, tm);
00380 if (buf == 0) return 0;
00381 break;
00382
00383 case 'x':
00384 buf = strptime(buf, En_US.sDate_format, tm);
00385 if (buf == 0) return 0;
00386 break;
00387
00388 case 'j':
00389 if (!isdigit(*buf)) return 0;
00390 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00391 {
00392 i *= 10;
00393 i += *buf - '0';
00394 }
00395 if (i > 365) return 0;
00396 tm->tm_yday = i;
00397 break;
00398
00399 case 'M':
00400 case 'S':
00401 if (*buf == 0 || isspace(*buf)) break;
00402 if (!isdigit(*buf)) return 0;
00403 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00404 {
00405 i *= 10;
00406 i += *buf - '0';
00407 }
00408 if (i > 59) return 0;
00409 if (c == 'M')
00410 tm->tm_min = i;
00411 else
00412 tm->tm_sec = i;
00413 if (*buf != 0 && isspace(*buf))
00414 while (*ptr != 0 && !isspace(*ptr)) ++ptr;
00415 break;
00416
00417 case 'H':
00418 case 'I':
00419 case 'k':
00420 case 'l':
00421 if (!isdigit(*buf)) return 0;
00422 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00423 {
00424 i *= 10;
00425 i += *buf - '0';
00426 }
00427 if (c == 'H' || c == 'k')
00428 {if (i > 23) return 0;}
00429 else if (i > 11) return 0;
00430 tm->tm_hour = i;
00431 if (*buf != 0 && isspace(*buf))
00432 while (*ptr != 0 && !isspace(*ptr)) ++ptr;
00433 break;
00434
00435 case 'p':
00436 if (strncasecmp(buf, En_US.am_string, En_US.len_am_string) == 0)
00437 {
00438 if (tm->tm_hour > 12) return 0;
00439 if (tm->tm_hour == 12) tm->tm_hour = 0;
00440 buf += len;
00441 break;
00442 }
00443 if (strncasecmp(buf, En_US.pm_string, En_US.len_pm_string) == 0)
00444 {
00445 if (tm->tm_hour > 12) return 0;
00446 if (tm->tm_hour != 12) tm->tm_hour += 12;
00447 buf += len;
00448 break;
00449 }
00450 return 0;
00451
00452 case 'A':
00453 case 'a':
00454 for (i = 0; i < En_US.numWeekdays; ++i)
00455 {
00456 if (strncasecmp(buf, En_US.weekday_names[i],
00457 En_US.len_weekday_names[i]) == 0) break;
00458 if (strncasecmp(buf, En_US.abbrev_weekday_names[i],
00459 En_US.len_abbrev_weekday_names[i]) == 0) break;
00460 }
00461 if (i == En_US.numWeekdays) return 0;
00462 tm->tm_wday = i;
00463 buf += len;
00464 break;
00465
00466 case 'd':
00467 case 'e':
00468 if (!isdigit(*buf)) return 0;
00469 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00470 {
00471 i *= 10;
00472 i += *buf - '0';
00473 }
00474 if (i > 31) return 0;
00475 tm->tm_mday = i;
00476 if (*buf != 0 && isspace(*buf))
00477 while (*ptr != 0 && !isspace(*ptr)) ++ptr;
00478 break;
00479
00480 case 'B':
00481 case 'b':
00482 case 'h':
00483 for (i = 0; i < En_US.numMonths; ++i)
00484 {
00485 if (strncasecmp(buf, En_US.month_names[i],
00486 En_US.len_month_names[i]) == 0) break;
00487 if (strncasecmp(buf, En_US.abbrev_month_names[i],
00488 En_US.len_abbrev_month_names[i]) == 0) break;
00489 }
00490 if (i == En_US.numMonths) return 0;
00491 tm->tm_mon = i;
00492 buf += len;
00493 break;
00494
00495 case 'm':
00496 if (!isdigit(*buf)) return 0;
00497 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00498 {
00499 i *= 10;
00500 i += *buf - '0';
00501 }
00502 if (i < 1 || i > 12) return 0;
00503 tm->tm_mon = i - 1;
00504 if (*buf != 0 && isspace(*buf))
00505 while (*ptr != 0 && !isspace(*ptr)) ++ptr;
00506 break;
00507
00508 case 'Y':
00509 case 'y':
00510 if (*buf == 0 || isspace(*buf)) break;
00511 if (!isdigit(*buf)) return 0;
00512 for (i = 0; *buf != 0 && isdigit(*buf); ++buf)
00513 {
00514 i *= 10;
00515 i += *buf - '0';
00516 }
00517 if (c == 'Y') i -= 1900;
00518 if (i < 0) return 0;
00519 tm->tm_year = i;
00520 if (*buf != 0 && isspace(*buf))
00521 while (*ptr != 0 && !isspace(*ptr)) ++ptr;
00522 break;
00523 }
00524 }
00525
00526 return const_cast<char*>(buf);
00527 }
00528
00529 #endif
00530
00531 }
00532 }
00533