Comparing time regardless of date
我有一个对象,其目前的属性为DateTime。
该对象在一个时间范围内被标记为有效。默认值为00:00:00至23:59:59
用户在UI中输入值,并通过以下方式设置属性:
1 2 3 4 5 6 | new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, model.Hours, model.Minutes, model.Seconds) |
然后在它到达数据库时将其转换为UTC。
今天的日期是2013年8月29日。如果印度的同事运行此程序,它将在2013年8月28日18:30:00将数据存储在数据库中,因为它们比UTC早5.5小时,因此2013年8月29日00:00:00变为昨天。
当逻辑尝试确定对象是否有效时,逻辑是:
1 | if (DateTime.UtcNow.TimeOfDay > model.MyPropertyFromDB.TimeOfDay) |
我们正在尝试确定当前时间是否在00:00:00和23:59:59之间
这是因为14:00(当前时间)不大于18:30
什么是比较时间的最佳方法?
将值存储为DateTimeOffSet帮助,是否使用ToLocal()确定?
其他考虑因素是印度的用户正在使用在英国托管的应用程序,因此需要了解时区。
谢谢
和其他人一样,我仍然不清楚你到底想要什么。但很明显,你不应该这样做:
1 2 3 4 5 6 | new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, model.Hours, model.Minutes, model.Seconds) |
这会好得多:
1 |
但是你为什么这样做呢?这些任何一个都会让你回到当地的日期。我假设这将在服务器上运行,所以你真的希望服务器的时区影响这个结果吗?可能不是。请阅读:针对DateTime.Now的案例。
如果您想要UTC日期,您可以这样做:
1 |
无论服务器的时区如何,这至少会普遍相同。但是,我不认为这就是你所追求的。
不清楚的是,为什么用户只在您指定当前日期时输入时间。如果日期是相关的,那么用户不应该输入它并且它将成为您模型的一部分吗?
如果日期不相关,那么为什么要存储它?您可以在内部使用
我想可能日期是相关的,但你想控制它,而用户控制提供时间。如果是这种情况,那么您必须知道用户的时区(如果不是用户,则必须知道上下文的时区)。假设您有一个Windows时区标识符(请参阅时区标记wiki),那么您可以执行以下操作:
1 2 3 | var tz = TimeZoneInfo.FindSystemTimeZoneById(theTimeZoneId); var local = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz); var dt = local.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds)); |
如果您没有时区信息,则无法解决此问题。
作为一般建议,您可能想尝试使用Noda Time而不是内置的东西。它可以帮助你弄清楚这种事情。从主页面:
Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely.
这似乎直接是你在这里遇到的问题。如果你想澄清我提出的一些问题,我很乐意编辑我的答案并告诉你如何用Noda Time做到这一点。
为什么你的问题令人困惑
We are trying to determine if the current time is within a range of 00:00:00 and 23:59:59
所有时间都在该范围内。好吧,也许像
This fails as 14:00 (current time) is not greater than 18:30
等等 - 你没有说过比一次大于另一次的事情。
What would be the best approach to compare just times?
很难回答。它们都是UTC时代吗?是UTC还是一个本地?他们都是本地人吗?你知道当地时间吗?您是否准备好处理模糊或无效的当地时间对夏令时的过渡?
Would storing the values as DateTimeOffSet help?
或许,但你没有给我足够的信息。只有当日期部分相关并且您获得正确的偏移时,它才有用。
is using ToLocal() ok?
我会争辩说不,这不好。本上下文中的本地将为您提供服务器的时区,您可能不希望将其引入业务逻辑。
我会说这里使用的方法存在根本缺陷,你需要采取不同的方法。
1 2 3 4 5 6 | new DateTime(DateTime.Now.Year, // Server date DateTime.Now.Month, DateTime.Now.Day, model.Hours, // Local time model.Minutes, model.Seconds) |
除非你能够确切地知道用户所处的时区,否则我无法看到以这种方式"正常化"输入的方法。简单地说,没有简单的方法可以将以这种方式构建的日期转换为UTC。
我的第一个问题是,模型是如何从客户端传递到服务器的?如果您正在使用javascript / ajax,那么通过在客户端上构建datetime对象(包括其时区数据)然后依靠浏览器将其转换为UTC进行传输,解决方案应该相当简单。
如果您正在使用Razor MVC,那么您可以使用表单编码实现类似的功能,但您需要在服务器上调用ToUTC,因为浏览器不会自动为您定义此媒体格式的日期。
这两种方法都要求您在客户端上构建完整的datetime对象,然后提交它,而不是尝试在服务器上的秒,分钟,小时构建它。当然,只要日期时间在提交时完全形成,您就不需要将所有这些暴露给客户端。
一旦你有一个漂亮的UTC日期时间,你可以只提取时间,如果你不需要其余的。
希望这可以帮助。
皮特
因此,如果我理解正确,您在数据库中有一个以UTC保存的时间,并且您正在尝试确定它是否属于特定时间范围内?我不确定你是否想要本地时间或UTC的时间框架,所以这里都是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | DateTime dbTime = model.MyPropertyFromDB; TimeSpan minTime = new TimeSpan(0, 0, 0); TimeSpan maxTime = new TimeSpan(23, 59, 59); if (dbTime.TimeOfDay > minTime && dbTime.TimeOfDay < maxTime) { //Within time range (UTC) } if (dbTime.ToLocalTime().TimeOfDay > minTime && dbTime.ToLocalTime().TimeOfDay < maxTime) { //Within time range (local) } |
编辑:如果要将现在与数据库中对象的开始和结束时间进行比较:
1 2 3 4 5 6 7 8 | TimeSpan now = DateTime.UtcNow.TimeOfDay; TimeSpan startDate = model.startDate.TimeOfDay; TimeSpan endDate = model.endDate.TimeOfDay; if (now > startDate && now < endDate) { //Within time range (UTC) } |