cleanup: removed dependency on libc's strftime
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 May 2023 16:57:05 +0000 (11:57 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 11 May 2023 16:57:05 +0000 (11:57 -0500)
Removed needing to link against:

strftime
mktime
localtime
gmtime

CHANGELOG
core/runtime/platform/onyx/platform.onyx
core/runtime/platform/wasi/platform.onyx
core/std.onyx
core/time/date.onyx
core/time/time.onyx
docs/ideas/platform_layer.md
runtime/onyx_runtime.c
runtime/src/ort_time.h [deleted file]

index 7327c59f9f7149f1c76ddeead7ce85443b7a8426..1d076c9e25ab6a40d12dfaf31e4c684ecab6c91f 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,12 +7,15 @@ Additions:
 * `#distinct` types can now be made over any type.
     - Used to be only primitives.
 * New `logo-new-256.ico` for favicon on website.
+* `u8.to_upper` and `u8.to_lower`
 
 Removals:
 
 Changes:
 * Undefined function imports in `onyx run` no longer cause an immediate error.
     - Instead, an error is produced when the function is called.
+* API for time remains the same, but reduced dependencies on external time things,
+    like strftime, localtime and mktime.
 
 Bugfixes:
 * Fixed missing `use core` in `optional.onyx`.
index 8c750b729171d0b46db01e358dc2cdb13b09d46d..5936fcc478828f3eeaa5122dc1bccf27f4fd2f69 100644 (file)
@@ -65,10 +65,6 @@ ProcessData :: #distinct u64
 
     // Time and sleep
     __time :: () -> u64 ---
-    __time_localtime :: (time: u64, tm: &core.time.Timestamp) -> void ---
-    __time_gmtime    :: (time: u64, tm: &core.time.Timestamp) -> void ---
-    __time_mktime    :: (tm: &time.Timestamp) -> i64 ---
-    __time_strftime  :: (buf: [] u8, format: cstr, tm: &time.Timestamp) -> u32 ---
 
     // Processes
     __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> ProcessData ---
index 6adeab5937ffb525ce5e34e271541ab5179c4873..0394fb3a75763f345b117b82e1718c626f1e6b3a 100644 (file)
@@ -13,7 +13,8 @@ use wasi {
     IOVec, SubscriptionTagged, Subscription, Event, Size,
     poll_oneoff, fd_write, fd_datasync, fd_read,
     args_get, args_sizes_get,
-    proc_exit
+    proc_exit,
+    clock_time_get, Timestamp
 }
 use runtime {
     __runtime_initialize,
@@ -29,7 +30,7 @@ Supports_Files :: true
 Supports_Directories :: true
 Supports_Os :: true
 Supports_Processes :: false
-Supports_Time :: false
+Supports_Time :: true
 Supports_Networking :: false
 Supports_Type_Info :: true
 Supports_Threads :: false
@@ -80,6 +81,16 @@ __sleep :: (milliseconds: u32) {
     error_code := poll_oneoff(&subscription, &event, 1, &number_of_events);
 }
 
+__time :: () -> i64 {
+    time: Timestamp;
+    if clock_time_get(.Realtime, 1, &time) != .Success {
+        return 0;
+    }
+
+    // Need to return in milliseconds, not nanoseconds.
+    return time / 1000000;
+}
+
 
 // Sets up everything needed for execution.
 __start :: () {
index 9764cdf4187077202ef2edf1b7bb8c5271bb8be1..22b9fe0eb996abfed839d91f2b69188ffd656a34 100644 (file)
@@ -52,6 +52,8 @@ use runtime
 
 #load "./test/testing"
 
+#load "./time/time"
+
 #load "./misc/arg_parse"
 #load "./misc/method_ops"
 
@@ -81,9 +83,6 @@ use runtime
     #load "./os/process"
 }
 
-#if runtime.platform.Supports_Time {
-    #load "./time/time"
-}
 
 #if runtime.platform.Supports_Networking {
     #load "./net/net"
index ddcf148ef74a7af99c76cbcaff5f8c774cf3faa8..eeb21b3eaa74769a06321a8d1bb098e34821f3a7 100644 (file)
@@ -18,10 +18,8 @@ Date :: struct {
     }
 
 
-    #if runtime.platform.Supports_Time {
-        today :: () -> Date {
-            return now()->as_date();
-        }
+    today :: () -> Date {
+        return now()->as_date();
     }
 
     add_months :: (d: Date, days: i32) -> Date {
index 91d1bef029673ed0e4498a389fee3f439b977113..b497a12d0963f1b3858fa51f1cabed7b938bb353 100644 (file)
@@ -2,20 +2,11 @@ package core.time
 
 use core
 use core.os
+use core.io
+use core.string
 use core.conv
 use runtime
 
-use runtime.platform {
-    __time_gmtime,
-    __time_localtime,
-    __time_mktime,
-    __time_strftime
-}
-
-#if !runtime.platform.Supports_Time {
-    #error "'core.time' should only be used with the Onyx runtime, for now.";
-}
-
 //
 // This structure has to match the 'struct tm'
 // defined in time.h of POSIX.
@@ -67,21 +58,21 @@ now :: () -> Timestamp {
     //
     // Localtime operates on seconds, while os.time
     // returns milliseconds.
-    return localtime(current_time / 1000);
+    return gmtime(current_time / 1000);
 }
 
-to_epoch :: __time_mktime
+to_epoch :: tm_to_time
 
 
 localtime :: #match #local {}
 
 #overload
-localtime :: __time_localtime
+localtime :: time_to_tm
 
 #overload
 localtime :: (seconds: u64) -> Timestamp {
     t: Timestamp;
-    __time_localtime(seconds, &t);
+    localtime(seconds, &t);
     return t;
 }
 
@@ -89,31 +80,98 @@ localtime :: (seconds: u64) -> Timestamp {
 gmtime :: #match #local {}
 
 #overload
-gmtime :: __time_gmtime
+gmtime :: time_to_tm
 
 #overload
 gmtime :: (seconds: u64) -> Timestamp {
     t: Timestamp;
-    __time_gmtime(seconds, &t);
+    gmtime(seconds, &t);
     return t;
 }
 
 
-strftime :: (buf: [] u8, format: [] u8, tm: &Timestamp) -> str {
-    f := cast(cstr) core.alloc.from_stack(format.length + 1);
-    core.memory.copy(f, format.data, format.length);
-    f[format.length] = 0;
+#local weekdays   := str.[ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ];
+#local monthnames := str.[ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ];
+
+strftime :: (buf: [] u8, format_: [] u8, tm: &Timestamp) -> str {
+    s := io.buffer_stream_make(buf, fixed=true);
+    w := io.writer_make(&s, 0);
+
+    format := format_;
 
-    len := __time_strftime(buf, f, tm);
-    return buf[0..len];
+    while format.length > 0 {
+        c := format[0];
+        switch c {
+            case '%' {
+                string.advance(&format);
+                switch format[0] {
+                    case 'A', 'a' {
+                        day := weekdays[tm.wday];
+                        len := 3 if format[0] == 'a' else day.length;
+
+                        io.write_format(&w, "{}{}", day[0]->to_upper(), day[1..len]);
+                    }
+
+                    case 'B', 'b', 'h' {
+                        mon := monthnames[tm.mon];
+                        len := 3 if format[0] != 'B' else mon.length;
+
+                        io.write_format(&w, "{}{}", mon[0]->to_upper(), mon[1..len]);
+                    }
+
+                    case 'd', 'e' {
+                        io.write_format(&w, "{}", tm.mday);
+                    }
+
+                    case 'D' {
+                        io.write_format(&w, "{}/{}/{}", tm.mon + 1, tm.mday, tm.year - 100 if tm.year >= 100 else tm.year);
+                    }
+
+                    case 'H' do io.write_format(&w, "{w2}", tm.hour);
+                    case 'I' do io.write_format(&w, "{w2}", hour_to_12h(tm.hour));
+                    case 'j' do io.write_format(&w, "{}", tm.yday + 1);
+                    case 'm' do io.write_format(&w, "{}", tm.mon + 1);
+                    case 'M' do io.write_format(&w, "{w2}", tm.min);
+                    case 'p' do io.write(&w, "am" if tm.hour < 12 else "pm");
+
+                    case 'r' {
+                        io.write_format(&w, "{w2}:{w2}:{w2} {}", hour_to_12h(tm.hour), tm.min, tm.sec,
+                            "am" if tm.hour < 12 else "pm");
+                    }
+
+                    case 'R' {
+                        io.write_format(&w, "{w2}:{w2}", tm.hour, tm.min);
+                    }
+
+                    case 'S' do io.write_format(&w, "{w2}", tm.sec);
+                    case 'T' do io.write_format(&w, "{w2}:{w2}:{w2}", tm.hour, tm.min, tm.sec);
+                    case 'w' do io.write_format(&w, "{}", tm.wday);
+                    case 'Y' do io.write_format(&w, "{}", tm.year + 1900);
+                    case 'y' do io.write_format(&w, "{w2}", tm.year - 100 if tm.year >= 100 else tm.year);
+                    case '%' do io.write(&w, "%");
+                }
+            }
+
+            case #default {
+                io.write(&w, c);
+            }
+        }
+
+        string.advance(&format);
+    }
+
+    hour_to_12h :: (h: i32) -> i32 {
+        if h == 0 do return 12;
+        if h <= 12 do return h;
+        return h - 12;
+    }
+
+    return string.as_str(&s);
 }
 
 strptime :: (buf_: [] u8, format_: [] u8, tm: &Timestamp) -> bool {
     use core {*}
 
-    #persist weekdays   := str.[ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ];
-    #persist monthnames := str.[ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ];
-
     buf := buf_;
     format := format_;
 
@@ -338,3 +396,63 @@ strptime :: (buf_: [] u8, format_: [] u8, tm: &Timestamp) -> bool {
     }
 }
 
+#local
+time_to_tm :: (ts: i64, tm: &Timestamp) {
+    leapyear :: (year) => year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+
+    #persist year_lengths := ([12] i64).[
+        i64.[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+        i64.[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+    ];
+
+    year := 1970;
+    dayclock := ts % 86400;
+    dayno := ts / 86400;
+
+    tm.sec = ~~(dayclock % 60);
+    tm.min = ~~((dayclock % 3600) / 60);
+    tm.hour = ~~(dayclock / 3600);
+    tm.wday = ~~((dayno + 4) % 7);
+    while true {
+        yearsize := 366 if leapyear(year) else 365;
+        if dayno >= ~~yearsize {
+            dayno -= ~~yearsize;
+            year += 1;
+        } else {
+            break;
+        }
+    }
+
+    tm.year = ~~(year - 1900);
+    tm.yday = ~~dayno;
+
+    mon := 0;
+    year_length: [] i64 = year_lengths[1 if leapyear(year) else 0];
+    while dayno >= year_length[mon] {
+        dayno -= year_length[mon];
+        mon   += 1;
+    }
+    tm.mon = ~~mon;
+    tm.mday = ~~(dayno + 1);
+    tm.isdst = 0;
+}
+
+#local
+tm_to_time :: (tm: &Timestamp) -> i64 {
+    y := cast(i64, tm.year + 1900);
+    m := cast(i64, tm.mon + 1);
+    if m <= 2 {
+        y -= 1;
+        m += 12;
+    }
+    d := cast(i64, tm.mday);
+    h := cast(i64, tm.hour);
+    mi := cast(i64, tm.min);
+    s := cast(i64, tm.sec);
+
+    return (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400
+        + 3600 * h
+        + 60 * mi
+        + s;
+}
+
index e1ddb2b22e2b86cd2b713eb721ce8896628fd9f5..de0b39a63277c1748a8d4ce4c8da36b9871577c3 100644 (file)
@@ -94,10 +94,6 @@ this document will serve as that "header file"
 
 ### Procedures
 - `__time() -> u64`
-- `__time_localtime(time: u64, tm: ^core.time.Timestamp) -> void`
-- `__time_gmtime(time: u64, tm: ^core.time.Timestamp) -> void`
-- `__time_gmtime(tm: ^core.time.Timestamp) -> i64`
-- `__time_strftime(buf: [] u8, format: cstr, tm: ^core.time.Timestamp) -> u32`
 
 ### Values
 
index 0472fa6cd643d943c8acc0ecf15642b5baab8841..a85e9f6aac4c5140647b5ad4ad5cd087184fb943 100644 (file)
@@ -28,7 +28,6 @@
 #include "src/ort_threads.h"
 #include "src/ort_processes.h"
 #include "src/ort_os.h"
-#include "src/ort_time.h"
 #include "src/ort_cptr.h"
 #include "src/ort_net.h"
 
@@ -73,11 +72,6 @@ ONYX_LIBRARY {
     ONYX_FUNC(__lookup_env)
     ONYX_FUNC(__register_cleanup)
 
-    ONYX_FUNC(__time_localtime)
-    ONYX_FUNC(__time_gmtime)
-    ONYX_FUNC(__time_strftime)
-    ONYX_FUNC(__time_mktime)
-
     ONYX_FUNC(__net_create_socket)
     ONYX_FUNC(__net_close_socket)
     ONYX_FUNC(__net_setting)
diff --git a/runtime/src/ort_time.h b/runtime/src/ort_time.h
deleted file mode 100644 (file)
index 86f75ba..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-
-//
-// Dates and Times
-//
-ONYX_DEF(__time_localtime, (WASM_I64, WASM_I32), ()) {
-    u64 t = params->data[0].of.i64;
-    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *localtime(&t);
-    return NULL;
-}
-
-ONYX_DEF(__time_gmtime, (WASM_I64, WASM_I32), ()) {
-    u64 t = params->data[0].of.i64;
-    *(struct tm *) ONYX_PTR(params->data[1].of.i32) = *gmtime(&t);
-    return NULL;
-}
-
-ONYX_DEF(__time_strftime, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
-    u32 len = strftime(ONYX_PTR(params->data[0].of.i32), params->data[1].of.i32, ONYX_PTR(params->data[2].of.i32), ONYX_PTR(params->data[3].of.i32));
-    results->data[0] = WASM_I32_VAL(len); 
-    return NULL;
-}
-
-ONYX_DEF(__time_mktime, (WASM_I32), (WASM_I64)) {
-    struct tm *time = (struct tm *) ONYX_PTR(params->data[0].of.i32);
-    results->data[0] = WASM_I64_VAL(mktime(time));
-    return NULL;
-}
-