How to compare GMT time and local time in C? - c

my server uses local time of Prague (+ 2 hours) and visitor's request uses GMT time.
In code I want to compare these times but for that I need to convert them to the same time zone. How to do it? When I try to use gmtime() and localtime() they returns same result.
struct tm time;
struct stat data;
time_t userTime, serverTime;
// this time will send me user in GMT
strptime("Thu, 15 Apr 2021 17:20:21 GMT", "%a, %d %b %Y %X GMT", &time)
userTime = mktime(&time); // in GMT
// this time I will find in my server in another time zone
stat("test.txt", &data);
serverTime = data.st_mtimespec.tv_sec; // +2 hours (Prague)
// it's not possible to compare them (2 diferrent time zones)
if(serverTime < userTime) {
// to do
}
Thank you for answer.

On linux with glibc you can just use %Z with strptime to read GMT.
#define _XOPEN_SOURCE
#define _DEFAULT_SOURCE
#include <time.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
// this time will send me user in GMT
struct tm tm;
char *buf = "Thu, 15 Apr 2021 17:20:21 GMT";
char *r = strptime(buf, "%a, %d %b %Y %X %Z", &tm);
assert(r == buf + strlen(buf));
time_t userTime = timegm(&tm);
// this time represents time that has passed since epochzone
struct stat data;
stat("test.txt", &data);
// be portable, you need only seconds
// see https://pubs.opengroup.org/onlinepubs/007904875/basedefs/sys/stat.h.html
time_t serverTime = data.st_mtime;
// it's surely is possible to compare them
if (serverTime < userTime) {
// ok
}
}
// it's not possible to compare them (2 diferrent time zones)
But it is!
The time that has passed since an event can't be in a timezone. Count of seconds since epoch is how many seconds have passed since that event, it's relative time that has passed, it's distance in time. No matter in which timezone you are, no matter if daylight saving time or not, the time that has passed since an event is the same in every location (well, excluding relativistic effects, which we don't care about). Timezone is irrelevant. mktime returns the number of seconds since epoch. stat returns timespec which represents time that have passed since epoch. Timezone has nothing to do here. Once you represent time as relative to some event (ie. since epoch), then just compare them.

Related

Getting UTC time as time_t

I am trying to get UTC time as time_t.
Code below seems giving it correct but surprisingly prints local time only:
time_t mytime;
struct tm * ptm;
time ( &mytime ); // Get local time in time_t
ptm = gmtime ( &mytime ); // Find out UTC time
time_t utctime = mktime(ptm); // Get UTC time as time_t
printf("\nLocal time %X (%s) and UTC Time %X (%s)", mytime, ctime(&mytime), utctime, ctime(&utctime));
As we can see values of mytime and utctime we get are different.
However, when passed as parameters to ctime it converts them to same string.
Local time 55D0CB59 (Sun Aug 16 23:11:45 2015) and UTC Time 55D07E01
(Sun Aug 16 23:11:45 2015)
By definition the C time() function returns a time_t epoch time in UTC. So the code commented "//get the local time in time_t" is really getting UTC already, and the comment is incorrect.
From the Linux man-page:
time_t time(time_t *tloc);
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
Giving the code:
#include <time.h>
...
time_t utc_now = time( NULL );
The ctime result is a static variable. Do not use ctime twice in the same print statement. Do it in two separate print statements.
If ctime is replaced with asctime, the same problem will arise as asctime also returns the result as a static variable.
That's exactly what the documented behavior is supposed to do:
Interprets the value pointed by timer as a calendar time and converts it to a C-string containing a human-readable version of the corresponding time and date, in terms of local time.
You probably want to use asctime instead.
ctime function returns a C string containing the date and time information in a human-readable format.
To get time in UTC you can use gettimeofday() (for Linux)-
struct timeval ptm;
gettimeofday(&ptm,NULL);
long int ms = ptm.tv_sec * 1000 + ptm.tv_usec / 1000;
And you can see function GetSystemTime in for windows.

How many seconds between now and a specific time in C

I need to be able to count the seconds between now and a specific time (like the next time it's 3pm). I saw some similar questions but I wasn't able to use any of these.
C standard library has two representation of time: time_t is a seconds since Unix Epoch and struct tm where you can individially set seconds, minutes, etc.
So to get next moment in time when wall clock will show 3 p.m. you'll need take current time in seconds time(NULL), convert it to struct tm, advance time to 3 p.m. by setting structure fields, convert it back to time_t and calculate the difference:
#include <time.h>
#include <string.h>
#include <stdio.h>
int main() {
time_t now, next3pm;
struct tm threepm;
// Get current time (now)
now = time(NULL);
// Copy current date to a `threepm`, and set time
memcpy(&threepm, gmtime(&now), sizeof(struct tm));
if(threepm.tm_hour > 15) {
// Advance to a next day
++threepm.tm_mday;
}
threepm.tm_hour = 15;
threepm.tm_min = threepm.tm_sec = 0;
printf("%.f seconds till 3:00 PM\n", difftime(mktime(&threepm), now));
return 0;
}
I used UTC conversion functions gmtime()/mktime(). Since there is no timezoned version of mktime() you may need to convert time on your own. Using UTC time may cause trouble with advancing to a next day when it shouldn't do that (because it is already 15:00 according to UTC, but not yet 15:00 according to a local time).
Or, a classic joke version:
int main() {
time_t t;
struct tm* tm;
do {
t = time(NULL);
tm = gmtime(&t);
usleep(1000000);
} while(tm->tm_hour != 15 && tm->tm_min != 0);
puts("0 seconds till 3:00 PM");
}

GMT time to Seconds

I am implementing a cache in a proxy server. I am storing Expires: field from the response of request and next time if same request comes then i will compare the Expires time with current time.
Now I am stuck here. how should I convert Expire time format to seconds and then take a difference?
Is that any other way to get time difference ?
Format for Expire time is :
Sat, 10 Nov 2012 07:05:26 GMT
Suppose I have two time, how can I compare two times in GMT?
Here's a working example :
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv) {
struct tm t;
time_t seconds;
const char *expire = "Sat, 10 Nov 2012 07:05:25 GMT";
strptime(expire, "%a, %d %B %Y %H:%M:%S", &t);
seconds = mktime(&t);
printf("%lu\n", seconds);
}
Note that your times must be in GMT time since strptime() ignores the time zone (at least on Linux). You may also need to set your locale to the same locale used for your input times.
If your implementation has it, you could use the strptime function to convert that string to a struct tm, then mktime to convert that to a time_t for easy comparison.

GMT confusion with ctime functions [duplicate]

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.)

How to convert from UTC to local time in C?

It's a simple question, but the solution appears to be far from simple. I would like to know how to convert from UTC to local time. I am looking for a solution in C that's standard and more or less guaranteed to work on any computer at any location.
I have read the following links carefully but I can't find a solution there:
Converting string containing localtime into UTC in C
Converting Between Local Times and GMT/UTC in C/C++
I have tried a number of variations, such as (datetime is a string with time and date in UTC):
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);
Or
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(&lt);
No matter what I try printtime ends up being the same as UTC.
Edit 11-29-2013: based on the very helpful answer by "R" below I finally got around to create a working example. I found it to be working correct in the two timezones I tested it, CET and PST:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
long long diff_tm(struct tm *a, struct tm *b)
{
return a->tm_sec - b->tm_sec
+60LL*(a->tm_min - b->tm_min)
+3600LL*(a->tm_hour - b->tm_hour)
+86400LL*(a->tm_yday - b->tm_yday)
+(a->tm_year-70)*31536000LL
-(a->tm_year-69)/4*86400LL
+(a->tm_year-1)/100*86400LL
-(a->tm_year+299)/400*86400LL
-(b->tm_year-70)*31536000LL
+(b->tm_year-69)/4*86400LL
-(b->tm_year-1)/100*86400LL
+(b->tm_year+299)/400*86400LL;
}
int main()
{
time_t utc, local;
char buf[100];
const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */
struct tm *tp=malloc(sizeof(struct tm));
if(tp==NULL)
exit(-1);
struct tm *localt=malloc(sizeof(struct tm));
if(localt==NULL)
exit(-1);
memset(tp, 0, sizeof(struct tm));
memset(localt, 0, sizeof(struct tm));
printf("UTC date and time to be converted in local time: %s\n", datetime);
/* put values of datetime into time structure *tp */
strptime(datetime, "%Y %m %d %H %M %S %z", tp);
/* get seconds since EPOCH for this time */
utc=mktime(tp);
printf("UTC date and time in seconds since EPOCH: %d\n", utc);
/* lets convert this UTC date and time to local date and time */
struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new;
/* get time_t EPOCH value for e0 (Jan. 1, 1970) */
time_t pseudo=mktime(&e0);
/* get gmtime for this value */
e1=*gmtime(&pseudo);
/* calculate local time in seconds since EPOCH */
e0.tm_sec += utc - diff_tm(&e1, &e0);
/* assign to local, this can all can be coded shorter but I attempted to increase clarity */
local=e0.tm_sec;
printf("local date and time in seconds since EPOCH: %d\n", local);
/* convert seconds since EPOCH for local time into localt time structure */
localt=localtime(&local);
/* get nicely formatted human readable time */
strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt);
printf("local date and time: %s\n", buf);
}
It should compile without problems on most systems. I hard coded a time and date in UTC which then will be converted to the local time and date.
If you can assume POSIX (and thus the POSIX specification of time_t as seconds since the epoch), I would first use the POSIX formula to convert to seconds since the epoch:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
Next, use localtime((time_t []){0}) to get a struct tm representing the epoch in local time. Add the seconds since the epoch to the tm_sec field of this struct tm, then call mktime to canonicalize it.
Edit: Actually the only POSIX dependency is having a known epoch which (time_t)0 corresponds to. Perhaps you can find a way around that if you really need to... for instance using calls to both gmtime and localtime at time_t 0..
Edit 2: A sketch of how to do this:
#include <time.h>
#include <stdio.h>
long long diff_tm(struct tm *a, struct tm *b)
{
return a->tm_sec - b->tm_sec
+60LL*(a->tm_min - b->tm_min)
+3600LL*(a->tm_hour - b->tm_hour)
+86400LL*(a->tm_yday - b->tm_yday)
+(a->tm_year-70)*31536000LL
-(a->tm_year-69)/4*86400LL
+(a->tm_year-1)/100*86400LL
-(a->tm_year+299)/400*86400LL
-(b->tm_year-70)*31536000LL
+(b->tm_year-69)/4*86400LL
-(b->tm_year-1)/100*86400LL
+(b->tm_year+299)/400*86400LL;
}
int main(int argc, char **argv)
{
char buf[100];
struct tm e0 = { .tm_year = 70, .tm_mday = 1 }, e1, new;
time_t pseudo = mktime(&e0);
e1 = *gmtime(&pseudo);
e0.tm_sec += atoi(argv[1]) - diff_tm(&e1, &e0);
mktime(&e0);
strftime(buf, sizeof buf, "%c", &e0);
puts(buf);
}
Please don't mind the ugly output code. This program takes an argument in the form of "seconds relative to the POSIX epoch" and outputs the resulting time in local time. You can convert any UTC time to seconds since the epoch using the formula I cited above. Note that this code does not in any way depend on POSIX, but it does assume the offset returned by diff_tm combined with the seconds-since-the-epoch value does not overflow int. A fix for this would be to use a long long offset and a loop that keeps adding increments no larger than INT_MAX/2 (or smaller than INT_MIN/2) and calling mktime to renormalize until the offset reaches 0.
Ahm ... I might just be a beginner in C, but I got this working example:
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t abs_ts,loc_ts,gmt_ts;
struct tm loc_time_info,gmt_time_info;
/*Absolute time stamp.*/
time(&abs_ts);
/*Now get once the local time for this time stamp,
**and once the GMT (UTC without summer time) time stamp.*/
localtime_r(&abs_ts,&loc_time_info);
gmtime_r(&abs_ts,&gmt_time_info);
/*Convert them back.*/
loc_ts=mktime(&loc_time_info);
gmt_ts=mktime(&gmt_time_info);
/*Unfortunately, GMT still has summer time. Get rid of it:*/
if(gmt_time_info.tm_isdst==1)
{gmt_ts-=3600;}
printf("Local timestamp: %lu\n"
"UTC timestamp: %lu\n"
"Difference in hours: %lu\n\n",
loc_ts,
gmt_ts,
(loc_ts-gmt_ts)/3600);
return 0;
}
Which produces this output:
Local timestamp: 1412554119
GMT timestamp: 1412546919
Difference in hours: 2
Now you have the difference between UTC and local time in seconds. That should be enough to convert it.
One note to your code, aseq: you are using malloc without need here (you can memset values on the stack as well, and malloc can be expensive while stack allocation is often much faster), and you do not free it. That's very, very bad practise.
Another thing:
memset(tp, 0, sizeof(struct tm));
Would be better done if you'd pass sizeof(*tp) (or, if you put tp on the stack, sizeof(tp)) to memset. That ensures that even if the type of your object changes, it will still be fully memset.
To sum-up: the conversion of a broken down date (struct tm) in UTC to a (local) calendar time (time_t) is achieved with timegm() - the opposite of mktime() - BUT timegm() is not a standard function (how logic is that).
The C standard leaves us with only time(), gmtime(), mktime() and difftime().
A workaround found in other docs advises to emulate timegm() by setting first the environment variable TZ to a null string, then calling mktime() resulting in an UTC calendar time, then resetting TZ to its initial value, but once again, this is not standard.
Basically, as I understand it, the difference between a local time and UTC time is just an offset so if we can evaluate that offset, we can adjust the result of mktime(), so here's my proposition:
time_t my_timegm(struct tm *tm) {
time_t epoch = 0;
time_t offset = mktime(gmtime(&epoch));
time_t utc = mktime(tm);
return difftime(utc, offset);
}
A quick test:
int main(void) {
time_t now = time(0);
struct tm local = *localtime(&now);
struct tm utc = *gmtime(&now);
time_t t1 = mktime(&local);
time_t t2 = my_timegm(&utc);
assert(t1 == t2);
printf("t =%lu\nt1=%lu\nt2=%lu\n",now,t1,t2);
return 0;
}
//working stand alone function adjusting UTC to local date and time
//globals(unsigned integers): gps.Mth, gps.Yr, gps.Hm (eg:2115 for 21:15)
//adjust date and time according to UTC
//tz(timezone) eg: 1100, for 11 hours, tzdir: 1 forward, 0 backwards
void AdjustUTCToTimeZone(u16 tz, u8 tzdir){
u8 maxDayInAnyMonth[13] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //gps.Mth 1-12 (not zero)
if(gps.Yr%4==0){maxDayInAnyMonth[2]=29;}//adjust for leapyear
u8 maxDayUtcMth =maxDayInAnyMonth[gps.Mth];
u8 maxDayPrevMth=maxDayInAnyMonth[gps.Mth-1];
if(!maxDayPrevMth){maxDayPrevMth=31;} //month before utc month
u16 hr=(gps.Hm/100)*100;u16 m=gps.Hm-hr; //2115 --> 2100 hr and 15 min
if(tzdir){//adjusting forwards
tz+=gps.Hm;
if(tz>2400){gps.Hm=tz-2400;gps.Day++; //spill over to next day
if(gps.Day>maxDayUtcMth){ gps.Day=1;gps.Mth++; //spill over to next month
if(gps.Mth>12){gps.Mth=1; gps.Yr++; //spill over to next year
}
}
}else{gps.Hm=tz;}
}else{//adjusting backwards
if(tz>gps.Hm){gps.Hm=(2400-(tz-hr))+m;gps.Day--; // back to previous day
if(gps.Day==0){ //back to previous month
gps.Mth--;gps.Day=maxDayPrevMth;
if(!gps.Mth){gps.Mth=12; //back to previous year
gps.Yr--;
}
}
}else{gps.Hm-=tz;}
}
}
I think it's easier than that; time.h defines three variables:
extern int daylight;
extern long timezone;
extern char *tzname[];
which are loaded based on the TZ env variable when you call
tzset();
if you have a utc time in
struct tm date;
date.tm_isdst = 0;
convert it to a time_t using mktime
time_t utc = mktime( &date );
then convert it to local time
time_t local = utc - timezone + ( daylight?3600:0 );
timezone is the number of seconds away from utc for the current timezone and daylight is 1 to indicate daylight savings time is in play and zero for not.
A small caution: When I coded this for a microcontroller and cross compiled, it's time.h defined those variables with initial underscores.
See the man page for time.h
I found that the solution the OP gave did not work in cases when DST applies. For example, in my case, at the current time, DST was not in effect, but if I set the initial date which should convert to local time with DST, then it would not work, i.e. today's date is 3/1/2018 and DST is not in effect, but if I set the date for conversion to, say, 8/1/2018 0:00:00 when DST is in effect, then the solution given would convert to local time, but would not take DST into account. I found that initializing e0 to the date and hour of the initial date/time string and its member tm_isdst to -1 solved the problem. I then created the following program with complementary functions which you can include in your code. The initial format of the date and time is the same that MySQL uses, because I needed it for such purposes.
#include <stdio.h>
#include <time.h>
#include <string.h>
long long diff_tm(struct tm *a, struct tm *b) {
return a->tm_sec - b->tm_sec
+ 60LL * (a->tm_min - b->tm_min)
+ 3600LL * (a->tm_hour - b->tm_hour)
+ 86400LL * (a->tm_yday - b->tm_yday)
+ (a->tm_year - 70) * 31536000LL
- (a->tm_year - 69) / 4 * 86400LL
+ (a->tm_year - 1) / 100 * 86400LL
- (a->tm_year + 299) / 400 * 86400LL
- (b->tm_year - 70) * 31536000LL
+ (b->tm_year - 69) / 4 * 86400LL
- (b->tm_year - 1) / 100 * 86400LL
+ (b->tm_year + 299) /400 * 86400LL;
}
void localToUTC(char *buf, const char *localTime) {
struct tm tp;
strptime(localTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm res = *gmtime(&utc);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &res);
}
void utcToLocal(char *buf, const char *utcTime) {
struct tm tp;
strptime(utcTime, "%Y-%m-%d %H:%M:%S", &tp);
tp.tm_isdst = -1;
time_t utc = mktime(&tp);
struct tm e0 = { .tm_year = tp.tm_year, .tm_mday = tp.tm_mday, .tm_mon = tp.tm_mon, .tm_hour = tp.tm_hour, .tm_isdst = -1 };
time_t pseudo = mktime(&e0);
struct tm e1 = *gmtime(&pseudo);
e0.tm_sec += utc - diff_tm(&e1, &e0);
time_t local = e0.tm_sec;
struct tm localt = *localtime(&local);
strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &localt);
}
int main(void) {
char mytime_1[20] = "2018-02-28 13:00:00";
char utctime_1[20], back_1[20];
localToUTC(utctime_1, mytime_1);
utcToLocal(back_1, utctime_1);
printf("My time: %s\n", mytime_1);
printf("UTC time: %s\n", utctime_1);
printf("Back: %s\n", back_1);
printf("-------------------------------------------\n");
char mytime_2[20] = "2018-07-28 17:00:00";
char utctime_2[20], back_2[20];
localToUTC(utctime_2, mytime_2);
utcToLocal(back_2, utctime_2);
printf("My time: %s\n", mytime_2);
printf("UTC time: %s\n", utctime_2);
printf("Back: %s\n", back_2);
printf("-------------------------------------------\n");
return 0;
}
I followed the answer by #Dachschaden and I made an example which also shows human-readable output and I remove the DST option for the difference in seconds between UTC and local time. Here it is:
#include <time.h>
#include <stdio.h>
#define DATE_MAX_STR_SIZE 26
#define DATE_FMT "%FT%TZ%z"
int main() {
time_t now_time, now_time_local;
struct tm now_tm_utc, now_tm_local;
char str_utc[DATE_MAX_STR_SIZE];
char str_local[DATE_MAX_STR_SIZE];
time(&now_time);
gmtime_r(&now_time, &now_tm_utc);
localtime_r(&now_time, &now_tm_local);
/* human readable */
strftime(str_utc, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_utc);
strftime(str_local, DATE_MAX_STR_SIZE, DATE_FMT, &now_tm_local);
printf("\nUTC: %s", str_utc);
printf("\nLOCAL: %s\n", str_local);
/* seconds (epoch) */
/* let's forget about DST for time difference calculation */
now_tm_local.tm_isdst = 0;
now_tm_utc.tm_isdst = 0;
now_time_local = now_time + (mktime(&now_tm_local) - mktime(&now_tm_utc));
printf("\nUTC in seconds: %lu", now_time);
printf("\nLOCAL in seconds: %lu\n", now_time_local);
return 0;
}
Output on my machine is:
UTC: 2016-05-05T15:39:11Z-0500
LOCAL: 2016-05-05T11:39:11Z-0400
UTC in seconds: 1462462751
LOCAL in seconds: 1462448351
Note that DST is on in this case (there's a 1 hour time zone offset difference between UTC and LOCAL).
try this, test output:
utcEpochTime: 1487652688, localEpochTime: 1487699488, diff: 46800
$ python
>>>46800 / 60 / 60
13
the diff is 13 hours, which is good, as my timezone is UTC+8.
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[])
{
time_t utcEpochTime = time(0);
time_t localEpochTime = 0;
struct tm tm = {0};
localtime_r(&utcEpochTime, &tm);
tm.tm_isdst = -1;
localEpochTime = timegm(&tm);
printf("utcEpochTime: %d, localEpochTime: %d, diff: %d\n", (int)utcEpochTime, (int)localEpochTime, (int)(localEpochTime - utcEpochTime));
return 0;
}
A simple and effective way: Add (or subtract) the number of seconds between your time zone and UTC (considering daylight saving time).
As an example that worked just fine a minute ago, on December 30, 2017, with U.S. Mountain Standard Time (no DST), which is 7 hours behind UTC:
time_t current_time_UTC;
time_t current_time_MST;
struct tm *current_broken_time_MST;
uint32_t seven_hours_in_seconds = 25200; // Get this any way you want
current_time_UTC = time (NULL); // UTC
current_time_MST = current_time_UTC - seven_hours_in_seconds; // MST
current_broken_time_MST = localtime (&current_time_MST); // MST
Enjoy.
void CTestDlg::OnBtnTest()
{
HANDLE hFile;
WIN32_FIND_DATA wfd;
SYSTEMTIME systime;
FILETIME localtime;
char stime[32]; //
memset(&wfd, 0, sizeof(wfd));
if((hFile=FindFirstFile( "F:\\VC\\MFC\\Test\\Release\\Test.exe ", &wfd))==INVALID_HANDLE_VALUE)
{
char c[2];
DWORD dw=GetLastError();
wsprintf(c, "%d ", dw);
AfxMessageBox(c);
return ;//
}
FileTimeToLocalFileTime(&wfd.ftLastWriteTime,&localtime);
FileTimeToSystemTime(&localtime,&systime);
sprintf(stime, "%4d-%02d-%02d %02d:%02d:%02d ",
systime.wYear,systime.wMonth,systime.wDay,systime.wHour,
systime.wMinute,systime.wSecond);
AfxMessageBox(stime);
}

Resources