From 8824ffcf2ae571455349a2d7f4bb2a02328cbf6c Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Mon, 2 Oct 2023 17:54:42 +0530 Subject: [PATCH 01/12] Implemented Datetime object --- docs/docs/standard-lib/datetime.md | 47 +++++ src/optionals/datetime.c | 316 +++++++++++++++++++++++++---- src/optionals/datetime.h | 10 + tests/datetime/constants.du | 16 +- tests/datetime/datetime.du | 60 ++++++ tests/datetime/import.du | 1 + 6 files changed, 405 insertions(+), 45 deletions(-) create mode 100644 tests/datetime/datetime.du diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index b6558bbd8..debf3b977 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -33,6 +33,35 @@ import Datetime; | Datetime.SECONDS_IN_DAY | The number of seconds in a day. | | Datetime.SECONDS_IN_WEEK | The number of seconds in a week. | + +### Datetime.new() -> Datetime + +Returns a datetime object with current datetime. + +```cs +var datetime = Datetime.new(); +datetime.toString(); // Fri May 29 03:12:32 2020 +``` + +### Datetime.new(String, String) -> Datetime + +Returns a datetime object for given time string format. + +```cs +var datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.toString(); // Wed Jan 1 00:00:00 2020 +``` + +### Datetime.new(Number) -> Datetime + +Returns a datetime object for given number of seconds from epoch. + +```cs +const datetime = Datetime.new(1577836800); +datetime.toString(); // Wed Jan 1 00:00:00 2020 +``` + + ### Datetime.now() -> String Returns a human readable locale datetime string. @@ -107,3 +136,21 @@ the date string in the format of the first parameter. Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); // 1577836800 ``` +### datetimeObj.strptime() -> Number + +Returns a number which is the number of seconds from epoch, for a given datetime + +```cs +const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.strptime(); // 1577836800 +``` + +### datetimeObj.strftime() -> String + +Returns a user-defined datetime formatted string for a datetime object, see [Datetime formats](#datetime-formats). + +```cs +const datetime = Datetime.new(); +datetime.strftime("Today is %A"); // Today is Friday +``` + diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 3919003fb..a420fb1d6 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -11,43 +11,305 @@ #define HAS_STRPTIME #endif +#define AS_DATETIME(v) ((Datetime*)AS_ABSTRACT(v)->data) + + +void freeDatetime(DictuVM *vm, ObjAbstract *abstract) { + FREE(vm, Datetime, abstract->data); +} + + +Datetime* createDatetime(DictuVM *vm) { + Datetime *datetime = ALLOCATE(vm, Datetime, 1); + return datetime; +} + +char *datetimeTypeToString(ObjAbstract *abstract) { + UNUSED(abstract); + + char *queueString = malloc(sizeof(char) * 11); + snprintf(queueString, 11, ""); + return queueString; +} + +static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ + if (argCount != 1) { + runtimeError(vm, "strftime() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[1])) { + runtimeError(vm, "strftime() argument must be a string"); + return EMPTY_VAL; + } + + ObjString *format = AS_STRING(args[1]); + + /** this is to avoid an eternal loop while calling strftime() below */ + if (0 == format->length) + return OBJ_VAL(copyString(vm, "", 0)); + + char *fmt = format->chars; + int len = (format->length > 128 ? format->length * 4 : 128); + + char *point = ALLOCATE(vm, char, len); + if (point == NULL) { + runtimeError(vm, "Memory error on strftime()!"); + return EMPTY_VAL; + } + + Datetime *datetime = AS_DATETIME(args[0]); + struct tm tictoc; + tictoc.tm_sec = datetime->seconds; + tictoc.tm_min = datetime->minutes; + tictoc.tm_hour = datetime->hours; + tictoc.tm_mday = datetime->day; + tictoc.tm_mon = datetime->month; + tictoc.tm_year = datetime->year; + tictoc.tm_gmtoff = datetime->offset; + tictoc.tm_isdst = -1; + const time_t timestamp = mktime(&tictoc); + localtime_r(×tamp, &tictoc); + + /** + * strtime returns 0 when it fails to write - this would be due to the buffer + * not being large enough. In that instance we double the buffer length until + * there is a big enough buffer. + */ + + /** however is not guaranteed that 0 indicates a failure (`man strftime' says so). + * So we might want to catch up the eternal loop, by using a maximum iterator. + */ + int max_iterations = 8; // maximum 65536 bytes with the default 128 len, + // more if the given string is > 128 + int iterator = 0; + while (strftime(point, sizeof(char) * len, fmt, &tictoc) == 0) { + if (++iterator > max_iterations) { + FREE_ARRAY(vm, char, point, len); + return OBJ_VAL(copyString(vm, "", 0)); + } + + len *= 2; + + point = GROW_ARRAY(vm, point, char, len / 2, len); + if (point == NULL) { + runtimeError(vm, "Memory error on strftime()!"); + return EMPTY_VAL; + } + } + + int length = strlen(point); + + if (length != len) { + point = SHRINK_ARRAY(vm, point, char, len, length + 1); + } + + return OBJ_VAL(takeString(vm, point, length)); +} + + +static Value datetimeStrptime(DictuVM *vm, int argCount, Value *args){ + + if (argCount != 0) { + runtimeError(vm, "strptime() take 0 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Datetime *datetime = AS_DATETIME(args[0]); + struct tm tictoc = {0}; + + tictoc.tm_sec = datetime->seconds; + tictoc.tm_min = datetime->minutes; + tictoc.tm_hour = datetime->hours; + tictoc.tm_mday = datetime->day; + tictoc.tm_mon = datetime->month; + tictoc.tm_year = datetime->year; + tictoc.tm_gmtoff = datetime->offset; + tictoc.tm_isdst = -1; + const time_t timestamp = mktime(&tictoc); + localtime_r(×tamp, &tictoc); + + return NUMBER_VAL((double) mktime(&tictoc)+tictoc.tm_gmtoff); +} + +static Value datetimeToString(DictuVM *vm, int argCount, Value *args) { + if (argCount != 0) { + runtimeError(vm, "toString() takes no arguments (%d given)", argCount); + return EMPTY_VAL; + } + + Datetime *datetime = AS_DATETIME(args[0]); + struct tm tictoc; + tictoc.tm_sec = datetime->seconds; + tictoc.tm_min = datetime->minutes; + tictoc.tm_hour = datetime->hours; + tictoc.tm_mday = datetime->day; + tictoc.tm_mon = datetime->month; + tictoc.tm_year = datetime->year; + tictoc.tm_gmtoff = datetime->offset; + tictoc.tm_isdst = -1; + const time_t timestamp = mktime(&tictoc); + localtime_r(×tamp, &tictoc); + + char time[26]; + asctime_r(&tictoc, time); + + return OBJ_VAL(copyString(vm, time, strlen(time) - 1)); +} + + +ObjAbstract* newDatetimeObj( + DictuVM *vm, + unsigned char seconds, + unsigned char minutes, + unsigned char hours, + unsigned char day, + unsigned char month, + unsigned short year, + long offset + ) { + ObjAbstract *abstract = newAbstract(vm, freeDatetime, datetimeTypeToString); + push(vm, OBJ_VAL(abstract)); + + Datetime *datetime = createDatetime(vm); + datetime->seconds = seconds; + datetime->minutes = minutes; + datetime->hours = hours; + datetime->day = day; + datetime->month = month; + datetime->year = year; + datetime->offset = offset; + + /** + * Setup Queue object methods + */ + + defineNative(vm, &abstract->values, "strftime", datetimeStrftime); + defineNative(vm, &abstract->values, "strptime", datetimeStrptime); + defineNative(vm, &abstract->values, "toString", datetimeToString); + + abstract->data = datetime; + pop(vm); + + return abstract; +} + static Value nowNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); if (argCount != 0) { - runtimeError(vm, "now() takes no arguments (%d given)", argCount); + runtimeError(vm, "nowNative() takes no arguments (%d given)", argCount); return EMPTY_VAL; } time_t t = time(NULL); struct tm tictoc; - char time[26]; - localtime_r(&t, &tictoc); - asctime_r(&tictoc, time); - // -1 to remove newline - return OBJ_VAL(copyString(vm, time, strlen(time) - 1)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); } static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); if (argCount != 0) { - runtimeError(vm, "nowUTC() takes no arguments (%d given)", argCount); + runtimeError(vm, "nowUTCNative() takes no arguments (%d given)", argCount); return EMPTY_VAL; } time_t t = time(NULL); struct tm tictoc; - char time[26]; + gmtime_r(&t, &tictoc); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); +} + + +static Value newUTCDatetimeNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 0) { + runtimeError(vm, "nowUTCNative() takes no arguments (%d given)", argCount); + return EMPTY_VAL; + } + + time_t t = time(NULL); + struct tm tictoc; gmtime_r(&t, &tictoc); - asctime_r(&tictoc, time); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); +} - // -1 to remove newline - return OBJ_VAL(copyString(vm, time, strlen(time) - 1)); + +static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + #ifdef HAS_STRPTIME + if (argCount == 2) { + if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { + runtimeError(vm, "new() takes 2 arguments, must be strings (%d given)", argCount); + return EMPTY_VAL; + } + struct tm tictoc = {0}; + tictoc.tm_isdst = -1; + // char *end = strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc); + if (strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc) != NULL) { + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + } else { + return NIL_VAL; + } + } + #endif + + if(argCount == 1) { + if (!IS_NUMBER(args[0])) { + runtimeError(vm, "new() takes 1 argument , must be a number"); + return EMPTY_VAL; + } + + struct tm tictoc = {0}; + time_t num = (time_t)((long) AS_NUMBER(args[0])); + gmtime_r(&num, &tictoc); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + } + + if (argCount == 0) { + time_t t = time(NULL); + struct tm tictoc; + localtime_r(&t, &tictoc); + + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + } + + runtimeError(vm, "new() takes 0,1 or 2 arguments, (%d given)", argCount); + + return EMPTY_VAL; +} + +#ifdef HAS_STRPTIME +static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { + if (argCount != 2) { + runtimeError(vm, "strptime() takes 2 arguments (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { + runtimeError(vm, "strptime() arguments must be strings"); + return EMPTY_VAL; + } + + struct tm tictoc = {0}; + tictoc.tm_mday = 1; + tictoc.tm_isdst = -1; + + char *end = strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc); + + if (end == NULL) { + return NIL_VAL; + } + + return NUMBER_VAL((double) mktime(&tictoc) + tictoc.tm_gmtoff); } +#endif static Value strftimeNative(DictuVM *vm, int argCount, Value *args) { if (argCount != 1 && argCount != 2) { @@ -128,32 +390,6 @@ static Value strftimeNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(takeString(vm, point, length)); } -#ifdef HAS_STRPTIME -static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { - if (argCount != 2) { - runtimeError(vm, "strptime() takes 2 arguments (%d given)", argCount); - return EMPTY_VAL; - } - - if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { - runtimeError(vm, "strptime() arguments must be strings"); - return EMPTY_VAL; - } - - struct tm tictoc = {0}; - tictoc.tm_mday = 1; - tictoc.tm_isdst = -1; - - char *end = strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc); - - if (end == NULL) { - return NIL_VAL; - } - - return NUMBER_VAL((double) mktime(&tictoc)); -} -#endif - Value createDatetimeModule(DictuVM *vm) { ObjString *name = copyString(vm, "Datetime", 8); push(vm, OBJ_VAL(name)); @@ -163,8 +399,14 @@ Value createDatetimeModule(DictuVM *vm) { /** * Define Datetime methods */ + + defineNative(vm, &module->values, "new", newDatetimeNative); + defineNative(vm, &module->values, "newUTC", newUTCDatetimeNative); defineNative(vm, &module->values, "now", nowNative); defineNative(vm, &module->values, "nowUTC", nowUTCNative); + + // defineNative(vm, &module->values, "now", nowNative); + // defineNative(vm, &module->values, "nowUTC", nowUTCNative); defineNative(vm, &module->values, "strftime", strftimeNative); #ifdef HAS_STRPTIME defineNative(vm, &module->values, "strptime", strptimeNative); diff --git a/src/optionals/datetime.h b/src/optionals/datetime.h index 85c48bc29..b0d964955 100644 --- a/src/optionals/datetime.h +++ b/src/optionals/datetime.h @@ -12,6 +12,16 @@ #include "optionals.h" +typedef struct { + unsigned char seconds; /* seconds after the minute [0-60] */ + unsigned char minutes; /* minutes after the hour [0-59] */ + unsigned char hours; /* hours since midnight [0-23] */ + unsigned char day; /* day of the month [1-31] */ + unsigned char month; /* months since January [0-11] */ + unsigned short year; /* years since 1900 */ + long offset; /* offset from UTC in seconds */ +} Datetime; + Value createDatetimeModule(DictuVM *vm); #endif //dictu_datetime_h diff --git a/tests/datetime/constants.du b/tests/datetime/constants.du index 28a99e32a..2e129ccde 100644 --- a/tests/datetime/constants.du +++ b/tests/datetime/constants.du @@ -15,30 +15,30 @@ class TestDatetimeConstants < UnitTest { const DATE_TIME_FORMAT = "%a %b %d %H:%M:%S %Y"; testSecondsInMinuteConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); const minAgo = startSec - Datetime.SECONDS_IN_MINUTE; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020"); + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").strptime(); this.assertTruthy(minAgo == endSec); } testSecondsInHourConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); const hourAgo = startSec - Datetime.SECONDS_IN_HOUR; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020"); + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").strptime(); this.assertTruthy(hourAgo == endSec); } testSecondsInDayConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); const dayAgo = startSec - Datetime.SECONDS_IN_DAY; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020"); + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").strptime(); this.assertTruthy(dayAgo == endSec); } testSecondsInWeekConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); const weekAgo = startSec - Datetime.SECONDS_IN_WEEK; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020"); + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").strptime(); this.assertTruthy(weekAgo == endSec); } } diff --git a/tests/datetime/datetime.du b/tests/datetime/datetime.du new file mode 100644 index 000000000..21e40071f --- /dev/null +++ b/tests/datetime/datetime.du @@ -0,0 +1,60 @@ + +/** + * new.du + * + * Testing the Datetime object + * + */ + + +from UnitTest import UnitTest; + +import Datetime; + +class TestDatetime < UnitTest { + testNewDatetimeDefault() { + const datetime = Datetime.new(); + this.assertNotNil(datetime); + } + testNewDatetimeUTC() { + const datetime = Datetime.newUTC(); + this.assertNotNil(datetime); + } + testNewDatetimeTimestampInteger() { + const datetime = Datetime.new(1577836800); + this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + } + testNewDatetimeFormatedString() { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + } + testDatetimeStrftime(data) { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.strftime(data["pattern"]), data["expected"]); + } + + testDatetimeStrptime() { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.strptime(), 1577836800); + } + + testDatetimeStrftimeProvider() { + return [ + {"pattern": "%y", "expected": "20"}, + {"pattern": "%Y", "expected": "2020"}, + {"pattern": "%b", "expected": "Jan"}, + {"pattern": "%B", "expected": "January"}, + {"pattern": "%m", "expected": "01"}, + {"pattern": "%d", "expected": "01"}, + {"pattern": "%a", "expected": "Wed"}, + {"pattern": "%A", "expected": "Wednesday"}, + {"pattern": "%H", "expected": "00"}, + {"pattern": "%M", "expected": "00"}, + {"pattern": "%S", "expected": "00"}, + {"pattern": "%Y-%m-%d %H:%M:%S", "expected": "2020-01-01 00:00:00"}, + {"pattern": "", "expected": ""}, // catch up the case of an empty string + ]; + } +} + +TestDatetime().run(); \ No newline at end of file diff --git a/tests/datetime/import.du b/tests/datetime/import.du index 042d113be..57bd438cf 100644 --- a/tests/datetime/import.du +++ b/tests/datetime/import.du @@ -7,3 +7,4 @@ import "constants.du"; import "strftime.du"; import "strptime.du"; +import "datetime.du"; From 888020d4d3a6b6bb88058f0c78d00874cade521e Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 3 Oct 2023 18:01:27 +0530 Subject: [PATCH 02/12] Removed gmtoff property usages --- src/optionals/datetime.c | 26 +++++++++++--------------- src/optionals/datetime.h | 1 - 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index a420fb1d6..eb1a04bcf 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -66,7 +66,6 @@ static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ tictoc.tm_mday = datetime->day; tictoc.tm_mon = datetime->month; tictoc.tm_year = datetime->year; - tictoc.tm_gmtoff = datetime->offset; tictoc.tm_isdst = -1; const time_t timestamp = mktime(&tictoc); localtime_r(×tamp, &tictoc); @@ -108,6 +107,7 @@ static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ } +#ifdef HAS_STRPTIME static Value datetimeStrptime(DictuVM *vm, int argCount, Value *args){ if (argCount != 0) { @@ -124,13 +124,13 @@ static Value datetimeStrptime(DictuVM *vm, int argCount, Value *args){ tictoc.tm_mday = datetime->day; tictoc.tm_mon = datetime->month; tictoc.tm_year = datetime->year; - tictoc.tm_gmtoff = datetime->offset; tictoc.tm_isdst = -1; const time_t timestamp = mktime(&tictoc); - localtime_r(×tamp, &tictoc); - return NUMBER_VAL((double) mktime(&tictoc)+tictoc.tm_gmtoff); + return NUMBER_VAL(timestamp+tictoc.tm_gmtoff); } +#endif + static Value datetimeToString(DictuVM *vm, int argCount, Value *args) { if (argCount != 0) { @@ -146,7 +146,6 @@ static Value datetimeToString(DictuVM *vm, int argCount, Value *args) { tictoc.tm_mday = datetime->day; tictoc.tm_mon = datetime->month; tictoc.tm_year = datetime->year; - tictoc.tm_gmtoff = datetime->offset; tictoc.tm_isdst = -1; const time_t timestamp = mktime(&tictoc); localtime_r(×tamp, &tictoc); @@ -165,8 +164,7 @@ ObjAbstract* newDatetimeObj( unsigned char hours, unsigned char day, unsigned char month, - unsigned short year, - long offset + unsigned short year ) { ObjAbstract *abstract = newAbstract(vm, freeDatetime, datetimeTypeToString); push(vm, OBJ_VAL(abstract)); @@ -178,7 +176,6 @@ ObjAbstract* newDatetimeObj( datetime->day = day; datetime->month = month; datetime->year = year; - datetime->offset = offset; /** * Setup Queue object methods @@ -206,7 +203,7 @@ static Value nowNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; localtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { @@ -221,7 +218,7 @@ static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; gmtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } @@ -236,7 +233,7 @@ static Value newUTCDatetimeNative(DictuVM *vm, int argCount, Value *args) { time_t t = time(NULL); struct tm tictoc; gmtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } @@ -251,9 +248,8 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { } struct tm tictoc = {0}; tictoc.tm_isdst = -1; - // char *end = strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc); if (strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc) != NULL) { - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } else { return NIL_VAL; } @@ -269,7 +265,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc = {0}; time_t num = (time_t)((long) AS_NUMBER(args[0])); gmtime_r(&num, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } if (argCount == 0) { @@ -277,7 +273,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; localtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year, tictoc.tm_gmtoff)); + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } runtimeError(vm, "new() takes 0,1 or 2 arguments, (%d given)", argCount); diff --git a/src/optionals/datetime.h b/src/optionals/datetime.h index b0d964955..3ba7f877f 100644 --- a/src/optionals/datetime.h +++ b/src/optionals/datetime.h @@ -19,7 +19,6 @@ typedef struct { unsigned char day; /* day of the month [1-31] */ unsigned char month; /* months since January [0-11] */ unsigned short year; /* years since 1900 */ - long offset; /* offset from UTC in seconds */ } Datetime; Value createDatetimeModule(DictuVM *vm); From 1fb7ff3e954b1c7dd7d8955aabf765b65f17e061 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 3 Oct 2023 18:10:35 +0530 Subject: [PATCH 03/12] Undefine strptime methods for windows systems --- docs/docs/standard-lib/datetime.md | 2 ++ src/optionals/datetime.c | 2 ++ tests/datetime/datetime.du | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index debf3b977..3f5bf8b5e 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -140,6 +140,8 @@ Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); // 1577836800 Returns a number which is the number of seconds from epoch, for a given datetime +**Note:** This is not available on windows systems. + ```cs const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); datetime.strptime(); // 1577836800 diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index eb1a04bcf..9ffa58633 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -182,7 +182,9 @@ ObjAbstract* newDatetimeObj( */ defineNative(vm, &abstract->values, "strftime", datetimeStrftime); + #ifdef HAS_STRPTIME defineNative(vm, &abstract->values, "strptime", datetimeStrptime); + #endif defineNative(vm, &abstract->values, "toString", datetimeToString); abstract->data = datetime; diff --git a/tests/datetime/datetime.du b/tests/datetime/datetime.du index 21e40071f..aa531351d 100644 --- a/tests/datetime/datetime.du +++ b/tests/datetime/datetime.du @@ -10,6 +10,7 @@ from UnitTest import UnitTest; import Datetime; +import System; class TestDatetime < UnitTest { testNewDatetimeDefault() { @@ -34,8 +35,10 @@ class TestDatetime < UnitTest { } testDatetimeStrptime() { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); - this.assertEquals(datetime.strptime(), 1577836800); + if (System.platform != "windows") { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.strptime(), 1577836800); + } } testDatetimeStrftimeProvider() { From b6381c217e581e149a42e2b80c47cf010572b6f1 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 3 Oct 2023 18:19:44 +0530 Subject: [PATCH 04/12] Prevent running strptime tests on windows systems --- docs/docs/standard-lib/datetime.md | 2 ++ tests/datetime/datetime.du | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index 3f5bf8b5e..2a3e14601 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -47,6 +47,8 @@ datetime.toString(); // Fri May 29 03:12:32 2020 Returns a datetime object for given time string format. +**Note:** This is not available on windows systems. + ```cs var datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); datetime.toString(); // Wed Jan 1 00:00:00 2020 diff --git a/tests/datetime/datetime.du b/tests/datetime/datetime.du index aa531351d..f1f48182b 100644 --- a/tests/datetime/datetime.du +++ b/tests/datetime/datetime.du @@ -26,12 +26,16 @@ class TestDatetime < UnitTest { this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); } testNewDatetimeFormatedString() { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); - this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + if (System.platform != "windows") { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + } } testDatetimeStrftime(data) { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); - this.assertEquals(datetime.strftime(data["pattern"]), data["expected"]); + if (System.platform != "windows") { + const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.strftime(data["pattern"]), data["expected"]); + } } testDatetimeStrptime() { From 386c7195e9cfb3b9389215e100f590b1e8b5d738 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Tue, 3 Oct 2023 18:23:32 +0530 Subject: [PATCH 05/12] Fixed documentation --- docs/docs/standard-lib/datetime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index 2a3e14601..5512b5aca 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -149,7 +149,7 @@ const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); datetime.strptime(); // 1577836800 ``` -### datetimeObj.strftime() -> String +### datetimeObj.strftime(String) -> String Returns a user-defined datetime formatted string for a datetime object, see [Datetime formats](#datetime-formats). From 246653a6034daadb1166ade374aecd1bc7e6a7fb Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Wed, 4 Oct 2023 19:30:16 +0530 Subject: [PATCH 06/12] Removed commented lines --- src/optionals/datetime.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 9ffa58633..3f8737547 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -403,8 +403,6 @@ Value createDatetimeModule(DictuVM *vm) { defineNative(vm, &module->values, "now", nowNative); defineNative(vm, &module->values, "nowUTC", nowUTCNative); - // defineNative(vm, &module->values, "now", nowNative); - // defineNative(vm, &module->values, "nowUTC", nowUTCNative); defineNative(vm, &module->values, "strftime", strftimeNative); #ifdef HAS_STRPTIME defineNative(vm, &module->values, "strptime", strptimeNative); From 9e0ef5fd9d3b0b98cbbf898b0d2f6a0e965a4cf7 Mon Sep 17 00:00:00 2001 From: Amuthan Mannar Date: Wed, 4 Oct 2023 19:35:20 +0530 Subject: [PATCH 07/12] Separate constant tests for datetime object --- tests/datetime/constants.du | 18 ++++++++--------- tests/datetime/datetime.du | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/tests/datetime/constants.du b/tests/datetime/constants.du index 2e129ccde..e8bb7e92e 100644 --- a/tests/datetime/constants.du +++ b/tests/datetime/constants.du @@ -15,34 +15,34 @@ class TestDatetimeConstants < UnitTest { const DATE_TIME_FORMAT = "%a %b %d %H:%M:%S %Y"; testSecondsInMinuteConstant() { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); const minAgo = startSec - Datetime.SECONDS_IN_MINUTE; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020"); this.assertTruthy(minAgo == endSec); } testSecondsInHourConstant() { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); const hourAgo = startSec - Datetime.SECONDS_IN_HOUR; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020"); this.assertTruthy(hourAgo == endSec); } testSecondsInDayConstant() { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); const dayAgo = startSec - Datetime.SECONDS_IN_DAY; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020"); this.assertTruthy(dayAgo == endSec); } testSecondsInWeekConstant() { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); const weekAgo = startSec - Datetime.SECONDS_IN_WEEK; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020"); this.assertTruthy(weekAgo == endSec); } } if (System.platform != "windows") { TestDatetimeConstants().run(); -} +} \ No newline at end of file diff --git a/tests/datetime/datetime.du b/tests/datetime/datetime.du index f1f48182b..0a85a5297 100644 --- a/tests/datetime/datetime.du +++ b/tests/datetime/datetime.du @@ -13,6 +13,9 @@ import Datetime; import System; class TestDatetime < UnitTest { + + const DATE_TIME_FORMAT = "%a %b %d %H:%M:%S %Y"; + testNewDatetimeDefault() { const datetime = Datetime.new(); this.assertNotNil(datetime); @@ -45,6 +48,42 @@ class TestDatetime < UnitTest { } } + testSecondsInMinuteConstant() { + if (System.platform != "windows") { + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const minAgo = startSec - Datetime.SECONDS_IN_MINUTE; + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").strptime(); + this.assertTruthy(minAgo == endSec); + } + } + + testSecondsInHourConstant() { + if (System.platform != "windows") { + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const hourAgo = startSec - Datetime.SECONDS_IN_HOUR; + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").strptime(); + this.assertTruthy(hourAgo == endSec); + } + } + + testSecondsInDayConstant() { + if (System.platform != "windows") { + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const dayAgo = startSec - Datetime.SECONDS_IN_DAY; + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").strptime(); + this.assertTruthy(dayAgo == endSec); + } + } + + testSecondsInWeekConstant() { + if (System.platform != "windows") { + const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const weekAgo = startSec - Datetime.SECONDS_IN_WEEK; + const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").strptime(); + this.assertTruthy(weekAgo == endSec); + } + } + testDatetimeStrftimeProvider() { return [ {"pattern": "%y", "expected": "20"}, From 5d4e5238aba472b0ddb29ddae18dc3e059a24dff Mon Sep 17 00:00:00 2001 From: Amuthan Mannan Date: Sat, 21 Oct 2023 21:48:48 +0530 Subject: [PATCH 08/12] Reimplemented methods of datetime module and objects --- docs/docs/standard-lib/datetime.md | 64 ++++------- src/optionals/datetime.c | 172 +++++------------------------ tests/datetime/constants.du | 16 +-- tests/datetime/datetime.du | 34 +++--- tests/datetime/strftime.du | 8 +- tests/datetime/strptime.du | 2 +- 6 files changed, 84 insertions(+), 212 deletions(-) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index 5512b5aca..678fc3ddd 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -40,18 +40,7 @@ Returns a datetime object with current datetime. ```cs var datetime = Datetime.new(); -datetime.toString(); // Fri May 29 03:12:32 2020 -``` - -### Datetime.new(String, String) -> Datetime - -Returns a datetime object for given time string format. - -**Note:** This is not available on windows systems. - -```cs -var datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); -datetime.toString(); // Wed Jan 1 00:00:00 2020 +datetime.strftime(); // Fri May 29 03:12:32 2020 ``` ### Datetime.new(Number) -> Datetime @@ -60,7 +49,7 @@ Returns a datetime object for given number of seconds from epoch. ```cs const datetime = Datetime.new(1577836800); -datetime.toString(); // Wed Jan 1 00:00:00 2020 +datetime.strftime(); // Wed Jan 1 00:00:00 2020 ``` @@ -80,6 +69,18 @@ Returns a human readable UTC datetime string. Datetime.now(); // Fri May 29 02:12:32 2020 ``` + +### Datetime.strptime(String, String) -> Datetime + +Returns a datetime object for given time string format. + +**Note:** This is not available on windows systems. + +```cs +var datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.strftime(); // Wed Jan 1 00:00:00 2020 +``` + ### Datetime formats | Directive | Description | Example | @@ -113,48 +114,31 @@ Datetime.now(); // Fri May 29 02:12:32 2020 | %% | A literal % | | -### Datetime.strftime(String, Number: time -> Optional) -> String - -Returns a user-defined datetime formatted string, see [Datetime formats](#datetime-formats). `strftime` also takes an optional argument -which is a UNIX timestamp, so the date is formatted from the given timestamp rather than -the current point in time. - -```cs -Datetime.strftime("Today is %A"); // Today is Friday - -var time = System.time(); -Datetime.strftime("Some point in time %H:%M:%S", time); -``` - -### Datetime.strptime(String, String) -> Number +### datetimeObj.getTime() -> Number -Returns a number which is the number of seconds from epoch. `strptime` expects two parameters -the first parameter being the date format, see [Datetime formats](#datetime-formats) and the second -the date string in the format of the first parameter. +Returns a number which is the number of seconds from epoch, for a given datetime **Note:** This is not available on windows systems. ```cs -Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); // 1577836800 +const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.getTime(); // 1577836800 ``` -### datetimeObj.strptime() -> Number +### datetimeObj.strftime(String -> Optional) -> String -Returns a number which is the number of seconds from epoch, for a given datetime - -**Note:** This is not available on windows systems. +Returns a user-defined datetime formatted string for a datetime object, see [Datetime formats](#datetime-formats). ```cs -const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); -datetime.strptime(); // 1577836800 +const datetime = Datetime.new(); +datetime.strftime("Today is %A"); // Today is Friday ``` -### datetimeObj.strftime(String) -> String +Returns in default format when user defined format is not provided -Returns a user-defined datetime formatted string for a datetime object, see [Datetime formats](#datetime-formats). ```cs const datetime = Datetime.new(); -datetime.strftime("Today is %A"); // Today is Friday +datetime.strftime(); // Sat Oct 21 21:44:25 2023 ``` diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 3f8737547..b94aae283 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -33,24 +33,36 @@ char *datetimeTypeToString(ObjAbstract *abstract) { } static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ - if (argCount != 1) { - runtimeError(vm, "strftime() takes 1 argument (%d given)", argCount); - return EMPTY_VAL; - } - if (!IS_STRING(args[1])) { - runtimeError(vm, "strftime() argument must be a string"); + + if (argCount > 1) { + runtimeError(vm, "strftime() takes 0 or 1 argument (%d given)", argCount); return EMPTY_VAL; } - ObjString *format = AS_STRING(args[1]); + char *fmt; + int len; + + int default_length = 128; - /** this is to avoid an eternal loop while calling strftime() below */ - if (0 == format->length) + if(argCount == 1) { + if (!IS_STRING(args[1])) { + runtimeError(vm, "strftime() argument must be a string"); + return EMPTY_VAL; + } + ObjString *format = AS_STRING(args[1]); + /** this is to avoid an eternal loop while calling strftime() below */ + if (0 == format->length) return OBJ_VAL(copyString(vm, "", 0)); - char *fmt = format->chars; - int len = (format->length > 128 ? format->length * 4 : 128); + fmt = format->chars; + len = (format->length > default_length ? format->length * 4 : default_length); + } + + else { + len = default_length; + fmt = "%a %b %d %H:%M:%S %Y"; + } char *point = ALLOCATE(vm, char, len); if (point == NULL) { @@ -108,10 +120,10 @@ static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ #ifdef HAS_STRPTIME -static Value datetimeStrptime(DictuVM *vm, int argCount, Value *args){ +static Value datetimeGetTime(DictuVM *vm, int argCount, Value *args){ if (argCount != 0) { - runtimeError(vm, "strptime() take 0 argument (%d given)", argCount); + runtimeError(vm, "getTime() take 0 argument (%d given)", argCount); return EMPTY_VAL; } @@ -132,31 +144,6 @@ static Value datetimeStrptime(DictuVM *vm, int argCount, Value *args){ #endif -static Value datetimeToString(DictuVM *vm, int argCount, Value *args) { - if (argCount != 0) { - runtimeError(vm, "toString() takes no arguments (%d given)", argCount); - return EMPTY_VAL; - } - - Datetime *datetime = AS_DATETIME(args[0]); - struct tm tictoc; - tictoc.tm_sec = datetime->seconds; - tictoc.tm_min = datetime->minutes; - tictoc.tm_hour = datetime->hours; - tictoc.tm_mday = datetime->day; - tictoc.tm_mon = datetime->month; - tictoc.tm_year = datetime->year; - tictoc.tm_isdst = -1; - const time_t timestamp = mktime(&tictoc); - localtime_r(×tamp, &tictoc); - - char time[26]; - asctime_r(&tictoc, time); - - return OBJ_VAL(copyString(vm, time, strlen(time) - 1)); -} - - ObjAbstract* newDatetimeObj( DictuVM *vm, unsigned char seconds, @@ -183,9 +170,8 @@ ObjAbstract* newDatetimeObj( defineNative(vm, &abstract->values, "strftime", datetimeStrftime); #ifdef HAS_STRPTIME - defineNative(vm, &abstract->values, "strptime", datetimeStrptime); + defineNative(vm, &abstract->values, "getTime", datetimeGetTime); #endif - defineNative(vm, &abstract->values, "toString", datetimeToString); abstract->data = datetime; pop(vm); @@ -241,22 +227,6 @@ static Value newUTCDatetimeNative(DictuVM *vm, int argCount, Value *args) { static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); - - #ifdef HAS_STRPTIME - if (argCount == 2) { - if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { - runtimeError(vm, "new() takes 2 arguments, must be strings (%d given)", argCount); - return EMPTY_VAL; - } - struct tm tictoc = {0}; - tictoc.tm_isdst = -1; - if (strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc) != NULL) { - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); - } else { - return NIL_VAL; - } - } - #endif if(argCount == 1) { if (!IS_NUMBER(args[0])) { @@ -278,7 +248,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } - runtimeError(vm, "new() takes 0,1 or 2 arguments, (%d given)", argCount); + runtimeError(vm, "new() takes 0 or 1 argument, (%d given)", argCount); return EMPTY_VAL; } @@ -299,95 +269,14 @@ static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { tictoc.tm_mday = 1; tictoc.tm_isdst = -1; - char *end = strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc); - - if (end == NULL) { - return NIL_VAL; + if (strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc) != NULL) { + return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } - return NUMBER_VAL((double) mktime(&tictoc) + tictoc.tm_gmtoff); + return NIL_VAL; } #endif -static Value strftimeNative(DictuVM *vm, int argCount, Value *args) { - if (argCount != 1 && argCount != 2) { - runtimeError(vm, "strftime() takes 1 or 2 arguments (%d given)", argCount); - return EMPTY_VAL; - } - - if (!IS_STRING(args[0])) { - runtimeError(vm, "strftime() argument must be a string"); - return EMPTY_VAL; - } - - time_t t; - - if (argCount == 2) { - if (!IS_NUMBER(args[1])) { - runtimeError(vm, "strftime() optional argument must be a number"); - return EMPTY_VAL; - } - - t = AS_NUMBER(args[1]); - } else { - time(&t); - } - - ObjString *format = AS_STRING(args[0]); - - /** this is to avoid an eternal loop while calling strftime() below */ - if (0 == format->length) - return OBJ_VAL(copyString(vm, "", 0)); - - char *fmt = format->chars; - - struct tm tictoc; - int len = (format->length > 128 ? format->length * 4 : 128); - - char *point = ALLOCATE(vm, char, len); - if (point == NULL) { - runtimeError(vm, "Memory error on strftime()!"); - return EMPTY_VAL; - } - - gmtime_r(&t, &tictoc); - - /** - * strtime returns 0 when it fails to write - this would be due to the buffer - * not being large enough. In that instance we double the buffer length until - * there is a big enough buffer. - */ - - /** however is not guaranteed that 0 indicates a failure (`man strftime' says so). - * So we might want to catch up the eternal loop, by using a maximum iterator. - */ - int max_iterations = 8; // maximum 65536 bytes with the default 128 len, - // more if the given string is > 128 - int iterator = 0; - while (strftime(point, sizeof(char) * len, fmt, &tictoc) == 0) { - if (++iterator > max_iterations) { - FREE_ARRAY(vm, char, point, len); - return OBJ_VAL(copyString(vm, "", 0)); - } - - len *= 2; - - point = GROW_ARRAY(vm, point, char, len / 2, len); - if (point == NULL) { - runtimeError(vm, "Memory error on strftime()!"); - return EMPTY_VAL; - } - } - - int length = strlen(point); - - if (length != len) { - point = SHRINK_ARRAY(vm, point, char, len, length + 1); - } - - return OBJ_VAL(takeString(vm, point, length)); -} - Value createDatetimeModule(DictuVM *vm) { ObjString *name = copyString(vm, "Datetime", 8); push(vm, OBJ_VAL(name)); @@ -403,7 +292,6 @@ Value createDatetimeModule(DictuVM *vm) { defineNative(vm, &module->values, "now", nowNative); defineNative(vm, &module->values, "nowUTC", nowUTCNative); - defineNative(vm, &module->values, "strftime", strftimeNative); #ifdef HAS_STRPTIME defineNative(vm, &module->values, "strptime", strptimeNative); #endif diff --git a/tests/datetime/constants.du b/tests/datetime/constants.du index e8bb7e92e..9c5aaa77c 100644 --- a/tests/datetime/constants.du +++ b/tests/datetime/constants.du @@ -15,30 +15,30 @@ class TestDatetimeConstants < UnitTest { const DATE_TIME_FORMAT = "%a %b %d %H:%M:%S %Y"; testSecondsInMinuteConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const minAgo = startSec - Datetime.SECONDS_IN_MINUTE; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020"); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").getTime(); this.assertTruthy(minAgo == endSec); } testSecondsInHourConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const hourAgo = startSec - Datetime.SECONDS_IN_HOUR; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020"); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").getTime(); this.assertTruthy(hourAgo == endSec); } testSecondsInDayConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const dayAgo = startSec - Datetime.SECONDS_IN_DAY; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020"); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").getTime(); this.assertTruthy(dayAgo == endSec); } testSecondsInWeekConstant() { - const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020"); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const weekAgo = startSec - Datetime.SECONDS_IN_WEEK; - const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020"); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").getTime(); this.assertTruthy(weekAgo == endSec); } } diff --git a/tests/datetime/datetime.du b/tests/datetime/datetime.du index 0a85a5297..82cfca620 100644 --- a/tests/datetime/datetime.du +++ b/tests/datetime/datetime.du @@ -26,60 +26,56 @@ class TestDatetime < UnitTest { } testNewDatetimeTimestampInteger() { const datetime = Datetime.new(1577836800); - this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + this.assertEquals(datetime.strftime(), "Wed Jan 01 00:00:00 2020"); } testNewDatetimeFormatedString() { if (System.platform != "windows") { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); - this.assertEquals(datetime.toString(), "Wed Jan 1 00:00:00 2020"); + const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.strftime(), "Wed Jan 01 00:00:00 2020"); } } testDatetimeStrftime(data) { if (System.platform != "windows") { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); this.assertEquals(datetime.strftime(data["pattern"]), data["expected"]); } } - testDatetimeStrptime() { + testDatetimeGetTime() { if (System.platform != "windows") { - const datetime = Datetime.new("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); - this.assertEquals(datetime.strptime(), 1577836800); + const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + this.assertEquals(datetime.getTime(), 1577836800); } } - testSecondsInMinuteConstant() { if (System.platform != "windows") { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const minAgo = startSec - Datetime.SECONDS_IN_MINUTE; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:11:32 2020").getTime(); this.assertTruthy(minAgo == endSec); } } - testSecondsInHourConstant() { if (System.platform != "windows") { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const hourAgo = startSec - Datetime.SECONDS_IN_HOUR; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 02:12:32 2020").getTime(); this.assertTruthy(hourAgo == endSec); } } - testSecondsInDayConstant() { if (System.platform != "windows") { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const dayAgo = startSec - Datetime.SECONDS_IN_DAY; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 28 03:12:32 2020").getTime(); this.assertTruthy(dayAgo == endSec); } } - testSecondsInWeekConstant() { if (System.platform != "windows") { - const startSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").strptime(); + const startSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 29 03:12:32 2020").getTime(); const weekAgo = startSec - Datetime.SECONDS_IN_WEEK; - const endSec = Datetime.new(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").strptime(); + const endSec = Datetime.strptime(this.DATE_TIME_FORMAT, "Fri May 22 03:12:32 2020").getTime(); this.assertTruthy(weekAgo == endSec); } } diff --git a/tests/datetime/strftime.du b/tests/datetime/strftime.du index cc56a4cea..4c30c78f5 100644 --- a/tests/datetime/strftime.du +++ b/tests/datetime/strftime.du @@ -11,11 +11,15 @@ import Datetime; class TestStrftimeDatetimeModule < UnitTest { testStrftime(data) { // 1577836800 is 1/1/2020 at 12:00 am (Wednesday) - this.assertEquals(Datetime.strftime(data["pattern"], 1577836800), data["expected"]); + this.assertEquals(Datetime.new(1577836800).strftime(data["pattern"]), data["expected"]); + } + + testStrftimeWithoutPattern() { + this.assertEquals(Datetime.new(1577836800).strftime(), "Wed Jan 01 00:00:00 2020"); } testStrftimeUPattern() { - this.assertTruthy(Datetime.strftime("%u", 1577836800) == "3" or Datetime.strftime("%u", 1577836800) == ""); + this.assertTruthy(Datetime.new(1577836800).strftime("%u") == "3" or Datetime.new(1577836800).strftime("%u") == ""); } testStrftimeProvider() { diff --git a/tests/datetime/strptime.du b/tests/datetime/strptime.du index 3fb2b6f0e..bbaa5896e 100644 --- a/tests/datetime/strptime.du +++ b/tests/datetime/strptime.du @@ -12,7 +12,7 @@ import System; class TestStrptimeDatetimeModule < UnitTest { testStrptime(data) { // 1577836800 is 1/1/2020 at 12:00 am (Wednesday) - this.assertEquals(Datetime.strptime(data["pattern"], data["value"]), 1577836800); + this.assertEquals(Datetime.strptime(data["pattern"], data["value"]).getTime(), 1577836800); } testStrptimeProvider() { From e9e26e9adb20379afe3c759b539688d5dd25f1ad Mon Sep 17 00:00:00 2001 From: Amuthan Mannan Date: Tue, 24 Oct 2023 12:30:43 +0530 Subject: [PATCH 09/12] Implemeneted formatting suggestions --- docs/docs/standard-lib/datetime.md | 2 +- src/optionals/datetime.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index 678fc3ddd..2ac53c840 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -134,7 +134,7 @@ const datetime = Datetime.new(); datetime.strftime("Today is %A"); // Today is Friday ``` -Returns in default format when user defined format is not provided +Returns in default format "%a %b %d %H:%M:%S %Y", when user defined format is not provided ```cs diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index b94aae283..3d38c5780 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -27,16 +27,16 @@ Datetime* createDatetime(DictuVM *vm) { char *datetimeTypeToString(ObjAbstract *abstract) { UNUSED(abstract); - char *queueString = malloc(sizeof(char) * 11); - snprintf(queueString, 11, ""); - return queueString; + char *datetimeString = malloc(sizeof(char) * 11); + snprintf(datetimeString, 11, ""); + return datetimeString; } static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ if (argCount > 1) { - runtimeError(vm, "strftime() takes 0 or 1 argument (%d given)", argCount); + runtimeError(vm, "strftime() takes 0 or 1 arguments (%d given)", argCount); return EMPTY_VAL; } @@ -183,7 +183,7 @@ static Value nowNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); if (argCount != 0) { - runtimeError(vm, "nowNative() takes no arguments (%d given)", argCount); + runtimeError(vm, "now() takes no arguments (%d given)", argCount); return EMPTY_VAL; } @@ -198,7 +198,7 @@ static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); if (argCount != 0) { - runtimeError(vm, "nowUTCNative() takes no arguments (%d given)", argCount); + runtimeError(vm, "nowUTC() takes no arguments (%d given)", argCount); return EMPTY_VAL; } @@ -214,7 +214,7 @@ static Value newUTCDatetimeNative(DictuVM *vm, int argCount, Value *args) { UNUSED(args); if (argCount != 0) { - runtimeError(vm, "nowUTCNative() takes no arguments (%d given)", argCount); + runtimeError(vm, "nowUTC() takes no arguments (%d given)", argCount); return EMPTY_VAL; } @@ -230,7 +230,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { if(argCount == 1) { if (!IS_NUMBER(args[0])) { - runtimeError(vm, "new() takes 1 argument , must be a number"); + runtimeError(vm, "new() argument must be a number"); return EMPTY_VAL; } @@ -248,7 +248,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); } - runtimeError(vm, "new() takes 0 or 1 argument, (%d given)", argCount); + runtimeError(vm, "new() takes 0 or 1 arguments (%d given)", argCount); return EMPTY_VAL; } From b31f751da26289ad16bcd7ce1e638b86ea97d346 Mon Sep 17 00:00:00 2001 From: MannarAmuthan Date: Mon, 4 Dec 2023 22:07:40 +0530 Subject: [PATCH 10/12] Represent time as signed integer --- src/optionals/datetime.c | 61 +++++++++++----------------------------- src/optionals/datetime.h | 8 ++---- 2 files changed, 19 insertions(+), 50 deletions(-) diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 3d38c5780..5d8141197 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -72,15 +72,13 @@ static Value datetimeStrftime(DictuVM *vm, int argCount, Value *args){ Datetime *datetime = AS_DATETIME(args[0]); struct tm tictoc; - tictoc.tm_sec = datetime->seconds; - tictoc.tm_min = datetime->minutes; - tictoc.tm_hour = datetime->hours; - tictoc.tm_mday = datetime->day; - tictoc.tm_mon = datetime->month; - tictoc.tm_year = datetime->year; + if(datetime->is_local){ + localtime_r(&datetime->time, &tictoc); + } + else{ + gmtime_r(&datetime->time, &tictoc); + } tictoc.tm_isdst = -1; - const time_t timestamp = mktime(&tictoc); - localtime_r(×tamp, &tictoc); /** * strtime returns 0 when it fails to write - this would be due to the buffer @@ -128,42 +126,18 @@ static Value datetimeGetTime(DictuVM *vm, int argCount, Value *args){ } Datetime *datetime = AS_DATETIME(args[0]); - struct tm tictoc = {0}; - - tictoc.tm_sec = datetime->seconds; - tictoc.tm_min = datetime->minutes; - tictoc.tm_hour = datetime->hours; - tictoc.tm_mday = datetime->day; - tictoc.tm_mon = datetime->month; - tictoc.tm_year = datetime->year; - tictoc.tm_isdst = -1; - const time_t timestamp = mktime(&tictoc); - - return NUMBER_VAL(timestamp+tictoc.tm_gmtoff); + return NUMBER_VAL(datetime->time); } #endif -ObjAbstract* newDatetimeObj( - DictuVM *vm, - unsigned char seconds, - unsigned char minutes, - unsigned char hours, - unsigned char day, - unsigned char month, - unsigned short year - ) { +ObjAbstract* newDatetimeObj( DictuVM *vm, long time, int is_local) { ObjAbstract *abstract = newAbstract(vm, freeDatetime, datetimeTypeToString); push(vm, OBJ_VAL(abstract)); Datetime *datetime = createDatetime(vm); - datetime->seconds = seconds; - datetime->minutes = minutes; - datetime->hours = hours; - datetime->day = day; - datetime->month = month; - datetime->year = year; - + datetime->time = time; + datetime->is_local = is_local; /** * Setup Queue object methods */ @@ -191,7 +165,7 @@ static Value nowNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; localtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + return OBJ_VAL(newDatetimeObj(vm, (long)t, true)); } static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { @@ -206,7 +180,7 @@ static Value nowUTCNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; gmtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + return OBJ_VAL(newDatetimeObj(vm, (long)t, false)); } @@ -221,7 +195,7 @@ static Value newUTCDatetimeNative(DictuVM *vm, int argCount, Value *args) { time_t t = time(NULL); struct tm tictoc; gmtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + return OBJ_VAL(newDatetimeObj(vm, (long)t, false)); } @@ -234,10 +208,8 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { return EMPTY_VAL; } - struct tm tictoc = {0}; time_t num = (time_t)((long) AS_NUMBER(args[0])); - gmtime_r(&num, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + return OBJ_VAL(newDatetimeObj(vm, (long)num, false)); } if (argCount == 0) { @@ -245,7 +217,7 @@ static Value newDatetimeNative(DictuVM *vm, int argCount, Value *args) { struct tm tictoc; localtime_r(&t, &tictoc); - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + return OBJ_VAL(newDatetimeObj(vm, (long)t, true)); } runtimeError(vm, "new() takes 0 or 1 arguments (%d given)", argCount); @@ -270,7 +242,8 @@ static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { tictoc.tm_isdst = -1; if (strptime(AS_CSTRING(args[1]), AS_CSTRING(args[0]), &tictoc) != NULL) { - return OBJ_VAL(newDatetimeObj(vm, tictoc.tm_sec, tictoc.tm_min, tictoc.tm_hour, tictoc.tm_mday, tictoc.tm_mon, tictoc.tm_year)); + const time_t timestamp = mktime(&tictoc); + return OBJ_VAL(newDatetimeObj(vm, (long)timestamp+tictoc.tm_gmtoff, false)); } return NIL_VAL; diff --git a/src/optionals/datetime.h b/src/optionals/datetime.h index 3ba7f877f..4d1b73443 100644 --- a/src/optionals/datetime.h +++ b/src/optionals/datetime.h @@ -13,12 +13,8 @@ #include "optionals.h" typedef struct { - unsigned char seconds; /* seconds after the minute [0-60] */ - unsigned char minutes; /* minutes after the hour [0-59] */ - unsigned char hours; /* hours since midnight [0-23] */ - unsigned char day; /* day of the month [1-31] */ - unsigned char month; /* months since January [0-11] */ - unsigned short year; /* years since 1900 */ + long time; /* timestamp */ + bool is_local; } Datetime; Value createDatetimeModule(DictuVM *vm); From 3da1164e43cd635dd67bd72707976c58f32edc89 Mon Sep 17 00:00:00 2001 From: MannarAmuthan Date: Sat, 6 Jan 2024 20:16:41 +0530 Subject: [PATCH 11/12] Implemented duration object --- src/optionals/datetime.c | 164 ++++++++++++++++++++++++++++++++++++- src/optionals/datetime.h | 5 ++ tests/datetime/duration.du | 57 +++++++++++++ tests/datetime/import.du | 1 + 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 tests/datetime/duration.du diff --git a/src/optionals/datetime.c b/src/optionals/datetime.c index 5d8141197..67377ad28 100644 --- a/src/optionals/datetime.c +++ b/src/optionals/datetime.c @@ -12,6 +12,12 @@ #endif #define AS_DATETIME(v) ((Datetime*)AS_ABSTRACT(v)->data) +#define AS_DURATION(v) ((Duration*)AS_ABSTRACT(v)->data) + + +static Value datetimeAddDuration(DictuVM *vm, int argCount, Value *args); +static Value datetimeSubDuration(DictuVM *vm, int argCount, Value *args); +static Value datetimeDelta(DictuVM *vm, int argCount, Value *args); void freeDatetime(DictuVM *vm, ObjAbstract *abstract) { @@ -139,7 +145,7 @@ ObjAbstract* newDatetimeObj( DictuVM *vm, long time, int is_local) { datetime->time = time; datetime->is_local = is_local; /** - * Setup Queue object methods + * Setup Datetime object methods */ defineNative(vm, &abstract->values, "strftime", datetimeStrftime); @@ -147,6 +153,10 @@ ObjAbstract* newDatetimeObj( DictuVM *vm, long time, int is_local) { defineNative(vm, &abstract->values, "getTime", datetimeGetTime); #endif + defineNative(vm, &abstract->values, "add", datetimeAddDuration); + defineNative(vm, &abstract->values, "sub", datetimeSubDuration); + defineNative(vm, &abstract->values, "delta", datetimeDelta); + abstract->data = datetime; pop(vm); @@ -250,6 +260,156 @@ static Value strptimeNative(DictuVM *vm, int argCount, Value *args) { } #endif + +// Duration object methods + +void freeDuration(DictuVM *vm, ObjAbstract *abstract) { + FREE(vm, Duration, abstract->data); +} + +Duration* createDuration(DictuVM *vm) { + Duration *duration = ALLOCATE(vm, Duration, 1); + return duration; +} + + +char *durationTypeToString(ObjAbstract *abstract) { + UNUSED(abstract); + + char *durationString = malloc(sizeof(char) * 11); + snprintf(durationString, 11, ""); + return durationString; +} + +static Value datetimeAddDuration(DictuVM *vm, int argCount, Value *args){ + + if (argCount != 1) { + runtimeError(vm, "add() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + Datetime *datetime = AS_DATETIME(args[0]); + Duration *duration = AS_DURATION(args[1]); + + struct tm* timeinfo = localtime(&datetime->time); + + timeinfo->tm_sec += duration->seconds; + + long newTime = mktime(timeinfo); + + return OBJ_VAL(newDatetimeObj(vm, newTime, datetime->is_local)); +} + +static Value datetimeSubDuration(DictuVM *vm, int argCount, Value *args){ + + if (argCount != 1) { + runtimeError(vm, "sub() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + Datetime *datetime = AS_DATETIME(args[0]); + Duration *duration = AS_DURATION(args[1]); + + struct tm* timeinfo = localtime(&datetime->time); + + timeinfo->tm_sec -= duration->seconds; + + long newTime = mktime(timeinfo); + + return OBJ_VAL(newDatetimeObj(vm, newTime, datetime->is_local)); +} + +ObjAbstract* newDurationObj( DictuVM *vm, unsigned long seconds) { + ObjAbstract *abstract = newAbstract(vm, freeDuration, durationTypeToString); + push(vm, OBJ_VAL(abstract)); + + Duration *duration = createDuration(vm); + duration->seconds = seconds; + + abstract->data = duration; + pop(vm); + + return abstract; +} + +static Value datetimeDelta(DictuVM *vm, int argCount, Value *args) { + + if (argCount != 1) { + runtimeError(vm, "delta() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + Datetime *datetimeOne = AS_DATETIME(args[0]); + Datetime *datetimeTwo = AS_DATETIME(args[1]); + + return OBJ_VAL(newDurationObj(vm, labs(datetimeOne->time - datetimeTwo->time))); +} + + +#define HANDLE_DURATION_PARAM(key, field) \ + if (strstr(key, #field)) { \ + if (!IS_NUMBER(entry->value)) { \ + runtimeError(vm, "Duration parameter key \"" #field "\" value must be a number"); \ + return EMPTY_VAL; \ + } \ + field = (unsigned int)AS_NUMBER(entry->value); \ + } + +static Value newDurationNative(DictuVM *vm, int argCount, Value *args) { + UNUSED(args); + + if (argCount != 1) { + runtimeError(vm, "duration() takes 1 argument (%d given)", argCount); + return EMPTY_VAL; + } + + if (!IS_DICT(args[0])) { + runtimeError(vm, "duration() argument must be a dictionary"); + return EMPTY_VAL; + } + + ObjDict *opts = AS_DICT(args[0]); + + unsigned int days = 0; + unsigned int seconds = 0; + unsigned int minutes = 0; + unsigned int hours = 0; + unsigned int weeks = 0; + + if (opts->count != 0) { + for (int i = 0; i <= opts->capacityMask; i++) { + DictItem *entry = &opts->entries[i]; + if (IS_EMPTY(entry->key)) { + continue; + } + + char *key; + + if (IS_STRING(entry->key)) { + ObjString *s = AS_STRING(entry->key); + key = s->chars; + } else { + runtimeError(vm, "Duration parameter key must be a string"); + return EMPTY_VAL; + } + + + HANDLE_DURATION_PARAM(key, days) + else HANDLE_DURATION_PARAM(key, seconds) + else HANDLE_DURATION_PARAM(key, minutes) + else HANDLE_DURATION_PARAM(key, hours) + else HANDLE_DURATION_PARAM(key, weeks) + else { + runtimeError(vm, "unknown parameter key '%s' found in duration dictionary", key); + return EMPTY_VAL; + } + } + } + + unsigned long final_seconds = seconds + (60*minutes) + (3600*hours) + (24 * 3600 * days) + (24 * 3600 * 7 * weeks); + + return OBJ_VAL(newDurationObj(vm, final_seconds)); +} + + Value createDatetimeModule(DictuVM *vm) { ObjString *name = copyString(vm, "Datetime", 8); push(vm, OBJ_VAL(name)); @@ -269,6 +429,8 @@ Value createDatetimeModule(DictuVM *vm) { defineNative(vm, &module->values, "strptime", strptimeNative); #endif + defineNative(vm, &module->values, "duration", newDurationNative); + /** * Define Datetime properties */ diff --git a/src/optionals/datetime.h b/src/optionals/datetime.h index 4d1b73443..89cecdc71 100644 --- a/src/optionals/datetime.h +++ b/src/optionals/datetime.h @@ -17,6 +17,11 @@ typedef struct { bool is_local; } Datetime; + +typedef struct { + unsigned long seconds; +} Duration; + Value createDatetimeModule(DictuVM *vm); #endif //dictu_datetime_h diff --git a/tests/datetime/duration.du b/tests/datetime/duration.du new file mode 100644 index 000000000..e88c95413 --- /dev/null +++ b/tests/datetime/duration.du @@ -0,0 +1,57 @@ +/** + * strftime.du + * + * Testing the Duration object + * + */ +from UnitTest import UnitTest; + +import Datetime; + +class TestDatetimeDuration < UnitTest { + testDurationAddition(data) { + var duration = Datetime.duration(data['duration']); + + var testDatetimeObj = Datetime.new(1577836800); + this.assertEquals(testDatetimeObj.add(duration).strftime(), data['expected']); + this.assertEquals(testDatetimeObj.strftime(), "Wed Jan 01 00:00:00 2020"); + } + + + testDurationSubtraction(data) { + var duration = Datetime.duration(data['duration']); + + var testDatetimeObj = Datetime.new(1577836800); + this.assertEquals(testDatetimeObj.sub(duration).strftime(), data['expected']); + this.assertEquals(testDatetimeObj.strftime(), "Wed Jan 01 00:00:00 2020"); + } + + testDelta() { + var datetimeOne = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); + var datetimeTwo = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-02 00:00:00"); + datetimeOne.delta(datetimeTwo); + } + + testDurationAdditionProvider() { + return [ + {"duration": { 'days': 1}, "expected": "Thu Jan 02 00:00:00 2020"}, + {"duration": {'days': 1, 'seconds': 3600, 'minutes': 1, 'hours': 1}, "expected": "Thu Jan 02 02:01:00 2020"}, + {"duration": {'weeks': 4 }, "expected": "Wed Jan 29 00:00:00 2020"}, + {"duration": { 'days': 100, 'seconds': 3600}, "expected": "Fri Apr 10 01:00:00 2020"}, + {"duration": {'days': 1096, 'weeks': 52}, "expected": "Sun Dec 31 00:00:00 2023"} + ]; + } + + + testDurationSubtractionProvider() { + return [ + {"duration": { 'days': 1}, "expected": "Tue Dec 31 00:00:00 2019"}, + {"duration": {'days': 1, 'seconds': 3600, 'minutes': 1, 'hours': 1}, "expected": "Mon Dec 30 21:59:00 2019"}, + {"duration": {'weeks': 4 }, "expected": "Wed Dec 04 00:00:00 2019"}, + {"duration": { 'days': 100, 'seconds': 3600}, "expected": "Sun Sep 22 23:00:00 2019"}, + {"duration": {'days': 1096, 'weeks': 52}, "expected": "Sat Jan 02 00:00:00 2016"} + ]; + } +} + +TestDatetimeDuration().run(); diff --git a/tests/datetime/import.du b/tests/datetime/import.du index 57bd438cf..39f2bce54 100644 --- a/tests/datetime/import.du +++ b/tests/datetime/import.du @@ -8,3 +8,4 @@ import "constants.du"; import "strftime.du"; import "strptime.du"; import "datetime.du"; +import "duration.du"; From 2d789e4c60f29956dda34b4865215af38dd7cb7e Mon Sep 17 00:00:00 2001 From: MannarAmuthan Date: Sun, 7 Jan 2024 11:05:56 +0530 Subject: [PATCH 12/12] Updated doc --- docs/docs/standard-lib/datetime.md | 47 ++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/docs/standard-lib/datetime.md b/docs/docs/standard-lib/datetime.md index 2ac53c840..e023a764d 100644 --- a/docs/docs/standard-lib/datetime.md +++ b/docs/docs/standard-lib/datetime.md @@ -142,3 +142,50 @@ const datetime = Datetime.new(); datetime.strftime(); // Sat Oct 21 21:44:25 2023 ``` + +## Duration + +A Duration object represents a time delta, the difference between two datetimes. This Duration object can be used to create new datetime objects, by performing operations such as addition and subtraction. + +### Datetime.duration(Dict) -> Duration + +Duration object is constructed by passing dictionary duration properties. weeks, days, hours, minutes and seconds are allowed properties. + +```cs +import Datetime; +const duration = Datetime.duration({'weeks':0, 'days': 1, 'seconds': 0, 'minutes': 0, 'hours': 0}); +``` + +### datetimeObj.add(Duration) -> Datetime + +Returns a new Datetime object after adding given duration. + +```cs +const duration = Datetime.duration({'weeks':0, 'days': 1, 'seconds': 0, 'minutes': 0, 'hours': 0}); +const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.add(duration).strftime(); // Thu Jan 02 00:00:00 2020 +``` + + +### datetimeObj.sub(Duration) -> Datetime + +Returns a new Datetime object after subtracting given duration. + +```cs +const duration = Datetime.duration({'weeks':53}); +const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +datetime.sub(duration).strftime(); // Wed Jan 02 00:00:00 2019 +``` + +### datetimeObj.delta(datetimeObj) -> Duration + +Returns a Duration object represents a delta with a given date. + +```cs +var datetimeOne = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-01 00:00:00"); +var datetimeTwo = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-01-02 00:00:00"); +var duration = datetimeOne.delta(datetimeTwo); + +const datetime = Datetime.strptime("%Y-%m-%d %H:%M:%S", "2020-05-01 00:00:00"); +datetime.add(duration).strftime(); // Sat May 02 00:00:00 2020 +```