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 

Documentation generated for frePPLe by  doxygen