date.cpp

Go to the documentation of this file.
00001 /*
00002  * date.cpp
00003  *
00004  * Copyright (c) 2003-2010 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 
00214 int
00215 GetMonthFromString
00216 (
00217 IN const char * month
00218 )
00219 throw()
00220 {
00221         ASSERT(month, "NULL month");
00222 
00223         for (const month_entry_t * p = s_Months; p->name; ++p) {
00224                 if (!strcasecmp(p->name, month))
00225                         return p->month;
00226         }
00227 
00228         // not found!
00229         return 0;
00230 }
00231 
00232 
00233 
00234 void
00235 getDisplayableDateFromNetTime
00236 (
00237 IN long time,
00238 OUT std::string& display
00239 )
00240 {
00241         ASSERT(time > 0, "Bad time: %ld", time);
00242 
00243         char buffer[32];
00244 
00245 #ifdef WIN32
00246         // TODO: get a threadsafe version?
00247         time_t ttTime;
00248         strncpy(buffer, ctime(&ttTime), 26);
00249 #else   // WIN32
00250         ctime_r(&time, buffer);
00251 #endif  // WIN32
00252 
00253         buffer[24] = 0;
00254 
00255         display = buffer;
00256 }
00257 
00258 
00259 const char *
00260 getDateString
00261 (
00262 IN long timestamp
00263 )
00264 {
00265         time_t ttTime = timestamp;
00266         struct tm * t = localtime(&ttTime);
00267         ASSERT(t, "null");
00268 
00269         static const int s_bufSize = 32;
00270         static char s_buffer[s_bufSize];
00271         sprintf(s_buffer, "%02d-%s-%04d %02d:%02d:%02d",
00272             t->tm_mday, get_month_display(t->tm_mon), t->tm_year + 1900,
00273             t->tm_hour, t->tm_min, t->tm_sec);
00274 
00275         return s_buffer;
00276 }
00277 
00278 
00279 
00280 void
00281 getDateStringFromNetTime
00282 (
00283 IN long time,
00284 OUT std::string& display
00285 )
00286 {
00287         ASSERT(time > 0, "Bad time: %ld", time);
00288 
00289         display = getDateString(time);
00290 }
00291 
00292 
00293 
00294 static const char *
00295 extractInteger
00296 (
00297 IN const char * s,
00298 IN char separator,
00299 OUT int& x,
00300 IN int min,
00301 IN int max,
00302 IN int digits_required
00303 )
00304 throw()
00305 {
00306         ASSERT(s, "null");
00307         // ASSERT(separator) -- can be zero
00308         ASSERT(max > min, "Bad range?");
00309 
00310         int digits_read = 0;
00311         x = 0;          // initialize
00312         while (*s && *s != separator) {
00313                 int i = *s - '0';
00314                 x = (10 * x) + i;
00315                 ++digits_read;
00316                 ++s;
00317         }
00318         if (*s)
00319                 ++s;    // move past separator
00320 
00321         // DPRINTF("read x=%d", x);
00322 
00323         if (digits_read < digits_required)
00324                 return NULL;
00325         if (x < min)
00326                 return NULL;
00327         if (x > max)
00328                 return NULL;
00329 
00330         return s;
00331 }
00332 
00333 
00334 
00335 static const char *
00336 extractString
00337 (
00338 IN const char * s,
00339 IN char separator,
00340 IO char * buf,
00341 IN int size
00342 )
00343 throw()
00344 {
00345         ASSERT(s, "null");
00346         ASSERT(buf, "null");
00347         ASSERT(size > 0, "Bad buffer size: %d", size);
00348 
00349         int idx = 0;
00350         while (*s && *s != separator && idx < size - 1) {
00351                 buf[idx] = *s;
00352                 ++idx;
00353                 ++s;
00354         }
00355         buf[idx] = 0;   // null-terminate
00356         if (*s)
00357                 ++s;    // step past separator
00358 
00359         if (0 == idx)
00360                 return 0;
00361 
00362         return s;
00363 }
00364 
00365 
00366 
00367 long
00368 getNetTimeFromDateString
00369 (
00370 IN const char * s
00371 )
00372 throw()
00373 {
00374         ASSERT(s, "null");
00375 
00376         struct tm t;
00377 
00378         // const char * date = s;       // save original pointer
00379 
00380         // extract fields
00381         s = extractInteger(s, '-', t.tm_mday, 1, 31, 1);
00382         if (!s)
00383                 return -1;
00384         // DPRINTF("mday: %d", t.tm_mday);
00385 
00386         static const int s_bufSize = 4;
00387         char buffer[s_bufSize];
00388         s = extractString(s, '-', buffer, s_bufSize);
00389         if (!s)
00390                 return -1;
00391         t.tm_mon = GetMonthFromString(buffer);
00392         if (t.tm_mon < 1)
00393                 return -1;
00394         --t.tm_mon;
00395         //DPRINTF("mon: %d", t.tm_mon);
00396 
00397         s = extractInteger(s, ' ', t.tm_year, -10000, +10000, 4);
00398         if (!s)
00399                 return -1;
00400         t.tm_year -= 1900;
00401         //DPRINTF("year: %d", t.tm_year);
00402 
00403         s = extractInteger(s, ':', t.tm_hour, 0, 23, 2);
00404         if (!s)
00405                 return -1;
00406         //DPRINTF("hour: %d", t.tm_hour);
00407 
00408         s = extractInteger(s, ':', t.tm_min, 0, 59, 2);
00409         if (!s)
00410                 return -1;
00411         //DPRINTF("min: %d", t.tm_min);
00412 
00413         s = extractInteger(s, 0, t.tm_sec, 0, 59, 2);
00414         if (!s)
00415                 return -1;
00416         //DPRINTF("sec: %d", t.tm_sec);
00417 
00418 /*
00419         DPRINTF("Extracted from '%s':", date);
00420         DPRINTF("  day/month/year %02d/%02d/%d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900);
00421         DPRINTF("  hh:mm:ss %02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
00422 */
00423         return mktime(&t);
00424 }
00425 
00426 
00427 
00428 int
00429 getWeekdayFromNetTime
00430 (
00431 IN long time
00432 )
00433 throw()
00434 {
00435         ASSERT(time > 0, "Bad net time");
00436 
00437         long day = time / (24 * 3600);  // divide seconds by seconds in a day
00438 
00439         // add 4 (Jan 1 1970 was a Thursday)
00440         return (day + 4) % 7;
00441 }
00442 
00443 
00444 const char *
00445 GetWeekday
00446 (
00447 IN int weekday_index
00448 )
00449 throw()
00450 {
00451         ASSERT(weekday_index >= 0, "Bad weekday index");
00452         ASSERT(weekday_index < 7, "Bad weekday index");
00453 
00454         return s_Weekdays[weekday_index];
00455 }
00456 
00457 
00458 
00459 const char *
00460 GetMonth
00461 (
00462 IN int month_index
00463 )
00464 throw()
00465 {
00466         return get_month_display(month_index);
00467 }
00468 
00469 
00470 
00471 /*
00472  * bucketing routines
00473  */
00474 
00475 long
00476 getDayBucket
00477 (
00478 IN long timestamp
00479 )
00480 throw()
00481 {
00482         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00483 
00484         // easy: just divide by seconds per day!
00485         return timestamp / s_secondsPerDay;
00486 }
00487 
00488 
00489 long
00490 getWeekBucket
00491 (
00492 IN long timestamp
00493 )
00494 throw()
00495 {
00496         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00497 
00498         return (timestamp - s_firstSunday) / (s_secondsPerWeek);
00499 }
00500 
00501 
00502 
00503 long
00504 getMonthBucket
00505 (
00506 IN long timestamp
00507 )
00508 throw()
00509 {
00510         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00511 
00512         time_t ttTime = timestamp;
00513         struct tm * tm = gmtime(&ttTime);
00514         return tm->tm_mon + (tm->tm_year * 12);
00515 }
00516 
00517 
00518 
00519 long
00520 getYearBucket
00521 (
00522 IN long timestamp
00523 )
00524 throw()
00525 {
00526         ASSERT(timestamp > 0, "bad timestamp: %ld", timestamp);
00527 
00528         time_t ttTime = timestamp;
00529         struct tm * tm = gmtime(&ttTime);
00530         return tm->tm_year;
00531 }
00532 
00533 
00534 
00535 std::string
00536 getDayBucketName
00537 (
00538 IN long day_bucket
00539 )
00540 {
00541         return getDateString(day_bucket * s_secondsPerDay);
00542 }
00543 
00544 
00545 
00546 std::string
00547 getWeekBucketName
00548 (
00549 IN long week_bucket
00550 )
00551 {
00552         return getDateString((week_bucket * s_secondsPerWeek) + s_firstSunday);
00553 }
00554 
00555 
00556 std::string
00557 getMonthBucketName
00558 (
00559 IN long month_bucket
00560 )
00561 {
00562         std::ostringstream out;
00563         out << s_MonthsDisplay[month_bucket % 12] << " " << (1900 + (month_bucket / 12));
00564         return out.str();
00565 }
00566 
00567 
00568 std::string
00569 getYearBucketName
00570 (
00571 IN long year_bucket
00572 )
00573 {
00574         std::ostringstream out;
00575         out << (1900 + year_bucket);
00576         return out.str();
00577 }
00578