diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index af7c8bffe..3de7d4b4f 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -13,6 +13,13 @@ +

Fix epicsTime_localtime() on Windows

+ +

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 +Launchpad bug 1528284.

+

Clean up after GNU readline()

If EPICS Base is built with readline support, any IOC that calls epicsExit() diff --git a/src/libCom/osi/os/WIN32/osdTime.cpp b/src/libCom/osi/os/WIN32/osdTime.cpp index eb2c7ad9d..239d21a2c 100644 --- a/src/libCom/osi/os/WIN32/osdTime.cpp +++ b/src/libCom/osi/os/WIN32/osdTime.cpp @@ -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 () : diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index e8e94a3e6..7e1b5a020 100644 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -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 diff --git a/src/libCom/test/epicsTimeZoneTest.c b/src/libCom/test/epicsTimeZoneTest.c new file mode 100644 index 000000000..dd12b1acf --- /dev/null +++ b/src/libCom/test/epicsTimeZoneTest.c @@ -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 + +#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(); +}