aboutsummaryrefslogtreecommitdiff
path: root/date.c
diff options
context:
space:
mode:
Diffstat (limited to 'date.c')
-rw-r--r--date.c418
1 files changed, 0 insertions, 418 deletions
diff --git a/date.c b/date.c
deleted file mode 100644
index 9250f5d..0000000
--- a/date.c
+++ /dev/null
@@ -1,418 +0,0 @@
-#include <janet.h>
-#include <time.h>
-
-// polyfills
-#if JANET_VERSION_MAJOR < 2 && JANET_VERSION_MINOR < 28
-const char* janet_getcbytes(const Janet *argv, int32_t n) {
- JanetByteView view = janet_getbytes(argv, n);
- const char *cstr = (const char *)view.bytes;
- if (strlen(cstr) != (size_t) view.len) {
- janet_panic("bytes contain embedded 0s");
- }
- return cstr;
-}
-
-const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) {
- if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
- return dflt;
- }
- return janet_getcbytes(argv, n);
-}
-#endif
-
-#if !defined(JANET_NO_SOURCEMAPS) && !defined(JANET_NO_DOCSTRINGS)
-#undef JANET_FN
-#define JANET_FN(CNAME, USAGE, DOCSTRING) \
- static const int32_t CNAME##_sourceline_ = __LINE__; \
- static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
- Janet CNAME (int32_t argc, Janet *argv)
-#endif
-
-// forward declarations
-JANET_CFUN(jgmtime);
-JANET_CFUN(jlocaltime);
-JANET_CFUN(jmktime);
-JANET_CFUN(jmktime_inplace);
-JANET_CFUN(jstrftime);
-JANET_CFUN(jtmdict);
-
-// native date/time module that's strictly based on the C specification
-// must build with c99, may opt-into c11+ features
-// we make native time_t and struct tm into abstract types to get free gc and methods
-
-static JanetMethod jtime_methods[] = {
- {"gmtime", jgmtime},
- {"localtime", jlocaltime},
- {NULL, NULL},
-};
-
-static JanetMethod jtm_methods[] = {
- {"mktime", jmktime},
- {"normalize", jmktime_inplace},
- {"strftime", jstrftime},
- {"todict", jtmdict},
- {NULL, NULL},
-};
-
-static int jtime_get(void *p, Janet key, Janet *out) {
- (void) p;
- if (!janet_checktype(key, JANET_KEYWORD)) {
- return 0;
- }
- return janet_getmethod(janet_unwrap_keyword(key), jtime_methods, out);
-}
-
-#define JDATE_KEYEQ(name) if(janet_keyeq(key, #name)) { *out = janet_wrap_integer(tm->tm_##name); return 1; }
-static int jtm_get(void *p, Janet key, Janet *out) {
- struct tm *tm = (struct tm*)p;
- if (!janet_checktype(key, JANET_KEYWORD)) {
- return 0;
- }
-
- // is it a method?
- if(janet_getmethod(janet_unwrap_keyword(key), jtm_methods, out)) {
- return 1;
- }
-
- // is it a tm member?
- JDATE_KEYEQ(sec);
- JDATE_KEYEQ(min);
- JDATE_KEYEQ(hour);
- JDATE_KEYEQ(mday);
- JDATE_KEYEQ(mon);
- // year is defined as years since 1900
- if (janet_keyeq(key, "year")) {
- *out = janet_wrap_integer(tm->tm_year + 1900);
- return 1;
- }
- JDATE_KEYEQ(wday);
- JDATE_KEYEQ(yday);
- if (janet_keyeq(key, "isdst")) {
- if(tm->tm_isdst == 0) {
- *out = janet_wrap_false();
- } else if (tm->tm_isdst > 0) {
- *out = janet_wrap_true();
- } else {
- *out = janet_ckeywordv("detect");
- }
- return 1;
- }
- return 0;
-}
-#undef JDATE_KEYEQ
-
-static inline int compare_time_t(time_t lhs, time_t rhs) {
- // difftime returns the difference in seconds
- return difftime(lhs, rhs);
-}
-
-static int jtime_compare(void *lhs, void *rhs) {
- time_t lhv = (*(time_t*)lhs);
- time_t rhv = (*(time_t*)rhs);
- return compare_time_t(lhv, rhv);
-}
-
-static int jtm_compare(void *lhs, void *rhs) {
- struct tm lhc = *((struct tm*)lhs);
- struct tm rhc = *((struct tm*)rhs);
- time_t lhv = mktime(&lhc);
- time_t rhv = mktime(&rhc);
- return compare_time_t(lhv, rhv);
-}
-
-#define JSTRFTIME_CHUNK 64
-// we expect buffer to be empty on init
-static JanetBuffer* strftime_buffer(const char *format, const struct tm *tm, JanetBuffer *buffer) {
- if(!buffer) buffer = janet_buffer(0);
- size_t offset = buffer->count;
- size_t written = 0;
- do {
- janet_buffer_extra(buffer, JSTRFTIME_CHUNK);
- written = strftime((char*)buffer->data + offset, buffer->capacity - offset,
- format, tm);
- } while (!written);
- buffer->count = written + offset; // does not include \0, which we don't want anyway
- return buffer;
-}
-#undef JSTRFTIME_CHUNK
-
-static void jtm_tostring(void *p, JanetBuffer *buffer) {
- // ISO 8601
- strftime_buffer("%F %T.000", (struct tm*)p, buffer);
-}
-
-static void jtime_tostring(void *p, JanetBuffer *buffer) {
- // ctime is deprecated but lets us know the intended approach is localtime()
- jtm_tostring(localtime(p), buffer);
-}
-
-static const char* keys[] = {
- "sec", "min", "hour", "mday", "mon", "year", "wday", "yday", NULL
-};
-static Janet jtm_next(void *p, Janet key) {
- (void) p;
- const char **ptr = keys;
- while(*ptr) {
- if (janet_keyeq(key, *ptr)) {
- return *(++ptr) ? janet_ckeywordv(*ptr) : janet_wrap_nil();
- }
- ptr++;
- }
- return janet_ckeywordv(keys[0]);
-}
-
-// TODO: hash
-static const JanetAbstractType jtime_type = {
- "time",
- NULL,
- NULL,
- jtime_get,
- NULL,
- NULL,
- NULL,
- jtime_tostring,
- jtime_compare,
- JANET_ATEND_COMPARE
-};
-
-static const JanetAbstractType jtm_type = {
- "tm",
- NULL,
- NULL,
- jtm_get,
- NULL,
- NULL,
- NULL,
- jtm_tostring,
- jtm_compare,
- NULL,
- jtm_next,
- JANET_ATEND_NEXT
-};
-
-static time_t *janet_getjtime(Janet *argv, int32_t n) {
- return (time_t*)janet_getabstract(argv, n, &jtime_type);
-}
-
-static struct tm *janet_getjtm(Janet *argv, int32_t n) {
- return (struct tm*)janet_getabstract(argv, n, &jtm_type);
-}
-
-JANET_FN(jgmtime,
- "",
- "") {
- janet_fixarity(argc, 1);
- time_t *time = janet_getjtime(argv, 0);
- struct tm *tm = janet_abstract(&jtm_type, sizeof(struct tm));
- struct tm *in = gmtime(time);
- *tm = *in;
- return janet_wrap_abstract(tm);
-}
-
-JANET_FN(jlocaltime,
- "",
- "") {
- janet_fixarity(argc, 1);
- time_t *time = janet_getjtime(argv, 0);
- struct tm *tm = janet_abstract(&jtm_type, sizeof(struct tm));
- struct tm *in = localtime(time);
- *tm = *in;
- return janet_wrap_abstract(tm);
-}
-
-// does not mutate input
-JANET_FN(jmktime,
- "",
- "") {
- janet_fixarity(argc, 1);
- struct tm *tm = janet_getjtm(argv, 0);
- struct tm new = *tm;
- time_t *time = janet_abstract(&jtime_type, sizeof(time_t));
- *time = mktime(&new);
- return janet_wrap_abstract(time);
-}
-
-// mutates input
-JANET_FN(jmktime_inplace,
- "",
- "") {
- janet_fixarity(argc, 1);
- struct tm *tm = janet_getjtm(argv, 0);
- time_t *time = janet_abstract(&jtime_type, sizeof(time_t));
- *time = mktime(tm);
- return janet_wrap_abstract(time);
-}
-
-JANET_FN(jtime,
- "",
- "") {
- janet_fixarity(argc, 0);
- time_t *jtime = (time_t*)janet_abstract(&jtime_type, sizeof(time_t));
- time(jtime);
- return janet_wrap_abstract(jtime);
-}
-
-struct strftime_format {
- const char* keyword;
- const char* format;
-};
-const static struct strftime_format builtin_formats[] = {
- {"iso8601", "%F %T.000"},
- {"locale", "%c"},
- // WARN: will not work as you expect if it came from gmtime
- {"rfc5322", "%a, %d %b %Y %T %z"},
- // variant of rfc5322 that is compatible with gmtime and only gmtime
- {"email", "%d %b %Y %T -0000"},
- {"timezone", "%Z"},
- {"tzoffset", "%z"},
- {NULL, NULL}
-};
-
-JANET_FN(jstrftime,
- "",
- "") {
- janet_fixarity(argc, 2);
- // we reverse the order of the function for tm to be the first arg for the method
- struct tm *tm = janet_getjtm(argv, 0);
-
- // determine format
- const char *format = NULL;
- // is it a preset?
- if (janet_checktype(argv[1], JANET_KEYWORD)) {
- const struct strftime_format *ptr = builtin_formats;
- while(ptr->keyword) {
- if (janet_keyeq(argv[1], ptr->keyword)) {
- format = ptr->format;
- break;
- }
- ptr++;
- }
- }
- // either not a preset or not found
- if (!format) format = janet_getcbytes(argv, 1);
-
- return janet_wrap_buffer(strftime_buffer(format, tm, NULL));
-}
-
-// common between dict->tm and dict->time
-static Janet tm_from_vals(JanetDictView dict) {
- struct tm *tm = janet_abstract(&jtm_type, sizeof(struct tm));
- Janet jsec = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("sec"));
- Janet jmin = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("min"));
- Janet jhour = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("hour"));
- Janet jmday = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("mday"));
- Janet jmon = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("mon"));
- Janet jyear = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("year"));
- Janet jwday = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("wday"));
- Janet jyday = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("yday"));
- Janet jisdst = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("isdst"));
-
- tm->tm_sec = janet_checktype(jsec, JANET_NUMBER) ? janet_unwrap_integer(jsec) : 0;
- tm->tm_min = janet_checktype(jmin, JANET_NUMBER) ? janet_unwrap_integer(jmin) : 0;
- tm->tm_hour = janet_checktype(jhour, JANET_NUMBER) ? janet_unwrap_integer(jhour) : 0;
- tm->tm_mday = janet_checktype(jmday, JANET_NUMBER) ? janet_unwrap_integer(jmday) : 0;
- tm->tm_mon = janet_checktype(jmon, JANET_NUMBER) ? janet_unwrap_integer(jmon) : 0;
- // year is defined as since 1900, so we normalize
- tm->tm_year = janet_checktype(jyear, JANET_NUMBER) ? janet_unwrap_integer(jyear) - 1900 : 0;
- tm->tm_wday = janet_checktype(jwday, JANET_NUMBER) ? janet_unwrap_integer(jwday) : 0;
- tm->tm_yday = janet_checktype(jyday, JANET_NUMBER) ? janet_unwrap_integer(jyday) : 0;
- tm->tm_isdst = janet_keyeq(jisdst, "detect") ? -1 : (janet_truthy(jisdst) ? 1 : 0);
- return janet_wrap_abstract(tm);
-}
-
-// convenience to make a time_t from a dictionary
-JANET_FN(jnewtime,
- "",
- "") {
- janet_fixarity(argc, 1);
- JanetDictView dict = janet_getdictionary(argv, 0);
- Janet args[1] = { tm_from_vals(dict) };
- return jmktime(1, args);
-}
-
-// convenience to make a struct tm from a dictionary
-JANET_FN(jnewtm,
- "",
- "") {
- janet_fixarity(argc, 1);
- JanetDictView dict = janet_getdictionary(argv, 0);
- return tm_from_vals(dict);
-}
-
-// convenience to make a dict out of a tm
-JANET_FN(jtmdict,
- "",
- "") {
- janet_fixarity(argc, 1);
- struct tm *tm = janet_getjtm(argv, 0);
- JanetTable *out = janet_table(9);
-
- janet_table_put(out, janet_ckeywordv("sec"), janet_wrap_integer(tm->tm_sec));
- janet_table_put(out, janet_ckeywordv("min"), janet_wrap_integer(tm->tm_min));
- janet_table_put(out, janet_ckeywordv("hour"), janet_wrap_integer(tm->tm_hour));
- janet_table_put(out, janet_ckeywordv("mday"), janet_wrap_integer(tm->tm_mday));
- janet_table_put(out, janet_ckeywordv("mon"), janet_wrap_integer(tm->tm_mon));
- // year is defined as since 1900, so we normalize
- janet_table_put(out, janet_ckeywordv("year"), janet_wrap_integer(tm->tm_year + 1900));
- janet_table_put(out, janet_ckeywordv("wday"), janet_wrap_integer(tm->tm_wday));
- janet_table_put(out, janet_ckeywordv("yday"), janet_wrap_integer(tm->tm_yday));
- if(tm->tm_isdst == 0) {
- janet_table_put(out, janet_ckeywordv("isdst"), janet_wrap_false());
- } else if (tm->tm_isdst > 0) {
- janet_table_put(out, janet_ckeywordv("isdst"), janet_wrap_true());
- } else {
- janet_table_put(out, janet_ckeywordv("isdst"), janet_ckeywordv("detect"));
- }
- return janet_wrap_table(out);
-}
-
-JANET_FN(jgettzoffset,
- "",
- "") {
- janet_fixarity(argc, 0);
- time_t *t = janet_smalloc(sizeof(time_t));
- time(t);
- struct tm *tm = localtime(t);
- // ISO 8601 format is -/+NNNN : 5 characters + 1 for \0
- // 8 is nice and 2^n aligned, closest over 6
- char buf[8];
- strftime(buf, 8, "%z", tm);
- // manual parsing for fun
- // the offset will be in minutes
- int offset = 0;
- offset += buf[4] - '0';
- offset += (buf[3] - '0') * 10;
- offset += (buf[2] - '0') * 60;
- offset += (buf[1] - '0') * 60 * 10;
- if (buf[0] == '-') offset *= -1;
- return janet_wrap_integer(offset);
-}
-
-JANET_FN(jgettz,
- "",
- "") {
- janet_fixarity(argc, 0);
- time_t *t = janet_smalloc(sizeof(time_t));
- time(t);
- struct tm *tm = localtime(t);
- return janet_wrap_buffer(strftime_buffer("%Z", tm, NULL));
-}
-
-static const JanetRegExt cfuns[] = {
- JANET_REG("gmtime", jgmtime),
- JANET_REG("localtime", jlocaltime),
- JANET_REG("mktime", jmktime),
- JANET_REG("mktime!", jmktime_inplace),
- JANET_REG("time", jtime),
- JANET_REG("strftime", jstrftime),
- JANET_REG("tm->dict", jtmdict),
- JANET_REG("dict->tm", jnewtm),
- JANET_REG("dict->time", jnewtime),
- JANET_REG("tz", jgettz),
- JANET_REG("tzoffset", jgettzoffset),
- JANET_REG_END,
-};
-
-JANET_MODULE_ENTRY(JanetTable *env) {
- janet_cfuns_ext(env, "date/native", cfuns);
-}