diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-30 01:53:39 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-30 01:53:39 -0400 |
| commit | 793717729ae46bd026eff882f7d4da819dec32e5 (patch) | |
| tree | cb35ac60f2821188bf86f3864ca99ad23f95944e /stdlib/datetime.c | |
| parent | 37780cb3234441d693dcb0f2d1d4efbd6632a0a6 (diff) | |
Parameterize with timezones
Diffstat (limited to 'stdlib/datetime.c')
| -rw-r--r-- | stdlib/datetime.c | 107 |
1 files changed, 81 insertions, 26 deletions
diff --git a/stdlib/datetime.c b/stdlib/datetime.c index bc1d88e7..6ad4966f 100644 --- a/stdlib/datetime.c +++ b/stdlib/datetime.c @@ -4,14 +4,18 @@ #include <err.h> #include <stdlib.h> #include <time.h> +#include <unistd.h> #include "datatypes.h" +#include "datetime.h" #include "optionals.h" #include "patterns.h" #include "stdlib.h" #include "text.h" #include "util.h" +static OptionalText_t _local_timezone = NULL_TEXT; + public Text_t DateTime$as_text(const DateTime_t *dt, bool colorize, const TypeInfo *type) { (void)type; @@ -44,8 +48,10 @@ public DateTime_t DateTime$now(void) return (DateTime_t){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec}; } -public DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second) +public DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone) { + if (timezone.length >= 0) + DateTime$set_local_timezone(timezone); struct tm info = { .tm_min=Int_to_Int32(minute, false), .tm_hour=Int_to_Int32(hour, false), @@ -55,19 +61,26 @@ public DateTime_t DateTime$new(Int_t year, Int_t month, Int_t day, Int_t hour, I .tm_isdst=-1, }; time_t t = mktime(&info); + if (timezone.length >= 0) + DateTime$set_local_timezone(_local_timezone); return (DateTime_t){.tv_sec=t + (time_t)second, .tv_usec=(suseconds_t)(fmod(second, 1.0) * 1e9)}; } -public DateTime_t DateTime$after(DateTime_t dt, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, bool local_time) +public DateTime_t DateTime$after(DateTime_t dt, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone) { double offset = seconds + 60.*minutes + 3600.*hours; dt.tv_sec += (time_t)offset; struct tm info = {}; - if (local_time) + if (timezone.length >= 0) { + OptionalText_t old_timezone = _local_timezone; + DateTime$set_local_timezone(timezone); localtime_r(&dt.tv_sec, &info); - else - gmtime_r(&dt.tv_sec, &info); + DateTime$set_local_timezone(old_timezone); + } else { + localtime_r(&dt.tv_sec, &info); + } + info.tm_mday += Int_to_Int32(days, false) + 7*Int_to_Int32(weeks, false); info.tm_mon += Int_to_Int32(months, false); info.tm_year += Int_to_Int32(years, false); @@ -96,13 +109,18 @@ CONSTFUNC public double DateTime$hours_till(DateTime_t now, DateTime_t then) public void DateTime$get( DateTime_t dt, Int_t *year, Int_t *month, Int_t *day, Int_t *hour, Int_t *minute, Int_t *second, - Int_t *nanosecond, Int_t *weekday, bool local_time) + Int_t *nanosecond, Int_t *weekday, OptionalText_t timezone) { struct tm info = {}; - if (local_time) + + if (timezone.length >= 0) { + OptionalText_t old_timezone = _local_timezone; + DateTime$set_local_timezone(timezone); localtime_r(&dt.tv_sec, &info); - else - gmtime_r(&dt.tv_sec, &info); + DateTime$set_local_timezone(old_timezone); + } else { + localtime_r(&dt.tv_sec, &info); + } if (year) *year = I(info.tm_year + 1900); if (month) *month = I(info.tm_mon + 1); @@ -114,27 +132,34 @@ public void DateTime$get( if (weekday) *weekday = I(info.tm_wday + 1); } -public Text_t DateTime$format(DateTime_t dt, Text_t fmt, bool local_time) +public Text_t DateTime$format(DateTime_t dt, Text_t fmt, OptionalText_t timezone) { struct tm info; - struct tm *final_info = local_time ? localtime_r(&dt.tv_sec, &info) : gmtime_r(&dt.tv_sec, &info); + if (timezone.length >= 0) { + OptionalText_t old_timezone = _local_timezone; + DateTime$set_local_timezone(timezone); + localtime_r(&dt.tv_sec, &info); + DateTime$set_local_timezone(old_timezone); + } else { + localtime_r(&dt.tv_sec, &info); + } static char buf[256]; - size_t len = strftime(buf, sizeof(buf), Text$as_c_string(fmt), final_info); + size_t len = strftime(buf, sizeof(buf), Text$as_c_string(fmt), &info); return Text$format("%.*s", (int)len, buf); } -public Text_t DateTime$date(DateTime_t dt, bool local_time) +public Text_t DateTime$date(DateTime_t dt, OptionalText_t timezone) { - return DateTime$format(dt, Text("%F"), local_time); + return DateTime$format(dt, Text("%F"), timezone); } -public Text_t DateTime$time(DateTime_t dt, bool seconds, bool am_pm, bool local_time) +public Text_t DateTime$time(DateTime_t dt, bool seconds, bool am_pm, OptionalText_t timezone) { Text_t text; if (seconds) - text = DateTime$format(dt, am_pm ? Text("%l:%M:%S%P") : Text("%T"), local_time); + text = DateTime$format(dt, am_pm ? Text("%l:%M:%S%P") : Text("%T"), timezone); else - text = DateTime$format(dt, am_pm ? Text("%l:%M%P") : Text("%H:%M"), local_time); + text = DateTime$format(dt, am_pm ? Text("%l:%M%P") : Text("%H:%M"), timezone); return Text$trim(text, Pattern(" "), true, true); } @@ -162,19 +187,21 @@ static inline Text_t num_format(long n, const char *unit) return Text$format((n == 1 || n == -1) ? "%ld %s %s" : "%ld %ss %s", n < 0 ? -n : n, unit, n < 0 ? "ago" : "later"); } -public Text_t DateTime$relative(DateTime_t dt, DateTime_t relative_to, bool local_time) +public Text_t DateTime$relative(DateTime_t dt, DateTime_t relative_to, OptionalText_t timezone) { struct tm info = {}; - if (local_time) - localtime_r(&dt.tv_sec, &info); - else - gmtime_r(&dt.tv_sec, &info); - struct tm relative_info = {}; - if (local_time) + + if (timezone.length >= 0) { + OptionalText_t old_timezone = _local_timezone; + DateTime$set_local_timezone(timezone); + localtime_r(&dt.tv_sec, &info); localtime_r(&relative_to.tv_sec, &relative_info); - else - gmtime_r(&relative_to.tv_sec, &relative_info); + DateTime$set_local_timezone(old_timezone); + } else { + localtime_r(&dt.tv_sec, &info); + localtime_r(&relative_to.tv_sec, &relative_info); + } double second_diff = DateTime$seconds_till(relative_to, dt); if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.) @@ -209,6 +236,34 @@ CONSTFUNC public DateTime_t DateTime$from_unix_timestamp(Int64_t timestamp) return (DateTime_t){.tv_sec=(time_t)timestamp}; } +public void DateTime$set_local_timezone(OptionalText_t timezone) +{ + if (timezone.length >= 0) { + setenv("TZ", Text$as_c_string(timezone), 1); + } else { + unsetenv("TZ"); + } + _local_timezone = timezone; + tzset(); +} + +public Text_t DateTime$get_local_timezone(void) +{ + if (_local_timezone.length < 0) { + static char buf[PATH_MAX]; + ssize_t len = readlink("/etc/localtime", buf, sizeof(buf)); + if (len < 0) + fail("Could not get local timezone!"); + + char *zoneinfo = strstr(buf, "/zoneinfo/"); + if (zoneinfo) + _local_timezone = Text$from_str(zoneinfo + strlen("/zoneinfo/")); + else + fail("Could not resolve local timezone!"); + } + return _local_timezone; +} + public const TypeInfo DateTime$info = { .size=sizeof(DateTime_t), .align=__alignof__(DateTime_t), |
