Search code examples
ctimeepochdstgmt

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


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

Solution

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