From 2d2617b63706b8781418565330d23449701b3a71 Mon Sep 17 00:00:00 2001 From: Chloe Kudryavtsev Date: Fri, 12 May 2023 16:53:08 -0400 Subject: big updates ok I figured out what the problem was, I think? put is nice too now, no more conversions eh also offsets are nice, makes iterating way easier --- src/date.h | 5 -- src/time.c | 14 +----- src/tm.c | 126 +++++++++++++++++++++++++++++++++++++-------------- src/util.c | 44 ------------------ test/01-native.janet | 17 +++++++ 5 files changed, 110 insertions(+), 96 deletions(-) create mode 100644 test/01-native.janet diff --git a/src/date.h b/src/date.h index de0eb2b..3661421 100644 --- a/src/date.h +++ b/src/date.h @@ -5,14 +5,11 @@ // util.c JanetBuffer *strftime_buffer(const char *format, const struct tm *tm, JanetBuffer *buffer); -struct tm *jd_tm_from_dict(JanetDictView dict); -JanetTable *jd_tm_to_table(struct tm *tm); // time.c extern const JanetRegExt jd_time_cfuns[]; time_t *jd_gettime(Janet *argv, int32_t n); time_t *jd_maketime(void); -JANET_CFUN(jd_dict_time); JANET_CFUN(jd_gmtime); JANET_CFUN(jd_localtime); JANET_CFUN(jd_time); @@ -21,8 +18,6 @@ JANET_CFUN(jd_time); extern const JanetRegExt jd_tm_cfuns[]; struct tm *jd_gettm(Janet *argv, int32_t n); struct tm *jd_maketm(void); -JANET_CFUN(jd_dict_tm); JANET_CFUN(jd_mktime); JANET_CFUN(jd_mktime_inplace); JANET_CFUN(jd_strftime); -JANET_CFUN(jd_tm_dict); diff --git a/src/time.c b/src/time.c index 64096c7..2993030 100644 --- a/src/time.c +++ b/src/time.c @@ -6,7 +6,6 @@ static JanetMethod jd_time_methods[] = { {"gmtime", jd_gmtime}, {"localtime", jd_localtime}, - {"todict", jd_dict_time}, {NULL, NULL}, }; @@ -25,8 +24,9 @@ static int jd_time_get(void *p, Janet key, Janet *out) { } // time_t is always a UTC-representation +// we hard-code the offset because of a macOS bug static void jd_time_tostring(void *p, JanetBuffer *buffer) { - strftime_buffer("%F %T.000 UTC", localtime(p), buffer); + strftime_buffer("%F %T.000 +0000", gmtime(p), buffer); } static const JanetAbstractType jd_time_t = { @@ -50,15 +50,6 @@ time_t *jd_maketime(void) { return janet_abstract(&jd_time_t, sizeof(time_t)); } -JANET_FN(jd_dict_time, - "(dict->time {...})", - "") { - janet_fixarity(argc, 1); - JanetDictView dict = janet_getdictionary(argv, 0); - struct tm *tm = jd_tm_from_dict(dict); - return janet_wrap_abstract(tm); -} - JANET_FN(jd_gmtime, "(gmtime (time))", "") { @@ -92,7 +83,6 @@ JANET_FN(jd_time, } const JanetRegExt jd_time_cfuns[] = { - JANET_REG("dict->time", jd_dict_time), JANET_REG("gmtime", jd_gmtime), JANET_REG("localtime", jd_localtime), JANET_REG("time", jd_time), diff --git a/src/tm.c b/src/tm.c index 5d2abff..d078def 100644 --- a/src/tm.c +++ b/src/tm.c @@ -8,7 +8,6 @@ static JanetMethod jd_tm_methods[] = { {"mktime!", jd_mktime_inplace}, {"normalize", jd_mktime_inplace}, {"strftime", jd_strftime}, - {"todict", jd_tm_dict}, {NULL, NULL}, }; @@ -20,6 +19,24 @@ static int jd_tm_compare(void *lhs, void *rhs) { return difftime(lhv, rhv); } +// C99 specifies ALL fields to be int +struct jd_tm_key { + const char *key; + const size_t off; +}; +static const struct jd_tm_key jd_tm_keys[] = { + {"sec", offsetof(struct tm, tm_sec)}, + {"min", offsetof(struct tm, tm_min)}, + {"hour", offsetof(struct tm, tm_hour)}, + {"mday", offsetof(struct tm, tm_mday)}, + {"mon", offsetof(struct tm, tm_mon)}, + {"year", offsetof(struct tm, tm_year)}, + {"wday", offsetof(struct tm, tm_wday)}, + {"yday", offsetof(struct tm, tm_yday)}, + {"isdst", offsetof(struct tm, tm_isdst)}, + {NULL, 0}, +}; + static int jd_tm_get(void *p, Janet key, Janet *out) { if (!janet_checktype(key, JANET_KEYWORD)) { return 0; @@ -30,32 +47,89 @@ static int jd_tm_get(void *p, Janet key, Janet *out) { return 1; } - // piggyback off jd_tm_to_table - JanetTable *tb = jd_tm_to_table(p); - *out = janet_table_rawget(tb, key); + const struct jd_tm_key *ptr = jd_tm_keys; + while (ptr->key) { + if (janet_keyeq(key, ptr->key)) { + int val = *((int*)(p + ptr->off)); + + // exceptional values + if (janet_keyeq(key, "year")) { + val += 1900; + } else if (janet_keyeq(key, "isdst")) { + *out = val ? (val > 0 ? janet_wrap_true() : janet_ckeywordv("detect")) : janet_wrap_false(); + return 1; + } + + *out = janet_wrap_integer(val); + return 1; + } + ptr++; + } return janet_checktype(*out, JANET_NIL); } - -static const char* jd_tm_keys[] = { - "sec", "min", "hour", "mday", "mon", "year", "wday", "yday", NULL, -}; static Janet jd_tm_next(void *p, Janet key) { (void) p; - const char **ptr = jd_tm_keys; - while (*ptr) { - if (janet_keyeq(key, *ptr)) { - return *(++ptr) ? janet_ckeywordv(*ptr) : janet_wrap_nil(); + const struct jd_tm_key *ptr = jd_tm_keys; + while (ptr->key) { + if (janet_keyeq(key, ptr->key)) { + return (++ptr)->key ? janet_ckeywordv(ptr->key) : janet_wrap_nil(); + } + ptr++; + } + return janet_ckeywordv(jd_tm_keys[0].key); +} + +static void jd_tm_put(void *data, Janet key, Janet value) { + // note that keyword, boolean are only valid for isdst + if (!janet_checktypes(value, JANET_TFLAG_NUMBER | JANET_TFLAG_KEYWORD | JANET_TFLAG_BOOLEAN)) { + janet_panicf("expected function or number, got %t", value); + } + const struct jd_tm_key *ptr = jd_tm_keys; + while (ptr->key) { + if (janet_keyeq(key, ptr->key)) { + int *loc = (int*)(data + ptr->off); + if (janet_keyeq(key, "year")) { + *loc = janet_unwrap_integer(value) - 1900; + } else if (janet_keyeq(key, "isdst")) { + *loc = janet_keyeq(value, "detect") ? -1 : (janet_truthy(value) ? 1 : 0); + } else { + *loc = janet_unwrap_integer(value); + } + return; } ptr++; } - return janet_ckeywordv(jd_tm_keys[0]); + + janet_panicf("tried to write to invalid field: %v", key); } -// struct tm can represent non-UTC -// it does not keep TZ information so we can't display it without potentially lying +#define MAX_INT_STRLEN 1024 static void jd_tm_tostring(void *p, JanetBuffer *buffer) { - strftime_buffer("%F %T.000", p, buffer); + janet_buffer_push_cstring(buffer, "{"); + char buf[MAX_INT_STRLEN]; + + const struct jd_tm_key *ptr = jd_tm_keys; + while (ptr->key) { + int *loc = (int*)(p + ptr->off); + janet_buffer_push_cstring(buffer, ":"); + janet_buffer_push_cstring(buffer, ptr->key); + janet_buffer_push_cstring(buffer, " "); + + // exceptional values + if (!strcmp(ptr->key, "year")) { + snprintf(buf, MAX_INT_STRLEN, "%d", *loc + 1900); + } else if (!strcmp(ptr->key, "isdst")) { + strcpy(buf, *loc ? (*loc > 0 ? "true" : ":detect") : "false"); + } else { + snprintf(buf, MAX_INT_STRLEN, "%d", *loc); + } + janet_buffer_push_cstring(buffer, buf); + + if ((++ptr)->key) janet_buffer_push_cstring(buffer, " "); + } + + janet_buffer_push_cstring(buffer, "}"); } static const JanetAbstractType jd_tm_t = { @@ -63,7 +137,7 @@ static const JanetAbstractType jd_tm_t = { NULL, NULL, jd_tm_get, - NULL, + jd_tm_put, NULL, NULL, jd_tm_tostring, @@ -81,14 +155,6 @@ struct tm *jd_maketm(void) { return janet_abstract(&jd_tm_t, sizeof(struct tm)); } -JANET_FN(jd_dict_tm, - "", - "") { - janet_fixarity(argc, 1); - JanetDictView dict = janet_getdictionary(argv, 0); - return janet_wrap_abstract(jd_tm_from_dict(dict)); -} - JANET_FN(jd_mktime, "", "") { @@ -111,14 +177,6 @@ JANET_FN(jd_mktime_inplace, return janet_wrap_abstract(time); } -JANET_FN(jd_tm_dict, - "", - "") { - janet_fixarity(argc, 1); - struct tm *tm = jd_gettm(argv, 0); - return janet_wrap_table(jd_tm_to_table(tm)); -} - // strftime struct strftime_format { const char *keyword; @@ -155,10 +213,8 @@ JANET_FN(jd_strftime, } const JanetRegExt jd_tm_cfuns[] = { - JANET_REG("dict->tm", jd_dict_tm), JANET_REG("mktime", jd_mktime), JANET_REG("mktime!", jd_mktime_inplace), JANET_REG("strftime", jd_strftime), - JANET_REG("tm->dict", jd_tm_dict), JANET_REG_END }; diff --git a/src/util.c b/src/util.c index 95d3ded..bc26163 100644 --- a/src/util.c +++ b/src/util.c @@ -12,47 +12,3 @@ JanetBuffer *strftime_buffer(const char *format, const struct tm *tm, JanetBuffe buffer->count = written + offset; // does not include \0, but we don't want it anyway return buffer; } - -static inline void tm_set_dict(JanetDictView dict, char *key, int *v) { - Janet k = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv(key)); - *v = janet_checktype(k, JANET_NUMBER) ? janet_unwrap_integer(k) : 0; -} -struct tm *jd_tm_from_dict(JanetDictView dict) { - struct tm *tm = jd_maketm(); - - tm_set_dict(dict, "sec", &tm->tm_sec); - tm_set_dict(dict, "min", &tm->tm_min); - tm_set_dict(dict, "hour", &tm->tm_hour); - tm_set_dict(dict, "mday", &tm->tm_mday); - tm_set_dict(dict, "mon", &tm->tm_mon); - Janet year = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("year")); - tm->tm_year = janet_checktype(year, JANET_NUMBER) ? janet_unwrap_integer(year) - 1900 : 0; - tm_set_dict(dict, "wday", &tm->tm_wday); - tm_set_dict(dict, "yday", &tm->tm_yday); - Janet isdst = janet_dictionary_get(dict.kvs, dict.cap, janet_ckeywordv("isdst")); - tm->tm_isdst = janet_keyeq(isdst, "detect") ? -1 : (janet_truthy(isdst) ? 1 : 0); - - return tm; -} - -#define PUTV(T, K, V) janet_table_put(T, janet_ckeywordv(K), V) -#define PUT(T, K, V) PUTV(T, K, janet_wrap_integer(V)) -JanetTable *jd_tm_to_table(struct tm *tm) { - JanetTable *out = janet_table(9); - - PUT(out, "sec", tm->tm_sec); - PUT(out, "min", tm->tm_min); - PUT(out, "hour", tm->tm_hour); - PUT(out, "mday", tm->tm_mday); - PUT(out, "mon", tm->tm_mon); - PUT(out, "year", tm->tm_year + 1900); - PUT(out, "wday", tm->tm_wday); - PUT(out, "yday", tm->tm_yday); - if (tm->tm_isdst < 0) { - PUTV(out, "isdst", janet_ckeywordv("detect")); - } else { - PUTV(out, "isdst", tm->tm_isdst ? janet_wrap_true() : janet_wrap_false()); - } - - return out; -} diff --git a/test/01-native.janet b/test/01-native.janet new file mode 100644 index 0000000..442a651 --- /dev/null +++ b/test/01-native.janet @@ -0,0 +1,17 @@ +(use date/native) + +# you always start with this +# `time` returns the time in UTC +(def now (time)) + +# if you want to modify the time, you should use localtime, like so +(def lt (:localtime now)) +(update lt :sec |(+ $ 2000)) +(update lt :year |(- $ 10)) + +(def gt (:gmtime now)) +(put gt :sec 13) + +(pp now) +(pp lt) +(pp gt) -- cgit v1.2.3