Browse Source

Merge pull request #6699 from dcamper/std.date_rev

HPCC-8572 Std.Date functionality

Reviewed-by: Gavin Halliday <ghalliday@hpccsystems.com>
Gavin Halliday 10 years ago
parent
commit
a6394359cf

File diff suppressed because it is too large
+ 1008 - 116
ecllibrary/std/Date.ecl


+ 199 - 24
ecllibrary/teststd/Date/TestDate.ecl

@@ -6,33 +6,208 @@ IMPORT Std.Date;
 
 EXPORT TestDate := MODULE
 
+  SHARED vCreateDateTimeFromSeconds := ROW(Date.CreateDateTimeFromSeconds(917872496)); // Feb 1, 1999 @ 12:34:56
+  SHARED vCreateDateFromSeconds := ROW(Date.CreateDateFromSeconds(917872496)); // Feb 1, 1999
+  SHARED vCreateTimeFromSeconds := ROW(Date.CreateTimeFromSeconds(917872496)); // 12:34:56
+  SHARED vCreateTime := ROW(Date.CreateTime(12,34,56)); // 12:34:56
+  SHARED vCreateDateTime := ROW(Date.CreateDateTime(1999,2,1,12,34,56)); // Feb 1, 1999 @ 12:34:56
+  SHARED vDate := Date.CurrentDate(); // UTC
+  SHARED vToday := Date.Today(); // Local
+  SHARED vTime := Date.CurrentTime(); // UTC
+  SHARED vTimeLocal := Date.CurrentTime(TRUE); // Local
+  SHARED vSeconds := Date.CurrentSeconds(); // UTC
+  SHARED vSecondsLocal := Date.CurrentSeconds(TRUE); // Local
+  SHARED vIndTimestamp := Date.CurrentTimestamp() : INDEPENDENT; // UTC, evaluated before all others
+  SHARED vTimestamp := Date.CurrentTimestamp(); // UTC
+  SHARED vTimestampLocal := Date.CurrentTimestamp(TRUE); // Local
+  SHARED vLocalTimeZoneOffset := Date.LocalTimeZoneOffset();
+
   EXPORT TestConstant := [
-    ASSERT(NOT date.IsLeapYear(1900), CONST);
-    ASSERT(date.IsLeapYear(1904), CONST);
-    ASSERT(NOT date.IsLeapYear(2100), CONST);
-    ASSERT(date.IsLeapYear(2000), CONST);
-    ASSERT(NOT date.IsLeapYear(1901), CONST);
-    ASSERT(date.FromDaysSince1900(0) = 19000101, CONST);
-    ASSERT(date.ToGregorianDate(1) = 00010101, CONST);
-    ASSERT(date.DaysSince1900(1900,1,1)=0, CONST);
-    ASSERT(date.FromGregorianYMD(1,1,1)=1, CONST);
-    ASSERT(date.ToJulianDate(1) = 00010101, CONST);
-    ASSERT(date.FromJulianYMD(1,1,1)=1, CONST);
-    ASSERT(date.MonthsBetween(19700101,19701231)=11, CONST);
-    ASSERT(date.MonthsBetween(19701231,19710101)=0, CONST);
-    ASSERT(date.MonthsBetween(19701231,19711231)=12, CONST);
-    ASSERT(date.MonthsBetween(19711231,19701231)=-12, CONST);
-    ASSERT(date.MonthsBetween(19700606,19700706)=1, CONST);
-    ASSERT(date.MonthsBetween(19700606,19700705)=0, CONST);
-    ASSERT(date.MonthsBetween(19700606,19700607)=0, CONST);
+    ASSERT(Date.FromDaysSince1900(0) = 19000101, CONST);
+    ASSERT(Date.ToGregorianDate(1) = 00010101, CONST);
+    ASSERT(Date.DaysSince1900(1900,1,1)=0, CONST);
+    ASSERT(Date.FromGregorianYMD(1,1,1)=1, CONST);
+    ASSERT(Date.ToJulianDate(1) = 00010101, CONST);
+    ASSERT(Date.FromJulianYMD(1,1,1)=1, CONST);
+
+    ASSERT(Date.Year(19990201) = 1999, CONST);
+    ASSERT(Date.Month(19990201) = 2, CONST);
+    ASSERT(Date.Day(19990201) = 1, CONST);
+
+    ASSERT(Date.Hour(123456) = 12, CONST);
+    ASSERT(Date.Minute(123456) = 34, CONST);
+    ASSERT(Date.Second(123456) = 56, CONST);
+
+    ASSERT(Date.DateFromParts(1999,2,1) = 19990201, CONST);
+    ASSERT(Date.TimeFromParts(12,34,56) = 123456, CONST);
+
+    ASSERT(NOT Date.IsLeapYear(1900), CONST);
+    ASSERT(Date.IsLeapYear(1904), CONST);
+    ASSERT(NOT Date.IsLeapYear(2100), CONST);
+    ASSERT(Date.IsLeapYear(2000), CONST);
+    ASSERT(NOT Date.IsLeapYear(1901), CONST);
+
+    ASSERT(Date.IsDateLeapYear(20000201) = TRUE, CONST);
+    ASSERT(Date.IsDateLeapYear(20010201) = FALSE, CONST);
+    ASSERT(Date.IsDateLeapYear(21000201) = FALSE, CONST);
+
+    ASSERT(Date.IsJulianLeapYear(2000) = TRUE, CONST);
+    ASSERT(Date.IsJulianLeapYear(2001) = FALSE, CONST);
+
+    ASSERT(Date.YearsBetween(20010615,20020615) = 1, CONST);
+    ASSERT(Date.YearsBetween(20010615,20020614) = 0, CONST);
+    ASSERT(Date.YearsBetween(20020615,20010615) = -1, CONST);
+
+    ASSERT(Date.MonthsBetween(19700101,19701231)=11, CONST);
+    ASSERT(Date.MonthsBetween(19701231,19710101)=0, CONST);
+    ASSERT(Date.MonthsBetween(19701231,19711231)=12, CONST);
+    ASSERT(Date.MonthsBetween(19711231,19701231)=-12, CONST);
+    ASSERT(Date.MonthsBetween(19700606,19700706)=1, CONST);
+    ASSERT(Date.MonthsBetween(19700606,19700705)=0, CONST);
+    ASSERT(Date.MonthsBetween(19700606,19700607)=0, CONST);
+
+    ASSERT(Date.DaysBetween(20010615,20020615) = 365, CONST);
+    ASSERT(Date.DaysBetween(20010615,20020614) = 364, CONST);
+    ASSERT(Date.DaysBetween(20020615,20010615) = -365, CONST);
+
     ASSERT(TRUE, CONST)
   ];
 
+  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);
+    ASSERT(Date.SecondsToParts(917872496).day = 1);
+    ASSERT(Date.SecondsToParts(917872496).hour = 12);
+    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.DayOfYear(20000101) = 1);
+    ASSERT(Date.DayOfYear(20001231) = 366);
+    ASSERT(Date.DayOfYear(20011231) = 365);
+
+    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);
+
+    ASSERT(Date.AdjustTime(180000, hour_delta:=7) = 010000);
+    ASSERT(Date.AdjustTime(180000, minute_delta:=420) = 010000);
+    ASSERT(Date.AdjustTime(180000, second_delta:=-86400) = 180000);
+
+    ASSERT(Date.AdjustTimeBySeconds(180000, 86400) = 180000);
+    ASSERT(Date.AdjustTimeBySeconds(180000, -86400) = 180000);
+
+    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(vSeconds + vLocalTimeZoneOffset = vSecondsLocal);
+
+    ASSERT(vTimestamp != vIndTimestamp); // Test for non-pure calls to C++ code
+
+    ASSERT(Date.TimestampToSeconds(vTimestamp) = vSeconds);
+    ASSERT(Date.TimestampToSeconds(vTimestampLocal) = vSecondsLocal);
+
+    // IsLocalDaylightSavingsInEffect() -- not possible to check without pinning both cluster location and date
+
+    ASSERT(Date.DatesForMonth(20141215).startDate = 20141201);
+    ASSERT(Date.DatesForMonth(20141215).endDate = 20141231);
+
+    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); // 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); // 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);
+    ASSERT(vCreateDateTimeFromSeconds.day = 1);
+    ASSERT(vCreateDateTimeFromSeconds.hour = 12);
+    ASSERT(vCreateDateTimeFromSeconds.minute = 34);
+    ASSERT(vCreateDateTimeFromSeconds.second = 56);
+
+    ASSERT(vCreateDateFromSeconds.year = 1999);
+    ASSERT(vCreateDateFromSeconds.month = 2);
+    ASSERT(vCreateDateFromSeconds.day = 1);
+
+    ASSERT(vCreateTimeFromSeconds.hour = 12);
+    ASSERT(vCreateTimeFromSeconds.minute = 34);
+    ASSERT(vCreateTimeFromSeconds.second = 56);
+
+    ASSERT(vCreateTime.hour = 12);
+    ASSERT(vCreateTime.minute = 34);
+    ASSERT(vCreateTime.second = 56);
+
+    ASSERT(vCreateDateTime.year = 1999);
+    ASSERT(vCreateDateTime.month = 2);
+    ASSERT(vCreateDateTime.day = 1);
+    ASSERT(vCreateDateTime.hour = 12);
+    ASSERT(vCreateDateTime.minute = 34);
+    ASSERT(vCreateDateTime.second = 56);
+
+    ASSERT(Date.TimeFromTimeRec(vCreateTime) = 123456);
+
+    ASSERT(Date.DateFromDateTimeRec(vCreateDateTime) = 19990201);
+
+    ASSERT(Date.TimeFromDateTimeRec(vCreateDateTime) = 123456);
+
+    ASSERT(Date.SecondsFromDateTimeRec(vCreateDateTime) = 917872496);
+
+    ASSERT(TRUE)
+  ];
+
   EXPORT TestDynamic := MODULE
     //Iterate through all lots of dates, incrementing the day and the date to check they convert correctly.
     Date_rec := Date.Date_rec;
     test_rec := { Date.Days_t day, Date_rec gregorian, Date_rec julian };
-    firstDate := DATASET([{ 1, ROW(Date.createDate(1,1,1)), ROW(Date.createDate(1,1,1))}], test_rec);
+    firstDate := DATASET([{ 1, ROW(Date.CreateDate(1,1,1)), ROW(Date.CreateDate(1,1,1))}], test_rec);
     daysInLeapYearMonth := [31,29,31,30,31,30,31,31,30,31,30,31];
     daysInNonLeapYearMonth := [31,28,31,30,31,30,31,31,30,31,30,31];
 
@@ -59,10 +234,10 @@ EXPORT TestDate := MODULE
 
     processNextDate(DATASET(test_rec) in) := FUNCTION
        next := PROJECT(in, nextRecord(LEFT));
-       result1 := ASSERT(next, Date.ToGregorianDate(next.day) = Date.DateFromRec(next.gregorian));
-       result2 := ASSERT(result1, next.day = Date.FromGregorianDate(Date.DateFromRec(next.gregorian)));
-       result3 := ASSERT(result2, Date.ToJulianDate(next.day) = Date.DateFromRec(next.julian));
-       result4 := ASSERT(result3, next.day = Date.FromJulianDate(Date.DateFromRec(next.julian)));
+       result1 := ASSERT(next, Date.ToGregorianDate(next.day) = Date.DateFromDateRec(next.gregorian));
+       result2 := ASSERT(result1, next.day = Date.FromGregorianDate(Date.DateFromDateRec(next.gregorian)));
+       result3 := ASSERT(result2, Date.ToJulianDate(next.day) = Date.DateFromDateRec(next.julian));
+       result4 := ASSERT(result3, next.day = Date.FromJulianDate(Date.DateFromDateRec(next.julian)));
        RETURN result4;
     END;
 
@@ -71,6 +246,6 @@ EXPORT TestDate := MODULE
     EXPORT Test01 := OUTPUT(x);
   END;
 
-  EXPORT Main := [EVALUATE(TestConstant), EVALUATE(TestDynamic)];
+  EXPORT Main := [EVALUATE(TestConstant), EVALUATE(TestDynamicFunctions), EVALUATE(TestDynamic)];
 
 END;

+ 57 - 24
ecllibrary/teststd/Date/TestFormat.ecl

@@ -6,36 +6,69 @@ IMPORT Std.Date;
 
 EXPORT TestFormat := MODULE
 
-  SHARED DateFormats := ['%d %b %Y', '%Y %b %d', '%Y%m%d', '%Y-%m-%d', '%d/%m/%Y'];
+  SHARED DateFormats := ['%d %b %Y', '%Y %b %d', '%Y%m%d', '%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y'];
+  SHARED TimeFormats := ['%H%M%S', '%T', '%R'];
 
   EXPORT TestConstant := [
-    ASSERT(date.ToString(19700101, '%Y-%m-%d') = '1970-01-01', CONST);
-    ASSERT(date.ToString(19700101, '%d/%m/%y') = '01/01/70', CONST);
-    ASSERT(date.ToString(20110302, '%d %b %Y') = '02 Mar 2011', CONST);
-    ASSERT(date.ToString(20111202, '%d %B %Y') = '02 December 2011', CONST);
-
-    ASSERT(date.FromString('19700001', '%Y%m%d') = 0, CONST);
-    ASSERT(date.FromString('19701000', '%Y%m%d') = 0, CONST);
-    ASSERT(date.FromString('19700101', '%Y%m%d') = 19700101, CONST);
-    ASSERT(date.FromString('68011', '%y%m%d') = 20680101, CONST);
-    ASSERT(date.FromString('69011', '%y%m%d') = 19690101, CONST);
-    ASSERT(date.FromString('1 \t Dec   2056', '%d %b %Y') = 20561201, CONST);
-    ASSERT(date.FromString('1 \t December  1862', '%d %b %Y') = 18621201, CONST);
-    ASSERT(date.FromString('31 \t jAN 12', '%d %b %Y') = 120131, CONST);
-    ASSERT(date.FromString('1 \t De   2056', '%d %b %Y') = 0, CONST);
-    ASSERT(date.FromString('1December1', '%d%b%Y') = 00011201, CONST);
-//    ASSERT(date.MatchDateString('1dec2011',DateFormats) = 20111201, CONST);
+    ASSERT(Date.FromStringToDate('19700001', '%Y%m%d') = 0, CONST);
+    ASSERT(Date.FromStringToDate('19701000', '%Y%m%d') = 0, CONST);
+    ASSERT(Date.FromStringToDate('19700101', '%Y%m%d') = 19700101, CONST);
+    ASSERT(Date.FromStringToDate('68011', '%y%m%d') = 20680101, CONST);
+    ASSERT(Date.FromStringToDate('69011', '%y%m%d') = 19690101, CONST);
+    ASSERT(Date.FromStringToDate('1 \t Dec   2056', '%d %b %Y') = 20561201, CONST);
+    ASSERT(Date.FromStringToDate('1 \t December  1862', '%d %b %Y') = 18621201, CONST);
+    ASSERT(Date.FromStringToDate('31 \t jAN 12', '%d %b %Y') = 120131, CONST);
+    ASSERT(Date.FromStringToDate('1 \t De   2056', '%d %b %Y') = 0, CONST);
+    ASSERT(Date.FromStringToDate('1December1', '%d%b%Y') = 00011201, CONST);
+    ASSERT(Date.FromStringToDate('1970-02-01', '%F') = 19700201, CONST);
+
+    ASSERT(Date.FromStringToTime('12:34:56', '%H:%M:%S') = 123456, CONST);
+    ASSERT(Date.FromStringToTime('12:34:56', '%T') = 123456, CONST);
+    ASSERT(Date.FromStringToTime('12:34', '%R') = 123400, CONST);
+
     ASSERT(TRUE)
   ];
 
   EXPORT TestDynamic := [
-    ASSERT(date.MatchDateString('1dec2011',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('2011dec1',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('1 december 2011',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('2011\tdecem\t01',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('20111201',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('2011-12-01',DateFormats) = 20111201);
-    ASSERT(date.MatchDateString('1/12/2011',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('1dec2011',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011dec1',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('1 december 2011',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011\tdecem\t01',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('20111201',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011-12-01',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('1/12/2011',DateFormats) = 20111201);
+
+    ASSERT(Date.DateToString(19700101, '%Y-%m-%d') = '1970-01-01');
+    ASSERT(Date.DateToString(19700101, '%d/%m/%y') = '01/01/70');
+    ASSERT(Date.DateToString(20110302, '%d %b %Y') = '02 Mar 2011');
+    ASSERT(Date.DateToString(20111202, '%d %B %Y') = '02 December 2011');
+
+    ASSERT(Date.MatchDateString('1dec2011',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011dec1',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('1 december 2011',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011\tdecem\t01',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('20111201',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('2011-12-01',DateFormats) = 20111201);
+    ASSERT(Date.MatchDateString('1/12/2011',DateFormats) = 20111201);
+
+    ASSERT(Date.MatchTimeString('123456',TimeFormats) = 123456);
+    ASSERT(Date.MatchTimeString('12:34:56',TimeFormats) = 123456);
+    ASSERT(Date.MatchTimeString('12:34',TimeFormats) = 123400);
+
+    ASSERT(Date.DateToString(19990201,'%F') = '1999-02-01');
+
+    ASSERT(Date.TimeToString(123456,'%T') = '12:34:56');
+
+    ASSERT(Date.SecondsToString(917872496,'%FT%T') = '1999-02-01T12:34:56');
+
+    ASSERT(Date.ConvertDateFormat('1/12/2011','%m/%d/%Y','%F') = '2011-01-12');
+
+    ASSERT(Date.ConvertTimeFormat('123456','%H%M%S','%T') = '12:34:56');
+
+    ASSERT(Date.ConvertDateFormatMultiple('1/31/2011',DateFormats,'%F') = '2011-01-31');
+
+    ASSERT(Date.ConvertTimeFormatMultiple('123456',TimeFormats,'%T') = '12:34:56');
+
     ASSERT(TRUE)
   ];
 

+ 1 - 0
plugins/CMakeLists.txt

@@ -19,6 +19,7 @@ add_subdirectory (fileservices)
 add_subdirectory (logging)
 add_subdirectory (parselib)
 add_subdirectory (stringlib)
+add_subdirectory (timelib)
 add_subdirectory (unicodelib)
 add_subdirectory (workunitservices)
 add_subdirectory (proxies)

+ 88 - 2
plugins/stringlib/stringlib.cpp

@@ -83,7 +83,9 @@ static const char * EclDefinition =
 "  SET OF STRING SplitWords(const string src, const string _separator, BOOLEAN allow_blanks) : c, pure,entrypoint='slSplitWords'; \n"
 "  STRING CombineWords(set of string src, const string _separator) : c, pure,entrypoint='slCombineWords'; \n"
 "  UNSIGNED4 StringToDate(const string src, const varstring format) : c, pure,entrypoint='slStringToDate'; \n"
+"  UNSIGNED4 StringToTimeOfDay(const string src, const varstring format) : c, pure,entrypoint='slStringToTimeOfDay'; \n"
 "  UNSIGNED4 MatchDate(const string src, set of varstring formats) : c, pure,entrypoint='slMatchDate'; \n"
+"  UNSIGNED4 MatchTimeOfDay(const string src, set of varstring formats) : c, pure,entrypoint='slMatchTimeOfDay'; \n"
 "  STRING FormatDate(UNSIGNED4 date, const varstring format) : c, pure,entrypoint='slFormatDate'; \n"
 "  STRING StringRepeat(const string src, unsigned4 n) : c, pure,entrypoint='slStringRepeat'; \n"
 "END;";
@@ -1320,7 +1322,7 @@ STRINGLIB_API void STRINGLIB_CALL slCombineWords(size32_t & __lenResult, void *
 
 //--------------------------------------------------------------------------------------------------------------------
 
-inline bool readValue(unsigned & value, size32_t & _offset, size32_t lenStr, const char * str, unsigned max)
+inline bool readValue(unsigned & value, size32_t & _offset, size32_t lenStr, const char * str, unsigned max, bool spaceIsZero = false)
 {
     unsigned total = 0;
     unsigned offset = _offset;
@@ -1332,6 +1334,8 @@ inline bool readValue(unsigned & value, size32_t & _offset, size32_t lenStr, con
         char next = str[offset+i];
         if (next >= '0' && next <= '9')
             total = total * 10 + (next - '0');
+    	else if (next == ' ' && spaceIsZero)
+            total = total * 10;
         else
             break;
     }
@@ -1384,6 +1388,44 @@ static const char * simple_strptime(size32_t lenStr, const char * str, const cha
         {
             switch (*curFormat++)
             {
+            // Recursive cases
+            case 'F':
+            	{
+            		const char*	newPtr = simple_strptime(lenStr-offset, str+offset, "%Y-%m-%d", tm);
+            		
+            		if (!newPtr)
+            			return NULL;
+            		offset = newPtr - str;
+            	}
+            	break;
+            case 'D':
+            	{
+            		const char*	newPtr = simple_strptime(lenStr-offset, str+offset, "%m/%d/%y", tm);
+            		
+            		if (!newPtr)
+            			return NULL;
+            		offset = newPtr - str;
+            	}
+            	break;
+            case 'R':
+            	{
+            		const char*	newPtr = simple_strptime(lenStr-offset, str+offset, "%H:%M", tm);
+            		
+            		if (!newPtr)
+            			return NULL;
+            		offset = newPtr - str;
+            	}
+            	break;
+            case 'T':
+            	{
+            		const char*	newPtr = simple_strptime(lenStr-offset, str+offset, "%H:%M:%S", tm);
+            		
+            		if (!newPtr)
+            			return NULL;
+            		offset = newPtr - str;
+            	}
+            	break;
+            // Non-recursive cases
             case 't':
                 while ((offset < lenStr) && isspace(src[offset]))
                     offset++;
@@ -1408,6 +1450,11 @@ static const char * simple_strptime(size32_t lenStr, const char * str, const cha
                     return NULL;
                 tm->tm_mday = value;
                 break;
+            case 'e':
+                if (!readValue(value, offset, lenStr, str, 2, true) || (value < 1) || (value > 31))
+                    return NULL;
+                tm->tm_mday = value;
+                break;
             case 'b':
             case 'B':
             case 'h':
@@ -1420,6 +1467,11 @@ static const char * simple_strptime(size32_t lenStr, const char * str, const cha
                     return NULL;
                 tm->tm_hour = value;
                 break;
+            case 'k':
+                if (!readValue(value, offset, lenStr, str, 2, true)|| (value > 24))
+                    return NULL;
+                tm->tm_hour = value;
+                break;
             case 'M':
                 if (!readValue(value, offset, lenStr, str, 2)|| (value > 59))
                     return NULL;
@@ -1457,6 +1509,12 @@ inline unsigned makeDate(const tm & tm)
     return (tm.tm_year + 1900) * 10000 + (tm.tm_mon + 1) * 100 + tm.tm_mday;
 }
 
+
+inline unsigned makeTimeOfDay(const tm & tm)
+{
+    return (tm.tm_hour * 10000) + (tm.tm_min * 100) + tm.tm_sec;
+}
+
 inline void extractDate(tm & tm, unsigned date)
 {
     tm.tm_year = (date / 10000) - 1900;
@@ -1475,16 +1533,26 @@ STRINGLIB_API unsigned STRINGLIB_CALL slStringToDate(size32_t lenS, const char *
     return 0;
 }
 
+STRINGLIB_API unsigned STRINGLIB_CALL slStringToTimeOfDay(size32_t lenS, const char * s, const char * fmtin)
+{
+    struct tm tm;
+    memset(&tm, 0, sizeof(tm));
+    if (simple_strptime(lenS, s, fmtin, &tm))
+        return makeTimeOfDay(tm);
+    return 0;
+}
+
 
 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;
@@ -1492,6 +1560,24 @@ STRINGLIB_API unsigned STRINGLIB_CALL slMatchDate(size32_t lenS, const char * s,
     return 0;
 }
 
+
+STRINGLIB_API unsigned STRINGLIB_CALL slMatchTimeOfDay(size32_t lenS, const char * s, bool isAllFormats, unsigned lenFormats, const void * _formats)
+{
+    struct tm 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;
+    }
+    return 0;
+}
+
 STRINGLIB_API void STRINGLIB_CALL slFormatDate(size32_t & __lenResult, char * & __result, unsigned date, const char * format)
 {
     size32_t len = 0;

+ 2 - 0
plugins/stringlib/stringlib.hpp

@@ -85,7 +85,9 @@ STRINGLIB_API unsigned STRINGLIB_CALL slCountWords(size32_t lenSrc, const char *
 STRINGLIB_API void STRINGLIB_CALL slSplitWords(bool & __isAllResult, size32_t & __lenResult, void * & __result, size32_t lenSrc, const char * src, size32_t lenSeparator, const char * separator, bool allowBlankItems);
 STRINGLIB_API void STRINGLIB_CALL slCombineWords(size32_t & __lenResult, void * & __result, bool isAllSrc, size32_t lenSrc, const char * src, size32_t lenSeparator, const char * separator, bool allowBlankItems);
 STRINGLIB_API unsigned STRINGLIB_CALL slStringToDate(size32_t lenS, const char * s, const char * fmtin);
+STRINGLIB_API unsigned STRINGLIB_CALL slStringToTimeOfDay(size32_t lenS, const char * s, const char * fmtin);
 STRINGLIB_API unsigned STRINGLIB_CALL slMatchDate(size32_t lenS, const char * s, bool isAllFormats, unsigned lenFormats, const void * _formats);
+STRINGLIB_API unsigned STRINGLIB_CALL slMatchTimeOfDay(size32_t lenS, const char * s, bool isAllFormats, unsigned lenFormats, const void * _formats);
 STRINGLIB_API void STRINGLIB_CALL slFormatDate(size32_t & __lenResult, char * & __result, unsigned date, const char * format);
 STRINGLIB_API void STRINGLIB_CALL slStringRepeat(unsigned & tgtLen, char * & tgt, unsigned srcLen, const char * src, unsigned n);
 }

+ 48 - 0
plugins/timelib/CMakeLists.txt

@@ -0,0 +1,48 @@
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+################################################################################
+
+
+# Component: timelib 
+
+#####################################################
+# Description:
+# ------------
+#    Cmake Input File for timelib
+#####################################################
+
+set ( toolsdir "${HPCC_SOURCE_DIR}/tools" )
+
+
+project( timelib ) 
+
+set (    SRCS 
+         timelib.cpp 
+    )
+
+include_directories ( 
+         ./../../system/include 
+         ./../../system/jlib
+         ./../../rtl/include
+         ./../../rtl/eclrtl
+    )
+
+ADD_DEFINITIONS( -D_USRDLL -DTIMELIB_EXPORTS )
+
+HPCC_ADD_LIBRARY( timelib SHARED ${SRCS} )
+install ( TARGETS timelib DESTINATION plugins )
+target_link_libraries ( timelib
+         eclrtl
+    )

+ 26 - 0
plugins/timelib/sourcedoc.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+################################################################################
+-->
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+<section>
+    <title>plugins/timelib</title>
+
+    <para>
+        The plugins/timelib directory contains the sources for the plugins/timelib library.
+    </para>
+</section>

+ 953 - 0
plugins/timelib/timelib.cpp

@@ -0,0 +1,953 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#include <platform.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <eclrtl.hpp>
+
+#ifdef _WIN32
+#include <sys/timeb.h>
+#endif
+
+#include "timelib.hpp"
+
+static const char * compatibleVersions[] = {
+    NULL };
+
+#define TIMELIB_VERSION "TIMELIB 1.0.0"
+
+static const char * EclDefinition =
+"EXPORT TMPartsRec := RECORD \n"
+"  INTEGER4 sec; \n"
+"  INTEGER4 min; \n"
+"  INTEGER4 hour; \n"
+"  INTEGER4 mday; \n"
+"  INTEGER4 mon; \n"
+"  INTEGER4 year; \n"
+"  INTEGER4 wday; \n"
+"END;"
+"EXPORT TMDateRangeRec := RECORD \n"
+"  UNSIGNED4 startDate; \n"
+"  UNSIGNED4 endDate; \n"
+"END;"
+"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"
+"  TRANSFORM(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, CONST VARSTRING format) : c,pure,entrypoint='tlDateToString'; \n"
+"  STRING TimeToString(UNSIGNED3 time, CONST VARSTRING format) : c,pure,entrypoint='tlTimeToString'; \n"
+"  STRING SecondsToString(INTEGER8 seconds, CONST 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"
+"  UNSIGNED4 AdjustTime(UNSIGNED3 time, INTEGER2 hour_delta, INTEGER4 minute_delta, INTEGER4 second_delta) : c,pure,entrypoint='tlAdjustTime'; \n"
+"  UNSIGNED4 AdjustTimeBySeconds(UNSIGNED3 time, INTEGER4 seconds_delta) : c,pure,entrypoint='tlAdjustTimeBySeconds'; \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,once,entrypoint='tlCurrentDate'; \n"
+"  UNSIGNED4 CurrentTime(BOOLEAN in_local_time) : c,entrypoint='tlCurrentTime'; \n"
+"  INTEGER8 CurrentSeconds(BOOLEAN in_local_time) : c,entrypoint='tlCurrentSeconds'; \n"
+"  INTEGER8 CurrentTimestamp(BOOLEAN in_local_time) : c,entrypoint='tlCurrentTimestamp'; \n"
+"  UNSIGNED4 GetLastDayOfMonth(UNSIGNED4 date) : c,pure,entrypoint='tlGetLastDayOfMonth'; \n"
+"  TRANSFORM(TMDateRangeRec) DatesForWeek(UNSIGNED4 date) : c,pure,entrypoint='tlDatesForWeek'; \n"
+"END;";
+
+TIMELIB_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
+{
+    if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
+    {
+        ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
+        pbx->compatibleVersions = compatibleVersions;
+    }
+    else if (pb->size != sizeof(ECLPluginDefinitionBlock))
+        return false;
+    pb->magicVersion = PLUGIN_VERSION;
+    pb->version = TIMELIB_VERSION;
+    pb->moduleName = "lib_timelib";
+    pb->ECL = EclDefinition;
+    pb->flags = PLUGIN_IMPLICIT_MODULE | PLUGIN_MULTIPLE_VERSIONS;
+    pb->description = "TimeLib time manipulation library";
+    return true;
+}
+
+IPluginContext * parentCtx = NULL;
+
+TIMELIB_API void setPluginContext(IPluginContext * _ctx) { parentCtx = _ctx; }
+
+//------------------------------------------------------------------------------
+
+#ifdef _WIN32
+const __int64 _onesec_in100ns = (__int64)10000000;
+
+static __int64 tlFileTimeToInt64(FILETIME f)
+{
+    __int64     seconds;
+
+    seconds = f.dwHighDateTime;
+    seconds <<= 32;
+    seconds |= f.dwLowDateTime;
+
+    return seconds;
+}
+
+static FILETIME tlInt64ToFileTime(__int64 seconds)
+{
+    FILETIME    f;
+
+    f.dwHighDateTime = (DWORD)((seconds >> 32) & 0x00000000FFFFFFFF);
+    f.dwLowDateTime = (DWORD)(seconds & 0x00000000FFFFFFFF);
+
+    return f;
+}
+
+static 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;
+}
+
+static 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));
+}
+
+static SYSTEMTIME tlTimeStructToSystemTime(const 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;
+}
+
+static void tlSystemTimeToTimeStruct_r(const 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;
+}
+
+static time_t tlFileTimeToSeconds(const FILETIME* f)
+{
+    const __int64   offset = I64C(11644473600); // Number of seconds between 1601 and 1970 (Jan 1 of each)
+
+    return static_cast<time_t>((tlFileTimeToInt64(*f) / _onesec_in100ns) - offset);
+}
+
+static 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;
+}
+
+static __int64 tlLocalTimeZoneDiffIn100nsIntervals()
+{
+    SYSTEMTIME  systemUTC;
+    SYSTEMTIME  systemLocal;
+    FILETIME    fileUTC;
+    FILETIME    fileLocal;
+
+    GetSystemTime(&systemUTC);
+    GetLocalTime(&systemLocal);
+
+    SystemTimeToFileTime(&systemUTC, &fileUTC);
+    SystemTimeToFileTime(&systemLocal, &fileLocal);
+
+    return tlFileTimeToInt64(fileLocal) - tlFileTimeToInt64(fileUTC);
+}
+
+static 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;
+
+            if (*unitsPtr == base)
+            {
+                *tensPtr += 1;
+                *unitsPtr = 0;
+            }
+        }
+    }
+}
+
+static 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 } };
+    int                 leapYearIndex = 0;
+
+    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);
+
+    leapYearIndex = ((((timeInfoPtr->tm_year + yearBase) % 4) == 0 && ((timeInfoPtr->tm_year + yearBase) % 100) != 0) || ((timeInfoPtr->tm_year + yearBase) % 400) == 0);
+    while (timeInfoPtr->tm_mday <= 0)
+    {
+        --timeInfoPtr->tm_mon;
+
+        if (timeInfoPtr->tm_mon < 0)
+        {
+            timeInfoPtr->tm_mon = 11;
+            --timeInfoPtr->tm_year;
+            leapYearIndex = ((((timeInfoPtr->tm_year + yearBase) % 4) == 0 && ((timeInfoPtr->tm_year + yearBase) % 100) != 0) || ((timeInfoPtr->tm_year + yearBase) % 400) == 0);
+        }
+
+        timeInfoPtr->tm_mday += monthLengths[leapYearIndex][timeInfoPtr->tm_mon];
+    }
+
+    while (timeInfoPtr->tm_mday > monthLengths[leapYearIndex][timeInfoPtr->tm_mon])
+    {
+        timeInfoPtr->tm_mday -= monthLengths[leapYearIndex][timeInfoPtr->tm_mon];
+        ++timeInfoPtr->tm_mon;
+
+        if (timeInfoPtr->tm_mon >= 12)
+        {
+            timeInfoPtr->tm_mon = 0;
+            ++timeInfoPtr->tm_year;
+            leapYearIndex = ((((timeInfoPtr->tm_year + yearBase) % 4) == 0 && ((timeInfoPtr->tm_year + yearBase) % 100) != 0) || ((timeInfoPtr->tm_year + yearBase) % 400) == 0);
+        }
+    }
+}
+
+//---------------------------
+
+static void tlWinLocalTime_r(const time_t* clock, struct tm* timeInfoPtr)
+{
+    SYSTEMTIME  s;
+    FILETIME    f;
+    __int64     time;
+
+    f = tlSecondsToFileTime(*clock);
+    time = tlFileTimeToInt64(f) + tlLocalTimeZoneDiffIn100nsIntervals();
+    f = tlInt64ToFileTime(time);
+
+    FileTimeToSystemTime(&f, &s);
+
+    tlSystemTimeToTimeStruct_r(&s, timeInfoPtr);
+}
+
+static void tlWinGMTime_r(const time_t* clock, struct tm* timeInfo)
+{
+    FILETIME    f;
+    SYSTEMTIME  s;
+
+    f = tlSecondsToFileTime(*clock);
+    FileTimeToSystemTime(&f, &s);
+    tlSystemTimeToTimeStruct_r(&s, timeInfo);
+}
+
+static 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->tm_wday = s.wDayOfWeek;
+
+    // The above assumes UTC but Linux's mktime() assumes a local
+    // time zone, so we need to offset the result into the local time zone
+    diff = tlLocalTimeZoneDiffIn100nsIntervals() / _onesec_in100ns;
+
+    return tlFileTimeToSeconds(&f) - diff;
+}
+#endif
+
+//------------------------------------------------------------------------------
+
+void tlLocalTime_r(const time_t* clock, struct tm* timeInfoPtr)
+{
+    #ifdef _WIN32
+    tlWinLocalTime_r(clock, timeInfoPtr);
+    #else
+    localtime_r(clock, timeInfoPtr);
+    #endif
+}
+
+void tlGMTime_r(const time_t* clock, struct tm* timeInfoPtr)
+{
+    #ifdef _WIN32
+    tlWinGMTime_r(clock, timeInfoPtr);
+    #else
+    gmtime_r(clock, timeInfoPtr);
+    #endif
+}
+
+time_t tlMKTime(struct tm* timeInfoPtr, bool inLocalTimeZone)
+{
+    time_t      the_time = 0;
+
+    #ifdef _WIN32
+    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;
+    unsigned int    month = (date - (year * 10000)) / 100;
+    unsigned int    day = date - (year * 10000) - (month * 100);
+
+    timeInfo->tm_year = year - 1900;
+    timeInfo->tm_mon = month - 1;
+    timeInfo->tm_mday = day;
+}
+
+unsigned int tlExtractDateFromTimeStruct(const struct tm* timeInfo)
+{
+    unsigned int    result = 0;
+
+    result = (timeInfo->tm_year + 1900) * 10000;
+    result += (timeInfo->tm_mon + 1) * 100;
+    result += timeInfo->tm_mday;
+
+    return result;
+}
+
+void tlInsertTimeIntoTimeStruct(struct tm* timeInfo, unsigned int time)
+{
+    unsigned int    hour = time / 10000;
+    unsigned int    minute = (time - (hour * 10000)) / 100;
+    unsigned int    second = time - (hour * 10000) - (minute * 100);
+
+    timeInfo->tm_hour = hour;
+    timeInfo->tm_min = minute;
+    timeInfo->tm_sec = second;
+}
+
+unsigned int tlExtractTimeFromTimeStruct(const struct tm* timeInfo)
+{
+    unsigned int    result = 0;
+
+    result = timeInfo->tm_hour * 10000;
+    result += timeInfo->tm_min * 100;
+    result += timeInfo->tm_sec;
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+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));
+
+    // Push each time part value into the tm struct
+    timeInfo.tm_sec = second;
+    timeInfo.tm_min = minute;
+    timeInfo.tm_hour = hour;
+    timeInfo.tm_mday = day;
+    timeInfo.tm_mon = month - 1;
+    timeInfo.tm_year = year - 1900;
+
+    the_time = tlMKTime(&timeInfo, is_local_time);
+
+    return static_cast<__int64>(the_time);
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API size32_t TIMELIB_CALL tlSecondsToParts(ARowBuilder& __self, __int64 seconds)
+{
+    struct tm       timeInfo;
+
+    struct TMParts
+    {
+        __int32 sec;
+        __int32 min;
+        __int32 hour;
+        __int32 mday;
+        __int32 mon;
+        __int32 year;
+        __int32 wday;
+    };
+
+    tlMakeTimeStructFromUTCSeconds(seconds, &timeInfo);
+
+    TMParts* result = reinterpret_cast<TMParts*>(__self.getSelf());
+
+    result->sec = timeInfo.tm_sec;
+    result->min = timeInfo.tm_min;
+    result->hour = timeInfo.tm_hour;
+    result->mday = timeInfo.tm_mday;
+    result->mon = timeInfo.tm_mon;
+    result->year = timeInfo.tm_year;
+    result->wday = timeInfo.tm_wday;
+
+    return static_cast<size32_t>(sizeof(TMParts));
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfYear(short year, unsigned short month, unsigned short day)
+{
+    unsigned int    dayOfYear = 0;
+
+    #ifdef _WIN32
+    SYSTEMTIME  s;
+
+    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));
+
+    // Push each time part value into the tm struct
+    timeInfo.tm_mday = day;
+    timeInfo.tm_mon = month - 1;
+    timeInfo.tm_year = year - 1900;
+
+    tlMKTime(&timeInfo);
+
+    dayOfYear = timeInfo.tm_yday;
+    #endif
+
+    return dayOfYear;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlGetDayOfWeek(short year, unsigned short month, unsigned short day)
+{
+    struct tm       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;
+
+    tlMKTime(&timeInfo);
+
+    return timeInfo.tm_wday;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API void TIMELIB_CALL tlDateToString(size32_t &__lenResult, char* &__result, unsigned int date, const char* format)
+{
+    struct tm       timeInfo;
+    const size_t    kBufferSize = 256;
+    char            buffer[kBufferSize];
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
+
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
+    __result = NULL;
+
+    if (__lenResult > 0)
+    {
+        __result = reinterpret_cast<char*>(CTXMALLOC(parentCtx, __lenResult));
+        memcpy(__result, buffer, __lenResult);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API void TIMELIB_CALL tlTimeToString(size32_t &__lenResult, char* &__result, unsigned int time, const char* format)
+{
+    struct tm       timeInfo;
+    const size_t    kBufferSize = 256;
+    char            buffer[kBufferSize];
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
+
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
+    __result = NULL;
+
+    if (__lenResult > 0)
+    {
+        __result = reinterpret_cast<char*>(rtlMalloc(__lenResult));
+        memcpy(__result, buffer, __lenResult);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API void TIMELIB_CALL tlSecondsToString(size32_t &__lenResult, char* &__result, __int64 seconds, const char* format)
+{
+    struct tm       timeInfo;
+    time_t          theTime = seconds;
+    const size_t    kBufferSize = 256;
+    char            buffer[kBufferSize];
+
+    memset(buffer, 0, kBufferSize);
+
+    tlGMTime_r(&theTime, &timeInfo);
+
+    __lenResult = strftime(buffer, kBufferSize, format, &timeInfo);
+    __result = NULL;
+
+    if (__lenResult > 0)
+    {
+        __result = reinterpret_cast<char*>(rtlMalloc(__lenResult));
+        memcpy(__result, buffer, __lenResult);
+    }
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDate(unsigned int date, short year_delta, int month_delta, int day_delta)
+{
+    struct tm       timeInfo;
+    unsigned int    result = 0;
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
+
+    timeInfo.tm_year += year_delta;
+    timeInfo.tm_mon += month_delta;
+    timeInfo.tm_mday += day_delta;
+
+    tlMKTime(&timeInfo);
+
+    result = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlAdjustDateBySeconds(unsigned int date, int seconds_delta)
+{
+    struct tm       timeInfo;
+    unsigned int    result = 0;
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
+    timeInfo.tm_sec = seconds_delta;
+
+    tlMKTime(&timeInfo);
+
+    result = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTime(unsigned int time, short hour_delta, int minute_delta, int second_delta)
+{
+    struct tm       timeInfo;
+    unsigned int    result = 0;
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
+
+    timeInfo.tm_hour += hour_delta;
+    timeInfo.tm_min += minute_delta;
+    timeInfo.tm_sec += second_delta;
+
+    tlMKTime(&timeInfo);
+
+    result = tlExtractTimeFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlAdjustTimeBySeconds(unsigned int time, int seconds_delta)
+{
+    struct tm       timeInfo;
+    unsigned int    result = 0;
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+
+    tlInsertTimeIntoTimeStruct(&timeInfo, time);
+    timeInfo.tm_sec += seconds_delta;
+
+    tlMKTime(&timeInfo);
+
+    result = tlExtractTimeFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+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;
+
+    tlLocalTime_r(&theTime, &timeInfo);
+
+    timeInfo.tm_year += year_delta;
+    timeInfo.tm_mon += month_delta;
+    timeInfo.tm_mday += day_delta;
+    timeInfo.tm_hour += hour_delta;
+    timeInfo.tm_min += minute_delta;
+    timeInfo.tm_sec += second_delta;
+
+    result = tlMKTime(&timeInfo);
+
+    return static_cast<__int64>(result);
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlAdjustCalendar(unsigned int date, short year_delta, int month_delta, int day_delta)
+{
+    struct tm       timeInfo;
+    unsigned int    year = date / 10000;
+    unsigned int    month = (date - (year * 10000)) / 100;
+    unsigned int    day = date - (year * 10000) - (month * 100);
+    int             expectedMonthVal = month + month_delta - 1;
+    time_t          seconds;
+    unsigned int    result = 0;
+
+    // Normalize the expected month value
+    if (expectedMonthVal >= 0)
+    {
+        expectedMonthVal = expectedMonthVal % 12;
+    }
+    else
+    {
+        expectedMonthVal = 12 - (abs(expectedMonthVal) % 12);
+    }
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+
+    timeInfo.tm_year = year - 1900;
+    timeInfo.tm_mon = month - 1;
+    timeInfo.tm_mday = day;
+
+    timeInfo.tm_year += year_delta;
+    timeInfo.tm_mon += month_delta;
+
+    seconds = 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;
+        tlMKTime(&timeInfo);
+    }
+
+    if (day_delta != 0)
+    {
+        // Now apply the day delta
+        timeInfo.tm_mday += day_delta;
+        tlMKTime(&timeInfo);
+    }
+
+    result = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API bool TIMELIB_CALL tlIsLocalDaylightSavingsInEffect()
+{
+    struct tm       timeInfo;
+    time_t          theTime = time(NULL);
+
+    tlLocalTime_r(&theTime, &timeInfo);
+
+    return (timeInfo.tm_isdst == 1);
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API int TIMELIB_CALL tlLocalTimeZoneOffset()
+{
+    int     offset = 0;
+
+    #ifdef _WIN32
+    offset = static_cast<int>(tlLocalTimeZoneDiffIn100nsIntervals() / _onesec_in100ns);
+    #else
+    struct tm       timeInfo;
+    time_t          theTime = time(NULL);
+
+    tlLocalTime_r(&theTime, &timeInfo);
+
+    offset = timeInfo.tm_gmtoff;
+    #endif
+
+    return offset;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlCurrentDate(bool in_local_time)
+{
+    struct tm       timeInfo;
+    time_t          theTime = time(NULL);
+    unsigned int    result = 0;
+
+    // Create time parts differently depending on whether you need
+    // UTC or local time
+    if (in_local_time)
+    {
+        tlLocalTime_r(&theTime, &timeInfo);
+    }
+    else
+    {
+        tlGMTime_r(&theTime, &timeInfo);
+    }
+
+    result = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API unsigned int TIMELIB_CALL tlCurrentTime(bool in_local_time)
+{
+    struct tm       timeInfo;
+    time_t          theTime = time(NULL);
+    unsigned int    result = 0;
+
+    // Create time parts differently depending on whether you need
+    // UTC or local time
+    if (in_local_time)
+    {
+        tlLocalTime_r(&theTime, &timeInfo);
+    }
+    else
+    {
+        tlGMTime_r(&theTime, &timeInfo);
+    }
+
+    result = tlExtractTimeFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentSeconds(bool in_local_time)
+{
+    time_t    result = time(NULL);
+
+    if (in_local_time)
+    {
+        result += tlLocalTimeZoneOffset();
+    }
+
+    return static_cast<__int64>(result);
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API __int64 TIMELIB_CALL tlCurrentTimestamp(bool in_local_time)
+{
+    __int64     result = 0;
+
+    #ifdef _WIN32
+    struct _timeb   now;
+
+    _ftime_s(&now);
+
+    result = (now.time * I64C(1000000)) + (now.millitm * 1000);
+    #else
+    struct timeval  tv;
+
+    if (gettimeofday(&tv, NULL) == 0)
+    {
+        result = (tv.tv_sec * I64C(1000000)) + tv.tv_usec;
+    }
+    #endif
+
+    if (in_local_time)
+    {
+        result += (static_cast<__int64>(tlLocalTimeZoneOffset()) * I64C(1000000));
+    }
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+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);
+
+    // Call mktime once to fix up any bogus data
+    tlMKTime(&timeInfo);
+
+    // Adjust and call again
+    timeInfo.tm_mon += 1;
+    timeInfo.tm_mday = 0;
+    tlMKTime(&timeInfo);
+
+    result = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return result;
+}
+
+//------------------------------------------------------------------------------
+
+TIMELIB_API size32_t TIMELIB_CALL tlDatesForWeek(ARowBuilder& __self, unsigned int date)
+{
+    struct tm       timeInfo;
+
+    struct TMDateRange
+    {
+        unsigned int    startDate;
+        unsigned int    endDate;
+    };
+
+    TMDateRange* result = reinterpret_cast<TMDateRange*>(__self.getSelf());
+
+    memset(&timeInfo, 0, sizeof(timeInfo));
+    tlInsertDateIntoTimeStruct(&timeInfo, date);
+
+    // Call mktime once to fix up any bogus data
+    tlMKTime(&timeInfo);
+
+    // Adjust and call again
+    timeInfo.tm_mday -= timeInfo.tm_wday;
+    tlMKTime(&timeInfo);
+
+    result->startDate = tlExtractDateFromTimeStruct(&timeInfo);
+
+    // Adjust to the beginning of the week
+    timeInfo.tm_mday += 6;
+    tlMKTime(&timeInfo);
+
+    result->endDate = tlExtractDateFromTimeStruct(&timeInfo);
+
+    return static_cast<size32_t>(sizeof(TMDateRange));
+}

+ 80 - 0
plugins/timelib/timelib.hpp

@@ -0,0 +1,80 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+#ifndef TIMELIB_INCL
+#define TIMELIB_INCL
+
+#ifdef _WIN32
+#define TIMELIB_CALL _cdecl
+#ifdef TIMELIB_EXPORTS
+#define TIMELIB_API __declspec(dllexport)
+#else
+#define TIMELIB_API __declspec(dllimport)
+#endif
+#else
+#define TIMELIB_CALL
+#define TIMELIB_API
+#endif
+
+#include <time.h>
+
+#include "platform.h"
+#include "hqlplugins.hpp"
+#include "eclinclude4.hpp"
+#include "eclrtl.hpp"
+
+extern "C" {
+
+#ifdef TIMELIB_EXPORTS
+TIMELIB_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb);
+TIMELIB_API void setPluginContext(IPluginContext * _ctx);
+#endif
+
+void tlMakeTimeStructFromUTCSeconds(time_t seconds, struct tm* timeInfo);
+void tlInsertDateIntoTimeStruct(struct tm* timeInfo, unsigned int date);
+unsigned int tlExtractDateFromTimeStruct(const struct tm* timeInfo);
+void tlInsertTimeIntoTimeStruct(struct tm* timeInfo, unsigned int time);
+unsigned int tlExtractTimeFromTimeStruct(const struct tm* timeInfo);
+
+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 size32_t TIMELIB_CALL tlSecondsToParts(ARowBuilder & __self, __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, __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 __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 __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 size32_t TIMELIB_CALL tlDatesForWeek(ARowBuilder & __self, unsigned int date);
+
+}
+#endif