diff --git a/src/core/os.c b/src/core/os.c index f12c8bfd..d74d0396 100644 --- a/src/core/os.c +++ b/src/core/os.c @@ -69,6 +69,13 @@ extern char **environ; void arc4random_buf(void *buf, size_t nbytes); #endif +/* Not POSIX, but all Unixes but Solaris have this function. */ +#if defined(JANET_POSIX) && !defined(__sun) +time_t timegm(struct tm *tm); +#elif defined(JANET_WINDOWS) +#define timegm _mkgmtime +#endif + /* Access to some global variables should be synchronized if not in single threaded mode, as * setenv/getenv are not thread safe. */ #ifdef JANET_THREADS @@ -656,6 +663,66 @@ static Janet os_date(int32_t argc, Janet *argv) { return janet_wrap_struct(janet_struct_end(st)); } +static int64_t entry_getint(Janet env_entry, char *field) { + Janet i; + if (janet_checktype(env_entry, JANET_TABLE)) { + JanetTable *entry = janet_unwrap_table(env_entry); + i = janet_table_get(entry, janet_ckeywordv(field)); + } else if (janet_checktype(env_entry, JANET_STRUCT)) { + const JanetKV *entry = janet_unwrap_struct(env_entry); + i = janet_struct_get(entry, janet_ckeywordv(field)); + } else { + return 0; + } + + if (janet_checktype(i, JANET_NIL)) { + return 0; + } + + if (!janet_checkint64(i)) { + janet_panicf("bad slot :%s, expected 64 bit signed integer, got %v", + field, i); + } + + return (int64_t)janet_unwrap_number(i); +} + +static Janet os_mktime(int32_t argc, Janet *argv) { + janet_arity(argc, 1, 2); + time_t t; + struct tm t_info = { 0 }; + + if (!janet_checktype(argv[0], JANET_TABLE) && + !janet_checktype(argv[0], JANET_STRUCT)) + janet_panic_type(argv[0], 0, JANET_TFLAG_DICTIONARY); + + t_info.tm_sec = entry_getint(argv[0], "seconds"); + t_info.tm_min = entry_getint(argv[0], "minutes"); + t_info.tm_hour = entry_getint(argv[0], "hours"); + t_info.tm_mday = entry_getint(argv[0], "month-day") + 1; + t_info.tm_mon = entry_getint(argv[0], "month"); + t_info.tm_year = entry_getint(argv[0], "year") - 1900; + + if (argc >= 2 && janet_truthy(argv[1])) { + /* local time */ + t = mktime(&t_info); + } else { + /* utc time */ +#ifdef __sun + janet_panic("os/mktime UTC not supported on Solaris"); + return janet_wrap_nil(); +#else + t = timegm(&t_info); +#endif + } + + if (t == (time_t)-1) { + janet_panicf("%s", strerror(errno)); + } + + return janet_wrap_number((double)t); +} + static Janet os_link(int32_t argc, Janet *argv) { janet_arity(argc, 2, 3); #ifdef JANET_WINDOWS @@ -1152,6 +1219,16 @@ static const JanetReg os_cfuns[] = { "Get the current time expressed as the number of seconds since " "January 1, 1970, the Unix epoch. Returns a real number.") }, + { + "os/mktime", os_mktime, + JDOC("(os/mktime date-struct &opt local)\n\n" + "Get the broken down date-struct time expressed as the number " + " of seconds since January 1, 1970, the Unix epoch. " + "Returns a real number. " + "Date is given in UTC unless local is truthy, in which case the " + "date is computed for the local timezone.\n\n" + "Inverse function to os/date.") + }, { "os/clock", os_clock, JDOC("(os/clock)\n\n" diff --git a/test/suite7.janet b/test/suite7.janet index cdffc3c9..3e8d19bb 100644 --- a/test/suite7.janet +++ b/test/suite7.janet @@ -226,6 +226,23 @@ :week-day 3} (os/date 1388608200)) "os/date") +# OS mktime test + +(assert (= 1388608200 (os/mktime {:year-day 0 + :minutes 30 + :month 0 + :dst false + :seconds 0 + :year 2014 + :month-day 0 + :hours 20 + :week-day 3})) "os/mktime") + +(def now (os/time)) +(assert (= (os/mktime (os/date now)) now) "UTC os/mktime") +(assert (= (os/mktime (os/date now true) true) now) "local os/mktime") +(assert (= (os/mktime {:year 1970}) 0) "os/mktime default values") + # Appending buffer to self (with-dyns [:out @""]