strftime: ISO-8601 leap weeks

This commit is contained in:
Josh Megnauth
2025-03-19 02:48:22 -04:00
parent 5ddf2ad000
commit ccc1b7f560
4 changed files with 95 additions and 3 deletions
+35
View File
@@ -208,6 +208,9 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
// Sunday-based week of year: %U
b'U' => w!("{}", ((*t).tm_yday + 7 - (*t).tm_wday) / 7),
// ISO-8601 week of year
b'V' => w!("{}", week_of_year(unsafe { &*t })),
// Weekday (0-6, Sunday=0): %w
b'w' => w!("{}", (*t).tm_wday),
@@ -246,3 +249,35 @@ pub unsafe fn strftime<W: WriteByte>(w: &mut W, format: *const c_char, t: *const
}
cw.written
}
/// Calculate number of weeks in a year as defined by ISO 8601
///
/// ## Source
/// 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
} else {
52
}
}
/// Calculate the week of the year accounting for leap weeks (ISO 8601)
///
/// ## Source
/// https://en.wikipedia.org/wiki/ISO_week_date
fn week_of_year(time: &tm) -> c_int {
let week = (10 + time.tm_yday - time.tm_wday) / 7;
if week <= 1 {
weeks_per_year(time.tm_year - 1)
} else if week > weeks_per_year(time.tm_year) {
1
} else {
week
}
}
@@ -7,3 +7,8 @@
6: 197 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
52
52
1
53
@@ -7,3 +7,8 @@
6: 197 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
52
52
1
53
+50 -3
View File
@@ -4,11 +4,53 @@
#include "test_helpers.h"
#define PRINT_BUFSZ 50
#define V_BUFSZ 3
void print(time_t timestamp, char* fmt) {
char* out = malloc(50);
size_t n = strftime(out, 50, fmt, localtime(&timestamp));
char out[PRINT_BUFSZ] = {0};
size_t n = strftime(out, PRINT_BUFSZ, fmt, localtime(&timestamp));
printf("%zu: %s\n", n, out);
free(out);
}
void test_v(void) {
char buf[V_BUFSZ] = {0};
struct tm time = {0};
// Example dates copied from Wikipedia
// Saturday 2005-01-01
time.tm_yday = 0;
time.tm_wday = 6;
time.tm_year = 2005;
strftime(buf, V_BUFSZ, "%V", &time);
puts(buf);
// Saturday 2005-12-31
time.tm_yday = 365;
time.tm_wday = 6;
strftime(buf, V_BUFSZ, "%V", &time);
puts(buf);
// Sunday 2006-01-01
time.tm_yday = 0;
time.tm_wday = 0;
time.tm_year = 2006;
strftime(buf, V_BUFSZ, "%V", &time);
puts(buf);
// Sunday 2008-12-28
time.tm_yday = 362;
time.tm_wday = 0;
time.tm_year = 2008;
strftime(buf, V_BUFSZ, "%V", &time);
puts(buf);
// Friday 2010-01-01
time.tm_yday = 0;
time.tm_wday = 5;
time.tm_year = 2010;
strftime(buf, V_BUFSZ, "%V", &time);
puts(buf);
}
int main(void) {
@@ -21,4 +63,9 @@ int main(void) {
print(1531839600, "%j %U");
print(1531839600, "%+");
print(1533669431, "%+%+%+%+%+"); // will overflow 50 characters
// ISO-8601 tests
test_v();
return EXIT_SUCCESS;
}