From e676fb8299c2e21a41a4fa683aed3ef2bff44050 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 13 Jan 2023 21:06:27 +0100 Subject: [PATCH] libc: Add a wide range of time manipulation functions, including reentrant variants --- apps/app.c | 13 +--- libc/include/bits/struct_tm.h | 19 +++++ libc/include/sys/mman.h | 2 + libc/include/time.h | 25 +++++++ libc/src/time.cpp | 133 ++++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 libc/include/bits/struct_tm.h diff --git a/apps/app.c b/apps/app.c index 1a49bef1..c5f55f95 100644 --- a/apps/app.c +++ b/apps/app.c @@ -1,8 +1,5 @@ -#include #include #include -#include -#include #include void bye() @@ -15,13 +12,6 @@ int main() atexit(bye); printf("Welcome to %s!\n", "Luna"); - time_t now = time(NULL); - printf("Realtime clock: %ld s\n", now); - - for (int i = 0; i < atoi("8"); i++) { console_write(".", 1); } - - console_write("\n", 1); - char* address = (char*)malloc(1); printf("address: %p\n", address); printf("memory at address: %c\n", *address); @@ -29,4 +19,7 @@ int main() printf("memory at address: %c\n", *address); free(address); + + time_t now = time(NULL); + printf("date: %s", ctime(&now)); } diff --git a/libc/include/bits/struct_tm.h b/libc/include/bits/struct_tm.h new file mode 100644 index 00000000..fb06286f --- /dev/null +++ b/libc/include/bits/struct_tm.h @@ -0,0 +1,19 @@ +/* bits/struct_tm.h: Broken-down time. */ + +#ifndef _BITS_STRUCT_TM_H +#define _BITS_STRUCT_TM_H + +struct tm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#endif diff --git a/libc/include/sys/mman.h b/libc/include/sys/mman.h index 65d5c12a..0158ddd0 100644 --- a/libc/include/sys/mman.h +++ b/libc/include/sys/mman.h @@ -1,3 +1,5 @@ +/* sys/mman.h: Memory allocation and deallocation. */ + #ifndef _LUNA_MMAN_H #define _LUNA_MMAN_H diff --git a/libc/include/time.h b/libc/include/time.h index 27979f90..a4e33333 100644 --- a/libc/include/time.h +++ b/libc/include/time.h @@ -4,6 +4,7 @@ #define _TIME_H #include +#include #include #ifdef __cplusplus @@ -17,6 +18,30 @@ extern "C" /* Get the current wall clock time. */ time_t time(time_t* tp); + /* Get the broken-down time representation of UNIX time, in local time. */ + struct tm* localtime(const time_t* tp); + + /* Get the broken-down time representation of UNIX time, in local time. */ + struct tm* localtime_r(const time_t* tp, struct tm* out); + + /* Get the broken-down time representation of UNIX time, in UTC time. */ + struct tm* gmtime(const time_t* tp); + + /* Get the broken-down time representation of UNIX time, in UTC time. */ + struct tm* gmtime_r(const time_t* tp, struct tm* out); + + /* Build a string representation of broken-down time. */ + char* asctime(const struct tm* tm); + + /* Build a string representation of broken-down time. */ + char* asctime_r(const struct tm* tm, char buf[26]); + + /* Build a string representation of UNIX time. */ + char* ctime(const time_t* tp); + + /* Build a string representation of UNIX time. */ + char* ctime_r(const time_t* tp, char buf[26]); + #ifdef __cplusplus } #endif diff --git a/libc/src/time.cpp b/libc/src/time.cpp index b33a3c6c..d97663f0 100644 --- a/libc/src/time.cpp +++ b/libc/src/time.cpp @@ -1,8 +1,93 @@ +#define _LUNA_SYSTEM_ERROR_EXTENSIONS #include +#include +#include #include #include #include +#define SECONDS_PER_DAY 86400 + +static int isleap(int year) +{ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +static int make_yday(int year, int month) +{ + static const short int upto[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int yd; + + yd = upto[month - 1]; + if (month > 2 && isleap(year)) yd++; + return yd; +} + +static int day_of_week(int year, int mon, int day) +{ + static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; + year -= mon < 3; + return (year + year / 4 - year / 100 + year / 400 + t[mon - 1] + day) % 7; +} + +static void time_to_struct_tm(time_t time, struct tm* result) +{ + result->tm_isdst = 0; // No DST/timezone support for now. + + int year = 1970; + + while (time > 0) + { + time_t seconds_in_year = (isleap(year) ? 366 : 365) * SECONDS_PER_DAY; + if (seconds_in_year <= time) + { + year++; + time -= seconds_in_year; + continue; + } + break; + } + + int month_days[] = { 31, isleap(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + int month = 0; + + while (1) + { + time_t seconds_in_month = month_days[month] * SECONDS_PER_DAY; + if (seconds_in_month <= time) + { + month++; + time -= seconds_in_month; + continue; + } + break; + } + + int day = (int)(time / SECONDS_PER_DAY); + time %= SECONDS_PER_DAY; + + check(day < month_days[month]); + + int hour = (int)(time / 3600); + time %= 3600; + + int min = (int)(time / 60); + time %= 60; + + result->tm_year = year - 1900; + result->tm_mon = month; + result->tm_yday = make_yday(year, month + 1) + day; + result->tm_wday = day_of_week(year, month + 1, day + 1); + result->tm_mday = day + 1; + result->tm_hour = hour; + result->tm_min = min; + result->tm_sec = (int)time; +} + +const char* wday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +const char* month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + extern "C" { int clock_gettime(clockid_t id, struct timespec* ts) @@ -20,4 +105,52 @@ extern "C" return ts.tv_sec; } + + struct tm* localtime_r(const time_t* tp, struct tm* out) + { + time_to_struct_tm(*tp, out); + return out; + } + + struct tm* gmtime_r(const time_t* tp, struct tm* out) + { + time_to_struct_tm(*tp, out); + return out; + } + + struct tm* localtime(const time_t* tp) + { + static struct tm out; + return localtime_r(tp, &out); + } + + struct tm* gmtime(const time_t* tp) + { + static struct tm out; + return gmtime_r(tp, &out); + } + + char* asctime_r(const struct tm* tm, char buf[26]) + { + string_format(buf, 26, "%s %s %.2d %.2d:%.2d:%.2d %4d\n", wday_names[tm->tm_wday], month_names[tm->tm_mon], + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900); + return buf; + } + + char* asctime(const struct tm* tm) + { + static char buf[26]; + return asctime_r(tm, buf); + } + + char* ctime_r(const time_t* tp, char buf[26]) + { + struct tm out; + return asctime_r(localtime_r(tp, &out), buf); + } + + char* ctime(const time_t* tp) + { + return asctime(localtime(tp)); + } }