如何衡量Java中的时间?

时间:2009-11-20 11:51:24

标签: java time

我希望有这样的东西:

public class Stream
{
    public startTime;
    public endTime;

    public getDuration()
    {
        return startTime - endTime;
    }
}

同样重要的是,例如,如果startTime为23:00,endTime为1:00,则持续时间为2:00。

要在Java中使用哪些类型来实现此目的?

15 个答案:

答案 0 :(得分:613)

不幸的是,到目前为止发布的十个答案中没有一个是完全正确的。

如果您要测量已用时间,并且希望正确,则必须使用System.nanoTime()。您不能使用System.currentTimeMillis(),除非您不介意结果错误。

nanoTime的目的是衡量已过去的时间,而currentTimeMillis的目的是衡量挂钟时间。你不能将它用于其他目的。原因是没有计算机的时钟是完美的;它总是漂移,偶尔需要纠正。这种修正可能是手动发生的,或者在大多数机器的情况下,有一个运行的过程并不断对系统时钟(“挂钟”)进行小的修正。这些往往经常发生。只要有闰秒,就会发生另一次这样的修正。

由于nanoTime的目的是测量经过的时间,因此它不受任何这些小修正的影响。这是你想要使用的。目前正在进行currentTimeMillis的所有时间都将被取消 - 甚至可能是否定的。

你可能会说,“这听起来不像真的那么重要”,我说,也许不是,但总的来说,不正确的代码比错误的代码更好吗?此外,无论如何,nanoTime更短。

以前发布的关于nanoTime的免责声明通常只有微秒精度有效。此外,根据情况(也可能是其他情况),调用可能需要超过一微秒才能完成,因此不要期望正确地计算非常小的时间间隔。

答案 1 :(得分:204)

  

要在Java中使用哪些类型来实现此目的?

简短回答是long。现在,更多关于如何衡量......

System.currentTimeMillis的()

执行此操作的“传统”方法确实是使用System.currentTimeMillis()

long startTime = System.currentTimeMillis();
// ... do something ...
long estimatedTime = System.currentTimeMillis() - startTime;

o.a.c.l.t.StopWatch

请注意,Commons Lang有一个StopWatch类,可用于测量执行时间(以毫秒为单位)。它有方法方法,如split()suspend()resume()等,允许在执行的不同点采取措施,您可能会觉得方便。看看吧。

System.nanoTime()

如果您正在寻找非常精确的经过时间测量值,您可能更愿意使用System.nanoTime()。来自它的javadoc:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

火腿

另一种选择是使用JAMon,这是一个收集统计数据的工具(执行时间,命中数,平均执行时间,最小值,最大值等) start()和stop()方法之间。下面是一个非常简单的例子:

import com.jamonapi.*;
...
Monitor mon=MonitorFactory.start("myFirstMonitor");
...Code Being Timed...
mon.stop();

在www.javaperformancetunning.com上查看this article以获得精彩的介绍。

使用AOP

最后,如果您不希望使用这些测量值混乱代码(或者如果您无法更改现有代码),那么AOP将是一个完美的武器。我不打算深入讨论这个问题,但至少我想提一下。

下面是使用AspectJ和JAMon的一个非常简单的方面(这里,切入点的短名称将用于JAMon监视器,因此调用thisJoinPoint.toShortString()):

public aspect MonitorAspect {
    pointcut monitor() : execution(* *.ClassToMonitor.methodToMonitor(..));

    Object arround() : monitor() {
        Monitor monitor = MonitorFactory.start(thisJoinPoint.toShortString());
        Object returnedObject = proceed();
        monitor.stop();
        return returnedObject;
    }
}

切入点定义可以很容易地根据类名,包名,方法名或这些方法的任意组合来监视任何方法。测量确实是AOP的完美用例。

答案 2 :(得分:35)

你的新班级:

public class TimeWatch {    
    long starts;

    public static TimeWatch start() {
        return new TimeWatch();
    }

    private TimeWatch() {
        reset();
    }

    public TimeWatch reset() {
        starts = System.currentTimeMillis();
        return this;
    }

    public long time() {
        long ends = System.currentTimeMillis();
        return ends - starts;
    }

    public long time(TimeUnit unit) {
        return unit.convert(time(), TimeUnit.MILLISECONDS);
    }
}

用法:

    TimeWatch watch = TimeWatch.start();
    // do something
    long passedTimeInMs = watch.time();
    long passedTimeInSeconds = watch.time(TimeUnit.SECONDS);

之后,过时的时间可以转换为您喜欢的任何格式,例如日历

格尔茨, GHAD

答案 3 :(得分:19)

如果目的是简单地将粗略的计时信息打印到程序日志中,那么Java项目的简单解决方案就是不要编写自己的秒表或计时器类,而只需使用属于的org.apache.commons.lang.time.StopWatch类。 Apache Commons Lang。

final StopWatch stopwatch = new StopWatch();
stopwatch.start();
LOGGER.debug("Starting long calculations: {}", stopwatch);
...
LOGGER.debug("Time after key part of calcuation: {}", stopwatch);
...
LOGGER.debug("Finished calculating {}", stopwatch);

答案 4 :(得分:15)

TL;博士

  

例如,如果startTime是23:00和endTime 1:00,则持续时间为2:00。

不可能。如果您只有时间,那么时钟会在午夜停止。如果没有日期的背景,我们如何知道你是在第二天,下周还是下一个十年的凌晨1点?

所以从晚上11点到凌晨1点意味着在22小时内向向后移动,逆时针运行时钟指针。请参阅下面的结果,否定二十二小时。

Duration.between(              // Represent a span of time a total number of seconds plus a fractional second in nanoseconds.
    LocalTime.of( 23 , 0 ) ,   // A time-of-day without a date and without a time zone. 
    LocalTime.of( 1 , 0 )      // A time-of-day clock stops at midnight. So getting to 1 AM from 11 PM means going backwards 22 hours.
)                              // Return a `Duration` object.
.toString()                    // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS
  

PT-22H

跨越午夜需要更大的日期背景以及时间(见下文)。

  

如何衡量Java中经过的时间?

  1. 使用Instant.now()
  2. 捕获UTC中的当前时刻
  3. 稍后再捕捉另一个这样的时刻。
  4. 将两者都传递给Duration.between
  5. (a)从生成的Duration对象中,通过调用各种to…Part方法,以纳秒为单位提取一些24小时的日,小时,分钟,秒和小数秒。
    (b)或者,致电toStringString PnYnMnDTnHnMnS生成Duration.between( // Represent a span of time a total number of seconds plus a fractional second in nanoseconds. then , // Some other `Instant` object, captured earlier with `Instant.now()`. Instant.now() // Capture the current moment in UTC with a resolution as fine as nanoseconds, depending on the limits of your host computer hardware clock and operating system. Generally you will get current moment in microseconds (six decimal digits of fractional second) in Java 9, 10, and 11, but only milliseconds in Java 8. ) // Return a `Duration` object. .toString() // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS
  6. 示例代码,使用standard ISO 8601 format个对象。

    2016-03-12T04:29:39.123456789Z
      

    PT3M27.602197S

    Java 8 +中的新技术

    我们现在已经在Java 8及更高版本Instant框架中构建了新技术。

    java.time

    java.time框架由java.time定义,受到非常成功的JSR 310项目的启发,由Joda-Time项目扩展,并在ThreeTen-Extra中进行了描述。 / p>

    与最早版本的Java捆绑在一起的旧日期时间类(如java.util.Date/.Calendar)已被证明设计不当,令人困惑且麻烦。它们被java.time类取代。

    分辨率

    其他答案讨论决议。

    java.time类的分辨率为Oracle Tutorial,最高为九位数,小数位为秒。例如,2016-03-12T04:29:39.123Z

    旧的java.util.Date/.Calendar类和Joda-Time类都具有毫秒分辨率(分数的3位数)。例如,LocalTime sooner = LocalTime.of ( 17, 00 ); LocalTime later = LocalTime.of ( 19, 00 );

    在Java 8中,由于遗留问题,当前时刻的分辨率最高只有毫秒级。在Java 9及更高版本中,只要计算机的硬件时钟运行得非常精细,当前时间就可以确定为纳秒分辨率。

    时间的日

    如果您真的只想使用缺少任何日期或时区的时间,请使用nanosecond课程。

    Duration duration = Duration.between ( sooner, later );
    

    LocalTime代表一个时间跨度,以秒为单位加上纳秒。

    System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration );
    

    转储到控制台。

    Duration::toString
      

    早点:17:00 |晚点:19:00 |持续时间:PT2H

    ISO 8601

    请注意P的默认输出采用标准Duration格式。在此格式中,T标记开头(如'期间'),LocalTime将小时 - 分钟 - 秒部分中的任何年 - 月 - 日分开。

    越过午夜

    不幸的是,当您绕过午夜的时钟时,处理时间只会变得棘手。 23:00类通过假设您想要倒退到当天的早期点来处理此问题。

    使用与上述相同的代码,但从01:00转到PT-22H会导致负二十二小时(LocalTime sooner = LocalTime.of ( 23, 0 ); LocalTime later = LocalTime.of ( 1, 0 ); )。

    LocalDate localDate = LocalDate.of ( 2016, 1, 23 );
    ZoneId zoneId = ZoneId.of ( "America/Montreal" );
    ZonedDateTime sooner = ZonedDateTime.of ( localDate, LocalTime.of ( 23, 0 ), zoneId );
    
      

    早点:23:00 |之后:01:00持续时间:PT-22H

    日期 - 时间

    如果您打算跨越午夜,那么您可能有必要使用日期时间值而不是仅限时间的值。

    时区

    时区对日期至关重要。因此,我们指定三个项目:(1)期望日期,(2)期望的时间,以及(3)时区作为解释该日期和时间的上下文。在这里,我们任意选择ISO 8601区域的时区。

    如果您仅按Montréal定义日期,请使用offset-from-UTCZoneOffset。如果您有完整时区(偏移量以及处理夏令时等异常情况的规则),请使用OffsetDateTime ZoneId

    ZonedDateTime later = ZonedDateTime.of ( localDate.plusDays ( 1 ), LocalTime.of ( 1, 0 ), zoneId );
    

    我们将下一个时间指定为次日凌晨1:00。

    Duration

    我们以与上面相同的方式计算Duration duration = Duration.between ( sooner, later ); 。现在我们得到了这个问题预计的两个小时。

    System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration );
    

    转储到控制台。

    java.sql.*
      

    更快:2016-01-23T23:00-05:00 [美国/蒙特利尔] |之后:2016-01-24T01:00-05:00 [美国/蒙特利尔] |持续时间:PT2H

    夏令时

    如果手头的日期时间涉及夏令时(DST)或其他此类异常,则java.time类将根据需要进行调整。阅读课程文档了解详情。

    关于 java.time

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

    现在位于SimpleDateFormatJoda-Time项目建议迁移到maintenance mode类。

    要了解详情,请参阅java.time。并搜索Stack Overflow以获取许多示例和解释。规范是Oracle Tutorial

    您可以直接与数据库交换 java.time 对象。使用符合JSR 310或更高版本的JDBC driver。不需要字符串,不需要{{1}}类。

    从哪里获取java.time类?

    How to use ThreeTenABP…项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如ThreeTen-ExtraIntervalYearWeekYearQuarter

答案 5 :(得分:7)

Java提供静态方法System.currentTimeMillis()。这回来了很长的价值,所以这是一个很好的参考。很多其他类都接受'timeInMillis'参数,该参数也很长。

很多人发现使用Joda Time库更容易对日期和时间进行计算。

答案 6 :(得分:7)

值得注意的是

  • System.currentTimeMillis()最多只有毫秒精度。值得一提的是,在某些Windows系统上可以达到16毫秒。它具有较低的成本,替代品< 200 ns。
  • System.nanoTime()在大多数系统上只有微秒精确度,并且可以在Windows系统上跳跃100微秒(即有时它不像看起来那么准确)。
  • 日历是一种非常昂贵的计算时间的方法。 (我可以想到除了XMLGregorianCalendar)有时候它是最合适的解决方案,但要注意你应该只花费很长的时间间隔。

答案 7 :(得分:6)

  

在Java中使用哪些类型来实现此目的?

回答:长

public class Stream {
    public long startTime;
    public long endTime;

    public long getDuration() {
        return endTime - startTime;
    }
    // I  would add
    public void start() {
        startTime = System.currentTimeMillis();
    }
    public void stop() {
         endTime = System.currentTimeMillis();
     }
}

用法:

  Stream s = .... 

  s.start();

  // do something for a while 

  s.stop();

  s.getDuration(); // gives the elapsed time in milliseconds. 

这是我对你的第一个问题的直接回答。

对于最后一个“注释”,我建议你使用Joda Time。它包含一个interval类,适合您的需要。

答案 8 :(得分:3)

如果您更喜欢使用Java Calendar API,可以试试这个,

Date startingTime = Calendar.getInstance().getTime();
//later on
Date now = Calendar.getInstance().getTime();
long timeElapsed = now.getTime() - startingTime.getTime();

答案 9 :(得分:3)

如果您正在编写一个必须处理持续时间的应用程序,那么请查看专门用于处理持续时间,Intervals和句点的Joda-Time。您的getDuration()方法看起来可能会返回Joda-Time Interval:

DateTime start = new DateTime(2004, 12, 25, 0, 0, 0, 0);
DateTime end = new DateTime(2005, 1, 1, 0, 0, 0, 0);

public Interval getInterval() {
    Interval interval = new Interval(start, end);
}

答案 10 :(得分:2)

Byte Stream Reader Elapsed Time for 23.7 MB is 96 secs

import java.io.*;
import java.io.IOException;
import java.util.Scanner;

class ElaspedTimetoCopyAFileUsingByteStream
{

    private long startTime = 0;
    private long stopTime = 0;
    private boolean running = false;


    public void start() 
    {
        this.startTime = System.currentTimeMillis();
        this.running = true;
    }


    public void stop() 
    {
        this.stopTime = System.currentTimeMillis();
        this.running = false;
    }



    public long getElapsedTime() 
    {
        long elapsed;
        if (running) {
             elapsed = (System.currentTimeMillis() - startTime);
        }
        else {
            elapsed = (stopTime - startTime);
        }
        return elapsed;
    }



    public long getElapsedTimeSecs()                 
    {
        long elapsed;
        if (running) 
        {
            elapsed = ((System.currentTimeMillis() - startTime) / 1000);
        }
        else
        {
            elapsed = ((stopTime - startTime) / 1000);
        }
        return elapsed;
    }





    public static void main(String[] args) throws IOException
    {
        ElaspedTimetoCopyAFileUsingByteStream  s = new ElaspedTimetoCopyAFileUsingByteStream();
        s.start();

        FileInputStream in = null;
        FileOutputStream out = null;

      try {
         in = new FileInputStream("vowels.txt");   // 23.7  MB File
         out = new FileOutputStream("output.txt");

         int c;
         while ((c = in.read()) != -1) {
            out.write(c);
         }
      }finally {
         if (in != null) {
            in.close();
         }
         if (out != null) {
            out.close();
         }
      }



        s.stop();
        System.out.println("elapsed time in seconds: " + s.getElapsedTimeSecs());
    }
}

[Elapsed Time for Byte Stream Reader][1]

**Character Stream Reader Elapsed Time for 23.7 MB is 3 secs**

import java.io.*;
import java.io.IOException;
import java.util.Scanner;

class ElaspedTimetoCopyAFileUsingCharacterStream
{

    private long startTime = 0;
    private long stopTime = 0;
    private boolean running = false;


    public void start() 
    {
        this.startTime = System.currentTimeMillis();
        this.running = true;
    }


    public void stop() 
    {
        this.stopTime = System.currentTimeMillis();
        this.running = false;
    }



    public long getElapsedTime() 
    {
        long elapsed;
        if (running) {
             elapsed = (System.currentTimeMillis() - startTime);
        }
        else {
            elapsed = (stopTime - startTime);
        }
        return elapsed;
    }



    public long getElapsedTimeSecs()                 
    {
        long elapsed;
        if (running) 
        {
            elapsed = ((System.currentTimeMillis() - startTime) / 1000);
        }
        else
        {
            elapsed = ((stopTime - startTime) / 1000);
        }
        return elapsed;
    }





    public static void main(String[] args) throws IOException
    {
        ElaspedTimetoCopyAFileUsingCharacterStream  s = new ElaspedTimetoCopyAFileUsingCharacterStream();
        s.start();

         FileReader in = null;                // CharacterStream Reader
      FileWriter out = null;

      try {
         in = new FileReader("vowels.txt");    // 23.7 MB
         out = new FileWriter("output.txt");

         int c;
         while ((c = in.read()) != -1) {
            out.write(c);
         }
      }finally {
         if (in != null) {
            in.close();
         }
         if (out != null) {
            out.close();
         }
      }

              s.stop();
        System.out.println("elapsed time in seconds: " + s.getElapsedTimeSecs());
    }
}


[Elapsed Time for Character Stream Reader][2]


  [1]: https://i.stack.imgur.com/hYo8y.png
  [2]: https://i.stack.imgur.com/xPjCK.png

答案 11 :(得分:1)

如果您从System.currentTimeMillis()获取时间戳,那么您的时间变量应该很长。

答案 12 :(得分:0)

我发现这个代码在计时时非常有用:

public class Et {
    public Et() {
    reset();
    }
    public void reset() {
    t0=System.nanoTime();
    }
    public long t0() {
        return t0;
    }
    public long dt() {
        return System.nanoTime()-t0();
    }
    public double etms() {
    return etms(dt());
    }
    @Override public String toString() {
        return etms()+" ms.";
    }
    public static double etms(long dt) {
        return dt/1000000.; // 1_000_000. breaks cobertura
    }
    private Long t0;
}

答案 13 :(得分:0)

使用此:

SimpleDateFormat format = new SimpleDateFormat("HH:mm");

Date d1 = format.parse(strStartTime);
Date d2 = format.parse(strEndTime);

long diff = d2.getTime() - d1.getTime();
long diffSeconds,diffMinutes,diffHours;

if (diff > 0) {
diffSeconds = diff / 1000 % 60;
diffMinutes = diff / (60 * 1000) % 60;
diffHours = diff / (60 * 60 * 1000);
}
else{
long diffpos = (24*((60 * 60 * 1000))) + diff;
diffSeconds = diffpos / 1000 % 60;
diffMinutes = diffpos / (60 * 1000) % 60;
diffHours = (diffpos / (60 * 60 * 1000));
}
  

(同样重要的是,例如,如果startTime为23:00,endTime为1:00,则持续时间为2:00。)

“其他”部分可以正确

答案 14 :(得分:0)

我根据我偷走SO的东西建立了格式化功能。我需要一种方式来分析"日志消息中的东西,所以我需要一个固定长度的持续时间消息。

public static String GetElapsed(long aInitialTime, long aEndTime, boolean aIncludeMillis)
{
  StringBuffer elapsed = new StringBuffer();

  Map<String, Long> units = new HashMap<String, Long>();

  long milliseconds = aEndTime - aInitialTime;

  long seconds = milliseconds / 1000;
  long minutes = milliseconds / (60 * 1000);
  long hours = milliseconds / (60 * 60 * 1000);
  long days = milliseconds / (24 * 60 * 60 * 1000);

  units.put("milliseconds", milliseconds);
  units.put("seconds", seconds);
  units.put("minutes", minutes);
  units.put("hours", hours);
  units.put("days", days);

  if (days > 0)
  {
    long leftoverHours = hours % 24;
    units.put("hours", leftoverHours);
  }

  if (hours > 0)
  {
    long leftoeverMinutes = minutes % 60;
    units.put("minutes", leftoeverMinutes);
  }

  if (minutes > 0)
  {
    long leftoverSeconds = seconds % 60;
    units.put("seconds", leftoverSeconds);
  }

  if (seconds > 0)
  {
    long leftoverMilliseconds = milliseconds % 1000;
    units.put("milliseconds", leftoverMilliseconds);
  }

  elapsed.append(PrependZeroIfNeeded(units.get("days")) + " days ")
      .append(PrependZeroIfNeeded(units.get("hours")) + " hours ")
      .append(PrependZeroIfNeeded(units.get("minutes")) + " minutes ")
      .append(PrependZeroIfNeeded(units.get("seconds")) + " seconds ")
      .append(PrependZeroIfNeeded(units.get("milliseconds")) + " ms");

  return elapsed.toString();

}

private static String PrependZeroIfNeeded(long aValue)
{
  return aValue < 10 ? "0" + aValue : Long.toString(aValue);
}

还有一个测试类:

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import junit.framework.TestCase;

public class TimeUtilsTest extends TestCase
{

  public void testGetElapsed()
  {
    long start = System.currentTimeMillis();
    GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance();
    calendar.setTime(new Date(start));

    calendar.add(Calendar.MILLISECOND, 610);
    calendar.add(Calendar.SECOND, 35);
    calendar.add(Calendar.MINUTE, 5);
    calendar.add(Calendar.DAY_OF_YEAR, 5);

    long end = calendar.getTimeInMillis();

    assertEquals("05 days 00 hours 05 minutes 35 seconds 610 ms", TimeUtils.GetElapsed(start, end, true));

  }

}