【问题标题】:How to round time to the nearest quarter hour in java?java - 如何在java中将时间四舍五入到最近的一刻钟?
【发布时间】:2011-04-03 00:13:44
【问题描述】:

鉴于今天的时间,例如下午 2 点 24 分,我如何让它舍入到下午 2 点 30 分?

如果时间是下午 2 点 17 分,我该如何舍入到下午 2 点 15 分?

【问题讨论】:

    标签: java datetime time java-time


    【解决方案1】:

    四舍五入

    您需要使用模来截断一刻钟:

    Date whateverDateYouWant = new Date();
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(whateverDateYouWant);
    
    int unroundedMinutes = calendar.get(Calendar.MINUTE);
    int mod = unroundedMinutes % 15;
    calendar.add(Calendar.MINUTE, mod < 8 ? -mod : (15-mod));
    

    正如EJP所指出的,这也可以(替换最后一行,仅当日历为lenient时有效):

    calendar.set(Calendar.MINUTE, unroundedMinutes + mod);
    

    改进

    如果您想准确,您还必须截断较小的字段:

    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    

    您也可以使用Apache Commons / Lang 中的DateUtils.truncate() 来执行此操作:

    calendar = DateUtils.truncate(calendar, Calendar.MINUTE);
    

    【讨论】:

    • 呃,我只是从日历类构造它
    • 此方法会将分钟降至最接近的四分之一,您需要添加一些检查是否将其四舍五入到下一个小时,以便您也需要更新小时...
    • 当前版本修复了这些问题
    • (unroundedMinutes+8) % 15 比较简单,只要设置,不要加。
    • @EJP 你是对的,但我认为在语义层面上将时间设置为10:60 是一件糟糕的事情,即使我知道日历类足够聪明,可以将它变成@ 987654329@
    【解决方案2】:

    有了上面的答案,你最终会得到各种有趣的代码来处理数小时、数天等的溢出。

    我会使用自纪元以来的毫秒数。

    加 7.5 分钟或 7.5x60x1000 = 450000

    并截断为 900000 的倍数

    new Date(900000 * ((date.getTime() + 450000) / 900000))
    

    这是可行的,因为 ms 时间开始的时间恰好是 00:00:00。而且由于世界上的所有时区都以 15 分钟为单位变化,这不会影响四舍五入。

    (哎呀,我的 0 太多了,忘记了一些重要的括号:现在还为时过早)

    【讨论】:

    • 蛮力逼近,错过了闰秒之类的东西。虽然我同意必须有很多闰秒才能有所作为。
    • 你说的很对,我没有考虑闰秒。但是上次我检查时,java.util.Date 中不支持闰秒,因此人们会将他们的计算机时钟设置为与挂钟(或原子钟)匹配,并且 JVM 会计算出错误的纪元开始(关闭闰秒数)。所以在实践中这会起作用并且会节省一堆棘手的代码,这些代码也必须处理闰秒。结论:使用 ntp 使您的计算机时钟保持同步。
    • “世界上的所有时区都以 30 分钟的步长变化” - 并非所有时区都是如此。有些时区使用 15 分钟的增量。
    • @BT 我的意思不是听起来好争论。我只是在教育自己关于日期时间这个令人困惑的话题。您的链接指向一篇有趣的文章,谢谢。
    • 我不是那样想的,我也不是故意这样说的。我完全忘记了导致我写原始评论的信息
    【解决方案3】:

    如果你有分钟,你可以用以下函数将它们四舍五入:
    int minutes = i % 15 &lt; 8 ? i / 15 * 15 : (i / 15 + 1) * 15;

    【讨论】:

    • 为什么这么复杂?当您想要的是整数结果时,为什么还要除以浮点值?
    【解决方案4】:

    你可以使用这个简单的代码...

    int mode = min % 15;
    if (mode > 15 / 2) {
        min = 15 - mode;
    } else {
        min = 0 - mode;
    }
    cal.add(Calendar.MINUTE, min);
    

    【讨论】:

    • 感谢 8 来自 (15/2) 的提示。帮助我实现了其他四舍五入的版本,例如 5 分钟等等!
    • 简洁优雅的sn-p。作为对先前解决方案的改进,但缺少上下文(mincal 的定义位置)和解释。
    【解决方案5】:

    也许您可以使用实用程序库来处理日期,例如,您有一个对您有用的 round 方法:

    http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/time/DateUtils.html#round%28java.util.Calendar,%20int%29

    这里是代码示例:

        FastDateFormat formatter = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT;
    
        Date now = new Date();
        System.out.println("now = " + formatter.format(now));       
    
        // Get nearest second
        Date nearestSecond = DateUtils.round(now, Calendar.SECOND);
        System.out.println("nearestSecond = " + formatter.format(nearestSecond));
    
        // Get nearest minute
        Date nearestMinute = DateUtils.round(now, Calendar.MINUTE);
        System.out.println("nearestMinute = " + formatter.format(nearestMinute));
    
        // Get nearest hour
        Date nearestHour   = DateUtils.round(now, Calendar.HOUR);
        System.out.println("nearestHour = " + formatter.format(nearestHour));
    

    【讨论】:

    • 我知道这些,但我认为这里没有 15 分钟间隔的开箱即用解决方案
    【解决方案6】:

    很简单,求自 1970 年以来的四分位数的两倍,四舍五入并乘以 15 分钟:

    long timeMs = System.System.currentTimeMillis();
    
    long roundedtimeMs = Math.round( (double)( (double)timeMs/(double)(15*60*1000) ) ) * (15*60*1000) );
    

    用它设置你的 Date 或 Calendar 对象。

    【讨论】:

    • 括号不匹配。我认为代码应该是:long timeMs = System.currentTimeMillis();long roundedtimeMs = Math.round( (double)timeMs/(15*60*1000) ) * (15*60*1000);
    【解决方案7】:
    minutes = (int) (Math.round(minutes / 15.0) * 15.0);
    

    【讨论】:

      【解决方案8】:

      精彩的帖子,非常感谢你们!这正是我所需要的:)

      这是我基于 jour 工作的代码。

      我的用例是“鉴于现在是上午 11:47,我想设置两个日期来表示当前的 5 分钟帧:上午 11:45 和上午 11:50”

                  Calendar calendar = Calendar.getInstance();
                  calendar.set(Calendar.SECOND, 0);
                  calendar.set(Calendar.MILLISECOND, 0);
      
                  int modulo = calendar.get(Calendar.MINUTE) % 5;
                  if(modulo > 0) {
      
                      calendar.add(Calendar.MINUTE, -modulo);
                  }
      
                  myObject.setStartDate(calendar.getTime());
      
                  calendar.add(Calendar.MINUTE, 5);
                  myObject.setDueDate(calendar.getTime()); 
      

      【讨论】:

      • 帮助我得到了一个简洁的代码@Iboix。小的调整,我得到了我需要的日期时间 obj。谢谢
      【解决方案9】:

      Java 8 的注释实现。接受任意舍入单位和增量:

       public static ZonedDateTime round(ZonedDateTime input, TemporalField roundTo, int roundIncrement) {
          /* Extract the field being rounded. */
          int field = input.get(roundTo);
      
          /* Distance from previous floor. */
          int r = field % roundIncrement;
      
          /* Find floor and ceiling. Truncate values to base unit of field. */
          ZonedDateTime ceiling = 
              input.plus(roundIncrement - r, roundTo.getBaseUnit())
              .truncatedTo(roundTo.getBaseUnit());
      
          ZonedDateTime floor = 
              input.plus(-r, roundTo.getBaseUnit())
              .truncatedTo(roundTo.getBaseUnit());
      
          /*
           * Do a half-up rounding.
           * 
           * If (input - floor) < (ceiling - input) 
           * (i.e. floor is closer to input than ceiling)
           *  then return floor, otherwise return ceiling.
           */
          return Duration.between(floor, input).compareTo(Duration.between(input, ceiling)) < 0 ? floor : ceiling;
        }
      

      来源:我自己

      【讨论】:

      • 语义写得很好:变量和计算很清楚? 半价的替代方案:return r &lt; roundIncrement/2 ? floor : ceiling 其中roundIncrement/2 是阈值,即一半
      【解决方案10】:

      使用我在 Stackoverflow 上找到的一些代码,我创建了以下代码。它将每分钟输出四舍五入的季度。

      import java.time.LocalDateTime;
      import java.time.format.DateTimeFormatter;
      
      DateTimeFormatter Datum_Format = DateTimeFormatter.ofPattern("HH:mm");
      
      LocalDateTime time = LocalDateTime.now();
      for(int i=0; i<=59; i++) {
        time = time.withMinute(i);
        int Minute = time.getMinute();
        int Quarter = 15 * (int) Math.round(Minute / 15);
        if (Quarter == 60) { 
          Time2 = time.plusHours(1);
          Time2 = Time2.withMinute(0); 
          LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
        }
        else {
          Time2 = time; 
          Time2 = Time2.withMinute(Quarter); 
          LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
        }
      }
      

      当我将代码输出到控制台时,您必须将 LOG.info 替换为 System.out.println 之类的内容。

      结果:

      2016-08-16 15:14:31 INFO 15:05,15:00
      2016-08-16 15:14:31 INFO 15:06,15:00
      2016-08-16 15:14:31 INFO 15:07,15:00
      2016-08-16 15:14:31 INFO 15:08,15:15
      2016-08-16 15:14:31 INFO 15:09,15:15
      2016-08-16 15:14:31 信息 15:10,15:15

      【讨论】:

      • 请描述为什么这个答案比其他答案更好,或者它有什么不同以及为什么
      【解决方案11】:

      如果您只想四舍五入,这是一个使用 Java Time API 的更易读的版本:

      LocalDateTime time = LocalDateTime.now();
      LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS)
                                      .plusMinutes(15 * (time.getMinute() / 15));
      

      输出:

      2016-11-04T10:58:10.228

      2016-11-04T10:45:00

      【讨论】:

      • 顺便说一下,LocalDateTime 对于时间轴上的一个点而言,是实际时刻的错误类别。由于缺乏任何区域/偏移概念,它们是不确定的。使用相同的逻辑,但使用ZonedDateTime.now()
      • 顺便说一句,如果你想要一半,它可能是 (15 * (time.getMinute() + 7.5) / 15)。
      【解决方案12】:
      public static Date getCurrentDateTimeWithQuarterRounding() {
          final Calendar calendar = new GregorianCalendar();
          calendar.setTime(new Date());
          calendar.set(Calendar.MILLISECOND, 0);
          calendar.set(Calendar.SECOND, 0);
          final int minutes = calendar.get(Calendar.MINUTE);
      
          if (minutes < 15) {
              calendar.set(Calendar.MINUTE, 0);
          } else if (minutes >= 45) {
              calendar.set(Calendar.MINUTE, 45);
          } else if (minutes < 30) {
              calendar.set(Calendar.MINUTE, 15);
          } else {
              calendar.set(Calendar.MINUTE, 30);
          }
      
          return calendar.getTime();
      }
      

      【讨论】:

        【解决方案13】:

        另一种使用 java Instant api 的替代方法。

        Instant instant =  Instant.now();
        int intervalInMinutes = 10;
        instant.truncatedTo(ChronoUnit.MINUTES).minus(instant.atZone(ZoneId.of("UTC")).getMinute() % (1* intervalInMinutes),ChronoUnit.MINUTES);
        

        【讨论】:

          【解决方案14】:

          使用以下函数将分钟数四舍五入到上一季度getRecentQuater():DategetSysDate_LastQuarterMins("dd.MM.yyyy HH:mm:ss"):StringConverting LocalDateTime to Date

          public static Date getRecentQuater() {
              LocalDateTime time = LocalDateTime.now();
              LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS).plusMinutes(getLastQuarterValue(time.getMinute()));
              System.out.println("lastQuarter LocalDateTime: " + lastQuarter);
          
              Date date = Date.from(lastQuarter.atZone(ZoneId.systemDefault()).toInstant());
              System.out.println("lastQuarter Date: " + lastQuarter);
              return date;
          }
          public static String getSysDate_LastQuarterMins(String dateFormat) {
              Date date = getRecentQuater();
              SimpleDateFormat ft = new SimpleDateFormat (dateFormat);
              String sysDate_RoundMin = ft.format(date);
              System.out.println("getSysDate_LastQuarterMins() LocalDateTime : "+sysDate_RoundMin);
              return sysDate_RoundMin;
          }
          

          getSysDate_LastQuarterMins() : Mon Jan 20 17:30:00 CET 2020

          public static Date getSysDate_LastQuarterMins() {
              Calendar cal = Calendar.getInstance();
              cal.setTime( new Date(System.currentTimeMillis()) );
          
              int min = cal.get(Calendar.MINUTE);
          
              cal.set(Calendar.MINUTE, getLastQuarterValue(min));
              cal.set(Calendar.SECOND, 00);
              Date lastQuarter = cal.getTime();
              System.out.println("getSysDate_LastQuarterMins() Calendar : "+lastQuarter);
              return lastQuarter;
          }
          

          您可以从以下函数中找到 LastQuarter Value Round 值,函数调用diaplayLastQuarter_RoundValue(min) 提供了一些输出:

          Min: 10, LastQuarter:  0, Round: 15
          Min: 24, LastQuarter: 15, Round: 30
          Min: 36, LastQuarter: 30, Round: 30
          Min: 37, LastQuarter: 30, Round: 30
          Min: 38, LastQuarter: 30, Round: 45
          Min: 39, LastQuarter: 30, Round: 45
          Min: 44, LastQuarter: 30, Round: 45
          Min: 57, LastQuarter: 45, Round: 00 [57, 07:45:00, 08:00:00]
          
          public static void diaplayLastQuarter_RoundValue(int minutes) {
              System.out.format("Min: %2d, LastQuarter: %2d, Round: %2d\n",
                      minutes, getLastQuarterValue(minutes), getRoundValue(minutes));
          }
          public static int getLastQuarterValue(int minutes) {
              int min = 15 * (minutes / 15);
              //System.out.println("Min: "+minutes+", getLastQuarterValue : "+ min);
              return min;
          }
          public static int getRoundValue(int minutes) {
              getLastQuarterValue(minutes);
              int minRound = (int) (Math.round(minutes / 15.0) * 15.0);
              //System.out.println("Min: "+minutes+", getRoundValue : "+minRound);
              return minRound;
          }
          

          【讨论】:

            【解决方案15】:

            如果您需要将时间四舍五入到以Duration 提供的最接近的任意级别:

            static long truncateTo(long timeEpochMillis, Duration d) {
                long x = timeEpochMillis / d.toMillis();
                return x * d.toMillis();
            }
            

            【讨论】:

              【解决方案16】:

              java.time

              我建议您使用modern date-time API*

              import java.time.LocalDate;
              import java.time.LocalTime;
              import java.time.ZoneId;
              import java.time.ZonedDateTime;
              import java.time.temporal.ChronoUnit;
              import java.util.stream.Stream;
              
              public class Main {
                  public static void main(String[] args) {
                      
                      // Change it to the applicable ZoneId e.g. ZoneId.of("Asia/Kolkata")
                      ZoneId zoneId = ZoneId.systemDefault();
                      
                      Stream.of(
                                      "10:00", 
                                      "10:05", 
                                      "10:10", 
                                      "10:15", 
                                      "10:20", 
                                      "10:25", 
                                      "10:30"
                          ).forEach(t -> System.out.println(roundToNearestQuarter(t, zoneId)));
                  }
              
                  static ZonedDateTime roundToNearestQuarter(String strTime, ZoneId zoneId) {
                      LocalTime time = LocalTime.parse(strTime);
                      return LocalDate.now()
                                      .atTime(time)
                                      .atZone(zoneId)
                                      .truncatedTo(ChronoUnit.HOURS)
                                      .plusMinutes(15 * Math.round(time.getMinute() / 15.0));
                  }
              }
              

              输出:

              2021-04-02T10:00+01:00[Europe/London]
              2021-04-02T10:00+01:00[Europe/London]
              2021-04-02T10:15+01:00[Europe/London]
              2021-04-02T10:15+01:00[Europe/London]
              2021-04-02T10:15+01:00[Europe/London]
              2021-04-02T10:30+01:00[Europe/London]
              2021-04-02T10:30+01:00[Europe/London]
              

              如果您正在寻找时间,请使用ZonedDateTime#toLocalTime从获得的ZonedDateTime中获取LocalTime

              Trail: Date Time 了解有关现代日期时间 API 的更多信息。


              * java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到现代日期时间 API。出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。如果您正在为Android 项目和您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

              【讨论】:

                【解决方案17】:

                如果有人有兴趣获得最近的(向上或向下)五或十五间隔,我使用模块创建了一个函数来完成这项工作。

                public LocalTime roundToTheNearestInterval(LocalTime original, Integer measurementInterval) {
                        LocalTime nearest;
                        int mod;
                        switch (measurementInterval) {
                            case 5:
                                mod = original.getMinute() % 5;
                                nearest = mod >= 3 ?
                                        original.truncatedTo(ChronoUnit.HOURS)
                                                .plusMinutes((long) 5 * (original.getMinute() / 5) + 5) :
                                        original.truncatedTo(ChronoUnit.HOURS)
                                                .plusMinutes((long) 5 * (original.getMinute() / 5));
                                break;
                            case 15:
                                mod = original.getMinute() % 15;
                                nearest = mod >= 8 ?
                                        original.truncatedTo(ChronoUnit.HOURS)
                                                .plusMinutes((long) 15 * (original.getMinute() / 15) + 15) :
                                        original.truncatedTo(ChronoUnit.HOURS)
                                                .plusMinutes((long) 15 * (original.getMinute() / 15));
                                break;
                            default:
                                nearest = original;
                        }
                        return nearest;
                    }
                

                你可以试试这个单元测试

                 @Test
                    void roundToTheNearestInterval() {
                        //given
                        LocalTime originalTime1 = LocalTime.of(6, 31, 15);
                        LocalTime originalTime2 = LocalTime.of(19, 13, 42);
                        LocalTime originalTime3 = LocalTime.of(6, 37, 11);
                        LocalTime originalTime4 = LocalTime.of(19, 40, 34);
                        Integer measurementInterval_5min = 5;
                        Integer measurementInterval_15min = 15;
                
                        MyService myService = new MyService();
                
                        //when
                        LocalTime rounded1_5min = myService.roundToTheNearestInterval(originalTime1, measurementInterval_5min);
                        LocalTime rounded2_5min = myService.roundToTheNearestInterval(originalTime2, measurementInterval_5min);
                
                        LocalTime rounded1_15min = myService.roundToTheNearestInterval(originalTime3, measurementInterval_15min);
                        LocalTime rounded2_15min = myService.roundToTheNearestInterval(originalTime4, measurementInterval_15min);
                
                        //then
                        assertEquals(LocalTime.of(6, 30, 0), rounded1_5min);
                        assertEquals(LocalTime.of(19, 15, 0), rounded2_5min);
                        assertEquals(LocalTime.of(6, 30, 0), rounded1_15min);
                        assertEquals(LocalTime.of(19, 45, 0), rounded2_15min);
                
                    }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2020-02-07
                  • 2011-06-25
                  • 2015-11-27
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-10-25
                  相关资源
                  最近更新 更多