C#解析不带时区的字符串为DateTime,考虑夏令时的影响。

3
我读了很多SO上的文章,但似乎找不到一个好的解决方案。 提出的建议包括将日期转换为字符串多次,将位连接在末尾等等,所有这些都显得有点混乱。
所以问题是:
我们在世界各地拥有服务器。 所有服务器都在其本地时间运行,并保留具有对服务器本地的时间条目的日志。 一些服务器在遵守DST的区域中,而另一些则没有。
假设我从日志中得到这些字符串:2013-01-01 12:34:56, 2013-07-01 12:34:56 ,并且我知道此服务器在纽约,因此它是UTC-5或UTC-4(夏令时)。
我从香港的服务器日志中也得到了相同的字符串,那里没有使用DST,时区为+8。
我需要一段代码块告诉它:
这是表示时间的字符串 这是字符串来自的时区 如适用,应采用夏令时
代码将把字符串解析为DateTimeOffset,其中根据是否与DST相关来调整偏移量。
例如: 纽约服务器日志说“2013-01-01 ...”在1月份DST 不适用,因此解析的日期应该是: 纽约时间下的12:34:56,即UTC下的17:34:56(因为是-5,没有DST)
纽约服务器日志说“2013-07-01 ...” DST 适用于6月份,因此解析的日期应该是: 纽约时间下的12:34:56,即UTC下的16:34:56(因为它是-4,具有DST)
对于HK服务器,这两个日期时间都解析为UTC下的04:34:56。
谢谢大家。
2个回答

6

首先,我强烈建议您将系统更改为在UTC时区登录。这将使您的生活更加简单。

如果您真的被困在所拥有的内容中,您应该使用DateTime.TryParseExact,并使用DateTimeStyles 0(默认值)。这将给您一个具有Unspecified DateTimeKind的值,这正是您想要的。(它不是UTC,也不是解析的机器本地时间。)

然后,您可以使用TimeZoneInfo.GetUtcOffset(使用该日志的正确时区)来计算偏移量,并从两者一起创建DateTimeOffset

作为一个完全有偏见的附带说明,您还可以更改使用我维护的Noda Time项目,这将使您的代码更容易理解 :)


请注意,如果日志没有存储夏令时转换是否已发生的某些指示器,则当地时间可能是模糊的。这是无法避免的。记录UTC时间或在日志中记录偏移量是所有理由。 - Matt Johnson-Pint
非常有道理;我想我能够判断服务器上的时钟是否已经更新了夏令时的更改(从而实现一个模糊的日期)的唯一方法是保留前一行日期/时间的记忆,并查看当前行的日期/时间是否向后跳转... 嗯 - Caius Jard
我也会看一下Noda Time,谢谢Jon。起初我有些犹豫,因为我没有阅读过你的博客文章或任何支持文档(也许是因为我变老了,不愿改变我的想法 :D)。 - Caius Jard
现在看来,我想我会使用ZonedDateTime(local, zone, offset)构造函数,但让人困惑的是我应该为偏移量放什么,因为我并不一定知道它。在纽约,它将是-4或-5,具体取决于当地时间,但我现在只知道“为dst真/假进行调整”,而不知道具体偏移量。我知道Noda为什么要求我提供偏移量,因为它想检查我是否了解我所说的内容,并从我所声称的偏移量推断DST活动/非活动状态。而我正在寻找一种相反的解决方案,我想…… - Caius Jard
@CaiusJard:不要直接调用构造函数,而应该调用 DateTimeZone.AtLenientlyLocalDateTime.InZoneLeniently(或者严格版本),它们会为您进行映射。 - Jon Skeet

5

要获取不同日志文件中记录时间的世界标准时间(UTC),您需要知道本地时区的名称。然后,您可以使用DateTimeOffset结构体TimeZoneInfo进行计算:

public DateTime ParseAsUtc(string logDate, string timezoneName)
{
    var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timezoneName);
    var localDate = DateTime.Parse(logDate);
    var offset = new DateTimeOffset(localDate, timeZone.GetUtcOffset(localDate));
    return offset.ToUniversalTime().DateTime;
}

ParseAsUtc("2013-01-01 12:34:56", "Eastern Standard Time"); //01.01.2013 17:34:56
ParseAsUtc("2013-07-01 12:34:56", "Eastern Standard Time"); //01.07.2013 16:34:56
ParseAsUtc("2013-01-01 12:34:56", "China Standard Time");   //01.01.2013 04:34:56
ParseAsUtc("2013-01-01 12:34:56", "China Standard Time");   //01.07.2013 04:34:56

看看我在Jon Skeet的回答中的评论。这里也适用。 - Matt Johnson-Pint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接