【问题标题】:How to get the start and end times of a day如何获取一天的开始和结束时间
【发布时间】:2009-05-24 00:39:24
【问题描述】:

在我的 C# 应用程序中,我传递了一个格式为 yyyymmdd-yyyymmdd 的字符串变量,它表示起始日期和截止日期。我想分别获取这些日期的开始时间和结束时间。目前我有以下代码,但想知道是否有更多优雅的解决方案?

所以对于 pdr = 20090521-20090523 将得到“20090521 00:00:00”和“20090523 23:59:59”

private void ValidateDatePeriod(string pdr, out DateTime startDate, 
                                out DateTime endDate)
{
    string[] dates = pdr.Split('-');

    if (dates.Length != 2)
    {
        throw new Exception("Date period is of incorrect format");
    }

    if (dates[0].Length != 8 || dates[1].Length != 8)
    {
        throw new Exception("Split date periods are of incorrect format");
    }

    startDate = DateTime.ParseExact(dates[0] + " 00:00:00", 
        "yyyyMMdd HH:mm:ss", null);
    endDate = DateTime.ParseExact(dates[1] + "23:59:59", 
        "yyyyMMdd HH::mm:ss", null);
}

【问题讨论】:

  • 正如其他人所说,EndOfDay 可能是一个不必要且不准确的结构,因为它没有明确的时间点,除了明天的 StartOfDay。

标签: c# datetime


【解决方案1】:

我很惊讶incorrect answer 收到这么多赞:

正确的版本如下:

public static DateTime StartOfDay(this DateTime theDate)
{
    return theDate.Date;
}

public static DateTime EndOfDay(this DateTime theDate)
{
    return theDate.Date.AddDays(1).AddTicks(-1);
}

【讨论】:

  • @Chris F Carroll 提到,-1 勾号也可能不正确。
  • @liang,他提到:“AddTick(-1) 只能在没有小于一个刻度的时间间隔的约定下工作”。 .NET 中是否存在更小的时间反转?
  • 他在使用数据库时给出了例子。数据库实现各不相同。在这些情况下,-1 滴答可能不再是一天的结束。至少,不应该依赖于使用相等比较。本着浮点数的相同精神,您不能可靠地说出小于整数的最大浮点数是多少,这取决于实现细节。更大/更少可能更好。
  • 我喜欢这个解决方案,因为它很简单。正如@liang 所说,不使用 AddTicks(-1) 可能会更简单。例如:(time >= startOfDay && time < nextDay) .. 虽然发帖人在问如何结束一天。
  • @breez 建议的代码行如何解决该问题?您减去 1 个刻度,然后您仍然添加 1 天的刻度,并且无论如何都会超过最大值。只需添加一个简单的检查。天哪。
【解决方案2】:

你可以在某处定义两个扩展方法,像这样在一个实用程序类中:

public static DateTime EndOfDay(this DateTime date)
{
    return new DateTime(date.Year, date.Month, date.Day, 23, 59, 59, 999);
}

public static DateTime StartOfDay(this DateTime date)
{
    return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, 0);
}

然后像这样在代码中使用它们:

public DoSomething()
{
    DateTime endOfThisDay = DateTime.Now.EndOfDay();
}

【讨论】:

  • 不错的答案,但不正确。请查看我提供的屏幕截图以了解原因。
  • 确实如此。使用 Anar Khalilov 的那个会更好。
  • 我们可以只做 9999999 毫秒吗?这应该产生与@anarkhalilov 相同的结果
  • @IgorK,当然你可以加 7 个 9。尽管不太可能,但如果未来 .NET Framework 设计人员认为 7 还不够,将其变为 10 怎么办。它仍然可能不会使您的代码崩溃,但为什么要冒险,不是吗?
  • @anarkhalilov 我的错,它不会让我无论如何。我应该先尝试。创建新的 DateTime 对象时,如果不允许您指定超过 999 的毫秒数。
【解决方案3】:

如果您只担心 .Net 精度...

startDate = DateTime.ParseExact(dates[0], "yyyyMMdd");
endDate = DateTime.ParseExact(dates[1], "yyyyMMdd").AddTicks(-1).AddDays(1);

你真的不需要将额外的值连接到时间部分的字符串上。


作为附录,如果您将其用于针对例如数据库的查询...

startDate = DateTime.ParseExact(dates[0], "yyyyMMdd");
endDate = DateTime.ParseExact(dates[1], "yyyyMMdd").AddDays(1);

查询...

WHERE "startDate" >= @startDate AND "endDate" < @endDate

那么在 cmets 中提到的精度问题就不再重要了。在这种情况下,endDate 不是范围的一部分,而是外部边界。

【讨论】:

  • 注意:如果您正在使用数据库,您可能必须阅读有关“一天结束”的数据库准确性的小字。我打的例子是 SQL Server DateTime 类型。它精确到大约 3 毫秒,因此一天结束时变为 .AddMilliseconds(-3)
  • @ChrisFCarroll 就我个人而言,我更喜欢使用dtm &gt;= start &amp;&amp; dtm &lt; end,其中 end 是您之前查找的日期。
  • 我想你让我意识到“一天结束”实际上是一个不存在的概念,因此我们做错了。使用&lt; end 也不能解决精度问题,例如:Select Case When Cast('20140101 23:59:59.999' as DateTime) &lt; Cast('20140102' as DateTime) Then 'I have millisecond precision' Else 'I cannot tell the difference' End
  • @ChrisFCarroll 在这种情况下,end 实际上是在第二天开始之前。如果您要与表中的记录进行匹配,那么在这种情况下您的匹配也不会发生。
  • 如果值为01/01/9999 00:00:00.000呢?那会爆炸的。
【解决方案4】:
public static class DateTimeExtension {        
    public static DateTime StartOfTheDay(this DateTime d) => new DateTime(d.Year, d.Month, d.Day, 0, 0,0);
    public static DateTime EndOfTheDay(this DateTime d) => new DateTime(d.Year, d.Month, d.Day, 23, 59,59);
}

【讨论】:

    【解决方案5】:

    DateTime 对象有一个名为Date 的属性,它将只返回日期部分。 (时间部分默认为上午 12:00)。

    我会推荐一个更优雅的解决方案(恕我直言),如果您想在最后一天允许任何日期时间,那么您将 1 天添加到日期,并比较以允许大于或等于开始日期的时间,但严格小于结束日期(加上 1 天)。

    // Calling code.  beginDateTime and endDateTime are already set.
    // beginDateTime and endDateTime are inclusive.
    // targetDateTime is the date you want to check.
    beginDateTime = beginDateTime.Date;
    endDateTime = endDateTime.Date.AddDays(1);
    
    if ( beginDateTime <= targetDateTime &&
         targetDateTime < endDateTime )
       // Do something.
    

    【讨论】:

    • 我想从解析字符串中返回 2 个日期时间,因此调用方法可以使用这些日期来查询我的数据库。
    • @Matt:如果时间可以有亚秒级的分辨率,你的建议特别中肯。
    【解决方案6】:

    我在 C# 中使用以下代码

    public static DateTime GetStartOfDay(DateTime dateTime)
    {
        return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0);
    }
    public static DateTime GetEndOfDay(DateTime dateTime)
    {
        return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 999);
    }
    

    然后在 MS SQL 中我执行以下操作:

    if datepart(ms, @dateEnd) = 0
       set @dateEnd = dateadd(ms, -3, @dateEnd)
    

    这将导致 MS SQL 时间为 23:59:59.997,这是成为第二天之前的最长时间。

    你可以简单地使用:

    new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 999);
    

    这适用于 MS SQL,但在 .Net 方面并不准确。

    【讨论】:

      【解决方案7】:

      这几乎就是我会做的,只是一些小的调整(真的没什么大不了,只是吹毛求疵):

      • 应该使用TryParse()/TryParseExact() 方法返回false 而不是抛出异常。
      • FormatExceptionException 更具体
      • 无需检查 Length == 8,因为 ParseExact()/TryParseExact() 会这样做
      • 不需要"00:00:00""23:59:59"
      • return true/false 是否能够解析,而不是抛出异常(记得检查从这个方法返回的值!)

      代码:

      private bool ValidateDatePeriod(string pdr, out DateTime startDate, 
                              out DateTime endDate)
      {
         string[] dates = pdr.Split('-');
      
         if (dates.Length != 2)
         {
             return false;
         }
      
         // no need to check for Length == 8 because the following will do it anyway
         // no need for "00:00:00" or "23:59:59" either, I prefer AddDays(1)
      
         if(!DateTime.TryParseExact(dates[0], "yyyyMMdd", null, DateTimeStyles.None, out startDate))
            return false;
      
         if(!DateTime.TryParseExact(dates[1], "yyyyMMdd", null, DateTimeStyles.None, out endDate))
            return false;
      
         endDate = endDate.AddDays(1);
         return true;
      }
      

      【讨论】:

      • 感谢 cmets。很高兴知道我在正确的轨道上并感谢调整
      【解决方案8】:

      我认为我们做错了。没有一天结束这样的事情。 AddTick(-1) 仅在没有小于一个刻度的时间间隔的约定下工作。这取决于实现。诚然,这个问题带有一个参考实现,即.Net Framework DateTime 类,但我们仍然应该以此为线索,我们真正想要的功能不是EndOfDay(),而是StartOfNextDay()

      public static DateTime StartOfNextDay(this DateTime date)
      {
          return date.Date.AddDays(1);
      }
      

      【讨论】:

      • 我们为什么不做 AddSeconds(-1),而不是 Add Ticks?对于大多数情况,一天的最后一秒已经足够准确了。
      【解决方案9】:

      对于 SQL Server(已测试 2008 R2 版),此范围有效。

      StarDate '2016-01-11 00:00:01.990' 结束日期'2016-01-19 23:59:59.990'

      似乎滴答声大于一天的最后一秒,并自动舍入到第二天。所以我测试并工作,我制作了一个带有两个日期的虚拟表,用于检查 sql server 捕获哪些值并将这些参数插入到存储过程中。

      【讨论】:

        【解决方案10】:

        在 Java 8 中,您可以使用 LocalDate 执行此操作,如下所示:

            LocalDate localDateStart = LocalDate.now();
            Date startDate = Date.from(localDateStart.atStartOfDay(ZoneId.systemDefault()).toInstant());
        
            LocalDate localDateEnd = localDateStart.plusDays(1);
            Date endDate = Date.from(localDateEnd.atStartOfDay(ZoneId.systemDefault()).toInstant());
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-03-29
          • 1970-01-01
          • 2020-09-25
          • 1970-01-01
          • 1970-01-01
          • 2012-01-28
          • 2016-07-02
          相关资源
          最近更新 更多