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