Skip to content

Commit 8df7914

Browse files
Merge pull request #11 from dreamer-coding/main
Datetime extended patch
2 parents f2fd41e + ffcc53c commit 8df7914

File tree

4 files changed

+675
-65
lines changed

4 files changed

+675
-65
lines changed

code/logic/datetime.c

Lines changed: 230 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,83 @@
1212
* -----------------------------------------------------------------------------
1313
*/
1414
#include "fossil/sys/datetime.h"
15+
16+
#include <errno.h>
17+
#include <stdint.h>
18+
#include <stdlib.h>
1519
#include <stdio.h>
20+
#include <string.h>
1621
#include <time.h>
1722

1823
#if defined(_WIN32) || defined(_WIN64)
1924
#include <windows.h>
20-
#include <time.h>
25+
#include <time.h> // Windows-specific time utilities
2126
#else
2227
#include <sys/time.h>
23-
#include <unistd.h>
28+
#include <unistd.h> // for nanosleep, POSIX sleep functions
29+
#endif
30+
31+
static const int DAYS_IN_MONTH[12] = {
32+
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
33+
};
34+
35+
36+
/**
37+
* @brief Helper: return 1 if leap year, else 0.
38+
*/
39+
static int is_leap_year_internal(int year) {
40+
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
41+
}
42+
43+
/**
44+
* @brief Helper: return days in given month (1-12).
45+
*/
46+
static int days_in_month_internal(int year, int month) {
47+
if (month == 2)
48+
return DAYS_IN_MONTH[1] + is_leap_year_internal(year);
49+
return DAYS_IN_MONTH[month - 1];
50+
}
51+
52+
#if defined(_WIN32) || defined(_WIN64)
53+
/**
54+
* Windows replacement for timegm() using _mkgmtime().
55+
*/
56+
static inline time_t fossil_sys_timegm(struct tm *tm) {
57+
return _mkgmtime(tm);
58+
}
59+
#else
60+
/**
61+
* Portable fallback for timegm().
62+
*
63+
* If timegm() is available (GNU, BSD, etc.), just use it.
64+
* Otherwise, emulate it using mktime() after forcing UTC.
65+
*/
66+
static inline time_t fossil_sys_timegm(struct tm *tm) {
67+
#if defined(__USE_BSD) || defined(__USE_GNU)
68+
return timegm(tm);
69+
#else
70+
/* Fallback: Temporarily set TZ to UTC, call mktime, restore TZ. */
71+
char *old_tz = getenv("TZ");
72+
setenv("TZ", "", 1); // Force UTC
73+
tzset();
74+
75+
time_t result = mktime(tm);
76+
77+
if (old_tz)
78+
setenv("TZ", old_tz, 1);
79+
else
80+
unsetenv("TZ");
81+
82+
tzset();
83+
return result;
84+
#endif
85+
}
2486
#endif
2587

88+
89+
90+
// main source code
91+
2692
void fossil_sys_time_now(fossil_sys_time_datetime_t *dt) {
2793
if (!dt) return;
2894

@@ -96,3 +162,165 @@ int fossil_sys_time_days_in_month(int year, int month) {
96162
if (month == 2 && fossil_sys_time_is_leap_year(year)) return 29;
97163
return days_per_month[month - 1];
98164
}
165+
166+
void fossil_sys_time_normalize(fossil_sys_time_datetime_t *dt) {
167+
if (!dt) return;
168+
169+
// Normalize nanoseconds to [0, 1e9)
170+
while (dt->nanosecond >= 1000000000) {
171+
dt->second++;
172+
dt->nanosecond -= 1000000000;
173+
}
174+
while (dt->nanosecond < 0) {
175+
dt->second--;
176+
dt->nanosecond += 1000000000;
177+
}
178+
179+
// Normalize seconds, minutes, hours
180+
while (dt->second >= 60) {
181+
dt->minute++;
182+
dt->second -= 60;
183+
}
184+
while (dt->second < 0) {
185+
dt->minute--;
186+
dt->second += 60;
187+
}
188+
189+
while (dt->minute >= 60) {
190+
dt->hour++;
191+
dt->minute -= 60;
192+
}
193+
while (dt->minute < 0) {
194+
dt->hour--;
195+
dt->minute += 60;
196+
}
197+
198+
while (dt->hour >= 24) {
199+
dt->day++;
200+
dt->hour -= 24;
201+
}
202+
while (dt->hour < 0) {
203+
dt->day--;
204+
dt->hour += 24;
205+
}
206+
207+
// Normalize day/month/year
208+
while (dt->day > days_in_month_internal(dt->year, dt->month)) {
209+
dt->day -= days_in_month_internal(dt->year, dt->month);
210+
dt->month++;
211+
if (dt->month > 12) {
212+
dt->month = 1;
213+
dt->year++;
214+
}
215+
}
216+
217+
while (dt->day <= 0) {
218+
dt->month--;
219+
if (dt->month <= 0) {
220+
dt->month = 12;
221+
dt->year--;
222+
}
223+
dt->day += days_in_month_internal(dt->year, dt->month);
224+
}
225+
}
226+
227+
int fossil_sys_time_validate(const fossil_sys_time_datetime_t *dt) {
228+
if (!dt) return 0;
229+
if (dt->month < 1 || dt->month > 12) return 0;
230+
if (dt->day < 1 || dt->day > days_in_month_internal(dt->year, dt->month)) return 0;
231+
if (dt->hour < 0 || dt->hour > 23) return 0;
232+
if (dt->minute < 0 || dt->minute > 59) return 0;
233+
if (dt->second < 0 || dt->second > 59) return 0;
234+
if (dt->nanosecond < 0 || dt->nanosecond >= 1000000000) return 0;
235+
return 1;
236+
}
237+
238+
void fossil_sys_time_add_seconds(fossil_sys_time_datetime_t *dt, int64_t seconds) {
239+
if (!dt) return;
240+
dt->second += (int)seconds;
241+
fossil_sys_time_normalize(dt);
242+
}
243+
244+
int64_t fossil_sys_time_to_unix(const fossil_sys_time_datetime_t *dt) {
245+
if (!dt) return 0;
246+
struct tm t = {0};
247+
t.tm_year = dt->year - 1900;
248+
t.tm_mon = dt->month - 1;
249+
t.tm_mday = dt->day;
250+
t.tm_hour = dt->hour;
251+
t.tm_min = dt->minute;
252+
t.tm_sec = dt->second;
253+
time_t result = fossil_sys_timegm(&t); // UTC-safe variant of mktime
254+
return (int64_t)result;
255+
}
256+
257+
void fossil_sys_time_from_unix(int64_t timestamp, fossil_sys_time_datetime_t *dt) {
258+
if (!dt) return;
259+
time_t t = (time_t)timestamp;
260+
struct tm out;
261+
gmtime_r(&t, &out);
262+
dt->year = out.tm_year + 1900;
263+
dt->month = out.tm_mon + 1;
264+
dt->day = out.tm_mday;
265+
dt->hour = out.tm_hour;
266+
dt->minute = out.tm_min;
267+
dt->second = out.tm_sec;
268+
dt->nanosecond = 0;
269+
}
270+
271+
int64_t fossil_sys_time_diff_seconds(const fossil_sys_time_datetime_t *a,
272+
const fossil_sys_time_datetime_t *b) {
273+
return fossil_sys_time_to_unix(a) - fossil_sys_time_to_unix(b);
274+
}
275+
276+
uint64_t fossil_sys_time_monotonic_ns(void) {
277+
#if defined(CLOCK_MONOTONIC)
278+
struct timespec ts;
279+
clock_gettime(CLOCK_MONOTONIC, &ts);
280+
return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec;
281+
#else
282+
// Fallback: use wall clock (less precise)
283+
struct timespec ts;
284+
clock_gettime(CLOCK_REALTIME, &ts);
285+
return ((uint64_t)ts.tv_sec * 1000000000ULL) + (uint64_t)ts.tv_nsec;
286+
#endif
287+
}
288+
289+
void fossil_sys_time_sleep_ns(uint64_t nanoseconds) {
290+
struct timespec req, rem;
291+
req.tv_sec = nanoseconds / 1000000000ULL;
292+
req.tv_nsec = nanoseconds % 1000000000ULL;
293+
while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
294+
req = rem; // resume sleeping
295+
}
296+
}
297+
298+
fossil_sys_time_span_t fossil_sys_time_span_from_seconds(int64_t seconds) {
299+
fossil_sys_time_span_t span = {0};
300+
span.days = seconds / 86400;
301+
seconds %= 86400;
302+
span.hours = seconds / 3600;
303+
seconds %= 3600;
304+
span.minutes = seconds / 60;
305+
span.seconds = seconds % 60;
306+
return span;
307+
}
308+
309+
int64_t fossil_sys_time_span_to_seconds(const fossil_sys_time_span_t *span) {
310+
if (!span) return 0;
311+
return span->days * 86400 +
312+
span->hours * 3600 +
313+
span->minutes * 60 +
314+
span->seconds;
315+
}
316+
317+
void fossil_sys_time_add_span(fossil_sys_time_datetime_t *dt,
318+
const fossil_sys_time_span_t *span) {
319+
if (!dt || !span) return;
320+
dt->day += (int)span->days;
321+
dt->hour += (int)span->hours;
322+
dt->minute += (int)span->minutes;
323+
dt->second += (int)span->seconds;
324+
dt->nanosecond += span->nanoseconds;
325+
fossil_sys_time_normalize(dt);
326+
}

0 commit comments

Comments
 (0)