【问题标题】:Comparing time regardless of date比较时间而不考虑日期
【发布时间】:2013-09-01 22:57:51
【问题描述】:

我有一个对象,其属性当前为 DateTime。

对象在时间范围内被标记为有效。默认为 00:00:00 到 23:59:59

用户在 UI 中输入值并通过以下方式设置属性:

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 变成昨天。

当逻辑试图确定对象是否有效时,逻辑是:

if (DateTime.UtcNow.TimeOfDay > model.MyPropertyFromDB.TimeOfDay)

我们正在尝试确定当前时间是否在 00:00:00 和 23:59:59 的范围内

这会失败,因为 14:00(当前时间)不大于 18:30

比较时间的最佳方法是什么?

将值存储为 DateTimeOffSet 有帮助吗,使用 ToLocal() 好吗?

其他注意事项是印度用户正在使用在英国托管的应用,因此需要了解时区。

谢谢

【问题讨论】:

  • 您考虑在数据库中将日期保存为 utc 吗?
  • [这个给你解决了吗?][1] [1]:stackoverflow.com/questions/7577389/…
  • 也许这可以帮助你stackoverflow.com/a/14268167/1587864
  • @kostyan 我认为 OP 说他做了“然后当它到达数据库时转换为 UTC。”
  • 除此之外,您不应该调用DateTime.Now 三次 - 如果超出界限(一天或一小时),您会得到非常奇怪的结果。您应该调用它一次,然后使用结果三次。 (如果您使用 UtcNow 也一样,这几乎肯定是一个更好的主意。)

标签: c# .net datetime


【解决方案1】:

和其他人一样,我仍然不清楚您到底想要什么。但显然,您不应该这样做:

new DateTime(DateTime.Now.Year, 
             DateTime.Now.Month, 
             DateTime.Now.Day, 
             model.Hours, 
             model.Minutes, 
             model.Seconds)

这样会更好:

DateTime.Today.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))

但是你为什么要这样做呢?其中任何一个都会给您返回 local 日期。我假设这将在服务器上运行,所以你真的希望服务器的时区影响这个结果吗?可能不是。请阅读:The Case Against DateTime.Now.

如果您想要 UTC 日期,您可以这样做:

DateTime.UtcNow.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))

无论您的服务器所在的时区如何,这至少是普遍相同的。但是,我认为这不是你所追求的。

不清楚的是为什么用户只在您分配当前日期时输入时间。如果日期是相关的,那么用户不应该输入它并且它将成为您模型的一部分吗?

如果日期不相关,那么为什么要存储它?您可以在内部使用 TimeSpan 类型作为时间值。您没有说您的数据库是什么,但我们只是猜测它是 SQL Server,在这种情况下,您可以在表中的字段上使用 time 类型。

我认为日期可能是相关的,但您想控制它,而用户控制提供时间。如果是这种情况,那么您必须知道用户的时区(或者如果不是用户,则知道上下文的时区)。假设您有一个 Windows 时区标识符(请参阅 the timezone tag wiki),那么您可以执行以下操作:

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 是 .NET 的替代日期和时间 API。它可以帮助您更清晰地思考数据,并更准确地表达对数据的操作。

这似乎直接是您在这里遇到的问题。如果您想澄清我提出的一些问题,我很乐意编辑我的答案并向您展示如何使用 Noda Time 做到这一点。

为什么您的问题令人困惑

我们正在尝试确定当前时间是否在 00:00:00 和 23:59:59 的范围内

所有次都在该范围内。好吧,也许像23:59:59.1 这样的值会在它之外,但是您没有在模型中收集小数秒,所以这无关紧要。但是为什么你需要验证呢?也许您只是想避免根本不是有效时间的数字?喜欢99:99:99

这会失败,因为 14:00(当前时间)不大于 18:30

等等 - 你没有说比较一次比另一次更大。 14:0018:30 都还在您指定的范围内。

比较时间的最佳方法是什么?

很难回答。它们都是UTC时间吗?一个UTC,一个是本地的吗?他们都是本地人吗?你知道当地时间的时区吗?您准备好处理模糊或无效的当地时间到夏令时转换了吗?

将值存储为 DateTimeOffSet 有帮助吗?

也许,但你没有给我足够的信息。仅当日期部分相关并且您获得正确的偏移量时才会有所帮助。

使用 ToLocal() 好吗?

我认为不,这不好。在这种情况下,本地将为您提供服务器的时区,您可能不想将其引入业务逻辑中。

【讨论】:

    【解决方案2】:

    我想说这里使用的方法存在根本缺陷,您需要采取不同的方法。

    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,因为浏览器不会自动为您修复此媒体格式的日期。

    这两种方法都要求您在客户端上构建一个完整的日期时间对象然后提交它,而不是尝试在服务器上从秒、分钟、小时构建它。当然,您不需要将所有这些都暴露给客户,只要在提交时完全形成日期时间即可。

    一旦你有了一个不错的 UTC 日期时间,如果你不需要其余的时间,你可以只提取时间。

    希望这会有所帮助。

    皮特

    【讨论】:

      【解决方案3】:

      因此,如果我理解正确,您在数据库中保存了 UTC 时间,并且您正在尝试确定它是否在特定时间范围内?我不确定您是否想要当地时间或 UTC 的时间范围,所以这里都是:

      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)
      }
      

      编辑:如果您想将现在与数据库中对象的开始和结束时间进行比较:

      TimeSpan now = DateTime.UtcNow.TimeOfDay;
      TimeSpan startDate = model.startDate.TimeOfDay;
      TimeSpan endDate = model.endDate.TimeOfDay;
      
      if (now > startDate && now < endDate)
      {
          //Within time range (UTC)
      }
      

      【讨论】:

      • 是的,但 db 值是最小/最大值
      • 当前时间是否在 db 值范围内
      • 是的,但是 model.startDate.TimeOfDay;是 18:30,因为 UTC 转换为 00:00:00 到 18:30:00,现在是 14:00:00,所以它不大于并且失败
      • 您说这些值在数据库中存储为UTC。在这种情况下,model.startDate.TimeOfDay 是 00:00:00 UTC。没有转换到 18:30:00..
      • 如果您仍然没有找到您要寻找的答案,请编辑原始帖子,或者更好的是,创建一个新问题,说明:您要解决的问题到底是什么?你到底想解决什么问题?您正在使用的数据到底是什么?
      猜你喜欢
      • 2019-12-06
      • 2021-01-24
      • 1970-01-01
      • 2014-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-21
      • 2021-09-16
      相关资源
      最近更新 更多