/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * 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 #include #include #include /////////////////////////////////////////////////////////////////////////////// #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);