date.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/utils/date.cpp $ 00003 version : $LastChangedRevision: 1505 $ $LastChangedBy: jdetaeye $ 00004 date : $LastChangedDate: 2011-08-26 18:55:08 +0200 (Fri, 26 Aug 2011) $ 00005 ***************************************************************************/ 00006 00007 /*************************************************************************** 00008 * * 00009 * Copyright (C) 2007-2011 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 { "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 // No clude whether daylight saving time is in effect... 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 } // end namespace 00532 } // end namespace 00533