date.cpp

Go to the documentation of this file.
00001 /*
00002  * date.cpp
00003  *
00004  * Copyright (c) 2003-2009 Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of basic date functions (see date.h)
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "date.h"               // always include our own header first
00036 
00037 #include <sstream>
00038 
00039 #include <time.h>
00040 #include <math.h>
00041 
00042 
00043 
00044 static const long s_secondsPerDay       = 24 * 3600;
00045 static const long s_secondsPerWeek      = 7 * s_secondsPerDay;
00046 
00047 static const long s_firstSunday         = 3 * s_secondsPerDay;  // I think ???
00048 
00049 
00050 
00051 /*
00052  * typedefs
00053  */
00054 
00055 struct month_entry_t {
00056         const char     *name;
00057         int             month;
00058 };
00059 
00060 
00061 
00062 /*
00063  * structs (local only)
00064  */
00065 
00066 #define MONTH_ENTRY(name, month) { #name , month },
00067 
00068 static const month_entry_t s_Months[] = {
00069         MONTH_ENTRY(1,                  1)
00070         MONTH_ENTRY(01,                 1)
00071         MONTH_ENTRY(jan,                1)
00072         MONTH_ENTRY(january,            1)
00073 
00074         MONTH_ENTRY(2,                  2)
00075         MONTH_ENTRY(02,                 2)
00076         MONTH_ENTRY(feb,                2)
00077         MONTH_ENTRY(february,           2)
00078 
00079         MONTH_ENTRY(3,                  3)
00080         MONTH_ENTRY(03,                 3)
00081         MONTH_ENTRY(mar,                3)
00082         MONTH_ENTRY(march,              3)
00083 
00084         MONTH_ENTRY(4,                  4)
00085         MONTH_ENTRY(04,                 4)
00086         MONTH_ENTRY(apr,                4)
00087         MONTH_ENTRY(april,              4)
00088 
00089         MONTH_ENTRY(5,                  5)
00090         MONTH_ENTRY(05,                 5)
00091         MONTH_ENTRY(may,                5)
00092 
00093         MONTH_ENTRY(6,                  6)
00094         MONTH_ENTRY(06,                 6)
00095         MONTH_ENTRY(jun,                6)
00096         MONTH_ENTRY(june,               6)
00097 
00098         MONTH_ENTRY(7,                  7)
00099         MONTH_ENTRY(07,                 7)
00100         MONTH_ENTRY(jul,                7)
00101         MONTH_ENTRY(july,               7)
00102 
00103         MONTH_ENTRY(8,                  8)
00104         MONTH_ENTRY(08,                 8)
00105         MONTH_ENTRY(aug,                8)
00106         MONTH_ENTRY(august,             8)
00107 
00108         MONTH_ENTRY(9,                  9)
00109         MONTH_ENTRY(09,                 9)
00110         MONTH_ENTRY(sep,                9)
00111         MONTH_ENTRY(september,          9)
00112 
00113         MONTH_ENTRY(10,                 10)
00114         MONTH_ENTRY(oct,                10)
00115         MONTH_ENTRY(october,            10)
00116 
00117         MONTH_ENTRY(11,                 11)
00118         MONTH_ENTRY(nov,                11)
00119         MONTH_ENTRY(november,           11)
00120 
00121         MONTH_ENTRY(12,                 12)
00122         MONTH_ENTRY(dec,                12)
00123         MONTH_ENTRY(december,           12)
00124 
00125         // must be last!
00126         { NULL, 0 }
00127 };
00128 
00129 
00130 // 0-11 display
00131 static const char * s_MonthsDisplay[] = {
00132         "jan",
00133         "feb",
00134         "mar",
00135         "apr",
00136         "may",
00137         "jun",
00138         "jul",
00139         "aug",
00140         "sep",
00141         "oct",
00142         "nov",
00143         "dec"
00144 };
00145 
00146 
00147 // 0-6 display
00148 static const char * s_Weekdays[] = {
00149         "sun",
00150         "mon",
00151         "tue",
00152         "wed",
00153         "thu",
00154         "fri",
00155         "sat"
00156 };
00157 
00158 
00159 
00160 ////////////////////////////////////////////////////////////////////////////////
00161 //
00162 //      static helper methods
00163 //
00164 ////////////////////////////////////////////////////////////////////////////////
00165 
00166 static const char *
00167 get_month_display
00168 (
00169 IN int month
00170 )
00171 throw()
00172 {
00173         ASSERT(month >= 0, "invalid month index");
00174         ASSERT(month < 12, "invalid month index");
00175 
00176         return s_MonthsDisplay[month];
00177 }
00178 
00179 
00180 
00181 static double
00182 my_trunc
00183 (
00184 IN double x
00185 )
00186 throw()
00187 {
00188         ASSERT(x < 2.0e9, "x too big");
00189         ASSERT(x > -2.0e9, "x too small");
00190 
00191         double t;
00192         
00193         if (x >= 0.0) {
00194                 long l = (long) x;
00195                 double frac = x - l;
00196                 t = x - frac;
00197         } else {
00198                 t = -my_trunc(-x);
00199         }
00200 
00201         // DPRINTF("   trunc(%lf) = %lf", x, t);
00202         
00203         return t;
00204 }
00205 
00206 
00207 ////////////////////////////////////////////////////////////////////////////////
00208 //
00209 //      Public API
00210 //
00211 ////////////////////////////////////////////////////////////////////////////////
00212 
00213 int
00214 GetMonthFromString
00215 (
00216 IN const char * month
00217 )
00218 throw()
00219 {
00220         ASSERT(month, "NULL month");
00221 
00222         for (const month_entry_t * p = s_Months; p->name; ++p) {
00223                 if (!strcasecmp(p->name, month))
00224                         return p->month;
00225         }
00226 
00227         // not found!
00228         return 0;
00229 }
00230 
00231 
00232 
00233 void
00234 getDisplayableDateFromNetTime
00235 (
00236 IN long time,
00237 OUT std::string& display
00238 )
00239 {
00240         ASSERT(time > 0, "Bad time: %ld", time);
00241 
00242         char buffer[32];
00243 
00244 #ifdef WIN32
00245         // TODO: get a threadsafe version?
00246         strncpy(buffer, ctime(&time), 26);
00247 #else   // WIN32
00248         ctime_r(&time, buffer);
00249 #endif  // WIN32
00250 
00251         buffer[24] = 0;
00252 
00253         display = buffer;
00254 }
00255 
00256 
00257 const char *
00258 getDateString
00259 (
00260 IN long timestamp
00261 )
00262 {
00263         struct tm * t = localtime(&timestamp);
00264         ASSERT(t, "null");
00265 
00266         static const int s_bufSize = 32;
00267         static char s_buffer[s_bufSize];
00268         sprintf(s_buffer, "%02d-%s-%04d %02d:%02d:%02d",
00269             t->tm_mday, get_month_display(t->tm_mon), t->tm_year + 1900,
00270             t->tm_hour, t->tm_min, t->tm_sec);
00271 
00272         return s_buffer;
00273 }
00274 
00275 
00276 
00277 void
00278 getDateStringFromNetTime
00279 (
00280 IN long time,
00281 OUT std::string& display
00282 )
00283 {
00284         ASSERT(time > 0, "Bad time: %ld", time);
00285 
00286         display = getDateString(time);
00287 }
00288 
00289 
00290 
00291 static const char *
00292 extractInteger
00293 (
00294 IN const char * s,
00295 IN char separator,
00296 OUT int& x,
00297 IN int min,
00298 IN int max,
00299 IN int digits_required
00300 )
00301 throw()
00302 {
00303         ASSERT(s, "null");
00304         // ASSERT(separator) -- can be zero
00305         ASSERT(max > min, "Bad range?");
00306 
00307         int digits_read = 0;
00308         x = 0;          // initialize
00309         while (*s && *s != separator) {
00310                 int i = *s - '0';
00311                 x = (10 * x) + i;
00312                 ++digits_read;
00313                 ++s;
00314         }
00315         if (*s)
00316                 ++s;    // move past separator
00317 
00318         // DPRINTF("read x=%d", x);
00319 
00320         if (digits_read < digits_required)
00321                 return NULL;
00322         if (x < min)
00323                 return NULL;
00324         if (x > max)
00325                 return NULL;
00326 
00327         return s;
00328 }
00329 
00330 
00331 
00332 static const char *
00333 extractString
00334 (
00335 IN const char * s,
00336 IN char separator,
00337 IO char * buf,
00338 IN int size
00339 )
00340 throw()
00341 {
00342         ASSERT(s, "null");
00343         ASSERT(buf, "null");
00344         ASSERT(size > 0, "Bad buffer size: %d", size);
00345 
00346         int idx = 0;
00347         while (*s && *s != separator && idx < size - 1) {
00348                 buf[idx] = *s;
00349                 ++idx;
00350                 ++s;
00351         }
00352         buf[idx] = 0;   // null-terminate
00353         if (*s)
00354                 ++s;    // step past separator
00355 
00356         if (0 == idx)
00357                 return 0;
00358 
00359         return s;
00360 }
00361 
00362 
00363 
00364 long
00365 getNetTimeFromDateString
00366 (
00367 IN const char * s
00368 )
00369 throw()
00370 {
00371         ASSERT(s, "null");
00372 
00373         struct tm t;
00374 
00375         // const char * date = s;       // save original pointer
00376 
00377         // extract fields
00378         s = extractInteger(s, '-', t.tm_mday, 1, 31, 1);
00379         if (!s)
00380                 return -1;
00381         // DPRINTF("mday: %d", t.tm_mday);
00382 
00383         static const int s_bufSize = 4;
00384         char buffer[s_bufSize];
00385         s = extractString(s, '-', buffer, s_bufSize);
00386         if (!s)
00387                 return -1;
00388         t.tm_mon = GetMonthFromString(buffer);
00389         if (t.tm_mon < 1)
00390                 return -1;
00391         --t.tm_mon;
00392         //DPRINTF("mon: %d", t.tm_mon);
00393 
00394         s = extractInteger(s, ' ', t.tm_year, -10000, +10000, 4);
00395         if (!s)
00396                 return -1;
00397         t.tm_year -= 1900;
00398         //DPRINTF("year: %d", t.tm_year);
00399 
00400         s = extractInteger(s, ':', t.tm_hour, 0, 23, 2);
00401         if (!s)
00402                 return -1;
00403         //DPRINTF("hour: %d", t.tm_hour);
00404 
00405         s = extractInteger(s, ':', t.tm_min, 0, 59, 2);
00406         if (!s)
00407                 return -1;
00408         //DPRINTF("min: %d", t.tm_min);
00409 
00410         s = extractInteger(s, 0, t.tm_sec, 0, 59, 2);
00411         if (!s)
00412                 return -1;
00413         //DPRINTF("sec: %d", t.tm_sec);
00414 
00415 /*
00416         DPRINTF("Extracted from '%s':", date);
00417         DPRINTF("  day/month/year %02d/%02d/%d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900);
00418         DPRINTF("  hh:mm:ss %02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
00419 */
00420         return mktime(&t);
00421 }
00422 
00423 
00424 
00425 int
00426 getWeekdayFromNetTime
00427 (
00428 IN long time
00429 )
00430 throw()
00431 {
00432         ASSERT(time > 0, "Bad net time");
00433 
00434         long day = time / (24 * 3600);  // divide seconds by seconds in a day
00435 
00436         // add 4 (Jan 1 1970 was a Thursday)
00437         return (day + 4) % 7;
00438 }
00439 
00440 
00441 const char *
00442 GetWeekday
00443 (
00444 IN int weekday_index
00445 )
00446 throw()
00447 {
00448         ASSERT(weekday_index >= 0, "Bad weekday index");
00449         ASSERT(weekday_index < 7, "Bad weekday index");
00450 
00451         return s_Weekdays[weekday_index];
00452 }
00453 
00454 
00455 
00456 const char *
00457 GetMonth
00458 (
00459 IN int month_index
00460 )
00461 throw()
00462 {
00463         return get_month_display(month_index);
00464 }
00465 
00466 
00467 
00468 /*
00469  * bucketing routines
00470  */
00471 
00472 long
00473 getDayBucket
00474 (
00475 IN long timestamp
00476 )
00477 throw()
00478 {
00479         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00480 
00481         // easy: just divide by seconds per day!
00482         return timestamp / s_secondsPerDay;
00483 }
00484 
00485 
00486 long
00487 getWeekBucket
00488 (
00489 IN long timestamp
00490 )
00491 throw()
00492 {
00493         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00494 
00495         return (timestamp - s_firstSunday) / (s_secondsPerWeek);
00496 }
00497 
00498 
00499 
00500 long
00501 getMonthBucket
00502 (
00503 IN long timestamp
00504 )
00505 throw()
00506 {
00507         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00508 
00509         struct tm * tm = gmtime(&timestamp);
00510         return tm->tm_mon + (tm->tm_year * 12);
00511 }
00512 
00513 
00514 
00515 long
00516 getYearBucket
00517 (
00518 IN long timestamp
00519 )
00520 throw()
00521 {
00522         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00523 
00524         struct tm * tm = gmtime(&timestamp);
00525         return tm->tm_year;
00526 }
00527 
00528 
00529 
00530 std::string
00531 getDayBucketName
00532 (
00533 IN long day_bucket
00534 )
00535 {
00536         return getDateString(day_bucket * s_secondsPerDay);
00537 }
00538 
00539 
00540 
00541 std::string
00542 getWeekBucketName
00543 (
00544 IN long week_bucket
00545 )
00546 {
00547         return getDateString((week_bucket * s_secondsPerWeek) + s_firstSunday);
00548 }
00549 
00550 
00551 std::string
00552 getMonthBucketName
00553 (
00554 IN long month_bucket
00555 )
00556 {
00557         std::ostringstream out;
00558         out << s_MonthsDisplay[month_bucket % 12] << " " << (1900 + (month_bucket / 12));
00559         return out.str();
00560 }
00561 
00562 
00563 std::string
00564 getYearBucketName
00565 (
00566 IN long year_bucket
00567 )
00568 {
00569         std::ostringstream out;
00570         out << (1900 + year_bucket);
00571         return out.str();
00572 }
00573