如何获取另一个时区的夏令时状态
Posted
技术标签:
【中文标题】如何获取另一个时区的夏令时状态【英文标题】:How to get daylight saving status of another time zone 【发布时间】:2016-08-07 18:19:08 【问题描述】:假设我的 Windows 服务器应用程序在东部时区 (NY) 运行。
我将每个日期时间事件(它们发生的那一刻)转换并存储在 UTC 中,以便连接到服务器的任何客户端应用程序读取事件的 UTC 时间并将其转换并显示在客户端自己的 TZ 中。
但这是棘手的部分,一些事件报告它们在另一个州的时间的时间戳并且没有明确指定夏令时信息(例如 xx:xx:xx AM PT,意思是太平洋时间,但我不知道它当前是否在夏令时)。
服务器可以检查它是否处于夏令时,但那将是服务器自己的 TZ(即东部时间)。我能想到的最好办法是阅读服务器的本地夏令时信息并将其用于 PT,但我知道这不是 100% 准确的。尤其是当 ET 刚刚开始(或停止)使用夏令时并且 PT 仍有数小时的时间时,通过短窗口。
现在我的问题是,无论服务器应用程序位于哪个时区,有没有办法(在 Windows API 中)准确地找出另一个时区的夏令时状态?
编辑: 好的,如何从以东部时间运行的 Windows 机器将其转换为 UTC“2016 年 3 月 13 日星期日 1:15 AM PT”? (请注意,当正在运行的机器(在 ET 中)目前处于 DST 中并且要转换的日期/时间在 DST 中还不是刚刚时,特别选择了这个日期/时间。我已经涵盖了 UTC 转换但无法确定给定的日期/时间是否在 DST 中。
【问题讨论】:
this previous question 有帮助吗? 如何识别应用程序中的时区?如果像PT
这样的缩写是你所拥有的,你可能会觉得非常困难或不可能。这些输入是否仅限于某些固定值?或者他们真的可以是什么?
@MattJohnson 它们通常是 HTTP 标准日期/时间字符串(实际上包含 DST 信息,如 xx:xx:xx AM PST 或 PDT,如果 DST 处于活动状态)但有些服务器对提供 DST 信息,只需说它是 PT(太平洋时间),然后让您确定 PT 在给定的日期/时间是否处于 DST 中。系统 (Windows) 应该知道这一点,因为它会在 3 月和 11 月的第 2 和第 1 个星期日调整系统时钟,并可以通过 API 向用户提供这一点。
更糟糕的是,夏令时的开始和结束日期/时间因国家/地区而异(哎呀,甚至美国的某些州也有不同的观察方式,夏威夷和亚利桑那州没有夏令时)。因此,应用程序要确定给定的日期/时间是否在 DST 中将是一项非常艰巨的工作,但底层操作系统很容易向应用程序提供此信息,因为它们已经具有内置数据库适用于每个国家和时区。
是的,我对时区了如指掌。检查我的个人资料。 ;)。我要问的是,对于 your 应用,是否有一致的输入形式?您是否专门使用 RFC822/2822 样式的输入字符串及其允许的有限时区缩写集?或者这是任意随机输入?请注意,PST
和 PDT
是该规范的一部分(用于 http 标头),但 PT
不是。请准确。谢谢。
【参考方案1】:
Windows 的时区数据实际上存储在注册表中:
HKLM
SOFTWARE
Microsoft
Windows NT
CurrentVersion
Time Zones
“时区”实际上是两个词。
每个时区都有自己的子键,每个子键都有一个TZI
值,它是一个二进制REG_TZI_FORMAT
结构:
typedef struct _REG_TZI_FORMAT
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
REG_TZI_FORMAT;
两个相关的结构成员是StandardDate
和DaylightDate
。
更多详情请参考 MSDN:TIME_ZONE_INFORMATION
【讨论】:
@Remy:我添加了链接以避免复制 Microsoft 的代码,这些代码可能受版权保护并且没有明确标记的“合理使用”准则。 这与我的问题无关。注册表只是 Windows 保存时区信息以显示给用户的地方,以便他们可以设置计算机的时区。我需要的是找出另一个州(或国家)目前是否在夏令时。 它为您提供了解决问题所需的信息。DaylightDate
的SYSTEMTIME
的成员有你需要的。
这是我的 Windows 太平洋时间 TZI LONG Bias = 480 (minutes, which is correct) LONG StandardBias = 0 LONG DaylightBias = -60 SYSTEMTIME StandardDate = WORD wYear = 0 WORD wMonth = 11 WORD wDayOfWeek = 0 WORD wDay = 1 WORD wHour = 2 WORD wMinute = 0 WORD wSecond = 0 WORD wMilliseconds = 0 SYSTEMTIME DaylightDate = WORD wYear = 0 WORD wMonth = 3 WORD wDayOfWeek = 0 WORD wDay = 2 WORD wHour = 2 WORD wMinute = 0 WORD wSecond = 0 WORD wMilliseconds = 0
如您所见,StandardDate
和 DaylightDate
结构成员未反映正确的 DST 开始和结束日期【参考方案2】:
我不确定 Windows API,但通常你想要调用mktime
,远程时间的 TZ 值生效。在 Unix 下,您可以直接调用 setenv("TZ", whatever)
来执行此操作。不确定 Windows,但我认为你也可以在那里做同样的事情。
如果除了 mktime
和 timegm
之外,还有一个变体可以让您明确指定要使用的区域(而不是将 TZ 环境变量视为全局变量),那肯定会很好,但是这样一个变体不是标准的。
在不知道夏令时是否生效的情况下转换当地时间时,记得将tm_isdst
设置为-1,以便库为您计算。
这是一个例子:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
struct tm tm = 0;
time_t t;
tm.tm_hour = 12; tm.tm_min = 30, tm.tm_sec = 15; /* 12:30:15 */
tm.tm_year = 2005-1900; tm.tm_mon = 4-1; tm.tm_mday = 12; /* 2005-04-12 */
tm.tm_isdst = -1;
setenv("TZ", "America/Los_Angeles", 1);
t = mktime(&tm);
setenv("TZ", "America/New_York", 1);
printf("%ld = %.24s\n", t, ctime(&t));
这告诉您洛杉矶时间 12:30:15 对应于 Unix (UTC) 时间 1113334215 和纽约时间 15:30:15。
附录:这对 Windows 也无济于事,但如果您不喜欢每次需要在不同时区使用时间时从程序内部设置环境变量,请查看是否可以使用BSD 函数 tzalloc
、localtime_rz
和 mktime_z
。
【讨论】:
小点:最好在调用mktime()
之前完全填满struct tm
,比如struct tm tm = 0;
。该函数使用除 tm_wday, tm_yday
和 struct tm
之外的所有字段,可能有超过通常的 9 个字段。
你真的在 Windows 上运行过这段代码吗?根据我的经验,Windows 并不关注 TZ 环境变量。这是一个 POSIX 的事情。有关此主题的先前处理,请参阅How can a Windows program temporarily change its time zone?
@RobKennedy 不,我在 MacOS 上测试过。我有一个想法,这或类似的东西在 Windows 上是可能的,但我可能错了。
@SteveSummit 感谢您的工作和回答,但不幸的是它不适用于 Windows。 (另外,在系统中设置全局环境变量不会影响其他程序吗?或者该更改仅对当前程序是局部的?)
环境变量是进程本地的,至少在 Unix/Linux 中是这样。以上是关于如何获取另一个时区的夏令时状态的主要内容,如果未能解决你的问题,请参考以下文章
Python:如何将日期时间/时间戳从一个时区转换为另一个时区?