Bumped copyright dates for 2013
[barry.git] / src / time.cc
blobdfbfce64514a2607c5df78fcf3b58730989e1d43
1 ///
2 /// \file time.cc
3 /// Conversion between time_t and cenmin_t and back.
4 /// time_t is the POSIX time, seconds from Jan 1, 1970
5 /// min1900_t is the minutes from Jan 1, 1900
6 ///
8 /*
9 Copyright (C) 2005-2013, Net Direct Inc. (http://www.netdirect.ca/)
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "i18n.h"
25 #include "time.h"
26 #include "endian.h"
27 #include "debug.h"
28 #include "platform.h"
30 namespace Barry {
32 StaticTimeZone Zones[] = {
33 { 0x0000, -12, 0, "Eniwetok, Kwajalein" }, // (-12)
34 { 0x0001, -12, 0, "Midway Island, Samoa" }, // (-12)
35 { 0x0002, -10, 0, "Hawaii" }, // (-10)
36 { 0x0003, -9, 0, "Alaska" }, // (-9)
37 { 0x0004, -8, 0, "Pacific Time (US & Canada), Tijuana" }, // (-8)
38 { 0x000a, -7, 0, "Mountain Time (US & Canada)" }, // (-7)
39 { 0x000f, -7, 0, "Arizona" }, // (-7)
40 { 0x000d, -7, 0, "Chihuahua, La Paz, Mazatlan" }, // (-7)
41 { 0x0014, -6, 0, "Central Time (US & Canada)" }, // (-6)
42 { 0x0021, -6, 0, "Central America" }, // (-6)
43 { 0x0019, -6, 0, "Saskatchewan" }, // (-6)
44 { 0x001e, -6, 0, "Mexico City" }, // (-6)
45 { 0x0023, -5, 0, "Eastern Time (US & Canada)" }, // (-5)
46 { 0x002d, -5, 0, "Bogota, Lima, Quito" }, // (-5)
47 { 0x0028, -5, 0, "Indiana (East)" }, // (-5)
48 { 0x0032, -4, 0, "Atlantic Time (Canada)" }, // (-4)
49 { 0x0037, -4, 0, "Caracas, La Paz" }, // (-4)
50 { 0x0038, -4, 0, "Santiago" }, // (-4)
51 { 0x003c, -3, -30, "Newfoundland" }, // (-3.5)
52 { 0x0046, -3, 0, "Buenos Aires, Georgetown" }, // (-3)
53 { 0x0041, -3, 0, "Brasilia" }, // (-3)
54 { 0x0049, -3, 0, "Greenland" }, // (-3)
55 { 0x004b, -2, 0, "Mid-Atlantic" }, // (-2)
56 { 0x0053, -1, 0, "Cape Verde Island" }, // (-1)
57 { 0x0050, -1, 0, "Azores" }, // (-1)
58 { 0x0055, 0, 0, "Dublin, Edinburgh, Lisbon, London (GMT)" },
59 { 0x005a, 0, 0, "Casablanca, Monrovia (GMT)" },
60 { 0x006e, 1, 0, "Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna" }, // (+1)
61 { 0x0071, 1, 0, "West Central Africa" }, // (+1)
62 { 0x005f, 1, 0, "Belgrade, Bratislava, Budapest, Ljubljana, Prague" }, // (+1)
63 { 0x0069, 1, 0, "Brussels, Copenhagen, Madrid, Paris" }, // (+1)
64 { 0x0064, 1, 0, "Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb" }, // (+1)
65 { 0x008c, 2, 0, "Harare, Pretoria" }, // (+2)
66 { 0x0087, 2, 0, "Jerusalem" }, // (+2)
67 { 0x0073, 2, 0, "Bucharest" }, // (+2)
68 { 0x0078, 2, 0, "Cairo" }, // (+2)
69 { 0x0082, 2, 0, "Athens, Istanbul, Minsk" }, // (+2)
70 { 0x007d, 2, 0, "Helsinki, Riga, Tallinn" }, // (+2)
71 { 0x0096, 3, 0, "Kuwait, Riyadh" }, // (+3)
72 { 0x009b, 3, 0, "Nairobi" }, // (+3)
73 { 0x009e, 3, 0, "Baghdad" }, // (+3)
74 { 0x0091, 3, 0, "Moscow, St. Petersburg, Volgograd" }, // (+3)
75 { 0x00a0, 3, 30, "Tehran" }, // (+3.5)
76 { 0x00a5, 4, 0, "Abu Dhabi, Muscat" }, // (+4)
77 { 0x00aa, 4, 0, "Baku, Tbilisi, Yerevan" }, // (+4)
78 { 0x00af, 4, 30, "Kabul" }, // (+4.5)
79 { 0x00b9, 5, 0, "Islamabad, Karachi, Tashkent" }, // (+5)
80 { 0x00b4, 5, 0, "Ekaterinburg" }, // (+5)
81 { 0x00be, 5, 30, "Calcutta, Chennai, Mumbai, New Delhi" }, // (+5.5)
82 { 0x00c1, 5, 45, "Kathmandu" }, // (+5.75)
83 { 0x00c3, 6, 0, "Astana, Dhaka" }, // (+6)
84 { 0x00c8, 6, 0, "Sri Lanka" }, // (+6)
85 { 0x00c9, 6, 0, "Almaty, Novosibirsk" }, // (+6)
86 { 0x00cb, 6, 30, "Rangoon" }, // (+6.5)
87 { 0x00cd, 7, 0, "Bangkok, Hanoi, Jakarta" }, // (+7)
88 { 0x00cf, 7, 0, "Krasnoyarsk" }, // (+7)
89 { 0x00d2, 8, 0, "Beijing, Chongqing, Hong Kong, Urumqi" }, // (+8)
90 { 0x00d7, 8, 0, "Kuala Lumpur, Singapore" }, // (+8)
91 { 0x00e1, 8, 0, "Perth" }, // (+8)
92 { 0x00dc, 8, 0, "Taipei" }, // (+8)
93 { 0x00e3, 8, 0, "Irkutsk, Ulaan Bataar" }, // (+8)
94 { 0x00eb, 9, 0, "Osaka, Sapporo, Tokyo" }, // (+9)
95 { 0x00e6, 9, 0, "Seoul" }, // (+9)
96 { 0x00f0, 9, 0, "Yakutsk" }, // (+9)
97 { 0x00f5, 9, 30, "Darwin" }, // (+9.5)
98 { 0x00fa, 9, 30, "Adelaide" }, // (+9.5)
99 { 0x0104, 10, 0, "Brisbane" }, // (+10)
100 { 0x0113, 10, 0, "Guam, Port Moresby" }, // (+10)
101 { 0x00ff, 10, 0, "Canberra, Melbourne, Sydney" }, // (+10)
102 { 0x0109, 10, 0, "Hobart" }, // (+10)
103 { 0x010e, 10, 0, "Vladivostok" }, // (+10)
104 { 0x0118, 11, 0, "Magadan, Solomon Islands, New Caledonia" }, // (+11)
105 { 0x011d, 12, 0, "Fiji, Kamchatka, Marshall Islands" }, // (+12)
106 { 0x0122, 12, 0, "Auckland, Wellington" }, // (+12)
107 { 0x012c, 13, 0, "Nuku'alofa" }, // (+13)
108 { 0, 0, 0, 0 }
111 min1900_t time2min(time_t t)
113 if( t == 0 )
114 return htobl(0xffffffff);
116 min1900_t r = t / 60 + STDC_MIN1900_DIFF;
117 return htobl(r);
120 time_t min2time(min1900_t m)
122 if( (unsigned long) btohl(m) == 0xffffffff )
123 return 0;
124 else
125 return (btohl(m) - STDC_MIN1900_DIFF) * 60;
130 // GetStaticTimeZoneTable
132 /// Returns a pointer to an array of StaticTimeZone structs.
133 /// The last struct contains 0 in all fields, and can be used as
134 /// an "end of array" marker.
136 const StaticTimeZone* GetStaticTimeZoneTable()
138 return Zones;
142 // GetStaticTimeZone
144 /// Searches the internal timezone code table for the given Code
145 /// and returns a pointer to a StaticTimeZone struct found. If the
146 /// code is not found, a pointer to a valid StaticTimeZone struct is
147 /// is still returned, but the struct's Code contains STATIC_TIME_ZONE_CODE_ERR,
148 /// and the name is "Unknown time zone" (which is translatable with gettext).
149 /// The unknown timezone is the same offset as GMT.
151 const StaticTimeZone* GetStaticTimeZone(uint16_t Code)
153 static StaticTimeZone Unknown = {
154 STATIC_TIME_ZONE_CODE_ERR, 0, 0, N_("Unknown time zone") };
156 for( StaticTimeZone *z = Zones; z->Name; z++ ) {
157 if( Code == z->Code )
158 return z;
160 return &Unknown;
164 // GetStaticTimeZoneCode
166 /// Searches the internal timezone table for the first matching
167 /// Code. If no matching Code is found, STATIC_TIME_ZONE_CODE_ERR is returned.
169 /// This function does not adjust for daylight saving time.
171 uint16_t GetStaticTimeZoneCode(signed short HourOffset,
172 signed short MinOffset)
174 for( StaticTimeZone *z = Zones; z->Name; z++ ) {
175 if( HourOffset == z->HourOffset && MinOffset == z->MinOffset )
176 return z->Code;
178 return STATIC_TIME_ZONE_CODE_ERR;
181 /// This routine takes the day of the year and
182 /// returns a time_t adjusted from the first of
183 /// the year.
185 /// FIXME This function assumes the year hasn't changed,
186 /// but I don't have enough information to determine
187 /// where the year is in this header info
189 time_t DayToDate( uint16_t Day )
191 struct tm *now, then;
192 time_t t = time( NULL );
194 now = localtime( &t ); // need this to get year
195 // set to Jan 1 midnight, this year;
196 then.tm_sec = 0;
197 then.tm_min = 0;
198 then.tm_hour = 0;
199 then.tm_mday = 0;
200 then.tm_mon = 0;
201 then.tm_year = now->tm_year;
202 then.tm_isdst = -1;
203 t = mktime(&then);
204 t -= 60*60; // need to subract an hour
205 t += Day * 24 * 60 * 60; // Add the day converted to seconds
207 return t;
211 // Message2Time
213 /// Localize the funky math used to convert a Blackberry message
214 /// timestamp into a time_t.
216 /// Both r_date and r_time are expected to be fed in from the
217 /// Protocol::MessageRecord struct in raw form, without endian
218 /// conversion. This function handles that.
220 time_t Message2Time(uint16_t r_date, uint16_t r_time)
222 dout("Message2Time(0x" << std::hex << btohs(r_date) << ", 0x"
223 << btohs(r_time) << ")");
225 uint16_t day = ( btohs(r_date) & 0x01ff ) - 0x29;
226 time_t result = DayToDate( day );
227 result += (time_t)( btohs(r_time)*1.77 );
229 dout("Message2Time result: " << ctime(&result));
230 return result;
234 // ThreadTimeout
236 /// Creates a pthread_cond_timedwait() compatible timespec struct,
237 /// based on a given timeout in milliseconds. Note that the resulting
238 /// timespec is time-sensitive: the 'timer' starts as soon as this function
239 /// returns, since timespec is a specific time in the future,
240 /// and ThreadTimeout() calculates it based on the current time.
242 /// Returns the spec pointer, to make it easy to use with
243 /// pthread_cond_timedwait()
245 struct timespec* ThreadTimeout(int timeout_ms, struct timespec *spec)
247 struct timeval now;
248 #ifndef WIN32
249 gettimeofday(&now, NULL);
250 #else
251 SYSTEMTIME st;
252 FILETIME ft;
253 LONGLONG ll;
255 GetSystemTime(&st);
256 SystemTimeToFileTime(&st, &ft);
257 /* Now convert the file time to a timespec */
258 ll = (static_cast<LONGLONG>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
259 // Adjust the offset to 100ns periods from UNIX epoc
260 ll -= 116444736000000000;
261 // Adjust the offset to micro-seconds since the UNIX epoc
262 ll /= 10;
264 now.tv_sec = static_cast<long>(ll / 1000000);
265 now.tv_usec = static_cast<long>(ll % 1000000);
266 #endif
268 spec->tv_sec = now.tv_sec + timeout_ms / 1000;
269 spec->tv_nsec = (now.tv_usec + timeout_ms % 1000 * 1000) * 1000;
270 return spec;
274 // DaysInMonth
276 /// Returns the number of days in the month, given the tm_mon and tm_year
277 /// as specified in the struct tm argument. It _does_ take leap year into
278 /// account.
280 BXEXPORT int DaysInMonth(struct tm &t)
282 int year = t.tm_year + 1900;
284 switch( t.tm_mon )
286 case 1:
287 if( year % 400 == 0 || (year % 100 != 0 && year % 4 == 0) )
288 return 29;
289 else
290 return 28;
291 case 4:
292 case 6:
293 case 9:
294 case 11:
295 return 30;
296 default:
297 return 31;
303 } // namespace Barry
306 #ifdef __TEST_MODE__
308 #include <iostream>
309 #include <iomanip>
311 using namespace std;
312 using namespace Barry;
314 void display(const char *msg, time_t t)
316 cout << msg << ": " << ctime(&t);
317 cout << msg << " seconds: "
318 << setbase(10) << t
319 << "(0x" << setbase(16) << t << ")"
320 << endl;
321 cout << msg << " minutes: "
322 << setbase(10) << (t/60)
323 << "(0x" << setbase(16) << (t/60) << ")"
324 << endl;
325 cout << endl;
328 void calc(const char *msg, time_t t, min1900_t dbval)
330 cout << msg << endl;
331 display(" Initial time", t);
332 display(" DB Val", min2time(dbval));
335 int main()
337 struct tm start;
338 time_t t;
340 // set to Oct 4, 2005, 2pm;
341 start.tm_sec = 0;
342 start.tm_min = 0;
343 start.tm_hour = 14;
344 start.tm_mday = 4;
345 start.tm_mon = 9;
346 start.tm_year = 105;
347 start.tm_isdst = -1;
348 t = mktime(&start);
349 calc("Oct 4", t, 0x0350c118);
351 // comparison
352 t = time(NULL);
353 min1900_t m = time2min(t);
354 time_t tc = min2time(m);
355 cout << "Original time: " << t << endl;
356 cout << "time2min: " << m << endl;
357 cout << "min2time: " << tc << endl;
358 if( t == (tc + t % 60) )
359 cout << "Success! (orig == converted + mod)" << endl;
360 else
361 cout << "Failed!" << endl;
363 // time zone
364 cout << "Should say Eastern: " << GetStaticTimeZone(0x23)->Name << endl;
365 cout << "should say Unknown: " << GetStaticTimeZone(0xffff)->Name << endl;
368 #endif