【问题标题】:How can I round up the time to the nearest X minutes?如何将时间四舍五入到最接近的 X 分钟?
【发布时间】:2011-10-25 03:41:48
【问题描述】:

是否有一个简单的函数可以将DateTimeUP四舍五入到最接近的 15 分钟?

例如

2011-08-11 16:59 变为 2011-08-11 17:00

2011-08-11 17:00 保持为2011-08-11 17:00

2011-08-11 17:01 变为 2011-08-11 17:15

【问题讨论】:

    标签: c# datetime rounding


    【解决方案1】:
    DateTime RoundUp(DateTime dt, TimeSpan d)
    {
        return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
    }
    

    例子:

    var dt1 = RoundUp(DateTime.Parse("2011-08-11 16:59"), TimeSpan.FromMinutes(15));
    // dt1 == {11/08/2011 17:00:00}
    
    var dt2 = RoundUp(DateTime.Parse("2011-08-11 17:00"), TimeSpan.FromMinutes(15));
    // dt2 == {11/08/2011 17:00:00}
    
    var dt3 = RoundUp(DateTime.Parse("2011-08-11 17:01"), TimeSpan.FromMinutes(15));
    // dt3 == {11/08/2011 17:15:00}
    

    【讨论】:

    • 这个解决方案只是作为扩展方法进入了我的实用程序库。
    • 您不会使用此方法从 DateTime 对象中丢失信息吗?喜欢种类和时区,如果有设置?
    • @user14.. ( + d.Ticks - 1) 确保它会在必要时四舍五入。 / 和 * 是四舍五入的。示例第 12 轮到下 5 轮:(12 + 5 - 1) = 16, 16 / 5 = 3(因为它是整数数据类型),3 * 5 = 15。tada :)
    • @dtb 一个小补充,否则可能有点错误:您需要保留日期时间类型 ;-) DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
    • 如果有人需要这个答案的圆形版本: DateTime RoundDown(DateTime dt, TimeSpan d) { return new DateTime(dt.Ticks / d.Ticks * d.Ticks, dt.Kind); }
    【解决方案2】:

    想出了一个不涉及乘法除法long数字的解决方案。

    public static DateTime RoundUp(this DateTime dt, TimeSpan d)
    {
        var modTicks = dt.Ticks % d.Ticks;
        var delta = modTicks != 0 ? d.Ticks - modTicks : 0;
        return new DateTime(dt.Ticks + delta, dt.Kind);
    }
    
    public static DateTime RoundDown(this DateTime dt, TimeSpan d)
    {
        var delta = dt.Ticks % d.Ticks;
        return new DateTime(dt.Ticks - delta, dt.Kind);
    }
    
    public static DateTime RoundToNearest(this DateTime dt, TimeSpan d)
    {
        var delta = dt.Ticks % d.Ticks;
        bool roundUp = delta > d.Ticks / 2;
        var offset = roundUp ? d.Ticks : 0;
    
        return new DateTime(dt.Ticks + offset - delta, dt.Kind);
    }

    用法:

    var date = new DateTime(2010, 02, 05, 10, 35, 25, 450); // 2010/02/05 10:35:25
    var roundedUp = date.RoundUp(TimeSpan.FromMinutes(15)); // 2010/02/05 10:45:00
    var roundedDown = date.RoundDown(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00
    var roundedToNearest = date.RoundToNearest(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00
    

    【讨论】:

    • 我确信这会比使用乘法和除法更快,但我的测试表明并非如此。这超过 10000000 次迭代,模数方法在我的机器上花费了大约 610 毫秒,而 mult/div 方法花费了大约 500 毫秒。我猜 FPU 使老的问题不再是问题。这是我的测试代码:pastie.org/8610460
    • 很好地使用扩展。谢谢!
    • @Alovchin 谢谢。我已经更新了答案。我用您的代码创建了这个 ideone 以显示差异:ideone.com/EVKFp5
    • 这已经很老了,但是RoundUp 中的最后一个%d.Ticks 有必要吗? d.Ticks - (dt.Ticks % d.Ticks)) 必然小于d.Ticks,所以答案应该一样对吗?
    • 只是指出,模数是需要在 CPU 上进行除法运算的。但我同意使用整数除法的向下属性更优雅。
    【解决方案3】:

    如果您需要四舍五入到最近的时间间隔(不是向上) 那么我建议使用以下

        static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
        {
            int f=0;
            double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
            if (m >= 0.5)
                f=1;            
            return new DateTime(((dt.Ticks/ d.Ticks)+f) * d.Ticks);
        }
    

    【讨论】:

    • 这个答案不正确。 user1978424 有唯一一篇正确显示如何四舍五入到以下最近间隔的帖子:(具有讽刺意味的是,由于问题是向上四舍五入,所以投了反对票)
    【解决方案4】:
    void Main()
    {
        var date1 = new DateTime(2011, 8, 11, 16, 59, 00);
        date1.Round15().Dump();
    
        var date2 = new DateTime(2011, 8, 11, 17, 00, 02);
        date2.Round15().Dump();
    
        var date3 = new DateTime(2011, 8, 11, 17, 01, 23);
        date3.Round15().Dump();
    
        var date4 = new DateTime(2011, 8, 11, 17, 00, 00);
        date4.Round15().Dump();
    }
    
    public static class Extentions
    {
        public static DateTime Round15(this DateTime value)
        {   
            var ticksIn15Mins = TimeSpan.FromMinutes(15).Ticks;
    
            return (value.Ticks % ticksIn15Mins == 0) ? value : new DateTime((value.Ticks / ticksIn15Mins + 1) * ticksIn15Mins);
        }
    }
    

    结果:

    8/11/2011 5:00:00 PM
    8/11/2011 5:15:00 PM
    8/11/2011 5:15:00 PM
    8/11/2011 5:00:00 PM
    

    【讨论】:

    • 2011-08-11 17:00:01 被截断为2011-08-11 17:00:00
    • @JYelton:感谢您指出 +1。我更改了我的代码以适应它。
    • 提供您的代码 Linqpad 格式以便于验证可以节省大量时间。非常容易使用。
    【解决方案5】:

    由于我讨厌重新发明轮子,我可能会按照这个算法将 DateTime 值四舍五入到指定的时间增量(时间跨度):

    • 将要四舍五入的DateTime 值转换为代表TimeSpan 单位的整数和小数的十进制浮点值。
    • 使用Math.Round()将其四舍五入为整数。
    • 通过将舍入整数乘以TimeSpan 单位中的刻度数来缩小刻度。
    • 从四舍五入的刻度数中实例化一个新的DateTime 值并将其返回给调用者。

    代码如下:

    public static class DateTimeExtensions
    {
    
        public static DateTime Round( this DateTime value , TimeSpan unit )
        {
            return Round( value , unit , default(MidpointRounding) ) ;
        }
    
        public static DateTime Round( this DateTime value , TimeSpan unit , MidpointRounding style )
        {
            if ( unit <= TimeSpan.Zero ) throw new ArgumentOutOfRangeException("unit" , "value must be positive") ;
    
            Decimal  units        = (decimal) value.Ticks / (decimal) unit.Ticks ;
            Decimal  roundedUnits = Math.Round( units , style ) ;
            long     roundedTicks = (long) roundedUnits * unit.Ticks ;
            DateTime instance     = new DateTime( roundedTicks ) ;
    
            return instance ;
        }
    
    }
    

    【讨论】:

    • 这是用于舍入到 最近 DateTime 的好代码,但我还希望能够将 up 舍入为 @987654328 的倍数@。将MidpointRounding.AwayFromZero 传递给Round 并没有达到预期的效果。通过接受MidpointRounding 参数,您是否还有其他想法?
    【解决方案6】:

    我的版本

    DateTime newDateTimeObject = oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
    

    作为一种方法,它会像这样锁定

    public static DateTime GetNextQuarterHour(DateTime oldDateTimeObject)
    {
        return oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
    }
    

    这样称呼

    DateTime thisIsNow = DateTime.Now;
    DateTime nextQuarterHour = GetNextQuarterHour(thisIsNow);
    

    【讨论】:

    • 这不算秒
    【解决方案7】:

    注意:以上公式不正确,即:

    DateTime RoundUp(DateTime dt, TimeSpan d)
    {
        return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
    }
    

    应该改写为:

    DateTime RoundUp(DateTime dt, TimeSpan d)
    {
        return new DateTime(((dt.Ticks + d.Ticks/2) / d.Ticks) * d.Ticks);
    }
    

    【讨论】:

    • 我不同意。由于整数除法/ d.Ticks 向下舍入到最接近的 15 分钟间隔(我们称之为“块”),因此仅添加半块并不能保证向上舍入。考虑当你有 4.25 个块时。如果你添加 0.5 个块,然后测试你有多少个整数块,你仍然只有 4 个。比一个完整块添加一个刻度是正确的操作。它确保您始终向上移动到下一个块范围(向下舍入之前),但阻止您在精确块之间移动。 (IE,如果你将一个完整的块添加到 4.0 块中,5.0 会四舍五入到 5,当你想要 4。4.99 将是 4。)
    【解决方案8】:

    一个更详细的解决方案,它使用模数并避免不必要的计算。

    public static class DateTimeExtensions
    {
        public static DateTime RoundUp(this DateTime dt, TimeSpan ts)
        {
            return Round(dt, ts, true);
        }
    
        public static DateTime RoundDown(this DateTime dt, TimeSpan ts)
        {
            return Round(dt, ts, false);
        }
    
        private static DateTime Round(DateTime dt, TimeSpan ts, bool up)
        {
            var remainder = dt.Ticks % ts.Ticks;
            if (remainder == 0)
            {
                return dt;
            }
    
            long delta;
            if (up)
            {
                delta = ts.Ticks - remainder;
            }
            else
            {
                delta = -remainder;
            }
    
            return dt.AddTicks(delta);
        }
    }
    

    【讨论】:

      【解决方案9】:

      我见过很多有用的实现,比如来自@dtb 或@redent84 的实现。 由于性能差异可以忽略不计,我远离位移,只是创建了可读的代码。我经常在我的实用程序库中使用这些扩展。

      public static class DateTimeExtensions
      {
          public static DateTime RoundToTicks(this DateTime target, long ticks) => new DateTime((target.Ticks + ticks / 2) / ticks * ticks, target.Kind);
          public static DateTime RoundUpToTicks(this DateTime target, long ticks) => new DateTime((target.Ticks + ticks - 1) / ticks * ticks, target.Kind);
          public static DateTime RoundDownToTicks(this DateTime target, long ticks) => new DateTime(target.Ticks / ticks * ticks, target.Kind);
      
          public static DateTime Round(this DateTime target, TimeSpan round) => RoundToTicks(target, round.Ticks);
          public static DateTime RoundUp(this DateTime target, TimeSpan round) => RoundUpToTicks(target, round.Ticks);
          public static DateTime RoundDown(this DateTime target, TimeSpan round) => RoundDownToTicks(target, round.Ticks);
      
          public static DateTime RoundToMinutes(this DateTime target, int minutes = 1) => RoundToTicks(target, minutes * TimeSpan.TicksPerMinute);
          public static DateTime RoundUpToMinutes(this DateTime target, int minutes = 1) => RoundUpToTicks(target, minutes * TimeSpan.TicksPerMinute);
          public static DateTime RoundDownToMinutes(this DateTime target, int minutes = 1) => RoundDownToTicks(target, minutes * TimeSpan.TicksPerMinute);
      
          public static DateTime RoundToHours(this DateTime target, int hours = 1) => RoundToTicks(target, hours * TimeSpan.TicksPerHour);
          public static DateTime RoundUpToHours(this DateTime target, int hours = 1) => RoundUpToTicks(target, hours * TimeSpan.TicksPerHour);
          public static DateTime RoundDownToHours(this DateTime target, int hours = 1) => RoundDownToTicks(target, hours * TimeSpan.TicksPerHour);
      
      
          public static DateTime RoundToDays(this DateTime target, int days = 1) => RoundToTicks(target, days * TimeSpan.TicksPerDay);
          public static DateTime RoundUpToDays(this DateTime target, int days = 1) => RoundUpToTicks(target, days * TimeSpan.TicksPerDay);
          public static DateTime RoundDownToDays(this DateTime target, int days = 1) => RoundDownToTicks(target, days * TimeSpan.TicksPerDay);
      }
      

      【讨论】:

        【解决方案10】:

        优雅?

        dt.AddSeconds(900 - (x.Minute * 60 + x.Second) % 900)
        

        【讨论】:

        • 更正确的版本是:x.AddSeconds(900 - (x.AddSeconds(-1).Minute * 60 + x.AddSeconds(-1).Second) % 900).AddSeconds( -1),负责处理“停留”条件。
        【解决方案11】:

        这是一个四舍五入到最接近的 1 分钟的简单解决方案。它保留了 DateTime 的 TimeZone 和 Kind 信息。可以进一步修改以适应您自己的需要(如果您需要四舍五入到最接近的 5 分钟等)。

        DateTime dbNowExact = DateTime.Now;
        DateTime dbNowRound1 = (dbNowExact.Millisecond == 0 ? dbNowExact : dbNowExact.AddMilliseconds(1000 - dbNowExact.Millisecond));
        DateTime dbNowRound2 = (dbNowRound1.Second == 0 ? dbNowRound1 : dbNowRound1.AddSeconds(60 - dbNowRound1.Second));
        DateTime dbNow = dbNowRound2;
        

        【讨论】:

          【解决方案12】:

          您可以使用此方法,它使用指定的日期来确保它保持之前在 datetime 对象中指定的任何全球化和日期时间类型。

          const long LNG_OneMinuteInTicks = 600000000;
          /// <summary>
          /// Round the datetime to the nearest minute
          /// </summary>
          /// <param name = "dateTime"></param>
          /// <param name = "numberMinutes">The number minute use to round the time to</param>
          /// <returns></returns>        
          public static DateTime Round(DateTime dateTime, int numberMinutes = 1)
          {
              long roundedMinutesInTicks = LNG_OneMinuteInTicks * numberMinutes;
              long remainderTicks = dateTime.Ticks % roundedMinutesInTicks;
              if (remainderTicks < roundedMinutesInTicks / 2)
              {
                  // round down
                  return dateTime.AddTicks(-remainderTicks);
              }
          
              // round up
              return dateTime.AddTicks(roundedMinutesInTicks - remainderTicks);
          }
          

          .Net Fiddle Test

          如果你想使用TimeSpan进行四舍五入,你可以使用这个。

          /// <summary>
          /// Round the datetime
          /// </summary>
          /// <example>Round(dt, TimeSpan.FromMinutes(5)); => round the time to the nearest 5 minutes.</example>
          /// <param name = "dateTime"></param>
          /// <param name = "roundBy">The time use to round the time to</param>
          /// <returns></returns>        
          public static DateTime Round(DateTime dateTime, TimeSpan roundBy)
          {            
              long remainderTicks = dateTime.Ticks % roundBy.Ticks;
              if (remainderTicks < roundBy.Ticks / 2)
              {
                  // round down
                  return dateTime.AddTicks(-remainderTicks);
              }
          
              // round up
              return dateTime.AddTicks(roundBy.Ticks - remainderTicks);
          }
          

          TimeSpan Fiddle

          【讨论】:

          • 如果你想四舍五入到最接近的第 7 分钟 var d = new DateTime(2019, 04, 15, 9, 40, 0, 0); // 应该是 9:42 但这些方法都不是这样工作的,会发生什么?
          • Edit 看起来@soulflyman 的回答会产生正确的结果
          【解决方案13】:

          Ramon Smits 的解决方案,使用 DateTime.MaxValue 检查:

          DateTime RoundUp(DateTime dt, TimeSpan d) =>
              dt switch
              {
                  var max when max.Equals(DateTime.MaxValue) => max,
                  var v => new DateTime((v.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, v.Kind)
              };
          

          【讨论】:

            【解决方案14】:

            我的 DateTimeOffset 版本,基于 Ramon 的回答:

            public static class DateExtensions
            {
                public static DateTimeOffset RoundUp(this DateTimeOffset dt, TimeSpan d)
                {
                    return new DateTimeOffset((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Offset);
                }
            }
            

            【讨论】:

              【解决方案15】:

              你可以试试这个:

              string[] parts = ((DateTime)date_time.ToString("HH:mm:ss").Split(':');
              int hr = Convert.ToInt32(parts[0]);
              int mn = Convert.ToInt32(parts[1]);
              int sec2min = (int)Math.Round(Convert.ToDouble(parts[2]) / 60.0, 0);
              
              string adjTime = string.Format("1900-01-01 {0:00}:{1:00}:00.000",
              (mn + sec2min > 59 ? (hr + 1 > 23 ? 0 : hr + 1) : hr),
               mn + sec2min > 59 ? 60 - mn + sec2min : mn + sec2min);
              

              每个部分(hr,min)必须递增并调整为适当的溢出值,例如59 min> 00然后将hr加1,如果是23,hr变为00。 前任。 07:34:57 舍入为 07:35,09:59:45 舍入为 10:00,23:59:45 舍入为次日的 00:00。

              【讨论】:

              • 这里可能发错了,这是关于将时间的秒部分四舍五入到分钟部分。
              猜你喜欢
              • 2011-01-22
              • 2022-01-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-12-02
              • 1970-01-01
              相关资源
              最近更新 更多