不使用本地时间的夏令时(DST)偏移计算

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

我有如下代码来计算偏移量, 在这里,我在 EST 时区机器上执行了以下代码,其偏移量为 -18000 秒,即 UTC-5.00

请注意,由于某些限制,我无法使用 localtime(),这里说来话长。 所以我使用下面的逻辑从本地时间纪元减去 GMT 纪元,如下所示,它 return 完美地调整了偏移量。

现在我想要的是假设 DST(夏令时)的情况,其中偏移量将为 -14400 秒,即 UTC-4.00。由于 EST 时区的 DST(夏令时)+1 调整。

下面提到的我的代码可以工作吗?如果系统有 DST(夏令时)。 我现在无法实时测试,因为 DST(夏令时)在我的机器上未激活。

计划如下:

/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

所以想知道,当 DST 处于活动状态时,即在 2021 年 3 月 14 日至 2021 年 11 月 7 日之间,我的以下代码 return 是否会偏移 -14400。 time()函数是否考虑夏令时调整后的纪元时间。

#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

我认为它根本行不通,但我不确定。最好的情况是,它会在开关 to/from DST 附近给出不正确的结果。

如果您不信任系统时区数据库,您始终可以使用自己的 tz database 副本。 Steve Summit 指出数据库甚至附带了所有与时区相关的功能的参考实现,例如 localtime,因此无需自己解析数据库。

注意:数据库经常更新,因此如果您需要使用任意时区,您将需要某种进程来频繁更新您的应用程序。


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

time returns 自 1970 UTC 开始以来的秒数。

因此,结果不会改变您在世界上的位置。

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

mktime 不是从同一个地方 localtime 获取有关本地时间的信息吗?那么为什么 mktime 可以在 localtime 不能的系统上工作呢?

但假设 mktime 确实有效。

即使我们假设 mktimelocaltime 不能正常工作的系统上正常工作,我也不认为它会工作。我认为 gtm.tm_isdst 将始终为零(因为 gmtime 使用没有 DST 的 UTC),我认为这将导致以下 mktime 到 return 不同于想要的。但这可以通过将 gtm .tm_isdst 设置为 -1.

来解决

那么假设 mktime 做了 return 我们期望的事情。

即使 mktime return 符合我们的预期,该方法也存在缺陷。它会在开关 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

问题来了。 2021-03-13T03:30:00(local)是夏令时之后的,而我们只是2022-03-12T22:30:00(local),还是夏令时之前。这会给我们错误的偏移量。

通过对您的程序进行以下小修改:

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

它应该在 DST 生效时工作。将 tm_isdst 设置为 -1 会强制 mktime 自行决定 DST 是否生效。

您还可以跳过涉及 ltml_epoch 的操作以及对 localtime 的调用。 (我认为练习的重点是避免调用 localtime?)您可以使用

计算偏移量
offset = rawtime - g_epoch;

但最后这仍然是一个非常可怕的 hack,正如@ikegami 指出的那样,它在 DST 转换附近无法正常工作。例如,time_t 值 1636261200 正确对应 2021 年 11 月 7 日凌晨 1 点 EDT,但此代码将错误地确定它已经回落到 EST。这个问题会更糟(将申请更多的小时数)你当地的时区离格林威治标准时间越远。可能有办法解决这个问题,但不会特别漂亮。