date.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.9.1/src/utils/date.cpp $ 00003 version : $LastChangedRevision: 1656 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2012-03-27 19:05:34 +0200 (Tue, 27 Mar 2012) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2012 by Johan De Taeye, frePPLe bvba * 00010 * * 00011 * This library is free software; you can redistribute it and/or modify it * 00012 * under the terms of the GNU Lesser General Public License as published * 00013 * by the Free Software Foundation; either version 2.1 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 * This library is distributed in the hope that it will be useful, * 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * 00019 * General Public License for more details. * 00020 * * 00021 * You should have received a copy of the GNU Lesser General Public * 00022 * License along with this library; if not, write to the Free Software * 00023 * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * 00024 * USA * 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 /* This is the earliest date that we can represent. This not the 00044 * traditional epcoh start, but a year later. 1/1/1970 gave troubles 00045 * when using a timezone with positive offset to GMT. 00046 */ 00047 DECLARE_EXPORT const Date Date::infinitePast("1971-01-01T00:00:00",true); 00048 00049 /* This is the latest date that we can represent. This is not the absolute 00050 * limit of the internal representation, but more a convenient end date. */ 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 // Start date 00111 char r[65]; 00112 char *pos = r + start.toCharBuffer(r); 00113 00114 // Append the separator 00115 strcat(pos, separator.c_str()); 00116 pos += separatorlength; 00117 00118 // Append the end date 00119 end.toCharBuffer(pos); 00120 return r; 00121 } 00122 00123 00124 DECLARE_EXPORT size_t Date::toCharBuffer(char* str) const 00125 { 00126 // The standard library function localtime() is not re-entrant: the same 00127 // static structure is used for all calls. In a multi-threaded environment 00128 // the function is not to be used. 00129 // The POSIX standard defines a re-entrant version of the function: 00130 // localtime_r. 00131 // Visual C++ 6.0 and Borland 5.5 are missing it, but provide a thread-safe 00132 // variant without changing the function semantics. 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 // Optional minus sign 00151 if (*c == '-') 00152 { 00153 negative = true; 00154 ++c; 00155 } 00156 00157 // Compulsary 'P' 00158 if (*c != 'P') 00159 throw DataException("Invalid time string '" + string(s) + "'"); 00160 ++c; 00161 00162 // Parse the date part 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 // 1 Month = 1 Year / 12 = 365 days / 12 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 // Parse the time part 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 // Missing a time unit 00223 if (value) throw DataException("Invalid time string '" + string(s) + "'"); 00224 00225 // If no exceptions where thrown we can now store the value 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 // Null string passed - default value is infinite past 00235 lval = infinitePast.lval; 00236 return; 00237 } 00238 struct tm p; 00239 strptime(s, fmt.c_str(), &p); 00240 // No clue whether daylight saving time is in effect... 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 // The next method is only compiled if the function strptime 00263 // isn't available in your standard library. 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 // The "length" fields in this structure MUST match the values in the strings. 00291 static struct dtconv En_US = 00292 { 00293 { 00294 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 00295 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 00296 }, 00297 { 00298 3, 3, 3, 3, 3, 3, 00299 3, 3, 3, 3, 3, 3 00300 }, 00301 { 00302 "January", "February", "March", "April", "May", "June", "July", "August", 00303 "September", "October", "November", "December" 00304 }, 00305 { 00306 8, 8, 5, 5, 3, 4, 4, 6, 00307 9, 7, 8, 8 00308 }, 00309 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, 00310 { 3, 3, 3, 3, 3, 3, 3}, 00311 { 00312 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", 00313 "Saturday" 00314 }, 00315 { 00316 6, 6, 7, 9, 8, 6, 00317 8 00318 }, 00319 "%H:%M:%S", 00320 "%m/%d/%y", 00321 "%a %b %e %T %Z %Y", 00322 "AM", 00323 2, 00324 "PM", 00325 2, 00326 "%A, %B, %e, %Y", 00327 7, 00328 12 00329 }; 00330 00331 char c, *ptr; 00332 short i, len = 0; 00333 00334 // No clude whether daylight saving time is in effect... 00335 tm->tm_isdst = -1; 00336 00337 ptr = (char*) fmt; 00338 while (*ptr != 0) 00339 { 00340 00341 if (*buf == 0) break; 00342 c = *ptr++; 00343 if (c != '%') 00344 { 00345 if (isspace(c)) 00346 while (*buf != 0 && isspace(*buf)) buf++; 00347 else if (c != *buf++) return 0; 00348 continue; 00349 } 00350 00351 c = *ptr++; 00352 switch (c) 00353 { 00354 case 0: 00355 case '%': 00356 if (*buf++ != '%') return 0; 00357 break; 00358 00359 case 'C': 00360 buf = strptime(buf, En_US.lDate_format, tm); 00361 if (buf == 0) return 0; 00362 break; 00363 00364 case 'c': 00365 buf = strptime(buf, "%x %X", tm); 00366 if (buf == 0) return 0; 00367 break; 00368 00369 case 'D': 00370 buf = strptime(buf, "%m/%d/%y", tm); 00371 if (buf == 0) return 0; 00372 break; 00373 00374 case 'R': 00375 buf = strptime(buf, "%H:%M", tm); 00376 if (buf == 0) return 0; 00377 break; 00378 00379 case 'r': 00380 buf = strptime(buf, "%I:%M:%S %p", tm); 00381 if (buf == 0) return 0; 00382 break; 00383 00384 case 'T': 00385 buf = strptime(buf, "%H:%M:%S", tm); 00386 if (buf == 0) return 0; 00387 break; 00388 00389 case 'X': 00390 buf = strptime(buf, En_US.time_format, tm); 00391 if (buf == 0) return 0; 00392 break; 00393 00394 case 'x': 00395 buf = strptime(buf, En_US.sDate_format, tm); 00396 if (buf == 0) return 0; 00397 break; 00398 00399 case 'j': 00400 if (!isdigit(*buf)) return 0; 00401 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00402 { 00403 i *= 10; 00404 i += *buf - '0'; 00405 } 00406 if (i > 365) return 0; 00407 tm->tm_yday = i; 00408 break; 00409 00410 case 'M': 00411 case 'S': 00412 if (*buf == 0 || isspace(*buf)) break; 00413 if (!isdigit(*buf)) return 0; 00414 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00415 { 00416 i *= 10; 00417 i += *buf - '0'; 00418 } 00419 if (i > 59) return 0; 00420 if (c == 'M') 00421 tm->tm_min = i; 00422 else 00423 tm->tm_sec = i; 00424 if (*buf != 0 && isspace(*buf)) 00425 while (*ptr != 0 && !isspace(*ptr)) ++ptr; 00426 break; 00427 00428 case 'H': 00429 case 'I': 00430 case 'k': 00431 case 'l': 00432 if (!isdigit(*buf)) return 0; 00433 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00434 { 00435 i *= 10; 00436 i += *buf - '0'; 00437 } 00438 if (c == 'H' || c == 'k') 00439 {if (i > 23) return 0;} 00440 else if (i > 11) return 0; 00441 tm->tm_hour = i; 00442 if (*buf != 0 && isspace(*buf)) 00443 while (*ptr != 0 && !isspace(*ptr)) ++ptr; 00444 break; 00445 00446 case 'p': 00447 if (strncasecmp(buf, En_US.am_string, En_US.len_am_string) == 0) 00448 { 00449 if (tm->tm_hour > 12) return 0; 00450 if (tm->tm_hour == 12) tm->tm_hour = 0; 00451 buf += len; 00452 break; 00453 } 00454 if (strncasecmp(buf, En_US.pm_string, En_US.len_pm_string) == 0) 00455 { 00456 if (tm->tm_hour > 12) return 0; 00457 if (tm->tm_hour != 12) tm->tm_hour += 12; 00458 buf += len; 00459 break; 00460 } 00461 return 0; 00462 00463 case 'A': 00464 case 'a': 00465 for (i = 0; i < En_US.numWeekdays; ++i) 00466 { 00467 if (strncasecmp(buf, En_US.weekday_names[i], 00468 En_US.len_weekday_names[i]) == 0) break; 00469 if (strncasecmp(buf, En_US.abbrev_weekday_names[i], 00470 En_US.len_abbrev_weekday_names[i]) == 0) break; 00471 } 00472 if (i == En_US.numWeekdays) return 0; 00473 tm->tm_wday = i; 00474 buf += len; 00475 break; 00476 00477 case 'd': 00478 case 'e': 00479 if (!isdigit(*buf)) return 0; 00480 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00481 { 00482 i *= 10; 00483 i += *buf - '0'; 00484 } 00485 if (i > 31) return 0; 00486 tm->tm_mday = i; 00487 if (*buf != 0 && isspace(*buf)) 00488 while (*ptr != 0 && !isspace(*ptr)) ++ptr; 00489 break; 00490 00491 case 'B': 00492 case 'b': 00493 case 'h': 00494 for (i = 0; i < En_US.numMonths; ++i) 00495 { 00496 if (strncasecmp(buf, En_US.month_names[i], 00497 En_US.len_month_names[i]) == 0) break; 00498 if (strncasecmp(buf, En_US.abbrev_month_names[i], 00499 En_US.len_abbrev_month_names[i]) == 0) break; 00500 } 00501 if (i == En_US.numMonths) return 0; 00502 tm->tm_mon = i; 00503 buf += len; 00504 break; 00505 00506 case 'm': 00507 if (!isdigit(*buf)) return 0; 00508 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00509 { 00510 i *= 10; 00511 i += *buf - '0'; 00512 } 00513 if (i < 1 || i > 12) return 0; 00514 tm->tm_mon = i - 1; 00515 if (*buf != 0 && isspace(*buf)) 00516 while (*ptr != 0 && !isspace(*ptr)) ++ptr; 00517 break; 00518 00519 case 'Y': 00520 case 'y': 00521 if (*buf == 0 || isspace(*buf)) break; 00522 if (!isdigit(*buf)) return 0; 00523 for (i = 0; *buf != 0 && isdigit(*buf); ++buf) 00524 { 00525 i *= 10; 00526 i += *buf - '0'; 00527 } 00528 if (c == 'Y') i -= 1900; 00529 if (i < 0) return 0; 00530 tm->tm_year = i; 00531 if (*buf != 0 && isspace(*buf)) 00532 while (*ptr != 0 && !isspace(*ptr)) ++ptr; 00533 break; 00534 } 00535 } 00536 00537 return const_cast<char*>(buf); 00538 } 00539 00540 #endif 00541 00542 } // end namespace 00543 } // end namespace 00544