Merge branch 'time-improvements' into 'master'

Time improvements

See merge request redox-os/relibc!635
This commit is contained in:
Jeremy Soller
2025-03-29 19:29:21 +00:00
5 changed files with 132 additions and 15 deletions
+12 -3
View File
@@ -382,9 +382,8 @@ pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t {
}
};
let offset = FixedOffset::east((*timeptr).tm_gmtoff as _);
let offset = get_offset((*timeptr).tm_gmtoff).unwrap();
let tz = time_zone();
// Create DateTime<FixedOffset>
let datetime = match offset.from_local_datetime(&naive_local) {
MappedLocalTime::Single(datetime) => datetime,
@@ -486,11 +485,12 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t {
None => return -1,
};
let tz_name = CString::new(tz.name()).unwrap();
(*tm).tm_wday = dt.weekday().num_days_from_sunday() as _;
(*tm).tm_yday = dt.ordinal0() as _; // day of year starting at 0
(*tm).tm_isdst = dt.offset().dst_offset().num_hours() as _;
(*tm).tm_gmtoff = dt.offset().fix().local_minus_utc() as _;
(*tm).tm_zone = UTC_STR.as_ptr() as *const c_char;
(*tm).tm_zone = tz_name.into_raw().cast();
dt.timestamp()
}
@@ -726,6 +726,15 @@ unsafe fn set_timezone(
timezone = -c_long::from(ut_offset.fix().local_minus_utc());
}
#[inline(always)]
pub const fn get_offset(off: c_long) -> Option<FixedOffset> {
if off < 0 {
FixedOffset::west_opt(off as _)
} else {
FixedOffset::east_opt(off as _)
}
}
const fn blank_tm() -> tm {
tm {
tm_year: 0,
+21 -9
View File
@@ -2,8 +2,11 @@
// Following https://pubs.opengroup.org/onlinepubs/7908799/xsh/strftime.html
use alloc::string::String;
use super::tm;
use crate::platform::{self, types::*, WriteByte};
use super::{get_offset, tm};
use crate::{
c_str::CStr,
platform::{self, types::*, WriteByte},
};
// We use the langinfo constants
use crate::header::langinfo::{
@@ -157,7 +160,7 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
b'I' => w!("{:02}", ((*t).tm_hour + 12 - 1) % 12 + 1),
// Day of year: %j
b'j' => w!("{:03}", (*t).tm_yday),
b'j' => w!("{:03}", (*t).tm_yday + 1),
// etc.
b'k' => w!("{:2}", (*t).tm_hour),
@@ -223,11 +226,22 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
// Full year: %Y
b'Y' => w!("{}", (*t).tm_year + 1900),
// Timezone offset: %z => currently hard-coded "+0000"
b'z' => w!("+0000"), // TODO: Add real timezone support
// Timezone offset: %z
b'z' => {
let offset = (*t).tm_gmtoff;
let (sign, offset) = if offset < 0 {
('-', -offset)
} else {
('+', offset)
};
let mins = offset.div_euclid(60);
let min = mins.rem_euclid(60);
let hour = mins.div_euclid(60);
w!("{}{:02}{:02}", sign, hour, min)
}
// Timezone name: %Z => currently "UTC"
b'Z' => w!("UTC"), // TODO: Add real timezone name support
// Timezone name: %Z
b'Z' => w!("{}", CStr::from_ptr((*t).tm_zone).to_str().unwrap()),
// Date+time+TZ: %+
b'+' => w!(recurse "%a %b %d %T %Z %Y"),
@@ -256,8 +270,6 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
/// https://en.wikipedia.org/wiki/ISO_week_date
fn weeks_per_year(year: c_int) -> c_int {
let year = year as f64;
// TODO: This function can be made const on newer Rust compilers but this line prevents it on
// our version in CI
let p_y = (year + (year / 4.) - (year / 100.) + (year / 400.)) as c_int % 7;
if p_y == 4 {
53
@@ -4,7 +4,7 @@
11: 03:00:00 PM
5: 15:00
15: 15 1531839600 2
6: 197 28
6: 198 28
28: Tue Jul 17 15:00:00 UTC 2018
0: Tue Aug 07 19:17:11 UTC 2018Tue Aug 07 19:17:11 U
53
@@ -4,7 +4,7 @@
11: 03:00:00 PM
5: 15:00
15: 15 1531839600 2
6: 197 28
6: 198 28
28: Tue Jul 17 15:00:00 UTC 2018
0: Tue Aug 07 19:17:11 UTC 2018Tue Aug 07 19:17:11 U
53
+97 -1
View File
@@ -1,16 +1,110 @@
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include "test_helpers.h"
#define PRINT_BUFSZ 50
#define V_BUFSZ 3
#define BUFS 30
void print(time_t timestamp, char* fmt) {
char out[PRINT_BUFSZ] = {0};
size_t n = strftime(out, PRINT_BUFSZ, fmt, localtime(&timestamp));
printf("%zu: %s\n", n, out);
}
void strftime_mix(void) {
setenv("TZ", "Australia/Melbourne", 1);
tzset();
// Initialize a struct tm with a known fixed date and time.
// December 31, 2020 23:59:59, which is a Thursday.
struct tm t = {0};
t.tm_year = 2020 - 1900; // Years since 1900.
t.tm_mon = 11; // December (0-based, so 11 means December).
t.tm_mday = 31;
t.tm_hour = 23;
t.tm_min = 59;
t.tm_sec = 59;
t.tm_wday = 4; // Thursday (0 = Sunday, so Thursday = 4).
t.tm_yday = 365;
t.tm_gmtoff = 39600; // GMT offset for AEDT (UTC+11).
t.tm_isdst = 11; // Daylight saving time is in effect.
t.tm_zone = "AEDT"; // Time zone name.
char buf[BUFS];
strftime(buf, BUFS, "%a", &t);
assert(strcmp(buf, "Thu") == 0);
strftime(buf, BUFS, "%A", &t);
assert(strcmp(buf, "Thursday") == 0);
strftime(buf, BUFS, "%b", &t);
assert(strcmp(buf, "Dec") == 0);
strftime(buf, BUFS, "%B", &t);
assert(strcmp(buf, "December") == 0);
strftime(buf, BUFS, "%d", &t);
assert(strcmp(buf, "31") == 0);
strftime(buf, BUFS, "%H", &t);
assert(strcmp(buf, "23") == 0);
strftime(buf, BUFS, "%I", &t);
assert(strcmp(buf, "11") == 0);
// Day of the year as a zero-padded decimal number (001-366).
strftime(buf, BUFS, "%j", &t);
// Even though t.tm_yday is 365 (zero-based), %j is one-based: "366"
assert(strcmp(buf, "366") == 0);
strftime(buf, BUFS, "%m", &t);
assert(strcmp(buf, "12") == 0);
strftime(buf, BUFS, "%M", &t);
assert(strcmp(buf, "59") == 0);
strftime(buf, BUFS, "%p", &t);
assert(strcmp(buf, "PM") == 0);
strftime(buf, BUFS, "%S", &t);
assert(strcmp(buf, "59") == 0);
strftime(buf, BUFS, "%U", &t);
assert(strcmp(buf, "52") == 0);
strftime(buf, BUFS, "%w", &t);
assert(strcmp(buf, "4") == 0);
strftime(buf, BUFS, "%W", &t);
assert(strcmp(buf, "52") == 0);
strftime(buf, BUFS, "%y", &t);
assert(strcmp(buf, "20") == 0);
strftime(buf, BUFS, "%Y", &t);
assert(strcmp(buf, "2020") == 0);
strftime(buf, BUFS, "%Z", &t);
assert(strcmp(buf, "AEDT") == 0);
strftime(buf, BUFS, "%z", &t);
assert(strcmp(buf, "+1100") == 0);
strftime(buf, BUFS, "%F", &t);
assert(strcmp(buf, "2020-12-31") == 0);
strftime(buf, BUFS, "%R", &t);
assert(strcmp(buf, "23:59") == 0);
strftime(buf, BUFS, "%r", &t);
assert(strcmp(buf, "11:59:59 PM") == 0);
strftime(buf, BUFS, "%T", &t);
assert(strcmp(buf, "23:59:59") == 0);
}
void test_v(void) {
@@ -67,5 +161,7 @@ int main(void) {
// ISO-8601 tests
test_v();
strftime_mix();
return EXIT_SUCCESS;
}