time.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       time.cc
00003 ///             Conversion between time_t and cenmin_t and back.
00004 ///             time_t is the POSIX time, seconds from Jan 1, 1970
00005 ///             min1900_t is the minutes from Jan 1, 1900
00006 ///
00007 
00008 /*
00009     Copyright (C) 2005-2012, Net Direct Inc. (http://www.netdirect.ca/)
00010 
00011     This program is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015 
00016     This program 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.
00019 
00020     See the GNU General Public License in the COPYING file at the
00021     root directory of this project for more details.
00022 */
00023 
00024 #include "time.h"
00025 #include "endian.h"
00026 #include "debug.h"
00027 #include "platform.h"
00028 
00029 namespace Barry {
00030 
00031 StaticTimeZone Zones[] = {
00032         { 0x0000,  -12,   0, "Eniwetok, Kwajalein" }, // (-12)
00033         { 0x0001,  -12,   0, "Midway Island, Samoa" }, // (-12)
00034         { 0x0002,  -10,   0, "Hawaii" }, // (-10)
00035         { 0x0003,   -9,   0, "Alaska" }, // (-9)
00036         { 0x0004,   -8,   0, "Pacific Time (US & Canada), Tijuana" }, // (-8)
00037         { 0x000a,   -7,   0, "Mountain Time (US & Canada)" }, // (-7)
00038         { 0x000f,   -7,   0, "Arizona" }, // (-7)
00039         { 0x000d,   -7,   0, "Chihuahua, La Paz, Mazatlan" }, // (-7)
00040         { 0x0014,   -6,   0, "Central Time (US & Canada)" }, // (-6)
00041         { 0x0021,   -6,   0, "Central America" }, // (-6)
00042         { 0x0019,   -6,   0, "Saskatchewan" }, // (-6)
00043         { 0x001e,   -6,   0, "Mexico City" }, // (-6)
00044         { 0x0023,   -5,   0, "Eastern Time (US & Canada)" }, // (-5)
00045         { 0x002d,   -5,   0, "Bogota, Lima, Quito" }, // (-5)
00046         { 0x0028,   -5,   0, "Indiana (East)" }, // (-5)
00047         { 0x0032,   -4,   0, "Atlantic Time (Canada)" }, // (-4)
00048         { 0x0037,   -4,   0, "Caracas, La Paz" }, // (-4)
00049         { 0x0038,   -4,   0, "Santiago" }, // (-4)
00050         { 0x003c,   -3, -30, "Newfoundland" }, // (-3.5)
00051         { 0x0046,   -3,   0, "Buenos Aires, Georgetown" }, // (-3)
00052         { 0x0041,   -3,   0, "Brasilia" }, // (-3)
00053         { 0x0049,   -3,   0, "Greenland" }, // (-3)
00054         { 0x004b,   -2,   0, "Mid-Atlantic" }, // (-2)
00055         { 0x0053,   -1,   0, "Cape Verde Island" }, // (-1)
00056         { 0x0050,   -1,   0, "Azores" }, // (-1)
00057         { 0x0055,    0,   0, "Dublin, Edinburgh, Lisbon, London (GMT)" },
00058         { 0x005a,    0,   0, "Casablanca, Monrovia (GMT)" },
00059         { 0x006e,    1,   0, "Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna" }, // (+1)
00060         { 0x0071,    1,   0, "West Central Africa" }, // (+1)
00061         { 0x005f,    1,   0, "Belgrade, Bratislava, Budapest, Ljubljana, Prague" }, // (+1)
00062         { 0x0069,    1,   0, "Brussels, Copenhagen, Madrid, Paris" }, // (+1)
00063         { 0x0064,    1,   0, "Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb" }, // (+1)
00064         { 0x008c,    2,   0, "Harare, Pretoria" }, // (+2)
00065         { 0x0087,    2,   0, "Jerusalem" }, // (+2)
00066         { 0x0073,    2,   0, "Bucharest" }, // (+2)
00067         { 0x0078,    2,   0, "Cairo" }, // (+2)
00068         { 0x0082,    2,   0, "Athens, Istanbul, Minsk" }, // (+2)
00069         { 0x007d,    2,   0, "Helsinki, Riga, Tallinn" }, // (+2)
00070         { 0x0096,    3,   0, "Kuwait, Riyadh" }, // (+3)
00071         { 0x009b,    3,   0, "Nairobi" }, // (+3)
00072         { 0x009e,    3,   0, "Baghdad" }, // (+3)
00073         { 0x0091,    3,   0, "Moscow, St. Petersburg, Volgograd" }, // (+3)
00074         { 0x00a0,    3,  30, "Tehran" }, // (+3.5)
00075         { 0x00a5,    4,   0, "Abu Dhabi, Muscat" }, // (+4)
00076         { 0x00aa,    4,   0, "Baku, Tbilisi, Yerevan" }, // (+4)
00077         { 0x00af,    4,  30, "Kabul" }, // (+4.5)
00078         { 0x00b9,    5,   0, "Islamabad, Karachi, Tashkent" }, // (+5)
00079         { 0x00b4,    5,   0, "Ekaterinburg" }, // (+5)
00080         { 0x00be,    5,  30, "Calcutta, Chennai, Mumbai, New Delhi" }, // (+5.5)
00081         { 0x00c1,    5,  45, "Kathmandu" }, // (+5.75)
00082         { 0x00c3,    6,   0, "Astana, Dhaka" }, // (+6)
00083         { 0x00c8,    6,   0, "Sri Lanka" }, // (+6)
00084         { 0x00c9,    6,   0, "Almaty, Novosibirsk" }, // (+6)
00085         { 0x00cb,    6,  30, "Rangoon" }, // (+6.5)
00086         { 0x00cd,    7,   0, "Bangkok, Hanoi, Jakarta" }, // (+7)
00087         { 0x00cf,    7,   0, "Krasnoyarsk" }, // (+7)
00088         { 0x00d2,    8,   0, "Beijing, Chongqing, Hong Kong, Urumqi" }, // (+8)
00089         { 0x00d7,    8,   0, "Kuala Lumpur, Singapore" }, // (+8)
00090         { 0x00e1,    8,   0, "Perth" }, // (+8)
00091         { 0x00dc,    8,   0, "Taipei" }, // (+8)
00092         { 0x00e3,    8,   0, "Irkutsk, Ulaan Bataar" }, // (+8)
00093         { 0x00eb,    9,   0, "Osaka, Sapporo, Tokyo" }, // (+9)
00094         { 0x00e6,    9,   0, "Seoul" }, // (+9)
00095         { 0x00f0,    9,   0, "Yakutsk" }, // (+9)
00096         { 0x00f5,    9,  30, "Darwin" }, // (+9.5)
00097         { 0x00fa,    9,  30, "Adelaide" }, // (+9.5)
00098         { 0x0104,   10,   0, "Brisbane" }, // (+10)
00099         { 0x0113,   10,   0, "Guam, Port Moresby" }, // (+10)
00100         { 0x00ff,   10,   0, "Canberra, Melbourne, Sydney" }, // (+10)
00101         { 0x0109,   10,   0, "Hobart" }, // (+10)
00102         { 0x010e,   10,   0, "Vladivostok" }, // (+10)
00103         { 0x0118,   11,   0, "Magadan, Solomon Islands, New Caledonia" }, // (+11)
00104         { 0x011d,   12,   0, "Fiji, Kamchatka, Marshall Islands" }, // (+12)
00105         { 0x0122,   12,   0, "Auckland, Wellington" }, // (+12)
00106         { 0x012c,   13,   0, "Nuku'alofa" }, // (+13)
00107         { 0,         0,   0, 0 }
00108 };
00109 
00110 min1900_t time2min(time_t t)
00111 {
00112         if( t == 0 )
00113                 return htobl(0xffffffff);
00114 
00115         min1900_t r = t / 60 + STDC_MIN1900_DIFF;
00116         return htobl(r);
00117 }
00118 
00119 time_t min2time(min1900_t m)
00120 {
00121         if( (unsigned long) btohl(m) == 0xffffffff )
00122                 return 0;
00123         else
00124                 return (btohl(m) - STDC_MIN1900_DIFF) * 60;
00125 }
00126 
00127 
00128 //
00129 // GetStaticTimeZoneTable
00130 //
00131 /// Returns a pointer to an array of StaticTimeZone structs.
00132 /// The last struct contains 0 in all fields, and can be used as
00133 /// an "end of array" marker.
00134 ///
00135 const StaticTimeZone* GetStaticTimeZoneTable()
00136 {
00137         return Zones;
00138 }
00139 
00140 //
00141 // GetStaticTimeZone
00142 //
00143 /// Searches the internal timezone code table for the given Code
00144 /// and returns a pointer to a StaticTimeZone struct found.  If the
00145 /// code is not found, a pointer to a valid StaticTimeZone struct is
00146 /// is still returned, but the struct's Code contains STATIC_TIME_ZONE_CODE_ERR,
00147 /// and the name is "Unknown time zone."  The unknown timezone
00148 /// is the same offset as GMT.
00149 ///
00150 const StaticTimeZone* GetStaticTimeZone(uint16_t Code)
00151 {
00152         static StaticTimeZone Unknown = { STATIC_TIME_ZONE_CODE_ERR, 0, 0, "Unknown time zone" };
00153 
00154         for( StaticTimeZone *z = Zones; z->Name; z++ ) {
00155                 if( Code == z->Code )
00156                         return z;
00157         }
00158         return &Unknown;
00159 }
00160 
00161 //
00162 // GetStaticTimeZoneCode
00163 //
00164 /// Searches the internal timezone table for the first matching
00165 /// Code.  If no matching Code is found, STATIC_TIME_ZONE_CODE_ERR is returned.
00166 ///
00167 /// This function does not adjust for daylight saving time.
00168 ///
00169 uint16_t GetStaticTimeZoneCode(signed short HourOffset,
00170                                signed short MinOffset)
00171 {
00172         for( StaticTimeZone *z = Zones; z->Name; z++ ) {
00173                 if( HourOffset == z->HourOffset && MinOffset == z->MinOffset )
00174                         return z->Code;
00175         }
00176         return STATIC_TIME_ZONE_CODE_ERR;
00177 }
00178 
00179 /// This routine takes the day of the year and
00180 /// returns a time_t adjusted from the first of
00181 /// the year.
00182 ///
00183 /// FIXME This function assumes the year hasn't changed,
00184 /// but I don't have enough information to determine
00185 /// where the year is in this header info
00186 ///
00187 time_t DayToDate( uint16_t Day )
00188 {
00189         struct tm *now, then;
00190         time_t t = time( NULL );
00191 
00192         now = localtime( &t ); // need this to get year
00193         // set to Jan 1 midnight, this year;
00194         then.tm_sec = 0;
00195         then.tm_min = 0;
00196         then.tm_hour = 0;
00197         then.tm_mday = 0;
00198         then.tm_mon = 0;
00199         then.tm_year = now->tm_year;
00200         then.tm_isdst = -1;
00201         t = mktime(&then);
00202         t -= 60*60;                     // need to subract an hour
00203         t += Day * 24 * 60 * 60;        // Add the day converted to seconds
00204 
00205         return t;
00206 }
00207 
00208 //
00209 // Message2Time
00210 //
00211 /// Localize the funky math used to convert a Blackberry message
00212 /// timestamp into a time_t.
00213 ///
00214 /// Both r_date and r_time are expected to be fed in from the
00215 /// Protocol::MessageRecord struct in raw form, without endian
00216 /// conversion.  This function handles that.
00217 ///
00218 time_t Message2Time(uint16_t r_date, uint16_t r_time)
00219 {
00220         dout("Message2Time(0x" << std::hex << btohs(r_date) << ", 0x"
00221                 << btohs(r_time) << ")");
00222 
00223         time_t result = ( btohs(r_date) & 0x01ff ) - 0x29;
00224         result = DayToDate( result );
00225         result += (time_t)( btohs(r_time)*1.77 );
00226 
00227         dout("Message2Time result: " << ctime(&result));
00228         return result;
00229 }
00230 
00231 //
00232 // ThreadTimeout
00233 //
00234 /// Creates a pthread_cond_timedwait() compatible timespec struct,
00235 /// based on a given timeout in milliseconds.  Note that the resulting
00236 /// timespec is time-sensitive: the 'timer' starts as soon as this function
00237 /// returns, since timespec is a specific time in the future,
00238 /// and ThreadTimeout() calculates it based on the current time.
00239 ///
00240 /// Returns the spec pointer, to make it easy to use with
00241 /// pthread_cond_timedwait()
00242 ///
00243 struct timespec* ThreadTimeout(int timeout_ms, struct timespec *spec)
00244 {
00245 #ifndef WIN32
00246         struct timeval now;
00247         gettimeofday(&now, NULL);
00248 
00249         spec->tv_sec = now.tv_sec + timeout_ms / 1000;
00250         spec->tv_nsec = (now.tv_usec + timeout_ms % 1000 * 1000) * 1000;
00251 #else
00252         SYSTEMTIME now;
00253         GetSystemTime(&now);
00254 
00255         spec->tv_sec = now.wSecond + timeout_ms / 1000;
00256         spec->tv_nsec = (now.wMilliseconds + timeout_ms) * 1000;
00257 #endif
00258 
00259         return spec;
00260 }
00261 
00262 //
00263 // DaysInMonth
00264 //
00265 /// Returns the number of days in the month, given the tm_mon and tm_year
00266 /// as specified in the struct tm argument.  It _does_ take leap year into
00267 /// account.
00268 ///
00269 BXEXPORT int DaysInMonth(struct tm &t)
00270 {
00271         int year = t.tm_year + 1900;
00272 
00273         switch( t.tm_mon )
00274         {
00275         case 1:
00276                 if( year % 400 == 0 || (year % 100 != 0 && year % 4 == 0) )
00277                         return 29;
00278                 else
00279                         return 28;
00280         case 4:
00281         case 6:
00282         case 9:
00283         case 11:
00284                 return 30;
00285         default:
00286                 return 31;
00287         }
00288 }
00289 
00290 
00291 
00292 } // namespace Barry
00293 
00294 
00295 #ifdef __TEST_MODE__
00296 
00297 #include <iostream>
00298 #include <iomanip>
00299 
00300 using namespace std;
00301 using namespace Barry;
00302 
00303 void display(const char *msg, time_t t)
00304 {
00305         cout << msg << ": " << ctime(&t);
00306         cout << msg << " seconds: "
00307                 << setbase(10) << t
00308                 << "(0x" << setbase(16) << t << ")"
00309                 << endl;
00310         cout << msg << " minutes: "
00311                 << setbase(10) << (t/60)
00312                 << "(0x" << setbase(16) << (t/60) << ")"
00313                 << endl;
00314         cout << endl;
00315 }
00316 
00317 void calc(const char *msg, time_t t, min1900_t dbval)
00318 {
00319         cout << msg << endl;
00320         display("    Initial time", t);
00321         display("    DB Val", min2time(dbval));
00322 }
00323 
00324 int main()
00325 {
00326         struct tm start;
00327         time_t t;
00328 
00329         // set to Oct 4, 2005, 2pm;
00330         start.tm_sec = 0;
00331         start.tm_min = 0;
00332         start.tm_hour = 14;
00333         start.tm_mday = 4;
00334         start.tm_mon = 9;
00335         start.tm_year = 105;
00336         start.tm_isdst = -1;
00337         t = mktime(&start);
00338         calc("Oct 4", t, 0x0350c118);
00339 
00340         // comparison
00341         t = time(NULL);
00342         min1900_t m = time2min(t);
00343         time_t tc = min2time(m);
00344         cout << "Original time: " << t << endl;
00345         cout << "time2min:      " << m << endl;
00346         cout << "min2time:      " << tc << endl;
00347         if( t == (tc + t % 60) )
00348                 cout << "Success! (orig == converted + mod)" << endl;
00349         else
00350                 cout << "Failed!" << endl;
00351 
00352         // time zone
00353         cout << "Should say Eastern: " << GetStaticTimeZone(0x23)->Name << endl;
00354         cout << "should say Unknown: " << GetStaticTimeZone(0xffff)->Name << endl;
00355 }
00356 
00357 #endif
00358