【发布时间】:2021-06-11 10:04:56
【问题描述】:
给定一个 “本地” 的日期时间(即没有时区信息),例如:
| Datetime |
|---------------------|
| 2019-01-21 09:00:00 |
| 2019-02-21 09:00:00 |
| 2019-03-21 09:00:00 |
| 2019-04-21 09:00:00 |
| 2019-05-21 09:00:00 |
| 2019-06-21 09:00:00 |
| 2019-07-21 09:00:00 |
| 2019-08-21 09:00:00 |
| 2019-09-21 09:00:00 |
| 2019-10-21 09:00:00 |
| 2019-11-21 09:00:00 |
| 2019-12-21 09:00:00 |
如何获得该日期与 UTC 的偏移量? (假设机器本地时区信息)
例如,我的本地 PC 位于 东部 时区。 东部时区是:
- 300 分钟(5 小时) UTC
- 240 分钟(4 小时)在 UTC
取决于“夏令时”在该日期时间是否有效。
以上列表的含义:
| Datetime | Offset from UTC (minutes) |
|---------------------|----------------------------|
| 2019-01-21 09:00:00 | -300 (-5 hours) |
| 2019-02-21 09:00:00 | -300 (-5 hours) |
| 2019-03-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-04-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-05-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-06-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-07-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-08-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-09-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-10-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2019-11-21 09:00:00 | -300 (-5 hours) |
| 2019-12-21 09:00:00 | -300 (-5 hours) |
当然,如果日期 are from before 2007 发生变化,这些偏移量也会发生变化,答案会发生变化:
| Datetime | Offset from UTC (minutes) |
|---------------------|----------------------------|
| 2006-01-21 09:00:00 | -300 (-5 hours) |
| 2006-02-21 09:00:00 | -300 (-5 hours) |
| 2006-03-21 09:00:00 | -240 (-5 hours) |
| 2006-04-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-05-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-06-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-07-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-08-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-09-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 2006-10-21 09:00:00 | -240 (-5 hours) |
| 2006-11-21 09:00:00 | -300 (-5 hours) |
| 2006-12-21 09:00:00 | -300 (-5 hours) |
在 1977 年能源危机期间,答案将再次不同,因为该国全年实行夏令时:
| Datetime | Offset from UTC (minutes) |
|---------------------|----------------------------|
| 1977-01-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-02-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-03-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-04-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-05-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-06-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-07-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-08-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-09-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-10-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-11-21 09:00:00 | -240 (-4 hours) | Daylight savings
| 1977-12-21 09:00:00 | -240 (-4 hours) | Daylight savings
而before 1966 的答案又发生了一些变化。
Windows 知道所有这些事情。
所以问题是:
- 给出
FILETIME格式的日期时间 - 假定为当前 PC 的时区
- 如何获取日期时间与 UTC 的偏移量
- 日期时间
换句话说:
//Pesudocode. It may look like C#, but i'm using the native Win32 api
Int32 GetDateTimeMinutesOffsetFromUTC(DateTime value)
{
//2006-03-21 09:00:00 ==> -300
//2007-03-21 09:00:00 ==> -240
return -1; //todo
}
或
function GetDateTimeMinutesOffsetFromUtc(Value: TDateTime): Integer;
begin
//2006-03-21 09:00:00 ==> -300
//2007-03-21 09:00:00 ==> -240
Result := -1; //todo
end;
或
int GetDateTimeMinutesOffsetFromUtc(FILETIME value)
{
//2006-03-21 09:00:00 ==> -300
//2007-03-21 09:00:00 ==> -240
Result := -1; //todo
}
提醒一下,我使用的是 Win32 api。
- 这不是 C/C++(即我无权访问 C 标准库)
- 这不是 C#(即我无权访问 .NET Framework 类库)
- 这不是 Java(即我无权访问 Java 类库)
- 这不是 Python
- 这不是 Javascript、React、Rust、Django
我说的是 Windows 和 Win32 API。
SQL 服务器
你可以在SQL Server中看到上面的工作:
SELECT
EventDate,
DATEDIFF(minute, CAST(EventDate AS datetime) AT TIME ZONE 'Eastern Standard Time', EventDate) AS MinutesOffsetFromUTC
FROM (VALUES
('2019-01-21 09:00:00.000'),
('2019-02-21 09:00:00.000'),
('2019-03-21 09:00:00.000'),
('2019-04-21 09:00:00.000'),
('2019-05-21 09:00:00.000'),
('2019-06-21 09:00:00.000'),
('2019-07-21 09:00:00.000'),
('2019-08-21 09:00:00.000'),
('2019-09-21 09:00:00.000'),
('2019-10-21 09:00:00.000'),
('2019-11-21 09:00:00.000'),
('2019-12-21 09:00:00.000')
) foo(EventDate)
EventDate MinutesOffsetFromUTC
----------------------- --------------------
2019-01-21 09:00:00.000 -300
2019-02-21 09:00:00.000 -300
2019-03-21 09:00:00.000 -240
2019-04-21 09:00:00.000 -240
2019-05-21 09:00:00.000 -240
2019-06-21 09:00:00.000 -240
2019-07-21 09:00:00.000 -240
2019-08-21 09:00:00.000 -240
2019-09-21 09:00:00.000 -240
2019-10-21 09:00:00.000 -240
2019-11-21 09:00:00.000 -300
2019-12-21 09:00:00.000 -300
(12 rows affected)
研究工作
大多数用于将 "local" 转换为 "UTC" 并返回的 Winapi 函数,不考虑相关日期;但只使用daylight savings is in effect right now:
FileTimeToLocalFileTime 等函数应用当前夏令时 (DST) 偏差,而不是相关时间有效的偏差。
其他人会考虑转换的日期,但只查看现在的夏令时开始和结束规则 - 而不是当时的规则。
但TzSpecificLocalTimeToSystemTime 是确实了解日期时间和夏令时的一个函数:
TzSpecificLocalTimeToSystemTime 考虑夏令时 (DST) 是否对要转换的本地时间有效。
实际上,Windows 并不知道一切。它在注册表中保存历史“夏令时”日期的数据库:
所以对我来说,它真正知道的是
- 在 2007 年巨变之前
- 及之后
但在我的用例中对我来说已经足够了。它是good enough for SQL Server。
阅读奖励
【问题讨论】:
-
使用 .NET 框架?还是使用 Win32 API?
-
@Andy Native(因此有
winapi标签)。 -
但不可能从 8 字节 UTC 时间获取任何信息。这只是一些价值。你可以打电话给
GetTimeZoneInformation或者甚至可以是NtQuerySystemInformation(SystemTimeOfDayInformation,.. )来获取SYSTEM_TIMEOFDAY_INFORMATION。并使用此信息。或直接转换FileTimeToLocalFileTime+LocalFileTimeToFileTime -
zoned_time。要么使用它,要么复制它的实现。
-
@OleV.V. Certainly not