lib: show offset and rectype in HexDumpParser
[barry.git] / src / time.cc
blob67eb3c8841da85d585fe914759ea8142241ed779
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-2010, 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 "time.h"
25 #include "endian.h"
26 #include "debug.h"
28 namespace Barry {
30 TimeZone Zones[] = {
31 { 0x0000, -12, 0, "Eniwetok, Kwajalein (-12)" },
32 { 0x0001, -12, 0, "Midway Island, Samoa (-12)" },
33 { 0x0002, -10, 0, "Hawaii (-10)" },
34 { 0x0003, -9, 0, "Alaska (-9)" },
35 { 0x0004, -8, 0, "Pacific Time (US & Canada), Tijuana (-8)" },
36 { 0x000a, -7, 0, "Mountain Time (US & Canada) (-7)" },
37 { 0x000f, -7, 0, "Arizona (-7)" },
38 { 0x000d, -7, 0, "Chihuahua, La Paz, Mazatlan (-7)" },
39 { 0x0014, -6, 0, "Central Time (US & Canada) (-6)" },
40 { 0x0021, -6, 0, "Central America (-6)" },
41 { 0x0019, -6, 0, "Saskatchewan (-6)" },
42 { 0x001e, -6, 0, "Mexico City (-6)" },
43 { 0x0023, -5, 0, "Eastern Time (US & Canada) (-5)" },
44 { 0x002d, -5, 0, "Bogota, Lima, Quito (-5)" },
45 { 0x0028, -5, 0, "Indiana (East) (-5)" },
46 { 0x0032, -4, 0, "Atlantic Time (Canada) (-4)" },
47 { 0x0037, -4, 0, "Caracas, La Paz (-4)" },
48 { 0x0038, -4, 0, "Santiago (-4)" },
49 { 0x003c, -3, -30, "Newfoundland (-3.5)" },
50 { 0x0046, -3, 0, "Buenos Aires, Georgetown (-3)" },
51 { 0x0041, -3, 0, "Brasilia (-3)" },
52 { 0x0049, -3, 0, "Greenland (-3)" },
53 { 0x004b, -2, 0, "Mid-Atlantic (-2)" },
54 { 0x0053, -1, 0, "Cape Verde Island (-1)" },
55 { 0x0050, -1, 0, "Azores (-1)" },
56 { 0x0055, 0, 0, "Dublin, Edinburgh, Lisbon, London (GMT)" },
57 { 0x005a, 0, 0, "Casablanca, Monrovia (GMT)" },
58 { 0x006e, 1, 0, "Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna (+1)" },
59 { 0x0071, 1, 0, "West Central Africa (+1)" },
60 { 0x005f, 1, 0, "Belgrade, Bratislava, Budapest, Ljubljana, Prague (+1)" },
61 { 0x0069, 1, 0, "Brussels, Copenhagen, Madrid, Paris (+1)" },
62 { 0x0064, 1, 0, "Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb (+1)" },
63 { 0x008c, 2, 0, "Harare, Pretoria (+2)" },
64 { 0x0087, 2, 0, "Jerusalem (+2)" },
65 { 0x0073, 2, 0, "Bucharest (+2)" },
66 { 0x0078, 2, 0, "Cairo (+2)" },
67 { 0x0082, 2, 0, "Athens, Istanbul, Minsk (+2)" },
68 { 0x007d, 2, 0, "Helsinki, Riga, Tallinn (+2)" },
69 { 0x0096, 3, 0, "Kuwait, Riyadh (+3)" },
70 { 0x009b, 3, 0, "Nairobi (+3)" },
71 { 0x009e, 3, 0, "Baghdad (+3)" },
72 { 0x0091, 3, 0, "Moscow, St. Petersburg, Volgograd (+3)" },
73 { 0x00a0, 3, 30, "Tehran (+3.5)" },
74 { 0x00a5, 4, 0, "Abu Dhabi, Muscat (+4)" },
75 { 0x00aa, 4, 0, "Baku, Tbilisi, Yerevan (+4)" },
76 { 0x00af, 4, 30, "Kabul (+4.5)" },
77 { 0x00b9, 5, 0, "Islamabad, Karachi, Tashkent (+5)" },
78 { 0x00b4, 5, 0, "Ekaterinburg (+5)" },
79 { 0x00be, 5, 30, "Calcutta, Chennai, Mumbai, New Delhi (+5.5)" },
80 { 0x00c1, 5, 45, "Kathmandu (+5.75)" },
81 { 0x00c3, 6, 0, "Astana, Dhaka (+6)" },
82 { 0x00c8, 6, 0, "Sri Lanka (+6)" },
83 { 0x00c9, 6, 0, "Almaty, Novosibirsk (+6)" },
84 { 0x00cb, 6, 30, "Rangoon (+6.5)" },
85 { 0x00cd, 7, 0, "Bangkok, Hanoi, Jakarta (+7)" },
86 { 0x00cf, 7, 0, "Krasnoyarsk (+7)" },
87 { 0x00d2, 8, 0, "Beijing, Chongqing, Hong Kong, Urumqi (+8)" },
88 { 0x00d7, 8, 0, "Kuala Lumpur, Singapore (+8)" },
89 { 0x00e1, 8, 0, "Perth (+8)" },
90 { 0x00dc, 8, 0, "Taipei (+8)" },
91 { 0x00e3, 8, 0, "Irkutsk, Ulaan Bataar (+8)" },
92 { 0x00eb, 9, 0, "Osaka, Sapporo, Tokyo (+9)" },
93 { 0x00e6, 9, 0, "Seoul (+9)" },
94 { 0x00f0, 9, 0, "Yakutsk (+9)" },
95 { 0x00f5, 9, 30, "Darwin (+9.5)" },
96 { 0x00fa, 9, 30, "Adelaide (+9.5)" },
97 { 0x0104, 10, 0, "Brisbane (+10)" },
98 { 0x0113, 10, 0, "Guam, Port Moresby (+10)" },
99 { 0x00ff, 10, 0, "Canberra, Melbourne, Sydney (+10)" },
100 { 0x0109, 10, 0, "Hobart (+10)" },
101 { 0x010e, 10, 0, "Vladivostok (+10)" },
102 { 0x0118, 11, 0, "Magadan, Solomon Islands, New Caledonia (+11)" },
103 { 0x011d, 12, 0, "Fiji, Kamchatka, Marshall Islands (+12)" },
104 { 0x0122, 12, 0, "Auckland, Wellington (+12)" },
105 { 0x012c, 13, 0, "Nuku'alofa (+13)" },
106 { 0, 0, 0, 0 }
109 min1900_t time2min(time_t t)
111 if( t == 0 )
112 return htobl(0xffffffff);
114 min1900_t r = t / 60 + STDC_MIN1900_DIFF;
115 return htobl(r);
118 time_t min2time(min1900_t m)
120 if( (unsigned long) btohl(m) == 0xffffffff )
121 return 0;
122 else
123 return (btohl(m) - STDC_MIN1900_DIFF) * 60;
128 // GetTimeZoneTable
130 /// Returns a pointer to an array of TimeZone structs.
131 /// The last struct contains 0 in all fields, and can be used as
132 /// an "end of array" marker.
134 const TimeZone* GetTimeZoneTable()
136 return Zones;
140 // GetTimeZone
142 /// Searches the internal timezone code table for the given Code
143 /// and returns a pointer to a TimeZone struct found. If the
144 /// code is not found, a pointer to a valid TimeZone struct is
145 /// is still returned, but the struct's Code contains TIME_ZONE_CODE_ERR,
146 /// and the name is "Unknown time zone." The unknown timezone
147 /// is the same offset as GMT.
149 const TimeZone* GetTimeZone(unsigned short Code)
151 static TimeZone Unknown = { TIME_ZONE_CODE_ERR, 0, 0, "Unknown time zone" };
153 for( TimeZone *z = Zones; z->Name; z++ ) {
154 if( Code == z->Code )
155 return z;
157 return &Unknown;
161 // GetTimeZoneCode
163 /// Searches the internal timezone table for the first matching
164 /// Code. If no matching Code is found, TIME_ZONE_CODE_ERR is returned.
166 /// This function does not adjust for daylight saving time.
168 unsigned short GetTimeZoneCode(signed short HourOffset,
169 signed short MinOffset)
171 for( TimeZone *z = Zones; z->Name; z++ ) {
172 if( HourOffset == z->HourOffset && MinOffset == z->MinOffset )
173 return z->Code;
175 return TIME_ZONE_CODE_ERR;
178 /// This routine takes the day of the year and
179 /// returns a time_t adjusted from the first of
180 /// the year.
182 /// FIXME This function assumes the year hasn't changed,
183 /// but I don't have enough information to determine
184 /// where the year is in this header info
186 time_t DayToDate( unsigned short Day )
188 struct tm *now, then;
189 time_t t = time( NULL );
191 now = localtime( &t ); // need this to get year
192 // set to Jan 1 midnight, this year;
193 then.tm_sec = 0;
194 then.tm_min = 0;
195 then.tm_hour = 0;
196 then.tm_mday = 0;
197 then.tm_mon = 0;
198 then.tm_year = now->tm_year;
199 then.tm_isdst = -1;
200 t = mktime(&then);
201 t -= 60*60; // need to subract an hour
202 t += Day * 24 * 60 * 60; // Add the day converted to seconds
204 return t;
208 // Message2Time
210 /// Localize the funky math used to convert a Blackberry message
211 /// timestamp into a time_t.
213 /// Both r_date and r_time are expected to be fed in from the
214 /// Protocol::MessageRecord struct in raw form, without endian
215 /// conversion. This function handles that.
217 time_t Message2Time(uint16_t r_date, uint16_t r_time)
219 dout("Message2Time(0x" << std::hex << btohs(r_date) << ", 0x"
220 << btohs(r_time) << ")");
222 time_t result = ( btohs(r_date) & 0x01ff ) - 0x29;
223 result = DayToDate( result );
224 result += (time_t)( btohs(r_time)*1.77 );
226 dout("Message2Time result: " << ctime(&result));
227 return result;
231 // ThreadTimeout
233 /// Creates a pthread_cond_timedwait() compatible timespec struct,
234 /// based on a given timeout in milliseconds. Note that the resulting
235 /// timespec is time-sensitive: the 'timer' starts as soon as this function
236 /// returns, since timespec is a specific time in the future,
237 /// and ThreadTimeout() calculates it based on the current time.
239 /// Returns the spec pointer, to make it easy to use with
240 /// pthread_cond_timedwait()
242 BXEXPORT struct timespec* ThreadTimeout(int timeout_ms, struct timespec *spec)
244 struct timeval now;
245 gettimeofday(&now, NULL);
247 spec->tv_sec = now.tv_sec + timeout_ms / 1000;
248 spec->tv_nsec = (now.tv_usec + timeout_ms % 1000 * 1000) * 1000;
250 return spec;
255 } // namespace Barry
258 #ifdef __TEST_MODE__
260 #include <iostream>
261 #include <iomanip>
263 using namespace std;
264 using namespace Barry;
266 void display(const char *msg, time_t t)
268 cout << msg << ": " << ctime(&t);
269 cout << msg << " seconds: "
270 << setbase(10) << t
271 << "(0x" << setbase(16) << t << ")"
272 << endl;
273 cout << msg << " minutes: "
274 << setbase(10) << (t/60)
275 << "(0x" << setbase(16) << (t/60) << ")"
276 << endl;
277 cout << endl;
280 void calc(const char *msg, time_t t, min1900_t dbval)
282 cout << msg << endl;
283 display(" Initial time", t);
284 display(" DB Val", min2time(dbval));
287 int main()
289 struct tm start;
290 time_t t;
292 // set to Oct 4, 2005, 2pm;
293 start.tm_sec = 0;
294 start.tm_min = 0;
295 start.tm_hour = 14;
296 start.tm_mday = 4;
297 start.tm_mon = 9;
298 start.tm_year = 105;
299 start.tm_isdst = -1;
300 t = mktime(&start);
301 calc("Oct 4", t, 0x0350c118);
303 // comparison
304 t = time(NULL);
305 min1900_t m = time2min(t);
306 time_t tc = min2time(m);
307 cout << "Original time: " << t << endl;
308 cout << "time2min: " << m << endl;
309 cout << "min2time: " << tc << endl;
310 if( t == (tc + t % 60) )
311 cout << "Success! (orig == converted + mod)" << endl;
312 else
313 cout << "Failed!" << endl;
315 // time zone
316 cout << "Should say Eastern: " << GetTimeZone(0x23)->Name << endl;
317 cout << "should say Unknown: " << GetTimeZone(0xffff)->Name << endl;
320 #endif