Merged Michael's tzwin32 branch
This commit is contained in:
@@ -13,6 +13,13 @@
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Fix epicsTime_localtime() on Windows</h3>
|
||||
|
||||
<p>Simpler versions of the epicsTime_gmtime() and epicsTime_localtime()
|
||||
routines have been included in the Windows implementations, and a new test
|
||||
program added. The original versions do not report DST status properly. Fixes
|
||||
<a href="https://bugs.launchpad.net/bugs/1528284">Launchpad bug 1528284</a>.</p>
|
||||
|
||||
<h3>Clean up after GNU readline()</h3>
|
||||
|
||||
<p>If EPICS Base is built with readline support, any IOC that calls epicsExit()
|
||||
|
||||
@@ -121,148 +121,31 @@ static int osdTimeGetCurrent ( epicsTimeStamp *pDest )
|
||||
return epicsTimeOK;
|
||||
}
|
||||
|
||||
inline void UnixTimeToFileTime ( const time_t * pAnsiTime, LPFILETIME pft )
|
||||
{
|
||||
LONGLONG ll = Int32x32To64 ( *pAnsiTime, 10000000 ) + LL_CONSTANT(116444736000000000);
|
||||
pft->dwLowDateTime = static_cast < DWORD > ( ll );
|
||||
pft->dwHighDateTime = static_cast < DWORD > ( ll >>32 );
|
||||
}
|
||||
|
||||
static int daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31,
|
||||
31, 30, 31, 30, 31 };
|
||||
|
||||
static bool isLeapYear ( DWORD year )
|
||||
{
|
||||
if ( (year % 4) == 0 ) {
|
||||
return ( ( year % 100 ) != 0 || ( year % 400 ) == 0 );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int dayOfYear ( DWORD day, DWORD month, DWORD year )
|
||||
{
|
||||
DWORD nDays = 0;
|
||||
for ( unsigned m = 1; m < month; m++ ) {
|
||||
nDays += daysInMonth[m-1];
|
||||
if ( m == 2 && isLeapYear(year) ) {
|
||||
nDays++;
|
||||
}
|
||||
}
|
||||
return nDays + day;
|
||||
}
|
||||
|
||||
// synthesize a reentrant gmtime on WIN32
|
||||
int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM )
|
||||
{
|
||||
FILETIME ft;
|
||||
UnixTimeToFileTime ( pAnsiTime, &ft );
|
||||
|
||||
SYSTEMTIME st;
|
||||
BOOL status = FileTimeToSystemTime ( &ft, &st );
|
||||
if ( ! status ) {
|
||||
return epicsTimeERROR;
|
||||
struct tm * pRet = gmtime ( pAnsiTime );
|
||||
if ( pRet ) {
|
||||
*pTM = *pRet;
|
||||
return epicsTimeOK;
|
||||
}
|
||||
else {
|
||||
return errno;
|
||||
}
|
||||
|
||||
pTM->tm_sec = st.wSecond; // seconds after the minute - [0,59]
|
||||
pTM->tm_min = st.wMinute; // minutes after the hour - [0,59]
|
||||
pTM->tm_hour = st.wHour; // hours since midnight - [0,23]
|
||||
assert ( st.wDay >= 1 && st.wDay <= 31 );
|
||||
pTM->tm_mday = st.wDay; // day of the month - [1,31]
|
||||
assert ( st.wMonth >= 1 && st.wMonth <= 12 );
|
||||
pTM->tm_mon = st.wMonth - 1; // months since January - [0,11]
|
||||
assert ( st.wYear >= 1900 );
|
||||
pTM->tm_year = st.wYear - 1900; // years since 1900
|
||||
pTM->tm_wday = st.wDayOfWeek; // days since Sunday - [0,6]
|
||||
pTM->tm_yday = dayOfYear ( st.wDay, st.wMonth, st.wYear ) - 1;
|
||||
pTM->tm_isdst = 0;
|
||||
|
||||
return epicsTimeOK;
|
||||
}
|
||||
|
||||
// synthesize a reentrant localtime on WIN32
|
||||
int epicsShareAPI epicsTime_localtime (
|
||||
const time_t * pAnsiTime, struct tm * pTM )
|
||||
{
|
||||
FILETIME ft;
|
||||
UnixTimeToFileTime ( pAnsiTime, & ft );
|
||||
|
||||
TIME_ZONE_INFORMATION tzInfo;
|
||||
DWORD tzStatus = GetTimeZoneInformation ( & tzInfo );
|
||||
if ( tzStatus == TIME_ZONE_ID_INVALID ) {
|
||||
return epicsTimeERROR;
|
||||
struct tm * pRet = localtime ( pAnsiTime );
|
||||
if ( pRet ) {
|
||||
*pTM = *pRet;
|
||||
return epicsTimeOK;
|
||||
}
|
||||
|
||||
//
|
||||
// There are remarkable weaknessess in the FileTimeToLocalFileTime
|
||||
// interface so we dont use it here. Unfortunately, there is no
|
||||
// corresponding function that works on file time.
|
||||
//
|
||||
SYSTEMTIME st;
|
||||
BOOL success = FileTimeToSystemTime ( & ft, & st );
|
||||
if ( ! success ) {
|
||||
return epicsTimeERROR;
|
||||
else {
|
||||
return errno;
|
||||
}
|
||||
SYSTEMTIME lst;
|
||||
success = SystemTimeToTzSpecificLocalTime (
|
||||
& tzInfo, & st, & lst );
|
||||
if ( ! success ) {
|
||||
return epicsTimeERROR;
|
||||
}
|
||||
|
||||
//
|
||||
// We must convert back to file time so that we can determine if DST
|
||||
// is active...
|
||||
//
|
||||
FILETIME lft;
|
||||
success = SystemTimeToFileTime ( & lst, & lft );
|
||||
if ( ! success ) {
|
||||
return epicsTimeERROR;
|
||||
}
|
||||
|
||||
int is_dst = -1; // unknown state of dst
|
||||
if ( tzStatus != TIME_ZONE_ID_UNKNOWN &&
|
||||
tzInfo.StandardDate.wMonth != 0 &&
|
||||
tzInfo.DaylightDate.wMonth != 0) {
|
||||
// determine if the specified date is
|
||||
// in daylight savings time
|
||||
tzInfo.StandardDate.wYear = st.wYear;
|
||||
FILETIME StandardDateFT;
|
||||
success = SystemTimeToFileTime (
|
||||
& tzInfo.StandardDate, & StandardDateFT );
|
||||
if ( ! success ) {
|
||||
return epicsTimeERROR;
|
||||
}
|
||||
tzInfo.DaylightDate.wYear = st.wYear;
|
||||
FILETIME DaylightDateFT;
|
||||
success = SystemTimeToFileTime (
|
||||
& tzInfo.DaylightDate, & DaylightDateFT );
|
||||
if ( ! success ) {
|
||||
return epicsTimeERROR;
|
||||
}
|
||||
if ( CompareFileTime ( & lft, & DaylightDateFT ) >= 0
|
||||
&& CompareFileTime ( & lft, & StandardDateFT ) < 0 ) {
|
||||
is_dst = 1;
|
||||
}
|
||||
else {
|
||||
is_dst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pTM->tm_sec = lst.wSecond; // seconds after the minute - [0,59]
|
||||
pTM->tm_min = lst.wMinute; // minutes after the hour - [0,59]
|
||||
pTM->tm_hour = lst.wHour; // hours since midnight - [0,23]
|
||||
assert ( lst.wDay >= 1 && lst.wDay <= 31 );
|
||||
pTM->tm_mday = lst.wDay; // day of the month - [1,31]
|
||||
assert ( lst.wMonth >= 1 && lst.wMonth <= 12 );
|
||||
pTM->tm_mon = lst.wMonth - 1; // months since January - [0,11]
|
||||
assert ( lst.wYear >= 1900 );
|
||||
pTM->tm_year = lst.wYear - 1900; // years since 1900
|
||||
pTM->tm_wday = lst.wDayOfWeek; // days since Sunday - [0,6]
|
||||
pTM->tm_yday = dayOfYear ( lst.wDay, lst.wMonth, lst.wYear ) - 1;
|
||||
pTM->tm_isdst = is_dst;
|
||||
|
||||
return epicsTimeOK;
|
||||
}
|
||||
|
||||
currentTime::currentTime () :
|
||||
|
||||
@@ -68,6 +68,10 @@ epicsTimeTest_SRCS += epicsTimeTest.cpp
|
||||
testHarness_SRCS += epicsTimeTest.cpp
|
||||
TESTS += epicsTimeTest
|
||||
|
||||
TESTPROD_HOST += epicsTimeZoneTest
|
||||
epicsTimeZoneTest_SRCS += epicsTimeZoneTest.c
|
||||
TESTS += epicsTimeZoneTest
|
||||
|
||||
TESTPROD_HOST += epicsThreadTest
|
||||
epicsThreadTest_SRCS += epicsThreadTest.cpp
|
||||
testHarness_SRCS += epicsThreadTest.cpp
|
||||
|
||||
117
src/libCom/test/epicsTimeZoneTest.c
Normal file
117
src/libCom/test/epicsTimeZoneTest.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2015 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "epicsTime.h"
|
||||
#include "epicsUnitTest.h"
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define tzset _tzset
|
||||
#endif
|
||||
|
||||
static
|
||||
void setTZ(const char *base, const char *dst, int offset)
|
||||
{
|
||||
char tz[20];
|
||||
if(offset!=0 || dst)
|
||||
sprintf(tz, "%s%d%s", base, offset/3600, dst);
|
||||
else
|
||||
sprintf(tz, "%s", base);
|
||||
testDiag("TZ=\"%s\"", tz);
|
||||
|
||||
epicsEnvSet("TZ", tz);
|
||||
tzset();
|
||||
}
|
||||
|
||||
static
|
||||
void test_localtime(time_t T, int sec, int min, int hour,
|
||||
int mday, int mon, int year,
|
||||
int wday, int yday, int isdst)
|
||||
{
|
||||
struct tm B;
|
||||
testDiag("test_localtime(%ld, ...)", (long)T);
|
||||
if(epicsTime_localtime(&T, &B)!=epicsTimeOK) {
|
||||
testFail("epicsTime_localtime() error");
|
||||
testSkip(9, "epicsTime_localtime() failed");
|
||||
} else {
|
||||
B.tm_year += 1900; /* for readability */
|
||||
testPass("epicsTime_localtime() success");
|
||||
#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD)
|
||||
TEST(sec);
|
||||
TEST(min);
|
||||
TEST(hour);
|
||||
TEST(mday);
|
||||
TEST(mon);
|
||||
TEST(year);
|
||||
TEST(wday);
|
||||
TEST(yday);
|
||||
TEST(isdst);
|
||||
#undef TEST
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void test_gmtime(time_t T, int sec, int min, int hour,
|
||||
int mday, int mon, int year,
|
||||
int wday, int yday, int isdst)
|
||||
{
|
||||
struct tm B;
|
||||
testDiag("test_gmtime(%ld, ...)", (long)T);
|
||||
if(epicsTime_gmtime(&T, &B)!=epicsTimeOK) {
|
||||
testFail("epicsTime_localtime() error");
|
||||
testSkip(9, "epicsTime_localtime() failed");
|
||||
} else {
|
||||
B.tm_year += 1900; /* for readability */
|
||||
testPass("epicsTime_localtime() success");
|
||||
#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD)
|
||||
TEST(sec);
|
||||
TEST(min);
|
||||
TEST(hour);
|
||||
TEST(mday);
|
||||
TEST(mon);
|
||||
TEST(year);
|
||||
TEST(wday);
|
||||
TEST(yday);
|
||||
TEST(isdst);
|
||||
#undef TEST
|
||||
}
|
||||
}
|
||||
|
||||
MAIN(epicsTimeZoneTest)
|
||||
{
|
||||
testPlan(80);
|
||||
/* 1445259616
|
||||
* Mon Oct 19 09:00:16 2015 EDT
|
||||
* Mon Oct 19 08:00:16 2015 CDT
|
||||
* Mon Oct 19 13:00:16 2015 UTC
|
||||
*/
|
||||
testDiag("POSIX 1445259616");
|
||||
setTZ("EST", "EDT", 5*3600);
|
||||
test_localtime(1445259616ul, 16, 0, 9, 19, 9, 2015, 1, 291, 1);
|
||||
setTZ("CST", "CDT", 6*3600);
|
||||
test_localtime(1445259616ul, 16, 0, 8, 19, 9, 2015, 1, 291, 1);
|
||||
setTZ("UTC", NULL, 0);
|
||||
test_localtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0);
|
||||
test_gmtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0);
|
||||
/* 1421244931
|
||||
* Wed Jan 14 09:15:31 2015 EST
|
||||
* Wed Jan 14 08:15:31 2015 CST
|
||||
* Wed Jan 14 14:15:31 2015 UTC
|
||||
*/
|
||||
testDiag("POSIX 1421244931");
|
||||
setTZ("EST", "EDT", 5*3600);
|
||||
test_localtime(1421244931ul, 31, 15, 9, 14, 0, 2015, 3, 13, 0);
|
||||
setTZ("CST", "CDT", 6*3600);
|
||||
test_localtime(1421244931ul, 31, 15, 8, 14, 0, 2015, 3, 13, 0);
|
||||
setTZ("UTC", NULL, 0);
|
||||
test_localtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0);
|
||||
test_gmtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0);
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user