Jelajahi Sumber

HPCC-8572 Std.Date functionality

Time support added to Std.Date; new timelib plugin; stringlib modifications to support time; updated tests for new Std.Date functionality; additional date manipulation functions for Gregorian dates.

Signed-off-by: Dan S. Camper <dan.camper@lexisnexis.com>
Dan S. Camper 10 tahun lalu
induk
melakukan
bcab80afbc

+ 62 - 29
ecllibrary/std/Date.ecl

@@ -40,7 +40,7 @@ EXPORT Time_t := UNSIGNED3;
 
 
 // A signed number holding a number of seconds.  Can be used to represent either
-// a duration or the number of seconds since epoch.
+// a duration or the number of seconds since epoch (Jan 1, 1970).
 EXPORT Seconds_t := INTEGER8;
 
 
@@ -51,10 +51,9 @@ EXPORT DateTime_rec := RECORD
 END;
 
 
-// A signed number holding a number of seconds with millisecond resolution.
-// Can be used to represent either a duration or the number of seconds since
-// epoch.
-EXPORT Timestamp_t := REAL8;
+// A signed number holding a number of microseconds.  Can be used to represent
+// either a duration or the number of microseconds since epoch (Jan 1, 1970).
+EXPORT Timestamp_t := INTEGER8;
 
 
 // Simple structure to hold interesting values from a struct tm* instance.
@@ -146,9 +145,10 @@ EXPORT Time_t TimeFromParts(UNSIGNED1 hour, UNSIGNED1 minute, UNSIGNED1 second)
 
 
 /**
- * Combines date and time components to create a seconds type.
+ * Combines date and time components to create a seconds type.  The date must
+ * be represented within the Gregorian calendar after the year 1600.
  *
- * @param year                  The year (0-9999).
+ * @param year                  The year (1601-30827).
  * @param month                 The month (1-12).
  * @param day                   The day (1..daysInMonth).
  * @param hour                  The hour (0-23).
@@ -173,7 +173,8 @@ EXPORT Seconds_t SecondsFromParts(INTEGER2 year,
 
 /**
  * Converts the number of seconds since epoch to a structure containing
- * date and time parts.
+ * date and time parts.  The result must be representable within the
+ * Gregorian calendar after the year 1600.
  *
  * @param seconds               The number of seconds since epoch.
  * @return                      Module with exported attributes for year, month,
@@ -199,9 +200,19 @@ EXPORT SecondsToParts(Seconds_t seconds) := FUNCTION
     RETURN result;
 END;
 
+
+/**
+ * Converts the number of microseconds since epoch to the number of seconds
+ * since epoch.
+ *
+ * @param timestamp             The number of microseconds since epoch.
+ * @return                      The number of seconds since epoch.
+ */
+
+EXPORT Seconds_t TimestampToSeconds(Timestamp_t timestamp) := timestamp DIV 1000000;
+
 /**
- * Tests whether the year is a leap year in the Gregorian calendar
- * (or proleptic Gregorian).
+ * Tests whether the year is a leap year in the Gregorian calendar.
  *
  * @param year          The year (0-9999).
  * @return              True if the year is a leap year.
@@ -211,8 +222,7 @@ EXPORT BOOLEAN IsLeapYear(INTEGER2 year) := (year % 4 = 0) AND ((year % 100 != 0
 
 
 /**
- * Tests whether a date is a leap year in the Gregorian calendar
- * (or proleptic Gregorian).
+ * Tests whether a date is a leap year in the Gregorian calendar.
  *
  * @param date          The date.
  * @return              True if the year is a leap year.
@@ -242,7 +252,7 @@ EXPORT Days_t FromGregorianYMD(INTEGER2 year, UNSIGNED1 month, UNSIGNED1 day) :=
     m := month + 12*a - 3;
     jd := day + (153 * m + 2) DIV 5 + 365 * y + y DIV 4 - y DIV 100 + y DIV 400;
 
-    RETURN jd + (GregorianDateOrigin-1);
+    RETURN jd + (GregorianDateOrigin - 1);
 END;
 
 
@@ -261,7 +271,7 @@ EXPORT ToGregorianYMD(Days_t days) := FUNCTION
     daysIn100Years := 25*daysIn4Years-1;
     daysIn400Years := 4*daysIn100Years+1;
 
-    // Calulate days in each of the cycles.
+    // Calculate days in each of the cycles.
     adjustedDays := days - GregorianDateOrigin;
     num400Years := adjustedDays div daysIn400Years;
     rem400Years := adjustedDays % daysIn400Years;
@@ -316,9 +326,10 @@ END;
 
 /**
  * Returns a number representing the day of the year indicated by the given date.
+ * The date must be in the Gregorian calendar after the year 1600.
  *
  * @param date          A Date_t value.
- * @return              A number (0-365) representing the number of days since
+ * @return              A number (0-366) representing the number of days since
  *                      the beginning of the year.
  */
 
@@ -335,6 +346,7 @@ END;
 
 /**
  * Returns a number representing the day of the week indicated by the given date.
+ * The date must be in the Gregorian calendar after the year 1600.
  *
  * @param date          A Date_t value.
  * @return              A number 1-7 representing the day of the week, where 1 = Sunday.
@@ -587,12 +599,13 @@ EXPORT Seconds_t SecondsFromDateTimeRec(DateTime_rec datetime, BOOLEAN is_local_
 
 
 /**
- * Converts a string to a Date_t using the relevant string format.
+ * Converts a string to a Date_t using the relevant string format.  The resulting
+ * date must be representable within the Gregorian calendar after the year 1600.
  *
  * @param date_text     The string to be converted.
  * @param format        The format of the input string.
  *                      (See documentation for strftime)
- * @return              The time that was matched in the string.  Returns 0 if failed to match.
+ * @return              The date that was matched in the string.  Returns 0 if failed to match.
  *
  * Supported characters:
     %B          Full month name
@@ -639,7 +652,7 @@ EXPORT Date_t FromString(STRING date_text, VARSTRING format) :=
  * @param date_text     The string to be converted.
  * @param format        The format of the input string.
  *                      (See documentation for strftime)
- * @return              The date that was matched in the string.  Returns 0 if failed to match.
+ * @return              The time that was matched in the string.  Returns 0 if failed to match.
  *
  * Supported characters:
     %H          Hour (two digits)
@@ -677,7 +690,7 @@ EXPORT Date_t MatchDateString(STRING date_text, SET OF VARSTRING formats) :=
  * @param time_text     The string to be converted.
  * @param formats       A set of formats to check against the string.
  *                      (See documentation for strftime)
- * @return              The date that was matched in the string.
+ * @return              The time that was matched in the string.
  *                      Returns 0 if failed to match.
  */
 
@@ -825,6 +838,7 @@ EXPORT STRING ConvertTimeFormatMultiple(STRING time_text, SET OF VARSTRING from_
 
 /**
  * Adjusts a date by incrementing or decrementing year, month and/or day values.
+ * The date must be in the Gregorian calendar after the year 1600.
  * If the new calculated date is invalid then it will be normalized according
  * to mktime() rules.  Example: 20140130 + 1 month = 20140302.
  *
@@ -846,7 +860,8 @@ EXPORT Date_t AdjustDate(Date_t date,
 
 
 /**
- * Adjusts a date by adding or subtracting seconds.  If the new calculated
+ * Adjusts a date by adding or subtracting seconds.  The date must be in the
+ * Gregorian calendar after the year 1600.  If the new calculated
  * date is invalid then it will be normalized according to mktime() rules.
  * Example: 20140130 + 172800 seconds = 20140201.
  *
@@ -899,7 +914,8 @@ EXPORT Time_t AdjustTimeBySeconds(Time_t time, INTEGER4 seconds_delta) :=
  * hours, minutes and/or seconds.  This is performed by first converting the
  * seconds into a full date/time structure, applying any delta values to
  * individual date/time components, then converting the structure back to the
- * number of seconds.  If the interim structure is found to have an invalid
+ * number of seconds.  This interim date must lie within Gregorian calendar
+ * after the year 1600.  If the interim structure is found to have an invalid
  * date/time then it will be normalized according to mktime() rules.  Therefore,
  * some delta values (such as "1 month") are actually relative to the value of
  * the seconds argument.
@@ -944,7 +960,7 @@ EXPORT Seconds_t AdjustSeconds(Seconds_t seconds,
  * will result in Feb. 28, 2014; Jan. 31, 2014 + 1 month + 1 day will result
  * in Mar. 1, 2014.
  *
- * @param date          The date to adjust.
+ * @param date          The date to adjust, in the Gregorian calendar after 1600.
  * @param year_delta    The requested change to the year value;
  *                      optional, defaults to zero.
  * @param month_delta   The requested change to the month value;
@@ -1035,15 +1051,14 @@ EXPORT Seconds_t CurrentSeconds(BOOLEAN in_local_time = FALSE) :=
 
 
 /**
- * Returns the current date and time as the number of seconds since epoch,
- * with millisecond resolution.
+ * Returns the current date and time as the number of microseconds since epoch.
  *
  * @param in_local_time     TRUE if the returned value should be local to the
  *                          cluster computing the time, FALSE for UTC.
  *                          Optional, defaults to FALSE.
  * @return                  A Timestamp_t representing the current time in
- *                          millisecond resolution in UTC or local time,
- *                          depending on the argument.
+ *                          microseconds in UTC or local time, depending on
+ *                          the argument.
  */
 
 EXPORT Timestamp_t CurrentTimestamp(BOOLEAN in_local_time = FALSE) :=
@@ -1112,9 +1127,27 @@ EXPORT BOOLEAN IsValidDate(Date_t date,
                            INTEGER2 yearLowerBound = 1800,
                            INTEGER2 yearUpperBound = 2100) := FUNCTION
     yearInBounds := (Year(date) BETWEEN yearLowerBound AND yearUpperBound);
-    matches := (date = AdjustDate(date)); // AdjustDate normalizes, so this is a validation check
+    monthInBounds := (Month(date) BETWEEN 1 AND 12);
+    maxDayInMonth := CHOOSE(Month(date),1,IF(IsLeapYear(Year(date)),29,28),31,30,31,30,31,31,30,31,30,31);
+    dayInBounds := (Day(date) BETWEEN 1 AND maxDayInMonth);
+
+    RETURN yearInBounds AND monthInBounds AND dayInBounds;
+END;
+
+
+/**
+ * Tests whether a date is valid in the Gregorian calendar.  The year
+ * must be between 1601 and 30827.
+ *
+ * @param date              The Date_t to validate.
+ * @return                  TRUE if the date is valid, FALSE otherwise.
+ */
+
+EXPORT BOOLEAN IsValidGregorianDate(Date_t date) := FUNCTION
+    yearInBounds := (Year(date) BETWEEN 1601 AND 30827);
+    matchesNormalized := (date = AdjustDate(date)); // AdjustDate normalizes, so this is a validation check
 
-    RETURN yearInBounds AND matches;
+    RETURN yearInBounds AND matchesNormalized;
 END;
 
 
@@ -1127,7 +1160,7 @@ END;
 
 EXPORT BOOLEAN IsValidTime(Time_t time) := FUNCTION
     hourInBounds := (Hour(time) BETWEEN 0 AND 23);
-    minuteInBounds := (Hour(time) BETWEEN 0 AND 59);
+    minuteInBounds := (Minute(time) BETWEEN 0 AND 59);
     secondInBounds := (Second(time) BETWEEN 0 AND 59);
 
     RETURN hourInBounds AND minuteInBounds AND secondInBounds;

+ 39 - 11
ecllibrary/teststd/Date/TestDate.ecl

@@ -74,6 +74,7 @@ EXPORT TestDate := MODULE
 
   EXPORT TestDynamicFunctions := [
     ASSERT(Date.SecondsFromParts(1999,2,1,12,34,56,FALSE) = 917872496);     // UTC
+    ASSERT(Date.SecondsFromParts(1965,2,17,0,0,0,FALSE) = -153705600);      // UTC
 
     ASSERT(Date.SecondsToParts(917872496).year = 1999);
     ASSERT(Date.SecondsToParts(917872496).month = 2);
@@ -82,12 +83,29 @@ EXPORT TestDate := MODULE
     ASSERT(Date.SecondsToParts(917872496).minute = 34);
     ASSERT(Date.SecondsToParts(917872496).second = 56);
 
+    ASSERT(Date.SecondsToParts(-153705600).year = 1965);
+    ASSERT(Date.SecondsToParts(-153705600).month = 2);
+    ASSERT(Date.SecondsToParts(-153705600).day = 17);
+    ASSERT(Date.SecondsToParts(-153705600).hour = 0);
+    ASSERT(Date.SecondsToParts(-153705600).minute = 0);
+    ASSERT(Date.SecondsToParts(-153705600).second = 0);
+
+    ASSERT(Date.SecondsToParts(0).year = 1970);     // Epoch test
+    ASSERT(Date.SecondsToParts(0).month = 1);       // Epoch test
+    ASSERT(Date.SecondsToParts(0).day = 1);         // Epoch test
+    ASSERT(Date.SecondsToParts(0).hour = 0);        // Epoch test
+    ASSERT(Date.SecondsToParts(0).minute = 0);      // Epoch test
+    ASSERT(Date.SecondsToParts(0).second = 0);      // Epoch test
+
     ASSERT(Date.DayOfWeek(20140130) = 5);   // 5=Thursday
+    ASSERT(Date.DayOfWeek(19650217) = 4);   // 4=Wednesday
+    ASSERT(Date.DayOfWeek(20530213) = 5);   // 5=Thursday
 
     ASSERT(Date.AdjustDate(20000130, month_delta:=1) = 20000301);
     ASSERT(Date.AdjustDate(20000130, month_delta:=1, day_delta:=-1) = 20000229);
     ASSERT(Date.AdjustDate(20000229, year_delta:=1) = 20010301);
     ASSERT(Date.AdjustDate(20000229, year_delta:=-1) = 19990301);
+    ASSERT(Date.AdjustDate(19650217, year_delta:=49) = 20140217);
 
     ASSERT(Date.AdjustDateBySeconds(20140130, 172800) = 20140201);
 
@@ -101,20 +119,13 @@ EXPORT TestDate := MODULE
     ASSERT(Date.AdjustSeconds(917872496, hour_delta:=1) = 917876096);
 
     ASSERT(Date.AdjustCalendar(20140130, month_delta:=1) = 20140228);
-
     ASSERT(Date.AdjustCalendar(20000229, year_delta:=1) = 20010228);
     ASSERT(Date.AdjustCalendar(20000229, year_delta:=4) = 20040229);
 
-    ASSERT(Date.IsValidDate(vDate, 2014, 2050));
-    ASSERT(Date.IsValidDate(vToday, 2014, 2050));
-
-    ASSERT(Date.IsValidTime(vTime));
-    ASSERT(Date.IsValidTime(vTimeLocal));
-
     ASSERT(vSeconds + vLocalTimeZoneOffset = vSecondsLocal);
 
-    ASSERT(vSeconds = TRUNCATE(vTimestamp));
-    ASSERT(vSeconds + vLocalTimeZoneOffset = TRUNCATE(vTimestampLocal));
+    ASSERT(Date.TimestampToSeconds(vTimestamp) = vSeconds);
+    ASSERT(Date.TimestampToSeconds(vTimestampLocal) = vSecondsLocal);
 
     // IsLocalDaylightSavingsInEffect() -- not possible to check without pinning both cluster location and date
 
@@ -124,12 +135,29 @@ EXPORT TestDate := MODULE
     ASSERT(Date.DatesForWeek(20141030).startDate = 20141026);
     ASSERT(Date.DatesForWeek(20141030).endDate = 20141101);
 
+    ASSERT(Date.IsValidDate(vDate, 2014, 2050));
+    ASSERT(NOT Date.IsValidDate(vDate, 2000, 2010));
+    ASSERT(Date.IsValidDate(vToday, 2014, 2050));
+    ASSERT(NOT Date.IsValidDate(vToday, 2000, 2010));
     ASSERT(Date.IsValidDate(20141030) = TRUE);
     ASSERT(Date.IsValidDate(20000229) = TRUE);
-    ASSERT(Date.IsValidDate(20010229) = FALSE);
+    ASSERT(Date.IsValidDate(20010229) = FALSE); // Invalid date (leap year check)
+    ASSERT(Date.IsValidDate(20141000) = FALSE); // Invalid day
+    ASSERT(Date.IsValidDate(20141032) = FALSE); // Invalid day
+    ASSERT(Date.IsValidDate(20000001) = FALSE); // Invalid month
+    ASSERT(Date.IsValidDate(20001301) = FALSE); // Invalid month
 
+    ASSERT(Date.IsValidTime(vTime));
+    ASSERT(Date.IsValidTime(vTimeLocal));
     ASSERT(Date.IsValidTime(123456) = TRUE);
-    ASSERT(Date.IsValidTime(123465) = FALSE);
+    ASSERT(Date.IsValidTime(123465) = FALSE); // Invalid seconds
+    ASSERT(Date.IsValidTime(127456) = FALSE); // Invalid minutes
+    ASSERT(Date.IsValidTime(243456) = FALSE); // Invalid hours
+
+    ASSERT(NOT Date.IsValidGregorianDate(16001231));
+    ASSERT(Date.IsValidGregorianDate(16010101));
+    ASSERT(Date.IsValidGregorianDate(308271231));
+    ASSERT(NOT Date.IsValidGregorianDate(308280101));
 
     ASSERT(vCreateDateTimeFromSeconds.year = 1999);
     ASSERT(vCreateDateTimeFromSeconds.month = 2);

+ 4 - 2
plugins/stringlib/stringlib.cpp

@@ -1546,12 +1546,13 @@ STRINGLIB_API unsigned STRINGLIB_CALL slStringToTimeOfDay(size32_t lenS, const c
 STRINGLIB_API unsigned STRINGLIB_CALL slMatchDate(size32_t lenS, const char * s, bool isAllFormats, unsigned lenFormats, const void * _formats)
 {
     struct tm tm;
-    memset(&tm, 0, sizeof(tm));
 
     const char * formats = (const char *)_formats;
     for (unsigned off=0; off < lenFormats; )
     {
         const char * curFormat = formats+off;
+        
+        memset(&tm, 0, sizeof(tm));
         if (simple_strptime(lenS, s, curFormat, &tm))
             return makeDate(tm);
         off += strlen(curFormat) + 1;
@@ -1563,12 +1564,13 @@ STRINGLIB_API unsigned STRINGLIB_CALL slMatchDate(size32_t lenS, const char * s,
 STRINGLIB_API unsigned STRINGLIB_CALL slMatchTimeOfDay(size32_t lenS, const char * s, bool isAllFormats, unsigned lenFormats, const void * _formats)
 {
     struct tm tm;
-    memset(&tm, 0, sizeof(tm));
 
     const char * formats = (const char *)_formats;
     for (unsigned off=0; off < lenFormats; )
     {
         const char * curFormat = formats+off;
+        
+        memset(&tm, 0, sizeof(tm));
         if (simple_strptime(lenS, s, curFormat, &tm))
             return makeTimeOfDay(tm);
         off += strlen(curFormat) + 1;

+ 392 - 130
plugins/timelib/timelib.cpp

@@ -35,29 +35,29 @@ static const char * compatibleVersions[] = {
 #define TIMELIB_VERSION "TIMELIB 1.0.0"
 
 static const char * EclDefinition =
-"export TMPartsRec := RECORD \n"
+"EXPORT TMPartsRec := RECORD \n"
 "  UNSIGNED4 v; \n"
 "END;"
-"export TimeLib := SERVICE\n"
-"  integer4 SecondsFromParts(integer2 year, unsigned1 month, unsigned1 day, unsigned1 hour, unsigned1 minute, unsigned1 second, boolean is_local_time) : c,pure,entrypoint='tlSecondsFromParts'; \n"
+"EXPORT TimeLib := SERVICE\n"
+"  integer8 SecondsFromParts(integer2 year, unsigned1 month, unsigned1 day, unsigned1 hour, unsigned1 minute, unsigned1 second, boolean is_local_time) : c,pure,entrypoint='tlSecondsFromParts'; \n"
 "  DATASET(TMPartsRec) SecondsToParts(INTEGER8 seconds) : c,pure,entrypoint='tlSecondsToParts'; \n"
 "  UNSIGNED2 GetDayOfYear(INTEGER2 year, UNSIGNED1 month, UNSIGNED1 day) : c,pure,entrypoint='tlGetDayOfYear'; \n"
 "  UNSIGNED1 GetDayOfWeek(INTEGER2 year, UNSIGNED1 month, UNSIGNED1 day) : c,pure,entrypoint='tlGetDayOfWeek'; \n"
 "  STRING DateToString(UNSIGNED4 date, VARSTRING format) : c,pure,entrypoint='tlDateToString'; \n"
 "  STRING TimeToString(UNSIGNED3 time, VARSTRING format) : c,pure,entrypoint='tlTimeToString'; \n"
-"  STRING SecondsToString(INTEGER4 seconds, VARSTRING format) : c,pure,entrypoint='tlSecondsToString'; \n"
+"  STRING SecondsToString(INTEGER8 seconds, VARSTRING format) : c,pure,entrypoint='tlSecondsToString'; \n"
 "  UNSIGNED4 AdjustDate(UNSIGNED4 date, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta) : c,pure,entrypoint='tlAdjustDate'; \n"
 "  UNSIGNED4 AdjustDateBySeconds(UNSIGNED4 date, INTEGER4 seconds_delta) : c,pure,entrypoint='tlAdjustDateBySeconds'; \n"
 "  UNSIGNED3 AdjustTime(UNSIGNED3 time, INTEGER2 hour_delta, INTEGER4 minute_delta, INTEGER4 second_delta) : c,pure,entrypoint='tlAdjustTime'; \n"
 "  UNSIGNED3 AdjustTimeBySeconds(UNSIGNED3 time, INTEGER4 seconds_delta) : c,pure,entrypoint='tlAdjustTimeBySeconds'; \n"
-"  INTEGER4 AdjustSeconds(INTEGER4 seconds, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta, INTEGER2 hour_delta, INTEGER4 minute_delta, INTEGER4 second_delta) : c,pure,entrypoint='tlAdjustSeconds'; \n"
+"  INTEGER4 AdjustSeconds(INTEGER8 seconds, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta, INTEGER2 hour_delta, INTEGER4 minute_delta, INTEGER4 second_delta) : c,pure,entrypoint='tlAdjustSeconds'; \n"
 "  UNSIGNED4 AdjustCalendar(UNSIGNED4 date, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta) : c,pure,entrypoint='tlAdjustCalendar'; \n"
 "  BOOLEAN IsLocalDaylightSavingsInEffect() : c,pure,entrypoint='tlIsLocalDaylightSavingsInEffect'; \n"
 "  INTEGER4 LocalTimeZoneOffset() : c,pure,entrypoint='tlLocalTimeZoneOffset'; \n"
 "  UNSIGNED4 CurrentDate(BOOLEAN in_local_time) : c,pure,entrypoint='tlCurrentDate'; \n"
 "  UNSIGNED4 CurrentTime(BOOLEAN in_local_time) : c,pure,entrypoint='tlCurrentTime'; \n"
 "  INTEGER4 CurrentSeconds(BOOLEAN in_local_time) : c,pure,entrypoint='tlCurrentSeconds'; \n"
-"  REAL8 CurrentTimestamp(BOOLEAN in_local_time) : c,pure,entrypoint='tlCurrentTimestamp'; \n"
+"  INTEGER8 CurrentTimestamp(BOOLEAN in_local_time) : c,pure,entrypoint='tlCurrentTimestamp'; \n"
 "  UNSIGNED4 GetLastDayOfMonth(UNSIGNED4 date) : c,pure,entrypoint='tlGetLastDayOfMonth'; \n"
 "  DATASET(TMPartsRec) DatesForWeek(UNSIGNED4 date) : c,pure,entrypoint='tlDatesForWeek'; \n"
 "END;";
@@ -86,16 +86,297 @@ TIMELIB_API void setPluginContext(IPluginContext * _ctx) { parentCtx = _ctx; }
 
 //------------------------------------------------------------------------------
 
-void tlMakeTimeStructFromUTCSeconds(time_t seconds, struct tm* timeInfo)
+#ifdef _WINDOWS
+    const __int64 _onesec_in100ns = (__int64)10000000;
+
+    __int64 tlFileTimeToInt64(FILETIME f)
+    {
+        __int64     seconds;
+
+        seconds = f.dwHighDateTime;
+        seconds <<= 32;
+        seconds |= f.dwLowDateTime;
+
+        return seconds;
+    }
+
+    FILETIME tlInt64ToFileTime(__int64 seconds)
+    {
+        FILETIME    f;
+
+        f.dwHighDateTime = (DWORD)((seconds >> 32) & 0x00000000FFFFFFFF);
+        f.dwLowDateTime = (DWORD)(seconds & 0x00000000FFFFFFFF);
+
+        return f;
+    }
+
+    FILETIME tlFileTimeFromYear(WORD year)
+    {
+        SYSTEMTIME  s;
+        FILETIME    f;
+
+        memset(&s, 0, sizeof(s));
+
+        s.wYear = year;
+        s.wMonth = 1;
+        s.wDayOfWeek = 1;
+        s.wDay = 1;
+
+        SystemTimeToFileTime(&s, &f);
+
+        return f;
+    }
+
+    unsigned int tlYearDayFromSystemTime(const SYSTEMTIME* s)
+    {
+        __int64     seconds;
+        FILETIME    f1;
+        FILETIME    f2;
+
+        f1 = tlFileTimeFromYear(s->wYear);
+        SystemTimeToFileTime(s, &f2);
+
+        seconds = tlFileTimeToInt64(f2) - tlFileTimeToInt64(f1);
+
+        return static_cast<unsigned int>((seconds / _onesec_in100ns) / (60 * 60 * 24));
+    }
+
+    SYSTEMTIME tlTimeStructToSystemTime(struct tm* timeInfoPtr)
+    {
+        SYSTEMTIME s;
+
+        s.wYear = timeInfoPtr->tm_year + 1900;
+        s.wMonth = timeInfoPtr->tm_mon + 1;
+        s.wDayOfWeek = timeInfoPtr->tm_wday;
+        s.wDay = timeInfoPtr->tm_mday;
+        s.wHour = timeInfoPtr->tm_hour;
+        s.wMinute = timeInfoPtr->tm_min;
+        s.wSecond = timeInfoPtr->tm_sec;
+        s.wMilliseconds = 0;
+
+        return s;
+    }
+
+    void tlSystemTimeToTimeStruct_r(SYSTEMTIME* s, struct tm* timeInfoPtr)
+    {
+        memset(timeInfoPtr, 0, sizeof(struct tm));
+
+        timeInfoPtr->tm_year = s->wYear - 1900;
+        timeInfoPtr->tm_mon = s->wMonth - 1;
+        timeInfoPtr->tm_wday = s->wDayOfWeek;
+        timeInfoPtr->tm_mday = s->wDay;
+        timeInfoPtr->tm_yday = tlYearDayFromSystemTime(s);
+        timeInfoPtr->tm_hour = s->wHour;
+        timeInfoPtr->tm_min = s->wMinute;
+        timeInfoPtr->tm_sec = s->wSecond;
+        timeInfoPtr->tm_isdst = 0;
+    }
+
+    time_t tlFileTimeToSeconds(const FILETIME* f)
+    {
+        __int64     offset = 11644473600; // Number of seconds between 1601 and 1970 (Jan 1 of each)
+        __int64     seconds;
+
+        seconds = tlFileTimeToInt64(*f) - offset;
+
+        return static_cast<time_t>(seconds / _onesec_in100ns);
+    }
+
+    FILETIME tlSecondsToFileTime(const time_t seconds)
+    {
+        FILETIME    f1970 = tlFileTimeFromYear(1970);
+        FILETIME    f;
+        __int64     time;
+
+        time = (seconds * _onesec_in100ns) + tlFileTimeToInt64(f1970);
+
+        f = tlInt64ToFileTime(time);
+
+        return f;
+    }
+
+    __int64 tlLocalTimeDiff()
+    {
+        SYSTEMTIME  systemUTC;
+        SYSTEMTIME  systemLocal;
+        FILETIME    fileUTC;
+        FILETIME    fileLocal;
+
+        GetSystemTime(&systemUTC);
+        GetLocalTime(&systemLocal);
+
+        SystemTimeToFileTime(&systemUTC, &fileUTC);
+        SystemTimeToFileTime(&systemLocal, &fileLocal);
+
+        return tlFileTimeToInt64(fileUTC) - tlFileTimeToInt64(fileLocal);
+    }
+
+    void tlBoundaryMod(int* tensPtr, int* unitsPtr, int base)
+    {
+        if (*unitsPtr >= base)
+        {
+            *tensPtr += *unitsPtr / base;
+            *unitsPtr %= base;
+        }
+        else if (*unitsPtr < 0)
+        {
+            --*tensPtr;
+            *unitsPtr += base;
+            if (*unitsPtr < 0)
+            {
+                *tensPtr -= 1 + (-*unitsPtr) / base;
+                *unitsPtr = base - (-*unitsPtr) % base;
+            }
+        }
+    }
+
+    void tlNormalizeTimeStruct(struct tm* timeInfoPtr)
+    {
+        // Normalize incoming struct tm
+        const int           secondsPerMinute = 60;
+        const int           minutesPerHour = 60;
+        const int           hoursPerDay = 24;
+        const int           daysPerWeek = 7;
+        const int           daysPerNYear = 365;
+        const int           daysPerLYear = 366;
+        const int           yearLengths[2] = { daysPerNYear, daysPerLYear };
+        const int           secondsPerHour = secondsPerMinute * minutesPerHour;
+        const long          secondsPerDay = secondsPerHour * hoursPerDay;
+        const int           monthsPerYear = 12;
+        const int           yearBase = 1900;
+        const int           monthLengths[2][monthsPerYear] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };
+
+        tlBoundaryMod(&timeInfoPtr->tm_min, &timeInfoPtr->tm_sec, secondsPerMinute);
+        tlBoundaryMod(&timeInfoPtr->tm_hour, &timeInfoPtr->tm_min, minutesPerHour);
+        tlBoundaryMod(&timeInfoPtr->tm_mday, &timeInfoPtr->tm_hour, hoursPerDay);
+        tlBoundaryMod(&timeInfoPtr->tm_year, &timeInfoPtr->tm_mon, monthsPerYear);
+
+        while (timeInfoPtr->tm_mday <= 0)
+        {
+            --timeInfoPtr->tm_year;
+            int isLeapYear = ((((timeInfoPtr->tm_year + yearBase) % 4) == 0 && ((timeInfoPtr->tm_year + yearBase) % 100) != 0) || ((timeInfoPtr->tm_year + yearBase) % 400) == 0);
+            timeInfoPtr->tm_mday += yearLengths[isLeapYear];
+        }
+
+        for (;;)
+        {
+            int isLeapYear = ((((timeInfoPtr->tm_year + yearBase) % 4) == 0 && ((timeInfoPtr->tm_year + yearBase) % 100) != 0) || ((timeInfoPtr->tm_year + yearBase) % 400) == 0);
+            int i = monthLengths[isLeapYear][timeInfoPtr->tm_mon];
+
+            if (timeInfoPtr->tm_mday <= i)
+                break;
+            timeInfoPtr->tm_mday -= i;
+            if (++timeInfoPtr->tm_mon >= monthsPerYear)
+            {
+                timeInfoPtr->tm_mon = 0;
+                ++timeInfoPtr->tm_year;
+            }
+        }
+    }
+
+//---------------------------
+
+    void tlWinLocalTime_r(const time_t* clock, struct tm* timeInfoPtr)
+    {
+        SYSTEMTIME  s;
+        FILETIME    f;
+        __int64     time;
+
+        f = tlSecondsToFileTime(*clock);
+        time = tlFileTimeToInt64(f) - tlLocalTimeDiff();
+        f = tlInt64ToFileTime(time);
+
+        FileTimeToSystemTime(&f, &s);
+
+        tlSystemTimeToTimeStruct_r(&s, timeInfoPtr);
+    }
+
+    void tlWinGMTime_r(const time_t* clock, struct tm* timeInfo)
+    {
+        FILETIME    f;
+        SYSTEMTIME  s;
+
+        f = tlSecondsToFileTime(*clock);
+        FileTimeToSystemTime(&f, &s);
+        tlSystemTimeToTimeStruct_r(&s, timeInfo);
+    }
+
+    time_t tlWinMKTime(struct tm* timeInfoPtr)
+    {
+        SYSTEMTIME  s;
+        FILETIME    f;
+        time_t      diff;
+
+        // Windows apparently doesn't normalize/fix bogus date values before
+        // doing conversions, so we need to normalize them first
+        tlNormalizeTimeStruct(timeInfoPtr);
+
+        s = tlTimeStructToSystemTime(timeInfoPtr);
+        SystemTimeToFileTime(&s, &f);
+
+        // Reset day of week
+        FileTimeToSystemTime(&f, &s);
+        timeInfoPtr->wday = s.wDayOfWeek;
+
+        diff = tlLocalTimeDiff() / _onesec_in100ns;
+
+        return tlFileTimeToSeconds(&f) - diff;
+    }
+#endif
+
+//------------------------------------------------------------------------------
+
+void tlLocalTime_r(const time_t* clock, struct tm* timeInfoPtr)
 {
     #ifdef _WINDOWS
-        // gmtime is thread-safe under Windows
-        memcpy(timeInfo,gmtime(&seconds),sizeof(&timeInfo));
+        tlWinLocalTime_r(clock, timeInfoPtr);
     #else
-        gmtime_r(&seconds,timeInfo);
+        localtime_r(clock, timeInfoPtr);
     #endif
 }
 
+void tlGMTime_r(const time_t* clock, struct tm* timeInfoPtr)
+{
+    #ifdef _WINDOWS
+        tlWinGMTime_r(clock, timeInfoPtr);
+    #else
+        gmtime_r(clock, timeInfoPtr);
+    #endif
+}
+
+time_t tlMKTime(struct tm* timeInfoPtr, bool inLocalTimeZone)
+{
+    time_t      the_time = 0;
+
+    #ifdef _WINDOWS
+        the_time = tlWinMKTime(timeInfoPtr);
+
+        if (!inLocalTimeZone)
+        {
+            // Adjust for time zone offset
+            the_time += (tlLocalTimeZoneDiffIn100nsIntervals() / _onesec_in100ns);
+        }
+    #else
+        // Get the initial time components; note that mktime assumes local time
+        the_time = mktime(timeInfoPtr);
+
+        if (!inLocalTimeZone)
+        {
+            // Adjust for time zone offset
+            the_time += timeInfoPtr->tm_gmtoff;
+        }
+    #endif
+
+    return the_time;
+}
+
+//------------------------------------------------------------------------------
+
+void tlMakeTimeStructFromUTCSeconds(time_t seconds, struct tm* timeInfo)
+{
+    tlGMTime_r(&seconds, timeInfo);
+}
+
 void tlInsertDateIntoTimeStruct(struct tm* timeInfo, unsigned int date)
 {
     unsigned int    year = date / 10000;
@@ -142,12 +423,12 @@ unsigned int tlExtractTimeFromTimeStruct(struct tm* timeInfo)
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API time_t TIMELIB_CALL tlSecondsFromParts(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, bool is_local_time)
+TIMELIB_API __int64 TIMELIB_CALL tlSecondsFromParts(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, bool is_local_time)
 {
     struct tm       timeInfo;
     time_t          the_time = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
     // Push each time part value into the tm struct
     timeInfo.tm_sec = second;
@@ -157,21 +438,14 @@ TIMELIB_API time_t TIMELIB_CALL tlSecondsFromParts(int year, unsigned int month,
     timeInfo.tm_mon = month - 1;
     timeInfo.tm_year = year - 1900;
 
-    // Get the initial time components; note that mktime assumes local time
-    the_time = mktime(&timeInfo);
+    the_time = tlMKTime(&timeInfo, is_local_time);
 
-    if (!is_local_time)
-    {
-        // Adjust for time zone offset
-        the_time += timeInfo.tm_gmtoff;
-    }
-
-    return the_time;
+    return static_cast<__int64>(the_time);
 }
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API void TIMELIB_CALL tlSecondsToParts(size32_t &__lenResult, void* &__result, time_t seconds)
+TIMELIB_API void TIMELIB_CALL tlSecondsToParts(size32_t &__lenResult, void* &__result, __int64 seconds)
 {
     struct tm       timeInfo;
 
@@ -196,18 +470,34 @@ TIMELIB_API void TIMELIB_CALL tlSecondsToParts(size32_t &__lenResult, void* &__r
 
 TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfYear(short year, unsigned short month, unsigned short day)
 {
-    struct tm       timeInfo;
+    unsigned int    dayOfYear = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    #ifdef _WINDOWS
+        SYSTEMTIME  s;
 
-    // Push each time part value into the tm struct
-    timeInfo.tm_mday = day;
-    timeInfo.tm_mon = month - 1;
-    timeInfo.tm_year = year - 1900;
+        memset(&s, 0, sizeof(s));
+
+        s.wYear = year;
+        s.wMonth = month;
+        s.wDay = day;
+
+        dayOfYear = tlYearDayFromSystemTime(&s);
+    #else
+        struct tm       timeInfo;
+
+        memset(&timeInfo, 0, sizeof(timeInfo));
 
-    mktime(&timeInfo);
+        // Push each time part value into the tm struct
+        timeInfo.tm_mday = day;
+        timeInfo.tm_mon = month - 1;
+        timeInfo.tm_year = year - 1900;
 
-    return timeInfo.tm_yday;
+        tlMKTime(&timeInfo);
+
+        dayOfYear = timeInfo.tm_yday;
+    #endif
+
+    return dayOfYear;
 }
 
 //------------------------------------------------------------------------------
@@ -216,14 +506,14 @@ TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfWeek(short year, unsigned short
 {
     struct tm       timeInfo;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
     // Push each time part value into the tm struct
     timeInfo.tm_mday = day;
     timeInfo.tm_mon = month - 1;
     timeInfo.tm_year = year - 1900;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     return timeInfo.tm_wday;
 }
@@ -236,16 +526,16 @@ TIMELIB_API void TIMELIB_CALL tlDateToString(size32_t &__lenResult, char* &__res
     size_t          kBufferSize = 256;
     char            buffer[kBufferSize];
 
-    memset(&timeInfo,0,sizeof(timeInfo));
-    tlInsertDateIntoTimeStruct(&timeInfo,date);
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
 
-    __lenResult = strftime(buffer,kBufferSize,format,&timeInfo);
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
     __result = NULL;
 
     if (__lenResult > 0)
     {
         __result = reinterpret_cast<char*>(CTXMALLOC(parentCtx, __lenResult));
-        memcpy(__result,buffer,__lenResult);
+        memcpy(__result, buffer, __lenResult);
     }
 }
 
@@ -257,44 +547,39 @@ TIMELIB_API void TIMELIB_CALL tlTimeToString(size32_t &__lenResult, char* &__res
     size_t          kBufferSize = 256;
     char            buffer[kBufferSize];
 
-    memset(&timeInfo,0,sizeof(timeInfo));
-    tlInsertTimeIntoTimeStruct(&timeInfo,time);
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
 
-    __lenResult = strftime(buffer,kBufferSize,format,&timeInfo);
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
     __result = NULL;
 
     if (__lenResult > 0)
     {
         __result = reinterpret_cast<char*>(rtlMalloc(__lenResult));
-        memcpy(__result,buffer,__lenResult);
+        memcpy(__result, buffer, __lenResult);
     }
 }
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API void TIMELIB_CALL tlSecondsToString(size32_t &__lenResult, char* &__result, int seconds, const char* format)
+TIMELIB_API void TIMELIB_CALL tlSecondsToString(size32_t &__lenResult, char* &__result, __int64 seconds, const char* format)
 {
     struct tm   timeInfo;
     time_t      theTime = seconds;
     size_t      kBufferSize = 256;
     char        buffer[kBufferSize];
 
-    memset(buffer,kBufferSize,0);
+    memset(buffer, kBufferSize, 0);
 
-    #ifdef _WINDOWS
-        // gmtime is thread-safe under Windows
-        memcpy(&timeInfo,gmtime(&theTime),sizeof(timeInfo));
-    #else
-        gmtime_r(&theTime,&timeInfo);
-    #endif
+    tlGMTime_r(&theTime, &timeInfo);
 
-    __lenResult = strftime(buffer,kBufferSize,format,&timeInfo);
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
     __result = NULL;
 
     if (__lenResult > 0)
     {
         __result = reinterpret_cast<char*>(rtlMalloc(__lenResult));
-        memcpy(__result,buffer,__lenResult);
+        memcpy(__result, buffer, __lenResult);
     }
 }
 
@@ -305,15 +590,15 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDate(unsigned int date, short year
     struct tm       timeInfo;
     unsigned int    result = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
-    tlInsertDateIntoTimeStruct(&timeInfo,date);
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
 
     timeInfo.tm_year += year_delta;
     timeInfo.tm_mon += month_delta;
     timeInfo.tm_mday += day_delta;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     result = tlExtractDateFromTimeStruct(&timeInfo);
 
@@ -327,12 +612,12 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDateBySeconds(unsigned int date, i
     struct tm       timeInfo;
     unsigned int    result = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
-    tlInsertDateIntoTimeStruct(&timeInfo,date);
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
     timeInfo.tm_sec = seconds_delta;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     result = tlExtractDateFromTimeStruct(&timeInfo);
 
@@ -346,15 +631,15 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTime(unsigned int time, short hour
     struct tm       timeInfo;
     unsigned int    result = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
-    tlInsertTimeIntoTimeStruct(&timeInfo,time);
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
 
     timeInfo.tm_hour += hour_delta;
     timeInfo.tm_min += minute_delta;
     timeInfo.tm_sec += second_delta;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     result = tlExtractTimeFromTimeStruct(&timeInfo);
 
@@ -368,12 +653,12 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTimeBySeconds(unsigned int time, i
     struct tm       timeInfo;
     unsigned int    result = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
-    tlInsertTimeIntoTimeStruct(&timeInfo,time);
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
     timeInfo.tm_sec += seconds_delta;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     result = tlExtractTimeFromTimeStruct(&timeInfo);
 
@@ -382,17 +667,13 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTimeBySeconds(unsigned int time, i
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API time_t TIMELIB_CALL tlAdjustSeconds(time_t seconds, short year_delta, int month_delta, int day_delta, short hour_delta, int minute_delta, int second_delta)
+TIMELIB_API __int64 TIMELIB_CALL tlAdjustSeconds(__int64 seconds, short year_delta, int month_delta, int day_delta, short hour_delta, int minute_delta, int second_delta)
 {
     struct tm       timeInfo;
+    time_t          theTime = seconds;
     time_t          result = 0;
 
-    #ifdef _WINDOWS
-        // localtime is thread-safe under Windows
-        memcpy(&timeInfo,localtime(&seconds),sizeof(timeInfo));
-    #else
-        localtime_r(&seconds,&timeInfo);
-    #endif
+    tlLocalTime_r(&theTime, &timeInfo);
 
     timeInfo.tm_year += year_delta;
     timeInfo.tm_mon += month_delta;
@@ -401,9 +682,9 @@ TIMELIB_API time_t TIMELIB_CALL tlAdjustSeconds(time_t seconds, short year_delta
     timeInfo.tm_min += minute_delta;
     timeInfo.tm_sec += second_delta;
 
-    result = mktime(&timeInfo);
+    result = theTime = tlMKTime(&timeInfo);
 
-    return result;
+    return static_cast<__int64>(result);
 }
 
 //------------------------------------------------------------------------------
@@ -427,7 +708,7 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustCalendar(unsigned int date, short
         expectedMonthVal = 12 - (abs(expectedMonthVal) % 12);
     }
 
-    memset(&timeInfo,0,sizeof(timeInfo));
+    memset(&timeInfo, 0, sizeof(timeInfo));
 
     timeInfo.tm_year = year - 1900;
     timeInfo.tm_mon = month - 1;
@@ -436,19 +717,22 @@ TIMELIB_API unsigned int TIMELIB_CALL tlAdjustCalendar(unsigned int date, short
     timeInfo.tm_year += year_delta;
     timeInfo.tm_mon += month_delta;
 
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     if (timeInfo.tm_mon != expectedMonthVal)
     {
         // If the returned month doesn't match the expected month, we need to
         // go back to the last day of the previous month
         timeInfo.tm_mday = 0;
-        mktime(&timeInfo);
+        tlMKTime(&timeInfo);
     }
 
-    // Now apply the day delta
-    timeInfo.tm_mday += day_delta;
-    mktime(&timeInfo);
+    if (day_delta != 0)
+    {
+        // Now apply the day delta
+        timeInfo.tm_mday += day_delta;
+        tlMKTime(&timeInfo);
+    }
 
     result = tlExtractDateFromTimeStruct(&timeInfo);
 
@@ -462,12 +746,7 @@ TIMELIB_API bool TIMELIB_CALL tlIsLocalDaylightSavingsInEffect()
     struct tm       timeInfo;
     time_t          theTime = time(NULL);
 
-    #ifdef _WINDOWS
-        // localtime is thread-safe under Windows
-        memcpy(&timeInfo,localtime(&theTime),sizeof(timeInfo));
-    #else
-        localtime_r(&theTime,&timeInfo);
-    #endif
+    tlLocalTime_r(&theTime, &timeInfo);
 
     return (timeInfo.tm_isdst == 1);
 }
@@ -476,17 +755,20 @@ TIMELIB_API bool TIMELIB_CALL tlIsLocalDaylightSavingsInEffect()
 
 TIMELIB_API int TIMELIB_CALL tlLocalTimeZoneOffset()
 {
-    struct tm       timeInfo;
-    time_t          theTime = time(NULL);
+    int     offset = 0;
 
     #ifdef _WINDOWS
-        // localtime is thread-safe under Windows
-        memcpy(&timeInfo,localtime(&theTime),sizeof(timeInfo));
+        offset = tlLocalTimeZoneDiffIn100nsIntervals() / _onesec_in100ns;
     #else
-        localtime_r(&theTime,&timeInfo);
+        struct tm       timeInfo;
+        time_t          theTime = time(NULL);
+
+        tlLocalTime_r(&theTime, &timeInfo);
+
+        offset = timeInfo.tm_gmtoff;
     #endif
 
-    return timeInfo.tm_gmtoff;
+    return offset;
 }
 
 //------------------------------------------------------------------------------
@@ -501,21 +783,11 @@ TIMELIB_API unsigned int TIMELIB_CALL tlCurrentDate(bool in_local_time)
     // UTC or local time
     if (in_local_time)
     {
-        #ifdef _WINDOWS
-            // localtime is thread-safe under Windows
-            memcpy(&timeInfo,localtime(&theTime),sizeof(timeInfo));
-        #else
-            localtime_r(&theTime,&timeInfo);
-        #endif
+        tlLocalTime_r(&theTime, &timeInfo);
     }
     else
     {
-        #ifdef _WINDOWS
-            // gmtime is thread-safe under Windows
-            memcpy(&timeInfo,gmtime(&theTime),sizeof(timeInfo));
-        #else
-            gmtime_r(&theTime,&timeInfo);
-        #endif
+        tlGMTime_r(&theTime, &timeInfo);
     }
 
     result = tlExtractDateFromTimeStruct(&timeInfo);
@@ -535,21 +807,11 @@ TIMELIB_API unsigned int TIMELIB_CALL tlCurrentTime(bool in_local_time)
     // UTC or local time
     if (in_local_time)
     {
-        #ifdef _WINDOWS
-            // localtime is thread-safe under Windows
-            memcpy(&timeInfo,localtime(&theTime),sizeof(timeInfo));
-        #else
-            localtime_r(&theTime,&timeInfo);
-        #endif
+        tlLocalTime_r(&theTime, &timeInfo);
     }
     else
     {
-        #ifdef _WINDOWS
-            // gmtime is thread-safe under Windows
-            memcpy(&timeInfo,gmtime(&theTime),sizeof(timeInfo));
-        #else
-            gmtime_r(&theTime,&timeInfo);
-        #endif
+        tlGMTime_r(&theTime, &timeInfo);
     }
 
     result = tlExtractTimeFromTimeStruct(&timeInfo);
@@ -559,7 +821,7 @@ TIMELIB_API unsigned int TIMELIB_CALL tlCurrentTime(bool in_local_time)
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API time_t TIMELIB_CALL tlCurrentSeconds(bool in_local_time)
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentSeconds(bool in_local_time)
 {
     time_t    result = time(NULL);
 
@@ -568,33 +830,33 @@ TIMELIB_API time_t TIMELIB_CALL tlCurrentSeconds(bool in_local_time)
         result += tlLocalTimeZoneOffset();
     }
 
-    return result;
+    return static_cast<__int64>(result);
 }
 
 //------------------------------------------------------------------------------
 
-TIMELIB_API double TIMELIB_CALL tlCurrentTimestamp(bool in_local_time)
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentTimestamp(bool in_local_time)
 {
-    double          result = 0.0;
+    __int64     result = 0;
 
     #ifdef _WINDOWS
         struct _timeb   now;
 
         _ftime(&now);
 
-        result = now.time + (now.millitm / 1000.0);
+        result = (now.time * 1000000) + now.(millitm * 1000);
     #else
         struct timeval  tv;
 
-        if (gettimeofday(&tv,NULL) == 0)
+        if (gettimeofday(&tv, NULL) == 0)
         {
-            result = tv.tv_sec + (tv.tv_usec / 1000000.0);
+            result = (tv.tv_sec * 1000000) + tv.tv_usec;
         }
     #endif
 
     if (in_local_time)
     {
-        result += tlLocalTimeZoneOffset();
+        result += (static_cast<long>(tlLocalTimeZoneOffset()) * 1000000);
     }
 
     return result;
@@ -607,16 +869,16 @@ TIMELIB_API unsigned int TIMELIB_CALL tlGetLastDayOfMonth(unsigned int date)
     struct tm       timeInfo;
     unsigned int    result = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
-    tlInsertDateIntoTimeStruct(&timeInfo,date);
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
 
     // Call mktime once to fix up any bogus data
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     // Adjust and call again
     timeInfo.tm_mon += 1;
     timeInfo.tm_mday = 0;
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     result = tlExtractDateFromTimeStruct(&timeInfo);
 
@@ -631,21 +893,21 @@ TIMELIB_API void TIMELIB_CALL tlDatesForWeek(size32_t &__lenResult, void* &__res
     unsigned int    weekStartResult = 0;
     unsigned int    weekEndResult = 0;
 
-    memset(&timeInfo,0,sizeof(timeInfo));
-    tlInsertDateIntoTimeStruct(&timeInfo,date);
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
 
     // Call mktime once to fix up any bogus data
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     // Adjust and call again
     timeInfo.tm_mday -= timeInfo.tm_wday;
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     weekStartResult = tlExtractDateFromTimeStruct(&timeInfo);
 
     // Adjust to the beginning of the week
     timeInfo.tm_mday += 6;
-    mktime(&timeInfo);
+    tlMKTime(&timeInfo);
 
     weekEndResult = tlExtractDateFromTimeStruct(&timeInfo);
 

+ 10 - 6
plugins/timelib/timelib.hpp

@@ -48,25 +48,29 @@ unsigned int tlExtractDateFromTimeStruct(struct tm* timeInfo);
 void tlInsertTimeIntoTimeStruct(struct tm* timeInfo, unsigned int time);
 unsigned int tlExtractTimeFromTimeStruct(struct tm* timeInfo);
 
-TIMELIB_API time_t TIMELIB_CALL tlSecondsFromParts(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, bool is_local_time = false);
-TIMELIB_API void TIMELIB_CALL tlSecondsToParts(size32_t &__lenResult, void* &__result, time_t seconds);
+void tlLocalTime_r(const time_t* clock, struct tm* timeInfoPtr);
+void tlGMTime_r(const time_t* clock, struct tm* timeInfoPtr);
+time_t tlMKTime(struct tm* timeInfoPtr, bool inLocalTimeZone = true);
+
+TIMELIB_API __int64 TIMELIB_CALL tlSecondsFromParts(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, bool is_local_time = false);
+TIMELIB_API void TIMELIB_CALL tlSecondsToParts(size32_t &__lenResult, void* &__result, __int64 seconds);
 TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfYear(short year, unsigned short month, unsigned short day);
 TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfWeek(short year, unsigned short month, unsigned short day);
 TIMELIB_API void TIMELIB_CALL tlDateToString(size32_t &__lenResult, char* &__result, unsigned int date, const char* format);
 TIMELIB_API void TIMELIB_CALL tlTimeToString(size32_t &__lenResult, char* &__result, unsigned int time, const char* format);
 TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDate(unsigned int date, short year_delta, int month_delta, int day_delta);
-TIMELIB_API void TIMELIB_CALL tlSecondsToString(size32_t &__lenResult, char* &__result, int seconds, const char* format);
+TIMELIB_API void TIMELIB_CALL tlSecondsToString(size32_t &__lenResult, char* &__result, __int64 seconds, const char* format);
 TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDateBySeconds(unsigned int date, int seconds_delta);
 TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTime(unsigned int time, short hour_delta, int minute_delta, int second_delta);
 TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTimeBySeconds(unsigned int time, int seconds_delta);
-TIMELIB_API time_t TIMELIB_CALL tlAdjustSeconds(time_t seconds, short year_delta, int month_delta, int day_delta, short hour_delta, int minute_delta, int second_delta);
+TIMELIB_API __int64 TIMELIB_CALL tlAdjustSeconds(__int64 seconds, short year_delta, int month_delta, int day_delta, short hour_delta, int minute_delta, int second_delta);
 TIMELIB_API unsigned int TIMELIB_CALL tlAdjustCalendar(unsigned int date, short year_delta, int month_delta, int day_delta);
 TIMELIB_API bool TIMELIB_CALL tlIsLocalDaylightSavingsInEffect();
 TIMELIB_API int TIMELIB_CALL tlLocalTimeZoneOffset();
 TIMELIB_API unsigned int TIMELIB_CALL tlCurrentDate(bool in_local_time);
 TIMELIB_API unsigned int TIMELIB_CALL tlCurrentTime(bool in_local_time);
-TIMELIB_API time_t TIMELIB_CALL tlCurrentSeconds(bool in_local_time);
-TIMELIB_API double TIMELIB_CALL tlCurrentTimestamp(bool in_local_time);
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentSeconds(bool in_local_time);
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentTimestamp(bool in_local_time);
 TIMELIB_API unsigned int TIMELIB_CALL tlGetLastDayOfMonth(unsigned int date);
 TIMELIB_API void TIMELIB_CALL tlDatesForWeek(size32_t &__lenResult, void* &__result, unsigned int date);