#include "date_functions.h" #include GroundValue tmToGroundValue(struct tm t) { GroundStruct timeStruct = groundCreateStruct(); groundAddFieldToStruct(&timeStruct, "year", groundCreateValue(INT, t.tm_year + 1900)); groundAddFieldToStruct(&timeStruct, "month", groundCreateValue(INT, t.tm_mon + 1)); groundAddFieldToStruct(&timeStruct, "day", groundCreateValue(INT, t.tm_mday)); groundAddFieldToStruct(&timeStruct, "hour", groundCreateValue(INT, t.tm_hour)); groundAddFieldToStruct(&timeStruct, "minute", groundCreateValue(INT, t.tm_min)); groundAddFieldToStruct(&timeStruct, "second", groundCreateValue(INT, t.tm_sec)); groundAddFieldToStruct(&timeStruct, "weekDay", groundCreateValue(INT, t.tm_wday)); groundAddFieldToStruct(&timeStruct, "yearDay", groundCreateValue(INT, t.tm_yday + 1)); groundAddFieldToStruct(&timeStruct, "isDaylightSavingsTime", groundCreateValue(BOOL, t.tm_isdst)); GroundValue value = groundCreateValue(CUSTOM, &timeStruct); value.type = CUSTOM; return value; } GroundValue datetime_Now(GroundScope* scope, List args) { time_t now = time(NULL); struct tm t = {0}; localtime_r(&now, &t); // return a -datetime struct return tmToGroundValue(t); } GroundValue datetime_FromEpochLocal(GroundScope* scope, List args) { struct tm t = {0}; time_t ts = args.values[0].data.doubleVal; localtime_r(&ts, &t); return tmToGroundValue(t); } GroundValue datetime_FromEpochUTC(GroundScope* scope, List args) { struct tm t = {0}; time_t ts = args.values[0].data.doubleVal; gmtime_r(&ts, &t); return tmToGroundValue(t); } GroundValue datetime_ToEpochLocal(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t ts = mktime(&t); return groundCreateValue(DOUBLE, (double)ts); } GroundValue datetime_ToEpochUTC(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t ts = timegm(&t); return groundCreateValue(DOUBLE, (double)ts); } GroundValue formatDatetimeObj(GroundObject obj, char* formatString) { // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; mktime(&t); // normalise time // create a buffer for the string char* buffer = (char*)malloc(128); strftime(buffer, 128, formatString, &t); // return ground version of string return groundCreateValue(STRING, buffer); } GroundValue datetime_Format(GroundScope* scope, List args) { return formatDatetimeObj( *args.values[0].data.customVal, args.values[1].data.stringVal ); } GroundValue datetime_FromFormatted(GroundScope* scope, List args) { char* timeString = args.values[0].data.stringVal; char* formatString = args.values[1].data.stringVal; struct tm t = {0}; t.tm_isdst = -1; char* result = strptime(timeString, formatString, &t); if (result == NULL) { ERROR("Time string does not match format!", "ValueError"); } mktime(&t); return tmToGroundValue(t); } GroundValue datetime_ToISO8601UTC(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t ts = mktime(&t); gmtime_r(&ts, &t); char* buffer = (char*)malloc(128); strftime(buffer, 128, "%Y-%m-%dT%H:%M:%SZ", &t); return groundCreateValue(STRING, buffer); } GroundValue datetime_ToISO8601Local(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t ts = mktime(&t); localtime_r(&ts, &t); char* buffer = (char*)malloc(128); strftime(buffer, 128, "%Y-%m-%dT%H:%M:%S%z", &t); return groundCreateValue(STRING, buffer); } GroundValue datetime_Diff(GroundScope* scope, List args) { GroundObject obj1 = *args.values[0].data.customVal; GroundObject obj2 = *args.values[1].data.customVal; // check first timedate object GroundObjectField* year = groundFindField(obj1, "year"); if (year == NULL || year->value.type != INT) ERROR("Object 1 does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj1, "month"); if (month == NULL || month->value.type != INT) ERROR("Object 1 does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj1, "day"); if (day == NULL || day->value.type != INT) ERROR("Object 1 does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj1, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object 1 does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj1, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object 1 does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj1, "second"); if (second == NULL || second->value.type != INT) ERROR("Object 1 does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj1, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object 1 does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj1, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object 1 does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj1, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object 1 does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t1 = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; // check second timedate object year = groundFindField(obj2, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); month = groundFindField(obj2, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); day = groundFindField(obj2, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); hour = groundFindField(obj2, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); minute = groundFindField(obj2, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); second = groundFindField(obj2, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); weekDay = groundFindField(obj2, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); yearDay = groundFindField(obj2, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); isDaylightSavingsTime = groundFindField(obj2, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t2 = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t ts1 = mktime(&t1); time_t ts2 = mktime(&t2); return groundCreateValue(INT, ts2 - ts1); } GroundValue datetime_Add(GroundScope* scope, List args) { GroundObject obj = *args.values[0].data.customVal; long long secs = args.values[1].data.intVal; long long mins = args.values[2].data.intVal; long long hours = args.values[3].data.intVal; long long days = args.values[4].data.intVal; // check args GroundObjectField* year = groundFindField(obj, "year"); if (year == NULL || year->value.type != INT) ERROR("Object does not have year field as int", "ValueError"); GroundObjectField* month = groundFindField(obj, "month"); if (month == NULL || month->value.type != INT) ERROR("Object does not have month field as int", "ValueError"); GroundObjectField* day = groundFindField(obj, "day"); if (day == NULL || day->value.type != INT) ERROR("Object does not have day field as int", "ValueError"); GroundObjectField* hour = groundFindField(obj, "hour"); if (hour == NULL || hour->value.type != INT) ERROR("Object does not have hour field as int", "ValueError"); GroundObjectField* minute = groundFindField(obj, "minute"); if (minute == NULL || minute->value.type != INT) ERROR("Object does not have minute field as int", "ValueError"); GroundObjectField* second = groundFindField(obj, "second"); if (second == NULL || second->value.type != INT) ERROR("Object does not have second field as int", "ValueError"); GroundObjectField* weekDay = groundFindField(obj, "weekDay"); if (weekDay == NULL || weekDay->value.type != INT) ERROR("Object does not have weekDay field as int", "ValueError"); GroundObjectField* yearDay = groundFindField(obj, "yearDay"); if (yearDay == NULL || yearDay->value.type != INT) ERROR("Object does not have yearDay field as int", "ValueError"); GroundObjectField* isDaylightSavingsTime = groundFindField(obj, "isDaylightSavingsTime"); if (isDaylightSavingsTime == NULL || isDaylightSavingsTime->value.type != BOOL) ERROR("Object does not have isDaylightSavingsTime field as bool", "ValueError"); // construct tm struct from our ground struct struct tm t = { .tm_sec = second->value.data.intVal, .tm_min = minute->value.data.intVal, .tm_hour = hour->value.data.intVal, .tm_mday = day->value.data.intVal, .tm_mon = month->value.data.intVal - 1, .tm_year = year->value.data.intVal - 1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = -1 }; time_t base = mktime(&t); long long totalSeconds = secs + mins * 60 + hours * 3600 + days * 86400; base += totalSeconds; struct tm newT; localtime_r(&base, &newT); return tmToGroundValue(newT); }