【问题标题】:Calculate elapsed time in Java / Groovy在 Java / Groovy 中计算经过的时间
【发布时间】:2009-02-19 23:03:39
【问题描述】:

我有……


Date start = new Date()

...
...
...

Date stop = new Date()

我想获取这两个日期之间经过的年、月、日、小时、分钟和秒。

--

我会细化这个问题。

我只想得到经过的时间,作为一个绝对的衡量标准,即不考虑闰年、每个月的天数等。

因此,我认为不可能得到过去的年月,我能得到的只有天、小时、分钟和秒。

更具体地说,我想说明某项任务持续了例如

20 sec
13 min, 4 sec
2 h, 10 min, 2 sec
4 d, 4 h, 2 min, 2 sec

所以请原谅我不够精确。

【问题讨论】:

    标签: java date groovy


    【解决方案1】:

    我刚刚发现了这个源自 Groovy 的快速解决方案:

    import groovy.time.TimeCategory 
    import groovy.time.TimeDuration
    
    Date start = new Date()
    
    // do something here
    
    Date stop = new Date()
    
    TimeDuration td = TimeCategory.minus( stop, start )
    println td
    

    【讨论】:

    • 你不只是喜欢groovy吗?不需要一页代码来做一些简单的事情
    • @Booyeoo:或者更具体地说:import groovy.time.TimeCategory import groovy.time.TimeDuration@Patrick:只要你问:是的,我喜欢 Groovy。
    • 同时,在 Java 中有 13 种不同的方法可以做到这一点,而且都很糟糕。
    • startstopnew Date() 对象,任何人都想知道...
    【解决方案2】:
    import groovy.time.TimeCategory
    import groovy.time.TimeDuration
    
    time = { closure ->
        use(TimeCategory) {
            def started = new Date()
            def res = closure()
            TimeDuration duration = new Date() - started
            logger.info("Execution duration: " + duration.toMilliseconds() + "ms")
            res
        }
    }
    
    time {
        println 'A realy heavy operation here that you want to measure haha'
    }
    

    【讨论】:

      【解决方案3】:

      tl;博士

      Duration.between( then , Instant.now() )
      

      使用 java.time

      现代方式使用 java.time 类来代替麻烦的旧日期时间类。

      使用Instant 代替DateInstant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

      Instant then = Instant.now();
      …
      Instant now = Instant.now();
      

      Duration 类用于与时间线无关的时间跨度,分辨率为天-小时-分钟-秒-纳米。

      Duration d = Duration.between( then , now );
      

      对于分辨率为年-月-日的时间跨度,请使用Period 类。

      生成字符串是标准的ISO 8601 格式for durations:PnYnMnDTnHnMnSP 标志着开始。 T 将任何年-月-日与小时-分钟-秒分开。所以两个半小时是PT2H30M

      String output = d.toString();
      

      在 Java 9 及更高版本中,您可以使用 toDaysParttoHoursPart 等方法访问各个部分。

      在 Java 9 和更高版本中也是对 Instant.now 的改进,它在 microseconds 中捕获当前时刻,而不是在 Java 8 中看到的 milliseconds。当然,在所有版本的 Java 中,Instant 类都可以保持 nanoseconds 中的一个值。


      关于java.time

      java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

      Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

      要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

      从哪里获得 java.time 类?

      ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

      【讨论】:

        【解决方案4】:

        您可以通过除法和 mod 完成所有这些操作。

        long l1 = start.getTime();
        long l2 = stop.getTime();
        long diff = l2 - l1;
        
        long secondInMillis = 1000;
        long minuteInMillis = secondInMillis * 60;
        long hourInMillis = minuteInMillis * 60;
        long dayInMillis = hourInMillis * 24;
        
        long elapsedDays = diff / dayInMillis;
        diff = diff % dayInMillis;
        long elapsedHours = diff / hourInMillis;
        diff = diff % hourInMillis;
        long elapsedMinutes = diff / minuteInMillis;
        diff = diff % minuteInMillis;
        long elapsedSeconds = diff / secondInMillis;
        

        这应该会给你所有你要求的信息。

        编辑:由于人们似乎很困惑,不,这没有考虑闰年或夏令时切换等因素。这是经过的时间,这是 opensas 所要求的。

        【讨论】:

        • 没关系。考虑到差异,他并没有试图找出日期。他正试图找出两个日期的差异。第一个更难做到。第二个很简单。两个时间戳之间是否存在闰年。
        • 这取决于你如何看待它。如果 1 年等于 365 天,那么是的。再次阅读问题。 opensas 非常清楚,他想要一个绝对的时间度量,忽略诸如闰年之类的事情。因此,我相信这是上述问题的解决方案。
        • 闰年没那么简单。您至少应该提出警告,以免误导人们。
        • @starblue 我认为这从答案中显而易见,但我还是继续澄清了它。
        • +1 用于解决我认为绝大多数人的现实问题的解决方案,并且注意事项足够清楚
        【解决方案5】:

        我通常这样做:

        def start_time = System.currentTimeMillis()
        ...
        def end_time = System.currentTimeMillis()
        
        println  (end_time - start_time) +' ms'
        

        然后您可以使用 Duration Groovy 类 http://docs.groovy-lang.org/latest/html/api/groovy/time/Duration.html 将其分解为您想要的任何时间单位。

        【讨论】:

        • 您贡献了哪些其他答案中没有的内容?如果是链接,最好作为评论发布。
        【解决方案6】:

        使用标准 Date API 并不容易。

        您可能想查看Joda TimeJSR-310

        我不是 Joda 方面的专家,但我认为代码应该是:

        Interval interval = new Interval(d1.getTime(), d2.getTime());
        Period period = interval.toPeriod();
        System.out.printf(
            "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", 
            period.getYears(), period.getMonths(), period.getDays(),
            period.getHours(), period.getMinutes(), period.getSeconds());
        

        【讨论】:

          【解决方案7】:

          关于 JodaTime,我刚刚开始;感谢提出建议的响应者。这是建议的 Joda 代码的更精简版本:

          Period period = new Period(d1.getTime(), d2.getTime());
          System.out.printf(
              "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", 
              period.getYears(), period.getMonths(), period.getDays(),
              period.getHours(), period.getMinutes(), period.getSeconds());
          

          (不确定这是否有助于最初的问题,但肯定是搜索者)。

          【讨论】:

            【解决方案8】:

            从 Java 1.5 开始你应该使用TimeUnit.

            这是一个简单明了的例子。我认为在 groovy 中它可能会变短(一如既往)。

            /**
             * Formats a given {@link Date} to display a since-then timespan.
             * @param created
             * @return String with a format like "3 minutes ago"
             */
            public static String getElapsedTime(Date created) {
                long duration = System.currentTimeMillis() - created.getTime();
                long seconds = TimeUnit.MILLISECONDS.toSeconds(duration);
                long days = TimeUnit.MILLISECONDS.toDays(duration);
                long hours = TimeUnit.MILLISECONDS.toHours(duration);
                long minutes = TimeUnit.MILLISECONDS.toMinutes(duration);
                if (days > 0) {
                    return days + " days";
                }
                if (hours > 0) {
                    return hours + " hrs";
                }
                if (minutes > 0) {
                    return minutes + " minutes";
                }
            
                return seconds + " seconds";
            }
            

            哦,请避免多次退货;)

            【讨论】:

              【解决方案9】:

              其实,关于以上关于Joda-Time的回答。

              使用 Joda-Time 的 Period 类有一种更简单的方法:

              Period period = new Period(startDate, endDate);
              System.out.println(PeriodFormat.getDefault().print(period));
              

              要自定义输出,请查看 PeriodFormatPeriodFormatterPeriodFormatterBuilder 类。

              【讨论】:

              【解决方案10】:

              嗯,如果我明白你的要求,你想知道如果:

              开始 = 2001 年 1 月 1 日上午 10:00:00.000 和

              停止 = 2003 年 1 月 6 日,下午 12:01:00.000

              你想要的答案是 2 年 0 个月 5 天 2 小时 1 分钟

              不幸的是,这是一个似是而非的答案。如果日期是 1 月 2 日和 1 月 31 日呢?那是29天吗?好的,但是 2 月 2 日到 3 月 2 日是 28 (29) 天,但会被列为 1 个月?

              在不知道上下文的情况下,以秒或可能天为单位的时间长度是可变的,因为月和年的长度不同。两个日期之间的差异应该是静态单位,可以很容易地从 stop.getTime() - start.getTime() 计算出来(以毫秒为单位的差异)

              【讨论】:

              • 是的,我明白你的意思了......我猜最多应该是天,小时,分钟,秒......
              • 你也可以做几个星期 - 总是 7 天。
              【解决方案11】:

              除了我推荐推荐的上述出色的JodaTime API,您可以拥有的最佳标准Java 替代方案是java.util.Calendar。使用它麻烦(这是一种轻描淡写的说法......请看这里的单行 JodaTime 示例),但您也可以使用它计算经过的时间。重要的关键是您应该在循环中使用Calendar#add() 来获取年、月和日的经过值,以考虑闰日、年和世纪。您不应该从(毫秒)秒计算它们。

              这是一个基本的例子:

              import java.util.Calendar;
              
              public class Test {
              
                  public static void main(String[] args) throws Exception {
                      Calendar start = Calendar.getInstance();
                      start.set(1978, 2, 26, 12, 35, 0); // Just an example.
                      Calendar end = Calendar.getInstance();
              
                      Integer[] elapsed = new Integer[6];
                      Calendar clone = (Calendar) start.clone(); // Otherwise changes are been reflected.
                      elapsed[0] = elapsed(clone, end, Calendar.YEAR);
                      clone.add(Calendar.YEAR, elapsed[0]);
                      elapsed[1] = elapsed(clone, end, Calendar.MONTH);
                      clone.add(Calendar.MONTH, elapsed[1]);
                      elapsed[2] = elapsed(clone, end, Calendar.DATE);
                      clone.add(Calendar.DATE, elapsed[2]);
                      elapsed[3] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 3600000;
                      clone.add(Calendar.HOUR, elapsed[3]);
                      elapsed[4] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 60000;
                      clone.add(Calendar.MINUTE, elapsed[4]);
                      elapsed[5] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 1000;
              
                      System.out.format("%d years, %d months, %d days, %d hours, %d minutes, %d seconds", elapsed);
                  }
              
                  private static int elapsed(Calendar before, Calendar after, int field) {
                      Calendar clone = (Calendar) before.clone(); // Otherwise changes are been reflected.
                      int elapsed = -1;
                      while (!clone.after(after)) {
                          clone.add(field, 1);
                          elapsed++;
                      }
                      return elapsed;
                  }
              
              }
              

              它应该打印我现在的年龄 =)

              哦,我应该补充一下,您可以使用Calendar#setTime()Date“转换”为Calendar

              【讨论】:

                【解决方案12】:

                这很容易;你应该设置正确的时区

                import java.util.TimeZone;
                import java.util.logging.Logger;
                
                import org.joda.time.DateTimeZone;
                import org.joda.time.format.DateTimeFormat;
                import org.joda.time.format.DateTimeFormatter;
                
                public class ImportData {
                
                    private final static Logger log = Logger.getLogger(ImportData.class.getName());
                    private final static DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS");
                    private final static DateTimeFormatter dtfh = DateTimeFormat.forPattern("HH:mm:ss.SSS");
                
                    /**
                     * @param args the command line arguments
                     */
                    public static void main(String[] args) throws Exception {
                       TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
                       DateTimeZone.setDefault(DateTimeZone.forID("Europe/Berlin"));
                       // Quotes connection=Quotes.getInstance();
                       final long start  = System.currentTimeMillis();
                       // do something here ...
                       // connection.importTickdata();
                       Thread.currentThread().sleep(2000);
                       final long end  = System.currentTimeMillis();
                       log.info("[start]    " + dtf.print(start));
                       log.info("[end]      " + dtf.print(end));
                       TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
                       DateTimeZone.setDefault(DateTimeZone.forID("UTC"));
                       log.info("[duration] " + dtfh.print(end - start));
                       // connection.logOff();
                       // System.exit(0);
                    }
                

                返回:

                10.11.2010 00:08:12 ImportData 主 信息:[开始] 2010-11-10 00:08:10.306 10.11.2010 00:08:12 ImportData 主 信息:[结束] 2010-11-10 00:08:12.318 10.11.2010 00:08:12 ImportData 主 信息:[持续时间] 00:00:02.012

                【讨论】:

                  【解决方案13】:

                  创建 Date 对象没有任何意义,因为它所做的只是包装 System.currentTimeMillis()。 getTime() 函数只是解开 Date 对象。我建议你只使用这个函数来获取一个long。

                  如果您只需要第二精度,这很好。但是,如果您想要亚毫秒精度,请使用 System.nanoTime() 来获取经过时间。

                  【讨论】:

                    【解决方案14】:

                    这是我基于 Sebastian Celis 回答实现的完整功能。再说一次,从他的帖子中 - 这并没有考虑闰年或夏令时切换等因素。这是纯粹的经过时间

                    输出是根据我的需要量身定制的。这仅输出三个重要部分。而不是返回

                    4 个月 2 周 3 天 7 小时 28 分钟 43 秒

                    它只返回前 3 个块 (请参阅帖子末尾的更多示例运行)

                    4 个月,2 周,3 天

                    这里是完整的方法源代码:

                    /**
                     * Format milliseconds to elapsed time format
                     * 
                     * @param time difference in milliseconds
                     * @return Human readable string representation - eg. 2 days, 14 hours, 5 minutes
                     */
                    public static String formatTimeElapsedSinceMillisecond(long milisDiff) {
                        if(milisDiff<1000){ return "0 second";}
                    
                        String formattedTime = "";
                        long secondInMillis = 1000;
                        long minuteInMillis = secondInMillis * 60;
                        long hourInMillis = minuteInMillis * 60;
                        long dayInMillis = hourInMillis * 24;
                        long weekInMillis = dayInMillis * 7;
                        long monthInMillis = dayInMillis * 30;
                    
                        int timeElapsed[] = new int[6];
                        // Define time units - plural cases are handled inside loop
                        String timeElapsedText[] = {"second", "minute", "hour", "day", "week", "month"};
                        timeElapsed[5] = (int) (milisDiff / monthInMillis); // months
                        milisDiff = milisDiff % monthInMillis;
                        timeElapsed[4] = (int) (milisDiff / weekInMillis); // weeks
                        milisDiff = milisDiff % weekInMillis;
                        timeElapsed[3] = (int) (milisDiff / dayInMillis); // days
                        milisDiff = milisDiff % dayInMillis;
                        timeElapsed[2] = (int) (milisDiff / hourInMillis); // hours
                        milisDiff = milisDiff % hourInMillis;
                        timeElapsed[1] = (int) (milisDiff / minuteInMillis); // minutes
                        milisDiff = milisDiff % minuteInMillis;
                        timeElapsed[0] = (int) (milisDiff / secondInMillis); // seconds
                    
                        // Only adds 3 significant high valued units
                        for(int i=(timeElapsed.length-1), j=0; i>=0 && j<3; i--){
                            // loop from high to low time unit
                            if(timeElapsed[i] > 0){
                                formattedTime += ((j>0)? ", " :"") 
                                    + timeElapsed[i] 
                                    + " " + timeElapsedText[i] 
                                    + ( (timeElapsed[i]>1)? "s" : "" );
                                ++j;
                            }
                        } // end for - build string
                    
                        return formattedTime;
                    } // end of formatTimeElapsedSinceMillisecond utility method
                    

                    以下是一些示例测试语句:

                    System.out.println(formatTimeElapsedSinceMillisecond(21432424234L));
                    // Output: 8 months, 1 week, 1 day
                    
                    System.out.println(formatTimeElapsedSinceMillisecond(87724294L));
                    // Output: 1 day, 22 minutes, 4 seconds
                    
                    System.out.println(formatTimeElapsedSinceMillisecond(123434L));
                    // Output: 2 minutes, 3 seconds
                    

                    【讨论】:

                      【解决方案15】:

                      这是另一个类似的功能,它不会显示天、小时、分钟等。如果不需要,如果需要更改它的文字。

                      public class ElapsedTime {
                      
                          public static void main(String args[]) {
                      
                              long start = System.currentTimeMillis();
                              start -= (24 * 60 * 60 * 1000 * 2);
                              start -= (60 * 60 * 1000 * 2);
                              start -= (60 * 1000 * 3);
                              start -= (1000 * 55);
                              start -= 666;
                              long end = System.currentTimeMillis();
                              System.out.println(elapsedTime(start, end));
                      
                          }
                      
                          public static String elapsedTime(long start, long end) {
                      
                              String auxRet = "";
                      
                              long aux = end - start;
                              long days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0;
                              // days
                              if (aux > 24 * 60 * 60 * 1000) {
                                  days = aux / (24 * 60 * 60 * 1000);
                              }
                              aux = aux % (24 * 60 * 60 * 1000);
                              // hours
                              if (aux > 60 * 60 * 1000) {
                                  hours = aux / (60 * 60 * 1000);
                              }
                              aux = aux % (60 * 60 * 1000);
                              // minutes
                              if (aux > 60 * 1000) {
                                  minutes = aux / (60 * 1000);
                              }
                              aux = aux % (60 * 1000);
                              // seconds
                              if (aux > 1000) {
                                  seconds = aux / (1000);
                              }
                              milliseconds = aux % 1000;
                      
                              if (days > 0) {
                                  auxRet = days + " days ";
                              }
                              if (days != 0 || hours > 0) {
                                  auxRet += hours + " hours ";
                              }
                              if (days != 0 || hours != 0 || minutes > 0) {
                                  auxRet += minutes + " minutes ";
                              }
                              if (days != 0 || hours != 0 || minutes != 0 || seconds > 0) {
                                  auxRet += seconds + " seconds ";
                              }
                              auxRet += milliseconds + " milliseconds ";
                      
                              return auxRet;
                          }
                      
                      }
                      

                      【讨论】:

                        【解决方案16】:

                        这是一个问题,需要制定一种算法来考虑闰年以及除年之外的确切月数和天数。有趣的是,如果只使用一种计量单位,那是多么简单。例如,两天之间的总天数与在假设二十年内计算的月数和年数之后的提醒天数相比是正确的。

                        我目前正在努力从我的 PML 实现中提供它,例如,以以下形式:

                        unemployed <- date.difference[
                            From = 2009-07-01, 
                            Till = now, 
                            YEARS, MONTHS, DAYS
                        ]: yyyy-MM-dd
                        
                        $$-> *unemployed -> date.translate[ YEARS, MONTHS, DAYS ] -> print["Unemployed for:", .]
                        

                        当然,这对于精确的利率计算也很有用。

                        【讨论】:

                        • 这并没有回答如何。我将立即使用日历提供一个特定于 Java 的答案。
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-12-12
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-04-25
                        • 2020-12-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多