Home > Mobile >  offset calculation for day light saving(DST) without using localtime
offset calculation for day light saving(DST) without using localtime

Time:12-23

I have CODE as below to calculate offset, Here I executed below code on EST timezone machine whose offset is -18000 seconds i.e. UTC-5.00

Please note I can't use localtime() due to some limitations which is very long story to mention here. So I used below logic to do minus of GMT epoch from localtime epoch as below and it returned perfectly the offset.

Now what I want here is suppose case of DST(Day light Saving), where offset will be -14400 seconds i.e. UTC-4.00. Due to 1 adjustment of DST(Day light Saving) for EST time zone.

So will my code mentioned below work? in case system is having DST(Day light Saving). I can not test live right now, as DST(Day light Saving) is not active on my machine.

Its planned as below:

/etc/localtime  Sun Mar 14 06:59:59 2021 UT = Sun Mar 14 01:59:59 2021 EST isdst=0 gmtoff=-18000
/etc/localtime  Sun Mar 14 07:00:00 2021 UT = Sun Mar 14 03:00:00 2021 EDT isdst=1 gmtoff=-14400
/etc/localtime  Sun Nov  7 05:59:59 2021 UT = Sun Nov  7 01:59:59 2021 EDT isdst=1 gmtoff=-14400
/etc/localtime  Sun Nov  7 06:00:00 2021 UT = Sun Nov  7 01:00:00 2021 EST isdst=0 gmtoff=-18000

So wanted to know, when DST is active, i.e. say between 14 March 2021 to 7 Nov 2021, will my below code return offset of -14400. Does time() function consider epoch time after DST adjustment.

#include <stdio.h>
#include <time.h>

int main ()
{
  time_t rawtime, g_epoch, l_epoch;
  struct tm * gtm, *ltm;
  signed long int offset;
  
  //Get current epoch time
  time ( &rawtime );
  
 // convert rawtime epoch to struct tm in GMT
  gtm = gmtime ( &rawtime );
// again convert back GMT struct tm to epoch
  g_epoch = mktime(gtm);

//convert rawtime epoch to "struct tm* ltm" in local time zone
  ltm= localtime(&rawtime);
// again convert back local time zone "struct tm* ltm" to epoch l_epoch
  l_epoch= mktime(ltm);
  

//calculate offset
  offset= l_epoch - g_epoch;
  
  printf("Rawtime: %u\n",rawtime);
 
  printf("GMTepoch: %u\n",g_epoch);

  printf("Local Epoch: %u\n",l_epoch);

  printf("Offset: %ld",offset);
  return 0;
}
O/p as below:
Rawtime: 1640176204
GMTepoch: 1640194204
Local Epoch: 1640176204
Offset: -18000

CodePudding user response:

By making this small modification to your program:

gtm = gmtime (&rawtime);
gtm->tm_isdst = -1;
g_epoch = mktime(gtm);

it should work when DST is in effect. Setting tm_isdst to -1 forces mktime to decide for itself whether DST is in effect.

You can also skip the manipulations involving ltm and l_epoch and the call to localtime. (I thought the whole point of the exercise was to avoid calling localtime?) You can compute your offset just fine with

offset = rawtime - g_epoch;

But in the end this is still a pretty dreadful hack, and as @ikegami has pointed out, it won't work properly in the vicinity of DST transitions. For example, the time_t value 1636261200 properly corresponds to 1am EDT on November 7, 2021, but this code will wrongly determine that it has already fallen back to EST. This problem will be worse (will apply for a larger number of hours) the farther your local time zone is from GMT. There's probably a way around this, but it won't be particularly pretty.

CodePudding user response:

I don't think it will work at all, but I'm not sure. Best case, it will give incorrect results near switches to/from DST.

If you can't trust the system time zone database, you can always use your own copy of the tz database. Steve Summit points out the database even comes with a reference implementation of all the time zone-related functions such as localtime, so there would be no need to parse the database yourself.

Note: The database is frequently updated, so you will need some kind of process to update your application frequently if you need to work with arbitrary time zones.


Does time() function consider epoch time after DST adjustment.

time returns the number of seconds since the start of 1970 UTC.

As such, the result doesn't change where you are in the world.

Local time                  Time Zone Examples   time()
-------------------------   ------------------   ----------
2021-06-01T12:00:00Z        Etc/UTC, E./Dublin   1622548800
2021-06-01T08:00:00-04:00   "EDT", A./New_York   1622548800
2021-06-01T07:00:00-05:00   "EST", A./Bogota     1622548800

2021-06-01T12:00:00Z        Etc/UTC, E./Dublin   1622548800
2021-06-01T12:00:00-04:00   "EDT", A./New_York   1622563200
2021-06-01T12:00:00-05:00   "EST", A./Bogota     1622566800

So will my code mentioned below work? in case system is having DST(Day light Saving).

Doesn't mktime take it's information about local time from the same place localtime does? So why would mktime work on a system where localtime doesn't?

But let's say mktime does work.

Even if we assume that mktime works correctly on a system where localtime doesn't, I don't think it will work. I think gtm.tm_isdst will always be zero (since gmtime uses UTC which doesn't have DST), and I think that will cause the following mktime to return something different than desired. But this can be addressed by setting gtm .tm_isdst to -1.

So let's say mktime does return what we expect.

Even if mktime returns what we expect, the approach is flawed. It will give the wrong offsets near switches to/from DST.

// Current time zone:  America/New_York
// Current local time: 2022-03-12T22:30:00-05:00
// Change to DST:      In 3.5 hours.

// Get the current epoch time.
// This returns the same thing worldwide.
time(&rawtime);              // rawtime = 1647142200

// Convert current epoch time to UTC.
gtm = gmtime(&rawtime);      // gtm = 2021-03-13T03:30:00
gtm .tm_isdst = -1;

// Note that mktime expects a local time.
// But we're passing the current UTC time.
// So g_epoch is the epoch time for
// 2021-03-13T03:30:00 America/New_York
g_epoch = mktime(gtm);       // rawtime = 1647156600

And there's the problem. 2021-03-13T03:30:00 (local) is after the change to DST, but we're only 2022-03-12T22:30:00 (local), which is still before the change to DST. This is going to give us the wrong offset.

  • Related