date.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/tags/0.8.0/src/utils/date.cpp $
00003   version : $LastChangedRevision: 1108 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2009-12-06 18:54:18 +0100 (Sun, 06 Dec 2009) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 by Johan De Taeye                                    *
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 

Generated on 16 Apr 2010 for frePPLe by  doxygen 1.6.1