We have firmware that will be deployed on devices all over the world and that will be synced to UTC by a host device. To track time, we end up converting between struct tm
and time_t
(Unix time) because we use a hardware RTC whose registers closely mirror struct tm
, but any time-based operations are done on Unix time. However, when we use mktime
it attempts to put everything into localtime
, which is not UTC on the build system. We could just add an offset, but it would be easier to just tell time.h
that our local time should be UTC, since the device is otherwise agnostic to the local time.
Is there a (non-invasive) way to do this other than changing the local time on the build system to UTC? Like, can we somehow use tzset
to inject this data? Or just set TZ
to something? I'm having difficulty understanding how I would set TZ
if I just wanted UTC.
CodePudding user response:
However, when we use
mktime
it attempts to put everything intolocaltime
...
The compiler's time zone setting at compile time is irrelevant to how code handles time at run time.
The easiest way to avoid local time in conversions is to hope your compiler offers struct_tm
(UTC) to time_t
, instead of using mktime()
(time zone dependent) as an extension like time_t timegm(struct tm *tm)
.
There is no simple standard solution. Tricks with divining the offset via locatime(), gmtime()
fail corner cases.
IMO, to convert struct_tm
(UTC) to UNIX time time_t
, simply write that code and get it reviewed. It is not that hard as there are few corner cases (aside from overflow).
Some sample code to change int year, int month, int day
(0:00:00) to MJD to get OP started.
#include <stdint.h>
static const short DaysMarch1ToBeginingOfMonth[12] = { //
0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337};
#ifndef INT32_C
#define INT32_C(x) ((int_least32_t)1*(x))
#endif
#define DaysPer400Years (INT32_C(365)*400 97)
#define DaysPer100Years (INT32_C(365)*100 24)
#define DaysPer4Years (365*4 1)
#define DaysPer1Year 365
#define MonthsPerYear 12
#define MonthsPer400Years (12*400)
#define MonthMarch 3
#define mjdOffset 0xA5BE1
#define mjd1900Jan1 15020
// November 17, 1858
// Example: 2015 December 31 --> ymd_to_mjd(2015, 12, 31)
int2x ymd_to_mjd(int year, int month, int day) {
// int2x is a type twice as wide as int to handle extreme int values.
// Use int (at least 32-bit) to handle common values.
int2x year2x = year;
year2x = month / MonthsPerYear;
month %= MonthsPerYear;
// Adjust for month/year to Mar ... Feb
while (month < MonthMarch) {
month = MonthsPerYear;
year2x--;
}
int2x d = (year2x / 400) * DaysPer400Years;
int y400 = (int) (year2x % 400);
d = (y400 / 100) * DaysPer100Years;
int y100 = y400 % 100;
d = (y100 / 4) * DaysPer4Years;
int y4 = y100 % 4;
d = y4 * DaysPer1Year;
d = DaysMarch1ToBeginingOfMonth[month - MonthMarch];
d = day;
// November 17, 1858 == MJD 0
d--;
d -= mjdOffset;
return d;
}