1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <pj/os.h>
21 #include <pj/errno.h>
22 #include <pj/string.h>
23 #include <pj/log.h>
24 #include <windows.h>
25
26 ///////////////////////////////////////////////////////////////////////////////
27
28 #define SECS_TO_FT_MULT 10000000
29
30 static LARGE_INTEGER base_time;
31
32 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
33 # define WINCE_TIME
34 #endif
35
36 #ifdef WINCE_TIME
37 /* Note:
38 * In Windows CE/Windows Mobile platforms, the availability of milliseconds
39 * time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most
40 * likely it won't be available. When it's not available, the
41 * SYSTEMTIME.wMilliseconds will contain a constant arbitrary value.
42 *
43 * Because of that, we need to emulate the milliseconds time resolution
44 * using QueryPerformanceCounter() (via pj_get_timestamp() API). However
45 * there is limitation on using this, i.e. the time returned by
46 * pj_gettimeofday() may be off by up to plus/minus 999 msec (the second
47 * part will be correct, however the msec part may be off), because we're
48 * not synchronizing the msec field with the change of value of the "second"
49 * field of the system time.
50 *
51 * Also there is other caveat which need to be handled (and they are
52 * handled by this implementation):
53 * - user may change system time, so pj_gettimeofday() needs to periodically
54 * checks if system time has changed. The period on which system time is
55 * checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro.
56 */
57 static LARGE_INTEGER g_start_time; /* Time gettimeofday() is first called */
58 static pj_timestamp g_start_tick; /* TS gettimeofday() is first called */
59 static pj_timestamp g_last_update; /* Last time check_system_time() is
60 called, to periodically synchronize
61 with up-to-date system time (in case
62 user changes system time). */
63 static pj_uint64_t g_update_period; /* Period (in TS) check_system_time()
64 should be called. */
65
66 /* Period on which check_system_time() is called, in seconds */
67 #ifndef PJ_WINCE_TIME_CHECK_INTERVAL
68 # define PJ_WINCE_TIME_CHECK_INTERVAL (10)
69 #endif
70
71 #endif
72
73 #ifdef WINCE_TIME
init_start_time(void)74 static pj_status_t init_start_time(void)
75 {
76 SYSTEMTIME st;
77 FILETIME ft;
78 pj_timestamp freq;
79 pj_status_t status;
80
81 GetLocalTime(&st);
82 SystemTimeToFileTime(&st, &ft);
83
84 g_start_time.LowPart = ft.dwLowDateTime;
85 g_start_time.HighPart = ft.dwHighDateTime;
86 g_start_time.QuadPart /= SECS_TO_FT_MULT;
87 g_start_time.QuadPart -= base_time.QuadPart;
88
89 status = pj_get_timestamp(&g_start_tick);
90 if (status != PJ_SUCCESS)
91 return status;
92
93 g_last_update.u64 = g_start_tick.u64;
94
95 status = pj_get_timestamp_freq(&freq);
96 if (status != PJ_SUCCESS)
97 return status;
98
99 g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64;
100
101 PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started"));
102
103 return PJ_SUCCESS;
104 }
105
check_system_time(pj_uint64_t ts_elapsed)106 static pj_status_t check_system_time(pj_uint64_t ts_elapsed)
107 {
108 enum { MIS = 5 };
109 SYSTEMTIME st;
110 FILETIME ft;
111 LARGE_INTEGER cur, calc;
112 DWORD diff;
113 pj_timestamp freq;
114 pj_status_t status;
115
116 /* Get system's current time */
117 GetLocalTime(&st);
118 SystemTimeToFileTime(&st, &ft);
119
120 cur.LowPart = ft.dwLowDateTime;
121 cur.HighPart = ft.dwHighDateTime;
122 cur.QuadPart /= SECS_TO_FT_MULT;
123 cur.QuadPart -= base_time.QuadPart;
124
125 /* Get our calculated system time */
126 status = pj_get_timestamp_freq(&freq);
127 if (status != PJ_SUCCESS)
128 return status;
129
130 calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64;
131
132 /* See the difference between calculated and actual system time */
133 if (calc.QuadPart >= cur.QuadPart) {
134 diff = (DWORD)(calc.QuadPart - cur.QuadPart);
135 } else {
136 diff = (DWORD)(cur.QuadPart - calc.QuadPart);
137 }
138
139 if (diff > MIS) {
140 /* System time has changed */
141 PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected "
142 "(diff=%u)", diff));
143 status = init_start_time();
144 } else {
145 status = PJ_SUCCESS;
146 }
147
148 return status;
149 }
150
151 #endif
152
153 // Find 1st Jan 1970 as a FILETIME
get_base_time(void)154 static pj_status_t get_base_time(void)
155 {
156 SYSTEMTIME st;
157 FILETIME ft;
158 pj_status_t status = PJ_SUCCESS;
159
160 memset(&st,0,sizeof(st));
161 st.wYear=1970;
162 st.wMonth=1;
163 st.wDay=1;
164 SystemTimeToFileTime(&st, &ft);
165
166 base_time.LowPart = ft.dwLowDateTime;
167 base_time.HighPart = ft.dwHighDateTime;
168 base_time.QuadPart /= SECS_TO_FT_MULT;
169
170 #ifdef WINCE_TIME
171 pj_enter_critical_section();
172 status = init_start_time();
173 pj_leave_critical_section();
174 #endif
175
176 return status;
177 }
178
pj_gettimeofday(pj_time_val * tv)179 PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
180 {
181 #ifdef WINCE_TIME
182 pj_timestamp tick;
183 pj_uint64_t msec_elapsed;
184 #else
185 SYSTEMTIME st;
186 FILETIME ft;
187 LARGE_INTEGER li;
188 #endif
189 pj_status_t status;
190
191 if (base_time.QuadPart == 0) {
192 status = get_base_time();
193 if (status != PJ_SUCCESS)
194 return status;
195 }
196
197 #ifdef WINCE_TIME
198 do {
199 status = pj_get_timestamp(&tick);
200 if (status != PJ_SUCCESS)
201 return status;
202
203 if (tick.u64 - g_last_update.u64 >= g_update_period) {
204 pj_enter_critical_section();
205 if (tick.u64 - g_last_update.u64 >= g_update_period) {
206 g_last_update.u64 = tick.u64;
207 check_system_time(tick.u64 - g_start_tick.u64);
208 }
209 pj_leave_critical_section();
210 } else {
211 break;
212 }
213 } while (1);
214
215 msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick);
216
217 tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000);
218 tv->msec = (long)(msec_elapsed % 1000);
219 #else
220 /* Standard Win32 GetLocalTime */
221 GetLocalTime(&st);
222 SystemTimeToFileTime(&st, &ft);
223
224 li.LowPart = ft.dwLowDateTime;
225 li.HighPart = ft.dwHighDateTime;
226 li.QuadPart /= SECS_TO_FT_MULT;
227 li.QuadPart -= base_time.QuadPart;
228
229 tv->sec = li.LowPart;
230 tv->msec = st.wMilliseconds;
231 #endif /* WINCE_TIME */
232
233 return PJ_SUCCESS;
234 }
235
pj_time_decode(const pj_time_val * tv,pj_parsed_time * pt)236 PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
237 {
238 LARGE_INTEGER li;
239 FILETIME ft;
240 SYSTEMTIME st;
241
242 li.QuadPart = tv->sec;
243 li.QuadPart += base_time.QuadPart;
244 li.QuadPart *= SECS_TO_FT_MULT;
245
246 ft.dwLowDateTime = li.LowPart;
247 ft.dwHighDateTime = li.HighPart;
248 FileTimeToSystemTime(&ft, &st);
249
250 pt->year = st.wYear;
251 pt->mon = st.wMonth-1;
252 pt->day = st.wDay;
253 pt->wday = st.wDayOfWeek;
254
255 pt->hour = st.wHour;
256 pt->min = st.wMinute;
257 pt->sec = st.wSecond;
258 pt->msec = tv->msec;
259
260 return PJ_SUCCESS;
261 }
262
263 /**
264 * Encode parsed time to time value.
265 */
pj_time_encode(const pj_parsed_time * pt,pj_time_val * tv)266 PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
267 {
268 SYSTEMTIME st;
269 FILETIME ft;
270 LARGE_INTEGER li;
271
272 pj_bzero(&st, sizeof(st));
273 st.wYear = (pj_uint16_t) pt->year;
274 st.wMonth = (pj_uint16_t) (pt->mon + 1);
275 st.wDay = (pj_uint16_t) pt->day;
276 st.wHour = (pj_uint16_t) pt->hour;
277 st.wMinute = (pj_uint16_t) pt->min;
278 st.wSecond = (pj_uint16_t) pt->sec;
279 st.wMilliseconds = (pj_uint16_t) pt->msec;
280
281 SystemTimeToFileTime(&st, &ft);
282
283 li.LowPart = ft.dwLowDateTime;
284 li.HighPart = ft.dwHighDateTime;
285 li.QuadPart /= SECS_TO_FT_MULT;
286 li.QuadPart -= base_time.QuadPart;
287
288 tv->sec = li.LowPart;
289 tv->msec = st.wMilliseconds;
290
291 return PJ_SUCCESS;
292 }
293
294 /**
295 * Convert local time to GMT.
296 */
pj_time_local_to_gmt(pj_time_val * tv)297 PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv)
298 {
299 #ifdef WINCE_TIME
300 return PJ_ENOTSUP;
301 #else
302 LARGE_INTEGER li;
303 FILETIME ft_local;
304 FILETIME ft_gmt;
305
306 li.QuadPart = tv->sec;
307 li.QuadPart += base_time.QuadPart;
308 li.QuadPart *= SECS_TO_FT_MULT;
309
310 ft_local.dwLowDateTime = li.LowPart;
311 ft_local.dwHighDateTime = li.HighPart;
312
313 if (LocalFileTimeToFileTime(&ft_local, &ft_gmt) == 0)
314 return PJ_EINVAL;
315
316 li.LowPart = ft_gmt.dwLowDateTime;
317 li.HighPart = ft_gmt.dwHighDateTime;
318 li.QuadPart /= SECS_TO_FT_MULT;
319 li.QuadPart -= base_time.QuadPart;
320
321 tv->sec = li.LowPart;
322
323 return PJ_SUCCESS;
324 #endif
325 }
326
327 /**
328 * Convert GMT to local time.
329 */
pj_time_gmt_to_local(pj_time_val * tv)330 PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv)
331 {
332 #ifdef WINCE_TIME
333 return PJ_ENOTSUP;
334 #else
335 LARGE_INTEGER li;
336 FILETIME ft_local;
337 FILETIME ft_gmt;
338
339 li.QuadPart = tv->sec;
340 li.QuadPart += base_time.QuadPart;
341 li.QuadPart *= SECS_TO_FT_MULT;
342
343 ft_gmt.dwLowDateTime = li.LowPart;
344 ft_gmt.dwHighDateTime = li.HighPart;
345
346 if (FileTimeToLocalFileTime(&ft_gmt, &ft_local) == 0)
347 return PJ_EINVAL;
348
349 li.LowPart = ft_local.dwLowDateTime;
350 li.HighPart = ft_local.dwHighDateTime;
351 li.QuadPart /= SECS_TO_FT_MULT;
352 li.QuadPart -= base_time.QuadPart;
353
354 tv->sec = li.LowPart;
355
356 return PJ_SUCCESS;
357 #endif
358 }
359
360
361