使用 DateTime 类处理夏令时

Posted

技术标签:

【中文标题】使用 DateTime 类处理夏令时【英文标题】:Working with daylight saving time with the DateTime class 【发布时间】:2015-04-09 16:24:47 【问题描述】:

我正在将 C++ 程序移植到 C#。该程序需要能够读取文件的“修改”时间戳并将其存储在List 中。这是我目前所拥有的:

C#代码:

ret = new List<Byte>(); //list to store the timestamp

var file = new FileInfo(filename);

//Get revision date/time
DateTime revTime_lastWriteTime_LT = file.LastWriteTime;

//Copy date/time into the List (binary buffer)
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Month));
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Day));
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Year % 100)); // 2-digit year
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Hour));
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Minute));
ret.Add(Convert.ToByte(revTime_lastWriteTime_LT.Second));

当我读入Hours 值时出现问题。如果在夏令时(如夏季)期间修改了文件,则 C++ 程序中的小时值会减一。我似乎无法在我的程序中复制它。在MSDN article 的DateTime 中,它在“DateTime 值”下说:“本地时间可能会受到夏令时的影响,它会从一天的长度中增加或减少一个小时”。在我的 C# 代码中,我确保使用 ToLocalTime() 将我的 DateTime 对象更改为本地时间,但显然我还没有设置文章所讨论的选项。 在读取夏令时修改的文件时,如何确保我的 DateTime 对象在本地时间减去一个值?

C++ 代码以防万一:

static WIN32_FILE_ATTRIBUTE_DATA get_file_data(const std::string & filename)

  WIN32_FILE_ATTRIBUTE_DATA ret;
  if (!GetFileAttributesEx(filename.c_str(), GetFileExInfoStandard, &ret))
    RaiseLastWin32Error();
  return ret;

//---------------------------------------------------------------------------
static SYSTEMTIME get_file_time(const std::string & filename)

  const WIN32_FILE_ATTRIBUTE_DATA data(get_file_data(filename));
  FILETIME local;
  if (!FileTimeToLocalFileTime(&data.ftLastWriteTime, &local))
    RaiseLastWin32Error();
  SYSTEMTIME ret;
  if (!FileTimeToSystemTime(&local, &ret))
    RaiseLastWin32Error();
  return ret;


void parse_asm()

    // Get revision date/time and size
    const SYSTEMTIME rev = get_file_time(filename);
    // Copy date/time into the binary buffer
    ret.push_back(rev.wMonth);
    ret.push_back(rev.wDay);
    ret.push_back(rev.wYear % 100); // 2-digit year
    ret.push_back(rev.wHour);
    ret.push_back(rev.wMinute);
    ret.push_back(rev.wSecond);

为清晰起见更新:

在 Windows 时间设置中,我在 (UTC-05:00) 东部时间(美国和加拿大)。该文件的最后修改时间为 2013 年 9 月 3 日星期二下午 12:13:52。 C++ 应用程序将小时值显示为 11,而 C# 应用程序使用上面的代码将小时值显示为 12。我需要 C# 应用程序显示与 C++ 应用程序相同的小时值。

【问题讨论】:

忽略二进制缓冲区位 - 如果您只打印出 revTime_lastWriteTime_LT 会发生什么?这与文件系统中显示的内容相比如何?请注意,在我的盒子上,LastWriteTime 已经返回了带有LocalDateTime,因此调用ToLocalTime 应该没有任何区别。问题是否可能实际上出在 C++ 代码上(您没有显示)? DateTimeIsDaylightSavingTime() 方法 将历史时间戳转换为本地时间充满了麻烦,您需要一个可靠的数据库来知道前几年 DST 何时生效。您通常可以指望 .NET 做到这一点。 C++,不,不太可能。如果您不能将其更改为使用 UTC,就像它应该的那样,那么保持 C++ 代码快乐是您的负担,请使用 TimeZoneInfo。 @JonSkeet 我不认为打电话给ToLocalTime 有什么不同,但我只是在阅读了 MSDN 文章后尝试了一下。我确信问题不在于 C++ 代码。我基本上处于C++文件总是正确的情况,哈哈。我试图打印出revTime_lastWriteTime_LT.Hour.ToString() 并没有减去一个小时。 @NadiaCibrikova 这不只是一个布尔属性吗? 【参考方案1】:

该错误实际上不在于 .NET,而在于您的 C++ 代码。您正在使用FileTimeToLocalFileTime,它有一个众所周知的错误,如KB932955 中所述:

注意 FileTimeToLocalFileTime() 函数和 LocalFileTimeToFileTime()函数执行UTC之间的转换 仅使用当前时区信息的时间和本地时间 和夏令时信息。无论发生这种转换 正在转换的时间戳。

因此,在您给出的示例中,美国东部时区 2013 年 9 月 3 日下午 12:13:52 确实应该是夏令时。但是因为它是现在(2015 年 2 月)而不是夏令时,所以您目前在 C++ 程序中获得 11 小时。如果您在下个月的转换(2015 年 3 月 8 日)之后运行完全相同的 C++ 代码,那么您将获得 12 个小时。

MSDN entry 的 FileTimeToLocalFileTime 函数的备注部分描述了 C++ 代码的修复:

要在将文件时间转换为本地时间时考虑夏令时,请使用以下函数序列代替 FileTimeToLocalFileTime

    FileTimeToSystemTime SystemTimeToTzSpecificLocalTime SystemTimeToFileTime

既然您了解了这个错误 - 如果您真的想在 C# 中保留该行为(我确实推荐),那么您可以执行以下操作:

TimeSpan currentOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow);
DateTime revTime_LastWriteTime_Lt = file.LastWriteTimeUtc.Add(currentOffset);

最好的做法是保持当前代码不变(使用file.LastWriteTime),然后调用已修复的错误。

【讨论】:

感谢@MattJohnson 提供的所有信息!如果我获得修复错误的批准,我肯定会使用您的答案。但是,如果我需要保留该错误,那么您的答案和@NadiaCibrikova 的有什么不同?他们似乎实现了相同的目标。 Nadia 的回答将总是忽略 DST。我的回答(最后)将与 C++ 错误的行为相匹配——即如果 当前 生效,则使用 DST,而不管时间戳如何。再次 - 我建议您保留问题中最初显示的 C# 代码。【参考方案2】:

抱歉,您需要使用Add 而不是AddHoursAdd 接受TimeSpan。所以你正在寻找:

 file.LastWriteTimeUtc.Add(TimeZoneInfo.Local.BaseUtcOffset);

【讨论】:

非常感谢@NadiaChirkova。你知道为什么这会产生与 C++ 代码相同的结果吗?这似乎是 C++ 代码从未处理过的额外步骤。 时间的内部表示是滴答声(100 ns)。然后如何转换为天和小时取决于语言。 .Net Framework 对 DST 有更好的支持,我不会称其为劣势。我想不出很多需要忽略 DST 的情况。 我不确定您是否真的想要这样做。这将有效地忽略夏令时,即使它适用于当地时区。

以上是关于使用 DateTime 类处理夏令时的主要内容,如果未能解决你的问题,请参考以下文章

为啥减去两个本地 DateTime 值似乎不能说明夏令时?

DateTime.ToLocalTime 在夏令时无法正常工作

ColdFusion 2018和三个Char夏令时代码的BlazeDS DateTime解析错误

time datetime 总结

使用python确定时间戳是不是在夏令时

学习笔记-模块之标准库time与datetime