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 

Documentation generated for frePPLe by  doxygen