Related
Disclaimer: It took me a solid 4-5 hours of looking for an answer and after figuring it out I decided to post it here for people in the same place.
OP-TEE is quite a good environment to develop TAs and CAs, however, there is no straightforward method of acquiring a datetime formatted properly. There is no struct tm either. Therefore, it made me wonder how do I get a datetime format in OP-TEE TAs?
What I spent a long time trying was to utilize the already supported mbedTLS libraries which, for a newcomer, would seem like they do support getting datetime format. After all, they do have gmtime which is supposed to return this value.
However, unfortunately, the gmtime and relevant functions have no implementation for the platform OP-TEE on ARMv8. That's a pretty tough luck.
So how do you get UTC time in an OP-TEE TA?
All OP-TEE development for ARMv8 is done using C. However, it lacks major libc support. Practically, it has very little libraries (e.g. string.h) which are skimmed down versions from the original libc corresponding libraries.
With that, the provided <time.h> in the OP-TEE contains nothing but a typedef for time_t and that's it.
The problem can be broken down to two sections:
How do you get the epochs since 1970 Jan 1st 00:00:00?
This is an interesting problem, and while the straight forward solution is to simply do this:
TEE_Time tt;
TEE_GetREETime(&tt);
This solution may not be satisfactory for many people who would not want to rely on REE's (Rich Execution Environment, AKA the vulnerable environment) count of epochs. This can be problematic for security sensitive operations where you need time to be legit and with no room for REE to modify it to perform a certain attack.
In the case described above, you will have to obtain the epochs from a hardware clock which will depend on the hardware board you are developing the TA on. You can even retrieve it from location devices which also return UTC time in NMEA statements. While it may not be 100% precise to the second, it may just be enough. If you need very high precision, you will need to get it from a hardware device securely.
Either way, you have to figure out how to get the epochs on your own. This answer focuses on the second part: getting the datetime.
Getting the datetime from the epochs. Once you resolved step 1, you need to process it to datetime. For this purpose, you need gmtime which does not exist in the OP-TEE. It has no implementation. And you need a minimalist implementation to keep things simple.
Luckily, I was able to find this answer. Which links to newlib libraries that develop for Free BSD which are ideal for embedded systems. Hence why it's useful here!
I was able to put it together from their implementation and you can use it here:
gmtime_r.h:
#include <inttypes.h>
#define SECSPERMIN 60L
#define MINSPERHOUR 60L
#define HOURSPERDAY 24L
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
#define DAYSPERWEEK 7
#define MONSPERYEAR 12
#define YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY 4
#define EPOCH_YEARS_SINCE_LEAP 2
#define EPOCH_YEARS_SINCE_CENTURY 70
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
typedef int64_t time_t;
struct tm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct tm* gmtime_r (time_t tim_p, struct tm* res);
gmtime_r.c:
#include "gmtime_r.h"
#define EPOCH_ADJUSTMENT_DAYS 719468L
/* year to which the adjustment was made */
#define ADJUSTED_EPOCH_YEAR 0
/* 1st March of year 0 is Wednesday */
#define ADJUSTED_EPOCH_WDAY 3
/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
#define DAYS_PER_ERA 146097L
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
#define DAYS_PER_CENTURY 36524L
/* there is one leap year every 4 years */
#define DAYS_PER_4_YEARS (3 * 365 + 366)
/* number of days in a non-leap year */
#define DAYS_PER_YEAR 365
/* number of days in January */
#define DAYS_IN_JANUARY 31
/* number of days in non-leap February */
#define DAYS_IN_FEBRUARY 28
/* number of years per era */
#define YEARS_PER_ERA 400
struct tm* gmtime_r (time_t tim_p, struct tm* res)
{
time_t days, rem;
const time_t lcltime = tim_p;
int era, weekday, year;
unsigned erayear, yearday, month, day;
unsigned long eraday;
days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
rem = lcltime % SECSPERDAY;
if (rem < 0)
{
rem += SECSPERDAY;
--days;
}
/* compute hour, min, and sec */
res->tm_hour = (int) (rem / SECSPERHOUR);
rem %= SECSPERHOUR;
res->tm_min = (int) (rem / SECSPERMIN);
res->tm_sec = (int) (rem % SECSPERMIN);
/* compute day of week */
if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
weekday += DAYSPERWEEK;
res->tm_wday = weekday;
/* compute year, month, day & day of year */
/* for description of this algorithm see
* http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
eraday = days - era * DAYS_PER_ERA; /* [0, 146096] */
erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
eraday / (DAYS_PER_ERA - 1)) / 365; /* [0, 399] */
yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0, 365] */
month = (5 * yearday + 2) / 153; /* [0, 11] */
day = yearday - (153 * month + 2) / 5 + 1; /* [1, 31] */
month += month < 10 ? 2 : -10;
year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);
res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + isleap(erayear);
res->tm_year = year - YEAR_BASE;
res->tm_mon = month;
res->tm_mday = day;
res->tm_isdst = 0;
return (res);
}
You can put both of these files in your TA folder, and make sure you add gmtime.c in the sources list in the sub.mk. And finally, in the TA itself you can use it:
TEE_Time tt;
TEE_GetREETime(&tt);
struct tm *lt, temp;
lt = gmtime((time_t)tt.seconds, &temp);
DMSG("%4d-%2d-%2d %2d:%2d:%2d", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
This will print the time and date in a proper format.
Currently, I have not yet ported the implementation of strftime, but soon I will do that as well which will auto-format the tm struct and add the 1900 to tm_year and 1 to tm_mon.
In the meantime, I hope this finds someone in need.
Timer security
To retrieve the current time in milliseconds since 1970-01-01, call TEE_GetSystemTime. The security level depends on what secure clocks are available on the specific system. The absolute minimum guarantee is that this value cannot decrease while a trusted application is running, but the REE may be able to control at what speed this proceeds. There is no guarantee across reboots.
OP-TEE comes with an implementation of TEE_GetSystemTime based on CNTPCT. This can be configured to be exclusive to the TrustZone secure world on any armv8 chip, but I don't know if all chip manufacturers actually do so.
When the timer is secure, this is a guarantee that short delays will be respected. For example, if you want to block at least 1 second between authentication attempts, this is good enough.
Many platforms cannot keep the date securely, because that requires a clock that's always powered on, which requires a battery that doesn't run out. Often the non-trusted world can arrange to set the time to an arbitrary value after a system reset. So if you need the current date with enough precision to verify that a certificate has not expired, that's not good enough, and you need to do something like establish a connection to a secure time server. Which is a bootstrap problem, since you can't verify that the time server's certificate hasn't expired.
Breaking down the time
OP-TEE provides a thin C library, not including anything like gmtime. If you want to calculate the date and time from the epoch time, you need to provide your own implementation.
Mbed TLS is a consumer, not a provider, of gmtime (more precisely gmtime_r). It uses this function to check the validity of certificates.
I didn't find a trivial way to get the time offset in minutes between the local time and the UTC time.
At first I intended to use tzset() but it doesn't provide the daylight saving time. According to the man page, it is simply an integer different of zero if day light saving is in effect. While it is usually an hour, it may be half an hour in some country.
I would prefer avoiding to compute the time difference between current UTC returned by gmtime() and localtime().
A more general solution would give me this information for a specified location and a positive time_t value, or at least locally.
Edit 1: the use case is to get the right local time offset for https://github.com/chmike/timez.
BTW, If you thought libc functions to manipulate time were Ok, read this https://rachelbythebay.com/w/2013/03/17/time/.
Edit 2: the best and simplest solution I have so far to compute the time offset to UTC in minutes is
// Bogus: assumes DST is always one hour
tzset();
int offset = (int)(-timezone / 60 + (daylight ? 60 : 0));
The problem is to determine the real day light saving time.
Edit 3: Inspired by the answer of #trenki, I came up with the following solution. This is a hack in that it tricks mktime() to consider the output of gmtime() as the localtime. The result is inaccurate when the DST change is in the time span between UTC time and localtime.
#include <stdio.h>
#include <time.h>
int main()
{
time_t rawtime = time(NULL);
struct tm *ptm = gmtime(&rawtime);
// Request that mktime() looksup dst in timezone database
ptm->tm_isdst = -1;
time_t gmt = mktime(ptm);
double offset = difftime(rawtime, gmt) / 60;
printf("%f\n", offset);
return 0;
}
This C code computes the local time offset in minutes relative to UTC. It assumes that DST is always one hour offset.
#include <stdio.h>
#include <time.h>
int main()
{
time_t rawtime = time(NULL);
struct tm *ptm = gmtime(&rawtime);
time_t gmt = mktime(ptm);
ptm = localtime(&rawtime);
time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0);
printf("%i\n", (int)offset);
}
It uses gmtime and localtime though. Why don't you want to use those functions?
Does your system's strftime() function support the %z and %Z specifiers? On FreeBSD,
%Z is replaced by the time zone name.
%z is replaced by the time zone offset from UTC; a leading plus sign
stands for east of UTC, a minus sign for west of UTC, hours and
minutes follow with two digits each and no delimiter between them
(common form for RFC 822 date headers).
and I can use this to print this:
$ date +"%Z: %z"
CEST: +0200
ISO C99 has this in 7.23.3.5 The strftime function:
%z is replaced by the offset from UTC in the ISO 8601 format
‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich),
or by no characters if no time zone is determinable. [tm_isdst]
%Z is replaced by the locale’s time zone name or abbreviation, or by no
characters if no time zone is determinable. [tm_isdst]
... to get local time offset ... relative to UTC?
#Serge Ballesta answer is good. So I though I would test it and clean-up a few details. I would have posted as a comment but obviously too big for that. I only exercised it for my timezone, but though others may want to try on their machine and zone.
I made to community wiki as not to garner rep. Imitation is the sincerest form of flattery
This answer is akin to #trenki except that it subtracts nearby struct tm values instead of assuming DST shift is 1 hour and time_t is in seconds.
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
// return difference in **seconds** of the tm_mday, tm_hour, tm_min, tm_sec members.
long tz_offset_second(time_t t) {
struct tm local = *localtime(&t);
struct tm utc = *gmtime(&t);
long diff = ((local.tm_hour - utc.tm_hour) * 60 + (local.tm_min - utc.tm_min))
* 60L + (local.tm_sec - utc.tm_sec);
int delta_day = local.tm_mday - utc.tm_mday;
// If |delta_day| > 1, then end-of-month wrap
if ((delta_day == 1) || (delta_day < -1)) {
diff += 24L * 60 * 60;
} else if ((delta_day == -1) || (delta_day > 1)) {
diff -= 24L * 60 * 60;
}
return diff;
}
void testtz(void) {
long off = -1;
int delta = 600;
for (time_t t = 0; t < LONG_MAX-delta; t+=delta) {
long off2 = tz_offset_second(t);
// Print time whenever offset changes.
if (off != off2) {
struct tm utc = *gmtime(&t);
printf("%10jd %04d-%02d-%02dT%02d:%02d:%02dZ\n", (intmax_t) t,
utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday,
utc.tm_hour, utc.tm_min, utc.tm_sec);
struct tm local = *localtime(&t);
off = off2;
printf("%10s %04d-%02d-%02d %02d:%02d:%02d %2d %6ld\n\n", "",
local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
local.tm_hour, local.tm_min, local.tm_sec, local.tm_isdst ,off);
fflush(stdout);
}
}
puts("Done");
}
Output
v----v Difference in seconds
0 1970-01-01T00:00:00Z
1969-12-31 18:00:00 0 -21600
5731200 1970-03-08T08:00:00Z
1970-03-08 03:00:00 1 -18000
26290800 1970-11-01T07:00:00Z
1970-11-01 01:00:00 0 -21600
...
2109222000 2036-11-02T07:00:00Z
2036-11-02 01:00:00 0 -21600
2120112000 2037-03-08T08:00:00Z
2037-03-08 03:00:00 1 -18000
2140671600 2037-11-01T07:00:00Z
2037-11-01 01:00:00 0 -21600
Done
IMHO the only foolproof and portable way is to use localtime and gmtime and manually compute the delta in minute because those 2 functions exist on all known systems. For example:
int deltam() {
time_t t = time(NULL);
struct tm *loc = localtime(&t);
/* save values because they could be erased by the call to gmtime */
int loc_min = loc->tm_min;
int loc_hour = loc->tm_hour;
int loc_day = loc->tm_mday;
struct tm *utc = gmtime(&t);
int delta = loc_min - utc->tm_min;
int deltaj = loc_day - utc->tm_mday;
delta += (loc_hour - utc->tm_hour) * 60;
/* hack for the day because the difference actually is only 0, 1 or -1 */
if ((deltaj == 1) || (deltaj < -1)) {
delta += 1440;
}
else if ((deltaj == -1) || (deltaj > 1)) {
delta -= 1440;
}
return delta;
}
Beware, I did not test all possible corner cases, but it could be a starting point for your requirement.
I would like to submit yet another answer to this question, one that AFAICS also deals with the IDL.
This solution depends on timegm and mktime. On Windows timegm is available as _mkgmtime from the CRT, in other words define a conditional macro.
#if _WIN32
# define timegm _mkgmtime
#endif
int local_utc_offset_minutes ( ) {
time_t t = time ( NULL );
struct tm * locg = localtime ( &t );
struct tm locl;
memcpy ( &locl, locg, sizeof ( struct tm ) );
return (int)( timegm ( locg ) - mktime ( &locl ) ) / 60;
}
Here is my way:
time_t z = 0;
struct tm * pdt = gmtime(&z);
time_t tzlag = mktime(pdt);
Alternative with automatic, local storage of struct tm:
struct tm dt;
memset(&dt, 0, sizeof(struct tm));
dt.tm_mday=1; dt.tm_year=70;
time_t tzlag = mktime(&dt);
tzlag, in seconds, will be the negative of the UTC offset; lag of your timezone Standard Time compared to UTC:
LocalST + tzlag = UTC
If you want to also account for "Daylight savings", subtract tm_isdst from tzlag, where tm_isdst is the field for a particular local time struct tm, after applying mktime to it (or after obtaining it with localtime ).
Why it works:
The set struct tm is for "epoch" moment, Jan 1 1970, which corresponds to a time_t of 0.
Calling mktime() on that date converts it to time_t as if it were UTC (thus getting 0), then subtracts the UTC offset from it in order to produce the output time_t. Thus it produces negative of UTC_offset.
Using nothing but the C standard library (plain ISO C, no POSIX, and thus no assumption that time_t is represented in "seconds since the epoch"), what is the simplest way to get a time_t value corresponding to 01 Jan 1970 00:00:00 UTC?
The UTC part is the key; otherwise, just using mktime on a properly initialized struct tm would trivially solve the problem.
Alternatively (this is actually the "point" of the question), how does one portably determine the number of POSIX seconds between a given time_t value, e.g. the current time obtained via time(0), and the epoch? By "POSIX seconds" I mean the definition used in POSIX's "Seconds Since the Epoch" which does not use leap seconds. If this sounds too complicated, just go back to the question as originally stated in the first paragraph and assume the epoch is representable in time_t.
Here's an entry for a way to do it, "simplest" if nobody beats it:
call mktime on a struct tm for 02 Jan 1970 00:00:00
call mktime on a struct tm for 31 Dec 1969 00:00:00. This could reasonably return -1, in which case treat it as 0.
Binary search between the two for a value of time_t that, when passed to gmtime, results in 01 Jan 1970 00:00:00
Assumes that no local time is ever more than 24 hours different from UTC, which I'm pretty sure is a fact. We could widen the boundaries if necessary, in extremis we could search between 0 and time(0).
The binary search could be improved on, for example by interpolation. But who knows, maybe some crazy time zone (or broken tzinfo data) could cause a daylight savings change in December/January. I doubt that happened for any real time zone. But it's not forbidden by the C standard, only by common sense.
If it wasn't for that, I think we could calculate based on gmtime(mktime(01 Jan)) (to get the time zone) and a comparison of 01 Jan 1970 00:00:00 vs 01 Jan 1970 00:00:01 (to get the precision of time_t).
Your problem is rather fundamental: ISO C punts on time zones almost entirely, simply providing mktime() and the localtime() and gmtime() conversions, with a hook for daylight savings. (They implement, you decide.)
So there seems like only two things you can do:
assume that time_t is seconds-since-epoch UTC and use gmtime() to verify that, and panic or alert if it ever fails; or
rely on a more comprehensive standard than ISO C
Step 1: Choose any time_t (the current time will work just fine) as a reference point; call it t0.
Step 2: Call gmtime on t0 and compute the difference between the result and the epoch in a broken-down struct tm form.
Step 3: Call localtime on t0 and apply the broken-down difference from step 2 to the resulting struct tm. Then call mktime on it to get back a time_t.
The result should be a time_t representing the epoch.
My first attempt to implement this had problems when the local time offsets are not constant over time, for example in localities where daylight time has been added or abandoned or which switched from observing one zone to another. This seems to be because the data in the struct tm on which the time zone information is based gets changed. Here is the original implementation, with its problems:
time_t get_epoch(time_t t0)
{
struct tm gmt = *gmtime(&t0);
struct tm tmp = *localtime(&t0);
tmp.tm_sec -= gmt.tm_sec;
tmp.tm_min -= gmt.tm_min;
tmp.tm_hour -= gmt.tm_hour;
tmp.tm_mday -= gmt.tm_mday-1;
tmp.tm_mon -= gmt.tm_mon;
tmp.tm_year -= gmt.tm_year-70;
return mktime(&tmp);
}
and an improved version, where posix_time is a function to compute the seconds since the epoch for a given struct tm using the POSIX formulae (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15), with additional work to handle years before 1970, etc. if needed:
time_t get_epoch(time_t t0)
{
struct tm gmt = *gmtime(&t0);
struct tm tmp = *localtime(&t0);
long long offset = posix_time(&gmt);
while (offset > INT_MAX) {
offset -= INT_MAX;
tmp.tm_sec -= INT_MAX;
mktime(&tmp);
}
while (offset < -INT_MAX+61) {
offset -= -INT_MAX+61;
tmp.tm_sec -= -INT_MAX+61;
mktime(&tmp);
}
tmp.tm_sec -= offset;
return mktime(&tmp);
}
For C89 compatibility, long long would have to be dropped and the number of mktime calls needed increases dramatically; offset could not be computed as a single value, but a loop would be needed to call mktime multiple times per year.
Edit: Perhaps the following implementation of the non-standard timegm would meet the non-POSIX requirements of the question:
#include <stdio.h>
#include <string.h>
#include <time.h>
time_t my_timegm(struct tm *tm) {
time_t t, g;
double dt; // seconds
struct tm *gm;
t = mktime(tm);
gm = gmtime(&t);
gm->tm_isdst = 0;
g = mktime(gm);
dt = difftime(t, g);
if (dt >= 0) {
tm->tm_sec += fmod(dt, 60); // needed to handle 16-bit ints
tm->tm_min += dt / 60;
}
else {
tm->tm_sec -= fmod(-dt, 60);
tm->tm_min -= -dt / 60;
}
return mktime(tm);
}
int main(void) { // prints time_t for 01 Jan 1970 00:00:00 UTC
struct tm start;
memset(&start, 0, sizeof start);
start.tm_year = 70; // = 1970
start.tm_mday = 1; // = 1st
printf("%ld\n" my_timegm(&start)); // gives 0 on any POSIX system
return 0;
}
This assumes mktime will, as per the Linux man page, act so that structure members .. outside their valid interval ... will be normalized, or at least ensure something sensible is returned. I don't know whether plain ISO C guarantees this.
My initial version of my_timegm was from http://lists.samba.org/archive/samba-technical/2002-November/025571.html, and is credited there to Beeman, Baushke, Sabol, and Zawinski:
time_t my_timegm(struct tm *tm) {
time_t t, g;
struct tm *gm;
t = mktime(tm);
if (t == -1) { // perhaps needed for DST changeover?
tm->tm_hour--;
if ((t = mktime(tm)) != -1)
t += 3600;
}
gm = gmtime(&t);
gm->tm_isdst = 0;
g = mktime(gm);
if (g == -1) {
gm->tm_hour--;
if ((g = mktime(gm)) != -1)
g += 3600;
}
return (t == -1 || g == -1) ? -1 : t - (g - t); // or difftime
}
I'm still thinking about the need for the code: tm->tm_hour--, etc.
My previous approach had too many problems stemming from ambiguity about how mktime resolves denormalized time representations for me to be comfortable with it, so I'm going to try merging the idea with Steve Jessop's guessing/search idea for a better approach:
Initialize a struct tm object tm0 to the calendar time of the epoch.
Call mktime on tm0. This will result in its being interpreted as local time, however, so the result will not be the desired answer. Call this time_t value t0.
Apply gmtime to t0 to convert it to a broken-down universal time. It should differ from the desired epoch by less than 24 hours (actually, at most 12).
Adjust tm0 by the difference and return to step 2. If step 3 gives the right broken-down universal time epoch, we are finished. Otherwise, repeat steps 2-4 (should not be necessary).
In code,
time_t get_epoch()
{
struct tm tm0 = { .tm_year = 70, .tm_mday = 1 }, gmt;
time_t t0;
for (;;) {
t0 = mktime(&tm0);
gmt = *gmtime(&t0);
if (!gmt.tm_sec && !gmt.tm_min && !gmt.tm_hour &&
!gmt.tm_yday && gmt.tm_year==70) return t0;
tm0.tm_sec -= gmt.tm_sec;
tm0.tm_min -= gmt.tm_min;
tm0.tm_hour -= gmt.tm_hour;
tm0.tm_mday -= gmt.tm_mday-1;
tm0.tm_mon -= gmt.tm_mon;
tm0.tm_year -= gmt.tm_year-70;
}
}
How do I do the above? There is mktime function but that treats the input as expressed in local time but how do i perform the conversion if my input tm variable happens to be in UTC.
Use timegm() instead of mktime()
for those on windows, the below function is available:
_mkgmtime
link for more info: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/mkgmtime-mkgmtime32-mkgmtime64
Here is a solution I use (Can't recall where I found it) when it isn't a windows platform
time_t _mkgmtime(const struct tm *tm)
{
// Month-to-day offset for non-leap-years.
static const int month_day[12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
// Most of the calculation is easy; leap years are the main difficulty.
int month = tm->tm_mon % 12;
int year = tm->tm_year + tm->tm_mon / 12;
if (month < 0) { // Negative values % 12 are still negative.
month += 12;
--year;
}
// This is the number of Februaries since 1900.
const int year_for_leap = (month > 1) ? year + 1 : year;
time_t rt = tm->tm_sec // Seconds
+ 60 * (tm->tm_min // Minute = 60 seconds
+ 60 * (tm->tm_hour // Hour = 60 minutes
+ 24 * (month_day[month] + tm->tm_mday - 1 // Day = 24 hours
+ 365 * (year - 70) // Year = 365 days
+ (year_for_leap - 69) / 4 // Every 4 years is leap...
- (year_for_leap - 1) / 100 // Except centuries...
+ (year_for_leap + 299) / 400))); // Except 400s.
return rt < 0 ? -1 : rt;
}
The answer of Loki Astari was a good start, timegm is one of the possible solutions. However, the man page of timegm gives a portable version of it, as timegm is not POSIX-compliant. Here it is:
#include <time.h>
#include <stdlib.h>
time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;
tz = getenv("TZ");
if (tz)
tz = strdup(tz);
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz) {
setenv("TZ", tz, 1);
free(tz);
} else
unsetenv("TZ");
tzset();
return ret;
}
timegm() works, but is not present on all systems.
Here's a version that only uses ANSI C. (EDIT: not strictly ANSI C! I'm doing math on time_t, assuming that the units are in seconds since the epoch. AFAIK, the standard does not define the units of time_t.) Note, it makes use of a hack, so-to-speak, to determine the machine's time zone and then adjusts the result from mktime accordingly.
/*
returns the utc timezone offset
(e.g. -8 hours for PST)
*/
int get_utc_offset() {
time_t zero = 24*60*60L;
struct tm * timeptr;
int gmtime_hours;
/* get the local time for Jan 2, 1900 00:00 UTC */
timeptr = localtime( &zero );
gmtime_hours = timeptr->tm_hour;
/* if the local time is the "day before" the UTC, subtract 24 hours
from the hours to get the UTC offset */
if( timeptr->tm_mday < 2 )
gmtime_hours -= 24;
return gmtime_hours;
}
/*
the utc analogue of mktime,
(much like timegm on some systems)
*/
time_t tm_to_time_t_utc( struct tm * timeptr ) {
/* gets the epoch time relative to the local time zone,
and then adds the appropriate number of seconds to make it UTC */
return mktime( timeptr ) + get_utc_offset() * 3600;
}
The following implementation of timegm(1) works swimmingly on Android, and probably works on other Unix variants as well:
time_t timegm( struct tm *tm ) {
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
POSIX page for tzset, describes global variable extern long timezone which contains the local timezone as an offset of seconds from UTC. This will be present on all POSIX compliant systems.
In order for timezone to contain the correct value, you will likely need to call tzset() during your program's initialization.
You can then just subtract timezone from the output of mktime to get the output in UTC.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t utc_mktime(struct tm *t)
{
return (mktime(t) - timezone) - ((t->tm_isdst > 0) * 3600);
}
int main(int argc, char **argv)
{
struct tm t = { 0 };
tzset();
utc_mktime(&t);
}
Note: Technically tzset() and mktime() aren't guaranteed to be threadsafe.
If a thread accesses tzname, [XSI] [Option Start] daylight, or timezone [Option End] directly while another thread is in a call to tzset(), or to any function that is required or allowed to set timezone information as if by calling tzset(), the behavior is undefined.
...but the majority of implementations are. GNU C uses mutexes in tzset() to avoid concurrent modifications to the global variables it sets, and mktime() sees very wide use in threaded programs without synchronization. I suspect if one were to encounter side effects, it would be from using setenv() to alter the value of TZ as done in the answer from #liberforce.
I was troubled by the issue of mktime() as well. My solution is the following
time_t myTimegm(std::tm * utcTime)
{
static std::tm tmv0 = {0, 0, 0, 1, 0, 80, 0, 0, 0}; //1 Jan 1980
static time_t utcDiff = std::mktime(&tmv0) - 315532801;
return std::mktime(utcTime) - utcDiff;
}
The idea is to get the time difference by calling std::mktime() with a known time (in this case 1980/01/01) and subtract its timestamp (315532801). Hope it helps.
Here's my take, which is based exclusively on time_t/tm conversion functions, and the only presumption it makes about time_t is that it is linear:
Pretending against better knowledge the tm structure holds local time (non-DST if anyone asks; it doesn't matter, but must be consistent with step 3), convert it to time_t.
Convert the date back into a tm structure, but this time in UTC representation.
Pretending against better knowledge that tm structure to also hold local (non-DST if anyone asks, but more importantly consistent with step 1), and convert it to time_t once more.
From the two time_t results I can now compute the difference between local time (non-DST if anyone asks) and UTC in time_t units.
Adding that difference to the first time_t result gives me the proper time in UTC.
Note that computation of the difference can conceivably be done once, and then applied later to as many dates as desired; this might be a way to solve issues arising from the lack of thread-safety in gmtime.
(Edit: Then again, this might cause issues if the time zone is changed between the date used to compute the offset, and the date to be converted.)
tm tt;
// populate tt here
tt.tm_isdst = 0;
time_t tLoc = mktime(&tt);
tt = *gmtime(&tLoc);
tt.tm_isdst = 0;
time_t tRev = mktime(&tt);
time_t tDiff = tLoc - tRev;
time_t tUTC = tLoc + tDiff;
Caveat: If the system uses a TAI-based time_t (or anything else that does respect leap seconds), the resulting time may be off by 1 second if applied to a point in time close to a leap second insertion.
This is really a comment with code to address the answer by Leo Accend:
Try the following:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
/*
* A bit of a hack that lets you pull DST from your Linux box
*/
time_t timegm( struct tm *tm ) { // From Leo's post, above
time_t t = mktime( tm );
return t + localtime( &t )->tm_gmtoff;
}
main()
{
struct timespec tspec = {0};
struct tm tm_struct = {0};
if (gettimeofday(&tspec, NULL) == 0) // clock_gettime() is better but not always avail
{
tzset(); // Not guaranteed to be called during gmtime_r; acquire timezone info
if (gmtime_r(&(tspec.tv_sec), &tm_struct) == &tm_struct)
{
printf("time represented by original utc time_t: %s\n", asctime(&tm_struct));
// Go backwards from the tm_struct to a time, to pull DST offset.
time_t newtime = timegm (&tm_struct);
if (newtime != tspec.tv_sec) // DST offset detected
{
printf("time represented by new time_t: %s\n", asctime(&tm_struct));
double diff = difftime(newtime, tspec.tv_sec);
printf("DST offset is %g (%f hours)\n", diff, diff / 3600);
time_t intdiff = (time_t) diff;
printf("This amounts to %s\n", asctime(gmtime(&intdiff)));
}
}
}
exit(0);
}
For all timezones and at all times would be exceedingly difficult if not impossible. You would need an accurate record of all the various arbitrary timezone and daylight savings time (DST) decrees. Sometimes, it is not clear who the local authority is, never mind what was decreed and when. Most systems, for example, are off by one second for uptime (time system has been up) or boottime (timestamp system booted), if a leap second was spanned. A good test would be a date that was once in DST but now is not (or vis versa). (It was not too long ago in the US that it changed.)
This seems like something no one should ever have to do, but I'm working on a kernel module for an embedded system (OpenWRT) in which it seems that time.h does include the timespec and time_t types, and the clock_gettime and gmtime functions, but does not include localtime, ctime, time, or, critically, the tm type.
When I attempt to cast the return pointer from gmtime to my own struct, I get a segfault.
So I guess I'd be content to solve the problem either of two ways—it'd be great to figure out how to get access to that missing type, or alternatively, how to roll my own method for decomposing a unix timestamp.
This should be accurate (fills out a cut-down imitation of a struct tm, my year uses Common Era instead of a 1900 CE epoch):
struct xtm
{
unsigned int year, mon, day, hour, min, sec;
};
#define YEAR_TO_DAYS(y) ((y)*365 + (y)/4 - (y)/100 + (y)/400)
void untime(unsigned long unixtime, struct xtm *tm)
{
/* First take out the hour/minutes/seconds - this part is easy. */
tm->sec = unixtime % 60;
unixtime /= 60;
tm->min = unixtime % 60;
unixtime /= 60;
tm->hour = unixtime % 24;
unixtime /= 24;
/* unixtime is now days since 01/01/1970 UTC
* Rebaseline to the Common Era */
unixtime += 719499;
/* Roll forward looking for the year. This could be done more efficiently
* but this will do. We have to start at 1969 because the year we calculate here
* runs from March - so January and February 1970 will come out as 1969 here.
*/
for (tm->year = 1969; unixtime > YEAR_TO_DAYS(tm->year + 1) + 30; tm->year++)
;
/* OK we have our "year", so subtract off the days accounted for by full years. */
unixtime -= YEAR_TO_DAYS(tm->year);
/* unixtime is now number of days we are into the year (remembering that March 1
* is the first day of the "year" still). */
/* Roll forward looking for the month. 1 = March through to 12 = February. */
for (tm->mon = 1; tm->mon < 12 && unixtime > 367*(tm->mon+1)/12; tm->mon++)
;
/* Subtract off the days accounted for by full months */
unixtime -= 367*tm->mon/12;
/* unixtime is now number of days we are into the month */
/* Adjust the month/year so that 1 = January, and years start where we
* usually expect them to. */
tm->mon += 2;
if (tm->mon > 12)
{
tm->mon -= 12;
tm->year++;
}
tm->day = unixtime;
}
My apologies for all the magic numbers. 367*month/12 is a neat trick to generate the 30/31 day sequence of the calendar. The calculation works with years that start in March until the fixup at the end, which makes things easy because then the leap day falls at the end of a "year".
In userspace glibc will do a lot of work with regards to handling the "local" part of time representation. Within the kernel this is not available. Probably you should not try to bother with this within your module, if needed do it in userspace.
A time_t is the number of seconds since Jan 1, 1970 UTC so decomposing that into month, day, and year isn't that difficult provided that you want the result in UTC. There is a bunch of source available by Googling "gmtime source". Most embedded systems leave out local time processing since it is a little more difficult due to the reliance on timezone setting and the environment.