[英]Best practice for adding/subtracting from universal or local DateTime
我正在尝试在DateTime周围添加一个包装器以包含时区信息。 这是我到目前为止所拥有的:
public struct DateTimeWithZone {
private readonly DateTime _utcDateTime;
private readonly TimeZoneInfo _timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone) {
_utcDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), timeZone);
_timeZone = timeZone;
}
public DateTime UniversalTime { get { return _utcDateTime; } }
public TimeZoneInfo TimeZone { get { return _timeZone; } }
public DateTime LocalTime { get { return TimeZoneInfo.ConvertTimeFromUtc(_utcDateTime, _timeZone); } }
public DateTimeWithZone AddDays(int numDays) {
return new DateTimeWithZone(TimeZoneInfo.ConvertTimeFromUtc(UniversalTime.AddDays(numDays), _timeZone), _timeZone);
}
public DateTimeWithZone AddDaysToLocal(int numDays) {
return new DateTimeWithZone(LocalTime.AddDays(numDays), _timeZone);
}
}
这是根据先前问题中提供的@Jon Skeet的答案改编的。
由于夏令时问题,我正在努力增加/减少时间。 根据以下内容,最佳做法是添加/减去通用时间:
https://msdn.microsoft.com/en-us/library/ms973825.aspx#datetime_topic3b
我遇到的问题是,如果我说:
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var date = new DateTimeWithZone(new DateTime(2003, 10, 26, 00, 00, 00), timeZone);
date.AddDays(1).LocalTime.ToString();
这将返回26/10/2003 23:00:00。 正如你所看到的那样,当地时间已经减少了一个小时(由于夏令时结束),所以如果我要显示它,它会说它与它刚刚添加一天的那天相同。 但是,如果我要说:
date.AddDaysToLocal(1).LocalTime.ToString();
我会回来27/10/2003 00:00:00并保留时间。 这看起来对我来说是正确的,但它违背了加入世界时的最佳做法。
如果有人能帮助澄清正确的方法,我会很感激。 请注意,我已经看过Noda Time,它目前需要做太多工作才能转换成它,我也想更好地理解这个问题。
两种方式都是正确的(或不正确的)取决于您需要做什么。
我喜欢将这些视为不同类型的计算:
按时间顺序计算。
日历计算。
按时间顺序计算涉及以物理时间为常规单位的时间算术。 例如,添加秒,纳秒,小时或天。
日历计算涉及人类发现方便的单位时间算术,但并不总是具有相同的物理时间长度。 例如,添加数月或数年(每个都有不同的天数)。
当您想要添加一个不一定具有固定秒数的粗单位时,日历计算很方便,但您仍希望在日期中保留更精细的字段单位,例如天,小时,分钟和秒。
在您的本地时间计算中,您添加一天,并假设日历计算是您的意图,您保留当地时间,尽管在本地日历中1天并非总是24小时。 请注意,本地时间的算术可能会导致本地时间与UTC有两次映射,甚至是映射到UTC的零映射。 因此,您的代码应该构建为使您知道这种情况永远不会发生,或者能够检测到它何时发生并以适合您的应用程序的任何方式做出反应(例如消除模糊映射的歧义)。
在您的UTC时间计算(按时间顺序计算)中,您总是添加86400秒,然后本地日历可能会做出反应,但这可能是由于UTC偏移更改(夏令时相关或其他)。 UTC偏移量变化可能大到24小时,因此添加按时间顺序排列的日期可能甚至不会使当月日历日突然加1。 按时间顺序计算的结果总是具有唯一的UTC < - >局部映射(假设输入具有唯一的映射)。
两种计算都很有用。 两者都是常见的。 知道您需要哪些,并知道如何使用API来计算您需要的任何内容。
只是为了补充霍华德的好答案,要明白你所指的“最佳实践”是关于经过一段时间的增量。 实际上,如果你想增加24小时,你会在UTC中这样做,你会发现你最终会在23:00结束,因为当天还有一个小时。
我通常会考虑添加一天作为日历计算(使用霍华德的术语),因此无论当天有多少小时都没关系 - 你在当地时间增加一天。
然后你必须验证结果是那天的有效时间,因为很可能在前向转换的“间隙”中找到了无效值。 你必须决定如何调整。 同样,当您转换为UTC时,您应该测试不明确的时间并相应地进行调整。
要知道,不这样做你自己的任何调整,你依靠的默认行为TimeZoneInfo
方法,在不明确的时间(尽管通常期望的行为是调整前 ),它向后调整,并ConvertTimeFromUtc
将抛出无效时间内的例外情况。
这就是为什么Noda Time中的ZonedDateTime
具有“解析器”的概念,以允许您更具体地控制此行为。 您的代码缺少任何类似的概念。
我还要补充说,虽然你说你已经看过Noda Time而且转换成它的工作太多了 - 我鼓励你再看一遍。 人们不一定需要改进他们的整个应用程序来使用它。 你可以,但你也可以在需要的地方介绍它。 例如,您可能希望在此DateTimeWithZone
类中内部使用它,以强制您沿着正确的路径前进。
还有一件事 - 当你在输入中使用SpecifyKind
时,你基本上就是要忽略输入类型。 由于您正在设计用于重用的通用代码,因此您可能会发现错误的可能性。 例如,我可能会传入DateTime.UtcNow
,您将假设它是基于时区的时间。 Noda Time通过使用单独的类型而不是“kind”来避免这个问题。 如果您要继续使用DateTime
,那么您应该评估应用适当操作的类型。 只是忽略它肯定会让你陷入困境。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.