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