1#include "time/tests/test_internal.h"
2
3TEST_GROUP_DECLARE(date_time, .intensity_desc = {
4 .curve = SCALE_PIECEWISE_LOG,
5 .unit = "years",
6 });
7
8/* NOTE: we use two different month conventions, since struct date_time
9 * is 0-11, and days_in_month is 1-12, but we probably want to change that */
10TEST_DECLARE_UNIT(date_time, leap_year_rule) {
11 TEST_ASSERT(is_leap_year(2004)); /* divisible by 4 */
12 TEST_ASSERT(!is_leap_year(1900)); /* but by 100, so not */
13 TEST_ASSERT(is_leap_year(2000)); /* unless also by 400 */
14 TEST_ASSERT(!is_leap_year(2100));
15
16 TEST_ASSERT(!is_leap_year(2001));
17 TEST_ASSERT(!is_leap_year(2002));
18 TEST_ASSERT(!is_leap_year(2003));
19 TEST_ASSERT(is_leap_year(2024));
20 TEST_ASSERT(is_leap_year(1600));
21
22 return TEST_SUCCESS;
23}
24
25TEST_DECLARE_UNIT(date_time, days_in_month_table) {
26 static const uint32_t common[13] = {0, 31, 28, 31, 30, 31, 30,
27 31, 31, 30, 31, 30, 31};
28
29 for (int m = 1; m <= 12; m++) {
30 TEST_ASSERT_EQ(days_in_month(2001, m), common[m]);
31 TEST_ASSERT_EQ(days_in_month(2000, m), (m == 2 ? 29 : common[m]));
32 }
33
34 TEST_ASSERT_EQ(days_in_month(1900, 2), 28);
35 TEST_ASSERT_EQ(days_in_month(2024, 2), 29);
36
37 return TEST_SUCCESS;
38}
39
40TEST_DECLARE_UNIT(date_time, days_in_year_sums) {
41 for (year_t y = 1998; y <= 2005; y++) {
42 uint32_t total = 0;
43 for (int m = 1; m <= 12; m++)
44 total += days_in_month(year: y, month: m);
45
46 TEST_ASSERT_EQ(total, (is_leap_year(y) ? 366u : 365u));
47 }
48
49 return TEST_SUCCESS;
50}
51
52/* Day of year 0 is Jan 1, the boundaries here are the first day of
53 * each following month*/
54TEST_DECLARE_UNIT(date_time, expand_month_boundaries) {
55 struct date_time dt = {.year = 2001, .day = 0, .sec = 0};
56
57 struct date_time_expanded e = date_time_expand(dt: &dt);
58 TEST_ASSERT(e.month == 0 && e.day_of_month == 1);
59
60 dt.day = 30; /* Jan 31st */
61 e = date_time_expand(dt: &dt);
62 TEST_ASSERT(e.month == 0 && e.day_of_month == 31);
63
64 dt.day = 31; /* Feb 1st */
65 e = date_time_expand(dt: &dt);
66 TEST_ASSERT(e.month == 1 && e.day_of_month == 1);
67
68 dt.day = 58; /* Feb 28th, common year */
69 e = date_time_expand(dt: &dt);
70 TEST_ASSERT(e.month == 1 && e.day_of_month == 28);
71
72 dt.day = 59; /* Mar 1st, common year */
73 e = date_time_expand(dt: &dt);
74 TEST_ASSERT(e.month == 2 && e.day_of_month == 1);
75
76 dt.day = 364; /* Dec 31st, common year */
77 e = date_time_expand(dt: &dt);
78 TEST_ASSERT(e.month == 11 && e.day_of_month == 31);
79
80 return TEST_SUCCESS;
81}
82
83/* Test months after Feb */
84TEST_DECLARE_UNIT(date_time, expand_leap_year_boundaries) {
85 struct date_time dt = {.year = 2000, .day = 59, .sec = 0};
86
87 struct date_time_expanded e = date_time_expand(dt: &dt);
88 TEST_ASSERT(e.month == 1 && e.day_of_month == 29); /* Feb 29th */
89
90 dt.day = 60;
91 e = date_time_expand(dt: &dt);
92 TEST_ASSERT(e.month == 2 && e.day_of_month == 1); /* Mar 1st */
93
94 dt.day = 365;
95 e = date_time_expand(dt: &dt);
96 TEST_ASSERT(e.month == 11 && e.day_of_month == 31); /* Dec 31st */
97
98 return TEST_SUCCESS;
99}
100
101TEST_DECLARE_UNIT(date_time, expand_time_of_day) {
102 struct date_time dt = {.year = 2001, .day = 0, .sec = 0};
103
104 struct date_time_expanded e = date_time_expand(dt: &dt);
105 TEST_ASSERT(e.hour == 0 && e.minute == 0 && e.second == 0);
106
107 dt.sec = 3661; /* 01:01:01 */
108 e = date_time_expand(dt: &dt);
109 TEST_ASSERT(e.hour == 1 && e.minute == 1 && e.second == 1);
110
111 dt.sec = 86399; /* 23:59:59 */
112 e = date_time_expand(dt: &dt);
113 TEST_ASSERT(e.hour == 23 && e.minute == 59 && e.second == 59);
114
115 return TEST_SUCCESS;
116}
117
118TEST_DECLARE_UNIT(date_time, expand_compact_roundtrip,
119 TEST_INTENSITY(2, 5, 100)) {
120 static const year_t base_years[] = {1999, 2000, 2001, 2024, 2100};
121 size_t num_years =
122 ctx->intensity_val ? ctx->intensity_val : TEST_ARRAY_LEN(base_years);
123
124 for (size_t i = 0; i < num_years; i++) {
125 year_t y = (i < TEST_ARRAY_LEN(base_years)) ? base_years[i]
126 : (year_t) (2000 + i);
127 uint16_t last = is_leap_year(year: y) ? 365 : 364;
128
129 for (uint16_t day = 0; day <= last; day++) {
130 struct date_time in = {.year = y, .day = day, .sec = 43200};
131
132 struct date_time_expanded e = date_time_expand(dt: &in);
133 struct date_time out;
134 date_time_compact(expanded: &e, out: &out);
135
136 TEST_ASSERT_EQ(out.year, in.year);
137 TEST_ASSERT_EQ(out.day, in.day);
138 TEST_ASSERT_EQ(out.sec, in.sec);
139
140 /* Expanded form must be OK too */
141 TEST_ASSERT_LT(e.month, 12);
142 TEST_ASSERT_GE(e.day_of_month, 1);
143 TEST_ASSERT_LE(e.day_of_month, days_in_month(y, e.month + 1));
144 TEST_ASSERT_LT(e.day_of_week, 7);
145 }
146 }
147
148 return TEST_SUCCESS;
149}
150
151/* Weekdays advance by one day, and anchoring one date pins the offset */
152TEST_DECLARE_UNIT(date_time, weekday_progression) {
153 struct date_time dt = {.year = 2001, .day = 0, .sec = 0};
154
155 uint8_t prev = date_time_expand(dt: &dt).day_of_week;
156 for (uint16_t day = 1; day <= 364; day++) {
157 dt.day = day;
158 uint8_t cur = date_time_expand(dt: &dt).day_of_week;
159
160 TEST_ASSERT_LT(cur, 7);
161 TEST_ASSERT_EQ(cur, (prev + 1) % 7);
162 prev = cur;
163 }
164
165 /* Jan 1 2001 was Monday, Jan 1 2000 a Saturday */
166 struct date_time y2001 = {.year = 2001, .day = 0, .sec = 0};
167 struct date_time y2000 = {.year = 2000, .day = 0, .sec = 0};
168 uint8_t mon = date_time_expand(dt: &y2001).day_of_week;
169 uint8_t sat = date_time_expand(dt: &y2000).day_of_week;
170
171 TEST_ASSERT_EQ((sat + 2) % 7, mon);
172
173 return TEST_SUCCESS;
174}
175