diff --git a/Makefile b/Makefile index 7d01555..fa7e00d 100644 --- a/Makefile +++ b/Makefile @@ -12,21 +12,24 @@ LIBRARIES = check glib-2.0 all: test example @echo "+++ All good.""" -test: tests/test-parser +test: tests/test-parser tests/test-timegm @echo "+++ Running parser test suite." tests/test-parser + @echo "+++ Running at-timegm test suite." + tests/test-timegm clean: - $(RM) src/example-at src/example-sim800 tests/test-parser + $(RM) src/example-at src/example-sim800 tests/test-parser tests/test-timegm $(RM) src/*.o src/modem/*.o tests/*.o PARSER = include/attentive/parser.h AT = include/attentive/at.h include/attentive/at-unix.h $(PARSER) -CELLULAR = include/attentive/cellular.h $(AT) +CELLULAR = include/attentive/cellular.h include/attentive/at-timegm.h $(AT) MODEM = src/modem/common.h $(CELLULAR) src/parser.o: src/parser.c $(PARSER) src/at-unix.o: src/at-unix.c $(AT) +src/at-timegm.o: src/at-timegm.c src/cellular.o: src/cellular.c $(CELLULAR) src/modem/common.o: src/modem/common.c $(MODEM) src/modem/generic.o: src/modem/generic.c $(MODEM) @@ -37,8 +40,9 @@ src/example-at.o: src/example-at.c $(AT) src/example-sim800.o: src/example-sim800.c $(CELLULAR) tests/test-parser: tests/test-parser.o src/parser.o +tests/test-timegm: tests/test-timegm.o src/at-timegm.o -src/example-at: src/example-at.o src/parser.o src/at-unix.o -src/example-sim800: src/example-sim800.o src/modem/sim800.o src/modem/common.o src/cellular.o src/at-unix.o src/parser.o +src/example-at: src/example-at.o src/parser.o src/at-unix.o src/at-timegm.o +src/example-sim800: src/example-sim800.o src/modem/sim800.o src/modem/common.o src/cellular.o src/at-unix.o src/at-timegm.o src/parser.o .PHONY: all test clean diff --git a/include/attentive/at-timegm.h b/include/attentive/at-timegm.h new file mode 100644 index 0000000..95a37f9 --- /dev/null +++ b/include/attentive/at-timegm.h @@ -0,0 +1,19 @@ +/* + * Copyright © 2025 Zyax AB + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef ATTENTIVE_AT_UNIX_H +#define ATTENTIVE_AT_UNIX_H + +#include +#include + +time_t at_timegm(const struct tm *tm); + +#endif + +/* vim: set ts=4 sw=4 et: */ diff --git a/src/at-timegm.c b/src/at-timegm.c new file mode 100644 index 0000000..b90e9c2 --- /dev/null +++ b/src/at-timegm.c @@ -0,0 +1,62 @@ +#include + +static int is_leap_year(int year) +{ + return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); +} + +static int days_in_month(int year, int month) +{ + static const int days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; + if (month == 1 && is_leap_year(year)) { + return 29; + } + return days[month]; +} + +time_t at_timegm(const struct tm *tm) +{ + int year = tm->tm_year + 1900; + int month = tm->tm_mon; + int day = tm->tm_mday; + int hour = tm->tm_hour; + int minute = tm->tm_min; + int second = tm->tm_sec; + + // Normalize month and year + if (month > 11) { + year += month / 12; + month %= 12; + } else if (month < 0) { + int years_diff = (11 - month) / 12; + year -= years_diff; + month += 12 * years_diff; + } + + // Days since Unix epoch + int64_t days = 0; + + // Years to days + for (int y = 1970; y < year; ++y) { + days += is_leap_year(y) ? 366 : 365; + } + for (int y = year; y < 1970; ++y) { + days -= is_leap_year(y) ? 366 : 365; + } + + // Months to days + for (int m = 0; m < month; ++m) { + days += days_in_month(year, m); + } + + // Days + days += (day - 1); + + // Total seconds + int64_t total_seconds = days * 86400 + + hour * 3600 + + minute * 60 + + second; + + return (time_t)total_seconds; +} diff --git a/src/modem/common.c b/src/modem/common.c index 45e3685..a125478 100644 --- a/src/modem/common.c +++ b/src/modem/common.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -135,7 +136,7 @@ int cellular_op_clock_gettime(struct cellular *modem, struct timespec *ts) /* Adjust values and perform conversion. */ tm.tm_year += 2000 - 1900; tm.tm_mon -= 1; - time_t unix_time = timegm(&tm); + time_t unix_time = at_timegm(&tm); if (unix_time == -1) { errno = EINVAL; return -1; diff --git a/src/modem/telit2.c b/src/modem/telit2.c index 824d503..b217097 100644 --- a/src/modem/telit2.c +++ b/src/modem/telit2.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -159,7 +160,7 @@ static int telit2_op_clock_gettime(struct cellular *modem, struct timespec *ts) /* Adjust values and perform conversion. */ tm.tm_year += 2000 - 1900; tm.tm_mon -= 1; - time_t unix_time = timegm(&tm); + time_t unix_time = at_timegm(&tm); if (unix_time == -1) { errno = EINVAL; return -1; diff --git a/tests/.gitignore b/tests/.gitignore index dadd09b..2d9b7d7 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1 +1,3 @@ test-parser +test-timegm + diff --git a/tests/test-timegm.c b/tests/test-timegm.c new file mode 100644 index 0000000..e3db54d --- /dev/null +++ b/tests/test-timegm.c @@ -0,0 +1,64 @@ +/* + * Copyright © 2015 Zyax AB + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + + #include + #include + #include + #include + + #include + #include + + #include + + + START_TEST(test_timegm) + { + printf(":: test_timegm\n"); + + struct tm tmg = { + .tm_sec = 7, + .tm_min = 14, + .tm_hour = 3, + .tm_mday = 19, + .tm_mon = 0, + .tm_year = 138, + .tm_wday = 2, + .tm_yday = 18, + .tm_isdst = 0 + }; + + time_t t = at_timegm(&tmg); + ck_assert_int_eq(t, 0x7fffffff); + } + END_TEST + + Suite *attentive_suite(void) + { + Suite *s = suite_create("attentive"); + TCase *tc; + + tc = tcase_create("timegm"); + tcase_add_test(tc, test_timegm); + suite_add_tcase(s, tc); + + return s; + } + + int main() + { + int number_failed; + Suite *s = attentive_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + } + + /* vim: set ts=4 sw=4 et: */