blob: 6da3c23ddda1c005e02b3c93c5ce64503d204f34 [file] [log] [blame]
Benny Prijono9cf138e2006-01-19 03:58:29 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono9cf138e2006-01-19 03:58:29 +00005 *
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/string.h>
Benny Prijonof1a47b82009-03-30 18:22:16 +000022#include <pj/log.h>
Benny Prijono9cf138e2006-01-19 03:58:29 +000023#include <windows.h>
24
25///////////////////////////////////////////////////////////////////////////////
26
27#define SECS_TO_FT_MULT 10000000
28
29static LARGE_INTEGER base_time;
30
Benny Prijonof1a47b82009-03-30 18:22:16 +000031#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
32# define WINCE_TIME
33#endif
34
35#ifdef WINCE_TIME
36/* Note:
37 * In Windows CE/Windows Mobile platforms, the availability of milliseconds
38 * time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most
39 * likely it won't be available. When it's not available, the
40 * SYSTEMTIME.wMilliseconds will contain a constant arbitrary value.
41 *
42 * Because of that, we need to emulate the milliseconds time resolution
43 * using QueryPerformanceCounter() (via pj_get_timestamp() API). However
44 * there is limitation on using this, i.e. the time returned by
45 * pj_gettimeofday() may be off by up to plus/minus 999 msec (the second
46 * part will be correct, however the msec part may be off), because we're
47 * not synchronizing the msec field with the change of value of the "second"
48 * field of the system time.
49 *
50 * Also there is other caveat which need to be handled (and they are
51 * handled by this implementation):
52 * - user may change system time, so pj_gettimeofday() needs to periodically
53 * checks if system time has changed. The period on which system time is
54 * checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro.
55 */
56static LARGE_INTEGER g_start_time; /* Time gettimeofday() is first called */
57static pj_timestamp g_start_tick; /* TS gettimeofday() is first called */
58static pj_timestamp g_last_update; /* Last time check_system_time() is
59 called, to periodically synchronize
60 with up-to-date system time (in case
61 user changes system time). */
62static pj_uint64_t g_update_period; /* Period (in TS) check_system_time()
63 should be called. */
64
65/* Period on which check_system_time() is called, in seconds */
66#ifndef PJ_WINCE_TIME_CHECK_INTERVAL
67# define PJ_WINCE_TIME_CHECK_INTERVAL (10)
68#endif
69
70#endif
71
72#ifdef WINCE_TIME
73static pj_status_t init_start_time(void)
Benny Prijono9cf138e2006-01-19 03:58:29 +000074{
75 SYSTEMTIME st;
76 FILETIME ft;
Benny Prijonof1a47b82009-03-30 18:22:16 +000077 pj_timestamp freq;
78 pj_status_t status;
79
80 GetLocalTime(&st);
81 SystemTimeToFileTime(&st, &ft);
82
83 g_start_time.LowPart = ft.dwLowDateTime;
84 g_start_time.HighPart = ft.dwHighDateTime;
85 g_start_time.QuadPart /= SECS_TO_FT_MULT;
86 g_start_time.QuadPart -= base_time.QuadPart;
87
88 status = pj_get_timestamp(&g_start_tick);
89 if (status != PJ_SUCCESS)
90 return status;
91
92 g_last_update.u64 = g_start_tick.u64;
93
94 status = pj_get_timestamp_freq(&freq);
95 if (status != PJ_SUCCESS)
96 return status;
97
98 g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64;
99
100 PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started"));
101
102 return PJ_SUCCESS;
103}
104
105static pj_status_t check_system_time(pj_uint64_t ts_elapsed)
106{
107 enum { MIS = 5 };
108 SYSTEMTIME st;
109 FILETIME ft;
110 LARGE_INTEGER cur, calc;
111 DWORD diff;
112 pj_timestamp freq;
113 pj_status_t status;
114
115 /* Get system's current time */
116 GetLocalTime(&st);
117 SystemTimeToFileTime(&st, &ft);
118
119 cur.LowPart = ft.dwLowDateTime;
120 cur.HighPart = ft.dwHighDateTime;
121 cur.QuadPart /= SECS_TO_FT_MULT;
122 cur.QuadPart -= base_time.QuadPart;
123
124 /* Get our calculated system time */
125 status = pj_get_timestamp_freq(&freq);
126 if (status != PJ_SUCCESS)
127 return status;
128
129 calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64;
130
131 /* See the difference between calculated and actual system time */
132 if (calc.QuadPart >= cur.QuadPart) {
133 diff = (DWORD)(calc.QuadPart - cur.QuadPart);
134 } else {
135 diff = (DWORD)(cur.QuadPart - calc.QuadPart);
136 }
137
138 if (diff > MIS) {
139 /* System time has changed */
140 PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected "
141 "(diff=%u)", diff));
142 status = init_start_time();
143 } else {
144 status = PJ_SUCCESS;
145 }
146
147 return status;
148}
149
150#endif
151
152// Find 1st Jan 1970 as a FILETIME
153static pj_status_t get_base_time(void)
154{
155 SYSTEMTIME st;
156 FILETIME ft;
157 pj_status_t status = PJ_SUCCESS;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000158
159 memset(&st,0,sizeof(st));
160 st.wYear=1970;
161 st.wMonth=1;
162 st.wDay=1;
163 SystemTimeToFileTime(&st, &ft);
164
165 base_time.LowPart = ft.dwLowDateTime;
166 base_time.HighPart = ft.dwHighDateTime;
167 base_time.QuadPart /= SECS_TO_FT_MULT;
Benny Prijonof1a47b82009-03-30 18:22:16 +0000168
169#ifdef WINCE_TIME
170 pj_enter_critical_section();
171 status = init_start_time();
172 pj_leave_critical_section();
173#endif
174
175 return status;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000176}
177
178PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
179{
Benny Prijonof1a47b82009-03-30 18:22:16 +0000180#ifdef WINCE_TIME
181 pj_timestamp tick;
182 pj_uint64_t msec_elapsed;
183#else
Benny Prijono9cf138e2006-01-19 03:58:29 +0000184 SYSTEMTIME st;
185 FILETIME ft;
186 LARGE_INTEGER li;
Benny Prijonof1a47b82009-03-30 18:22:16 +0000187#endif
188 pj_status_t status;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000189
Benny Prijonof1a47b82009-03-30 18:22:16 +0000190 if (base_time.QuadPart == 0) {
191 status = get_base_time();
192 if (status != PJ_SUCCESS)
193 return status;
194 }
Benny Prijono9cf138e2006-01-19 03:58:29 +0000195
Benny Prijonof1a47b82009-03-30 18:22:16 +0000196#ifdef WINCE_TIME
197 do {
198 status = pj_get_timestamp(&tick);
199 if (status != PJ_SUCCESS)
200 return status;
201
202 if (tick.u64 - g_last_update.u64 >= g_update_period) {
203 pj_enter_critical_section();
204 if (tick.u64 - g_last_update.u64 >= g_update_period) {
205 g_last_update.u64 = tick.u64;
206 check_system_time(tick.u64 - g_start_tick.u64);
207 }
208 pj_leave_critical_section();
209 } else {
210 break;
211 }
212 } while (1);
213
214 msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick);
215
216 tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000);
217 tv->msec = (long)(msec_elapsed % 1000);
218#else
219 /* Standard Win32 GetLocalTime */
Benny Prijono9cf138e2006-01-19 03:58:29 +0000220 GetLocalTime(&st);
221 SystemTimeToFileTime(&st, &ft);
222
223 li.LowPart = ft.dwLowDateTime;
224 li.HighPart = ft.dwHighDateTime;
225 li.QuadPart /= SECS_TO_FT_MULT;
226 li.QuadPart -= base_time.QuadPart;
227
228 tv->sec = li.LowPart;
229 tv->msec = st.wMilliseconds;
Benny Prijonof1a47b82009-03-30 18:22:16 +0000230#endif /* WINCE_TIME */
Benny Prijono9cf138e2006-01-19 03:58:29 +0000231
232 return PJ_SUCCESS;
233}
234
235PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
236{
237 LARGE_INTEGER li;
238 FILETIME ft;
239 SYSTEMTIME st;
240
241 li.QuadPart = tv->sec;
242 li.QuadPart += base_time.QuadPart;
243 li.QuadPart *= SECS_TO_FT_MULT;
244
245 ft.dwLowDateTime = li.LowPart;
246 ft.dwHighDateTime = li.HighPart;
247 FileTimeToSystemTime(&ft, &st);
248
249 pt->year = st.wYear;
250 pt->mon = st.wMonth-1;
251 pt->day = st.wDay;
252 pt->wday = st.wDayOfWeek;
253
254 pt->hour = st.wHour;
255 pt->min = st.wMinute;
256 pt->sec = st.wSecond;
257 pt->msec = tv->msec;
258
259 return PJ_SUCCESS;
260}
261
262/**
263 * Encode parsed time to time value.
264 */
265PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
266{
267 SYSTEMTIME st;
268 FILETIME ft;
269 LARGE_INTEGER li;
270
Benny Prijonoac623b32006-07-03 15:19:31 +0000271 pj_bzero(&st, sizeof(st));
Benny Prijonoedb566c2006-03-09 10:21:30 +0000272 st.wYear = (pj_uint16_t) pt->year;
273 st.wMonth = (pj_uint16_t) (pt->mon + 1);
274 st.wDay = (pj_uint16_t) pt->day;
275 st.wHour = (pj_uint16_t) pt->hour;
276 st.wMinute = (pj_uint16_t) pt->min;
277 st.wSecond = (pj_uint16_t) pt->sec;
278 st.wMilliseconds = (pj_uint16_t) pt->msec;
Benny Prijono9cf138e2006-01-19 03:58:29 +0000279
280 SystemTimeToFileTime(&st, &ft);
281
282 li.LowPart = ft.dwLowDateTime;
283 li.HighPart = ft.dwHighDateTime;
284 li.QuadPart /= SECS_TO_FT_MULT;
285 li.QuadPart -= base_time.QuadPart;
286
287 tv->sec = li.LowPart;
288 tv->msec = st.wMilliseconds;
289
290 return PJ_SUCCESS;
291}
292
293/**
294 * Convert local time to GMT.
295 */
296PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
297
298/**
299 * Convert GMT to local time.
300 */
301PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
302
303