summaryrefslogtreecommitdiff
path: root/pjlib/src/pj/os_time_win32.c
blob: 2143e2ec360b31a61f0cb9f84bb396536721bcfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/* $Id: os_time_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pj/os.h>
#include <pj/string.h>
#include <pj/log.h>
#include <windows.h>

///////////////////////////////////////////////////////////////////////////////

#define SECS_TO_FT_MULT 10000000

static LARGE_INTEGER base_time;

#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
#   define WINCE_TIME
#endif

#ifdef WINCE_TIME
/* Note:
 *  In Windows CE/Windows Mobile platforms, the availability of milliseconds
 *  time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most
 *  likely it won't be available. When it's not available, the 
 *  SYSTEMTIME.wMilliseconds will contain a constant arbitrary value.
 *
 *  Because of that, we need to emulate the milliseconds time resolution
 *  using QueryPerformanceCounter() (via pj_get_timestamp() API). However 
 *  there is limitation on using this, i.e. the time returned by 
 *  pj_gettimeofday() may be off by up to plus/minus 999 msec (the second
 *  part will be correct, however the msec part may be off), because we're 
 *  not synchronizing the msec field with the change of value of the "second"
 *  field of the system time.
 *
 *  Also there is other caveat which need to be handled (and they are 
 *  handled by this implementation):
 *   - user may change system time, so pj_gettimeofday() needs to periodically
 *     checks if system time has changed. The period on which system time is
 *     checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro.
 */
static LARGE_INTEGER g_start_time;  /* Time gettimeofday() is first called  */
static pj_timestamp  g_start_tick;  /* TS gettimeofday() is first called  */
static pj_timestamp  g_last_update; /* Last time check_system_time() is 
				       called, to periodically synchronize
				       with up-to-date system time (in case
				       user changes system time).	    */
static pj_uint64_t   g_update_period; /* Period (in TS) check_system_time()
				         should be called.		    */

/* Period on which check_system_time() is called, in seconds		    */
#ifndef PJ_WINCE_TIME_CHECK_INTERVAL
#   define PJ_WINCE_TIME_CHECK_INTERVAL (10)
#endif

#endif

#ifdef WINCE_TIME
static pj_status_t init_start_time(void)
{
    SYSTEMTIME st;
    FILETIME ft;
    pj_timestamp freq;
    pj_status_t status;

    GetLocalTime(&st);
    SystemTimeToFileTime(&st, &ft);

    g_start_time.LowPart = ft.dwLowDateTime;
    g_start_time.HighPart = ft.dwHighDateTime;
    g_start_time.QuadPart /= SECS_TO_FT_MULT;
    g_start_time.QuadPart -= base_time.QuadPart;

    status = pj_get_timestamp(&g_start_tick);
    if (status != PJ_SUCCESS)
	return status;

    g_last_update.u64 = g_start_tick.u64;

    status = pj_get_timestamp_freq(&freq);
    if (status != PJ_SUCCESS)
	return status;

    g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64;

    PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started"));

    return PJ_SUCCESS;
}

static pj_status_t check_system_time(pj_uint64_t ts_elapsed)
{
    enum { MIS = 5 };
    SYSTEMTIME st;
    FILETIME ft;
    LARGE_INTEGER cur, calc;
    DWORD diff;
    pj_timestamp freq;
    pj_status_t status;

    /* Get system's current time */
    GetLocalTime(&st);
    SystemTimeToFileTime(&st, &ft);
    
    cur.LowPart = ft.dwLowDateTime;
    cur.HighPart = ft.dwHighDateTime;
    cur.QuadPart /= SECS_TO_FT_MULT;
    cur.QuadPart -= base_time.QuadPart;

    /* Get our calculated system time */
    status = pj_get_timestamp_freq(&freq);
    if (status != PJ_SUCCESS)
	return status;

    calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64;

    /* See the difference between calculated and actual system time */
    if (calc.QuadPart >= cur.QuadPart) {
	diff = (DWORD)(calc.QuadPart - cur.QuadPart);
    } else {
	diff = (DWORD)(cur.QuadPart - calc.QuadPart);
    }

    if (diff > MIS) {
	/* System time has changed */
	PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected "
				      "(diff=%u)", diff));
	status = init_start_time();
    } else {
	status = PJ_SUCCESS;
    }

    return status;
}

#endif

// Find 1st Jan 1970 as a FILETIME 
static pj_status_t get_base_time(void)
{
    SYSTEMTIME st;
    FILETIME ft;
    pj_status_t status = PJ_SUCCESS;

    memset(&st,0,sizeof(st));
    st.wYear=1970;
    st.wMonth=1;
    st.wDay=1;
    SystemTimeToFileTime(&st, &ft);
    
    base_time.LowPart = ft.dwLowDateTime;
    base_time.HighPart = ft.dwHighDateTime;
    base_time.QuadPart /= SECS_TO_FT_MULT;

#ifdef WINCE_TIME
    pj_enter_critical_section();
    status = init_start_time();
    pj_leave_critical_section();
#endif

    return status;
}

PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
{
#ifdef WINCE_TIME
    pj_timestamp tick;
    pj_uint64_t msec_elapsed;
#else
    SYSTEMTIME st;
    FILETIME ft;
    LARGE_INTEGER li;
#endif
    pj_status_t status;

    if (base_time.QuadPart == 0) {
	status = get_base_time();
	if (status != PJ_SUCCESS)
	    return status;
    }

#ifdef WINCE_TIME
    do {
	status = pj_get_timestamp(&tick);
	if (status != PJ_SUCCESS)
	    return status;

	if (tick.u64 - g_last_update.u64 >= g_update_period) {
	    pj_enter_critical_section();
	    if (tick.u64 - g_last_update.u64 >= g_update_period) {
		g_last_update.u64 = tick.u64;
		check_system_time(tick.u64 - g_start_tick.u64);
	    }
	    pj_leave_critical_section();
	} else {
	    break;
	}
    } while (1);

    msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick);

    tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000);
    tv->msec = (long)(msec_elapsed % 1000);
#else
    /* Standard Win32 GetLocalTime */
    GetLocalTime(&st);
    SystemTimeToFileTime(&st, &ft);

    li.LowPart = ft.dwLowDateTime;
    li.HighPart = ft.dwHighDateTime;
    li.QuadPart /= SECS_TO_FT_MULT;
    li.QuadPart -= base_time.QuadPart;

    tv->sec = li.LowPart;
    tv->msec = st.wMilliseconds;
#endif	/* WINCE_TIME */

    return PJ_SUCCESS;
}

PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
{
    LARGE_INTEGER li;
    FILETIME ft;
    SYSTEMTIME st;

    li.QuadPart = tv->sec;
    li.QuadPart += base_time.QuadPart;
    li.QuadPart *= SECS_TO_FT_MULT;

    ft.dwLowDateTime = li.LowPart;
    ft.dwHighDateTime = li.HighPart;
    FileTimeToSystemTime(&ft, &st);

    pt->year = st.wYear;
    pt->mon = st.wMonth-1;
    pt->day = st.wDay;
    pt->wday = st.wDayOfWeek;

    pt->hour = st.wHour;
    pt->min = st.wMinute;
    pt->sec = st.wSecond;
    pt->msec = tv->msec;

    return PJ_SUCCESS;
}

/**
 * Encode parsed time to time value.
 */
PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
{
    SYSTEMTIME st;
    FILETIME ft;
    LARGE_INTEGER li;

    pj_bzero(&st, sizeof(st));
    st.wYear = (pj_uint16_t) pt->year;
    st.wMonth = (pj_uint16_t) (pt->mon + 1);
    st.wDay = (pj_uint16_t) pt->day;
    st.wHour = (pj_uint16_t) pt->hour;
    st.wMinute = (pj_uint16_t) pt->min;
    st.wSecond = (pj_uint16_t) pt->sec;
    st.wMilliseconds = (pj_uint16_t) pt->msec;
    
    SystemTimeToFileTime(&st, &ft);

    li.LowPart = ft.dwLowDateTime;
    li.HighPart = ft.dwHighDateTime;
    li.QuadPart /= SECS_TO_FT_MULT;
    li.QuadPart -= base_time.QuadPart;

    tv->sec = li.LowPart;
    tv->msec = st.wMilliseconds;

    return PJ_SUCCESS;
}

/**
 * Convert local time to GMT.
 */
PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);

/**
 * Convert GMT to local time.
 */
PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);