如何在Java中测量时间流逝?

我想要这样的东西:

public class Stream
{
public startTime;
public endTime;


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

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

为了在Java中实现这一点,应该使用哪些类型?

494593 次浏览

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

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

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

你的新类:

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);

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

< p > Greetz, 查德< / p >

如果你更喜欢使用Java的日历API,你可以试试这个,

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

为了在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。它包含一个适合你需要的时间间隔类。

为了在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有一个秒表类,可用于以毫秒为单位测量执行时间。它有方法,如split()suspend()resume()等,允许在执行的不同点采取措施,你可能会发现方便。看一看。

system . nanotime ()

如果你正在寻找极其精确的时间测量,你可能更喜欢使用System.nanoTime()。从它的javadoc:

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

安达卢

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

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

查看www.javaperformancetunning.com上的这篇文章有一个很好的介绍。

使用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的一个完美用例。

如果你正在编写一个必须处理持续时间的应用程序,那么请看看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);
}

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

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);

值得注意的是

  • System.currentTimeMillis()最多只有毫秒精度。在某些windows系统上,它可能是16毫秒。它的成本比其他产品低。200 ns。
  • System.nanoTime()在大多数系统上只有微秒的精度,在windows系统上可以跳跃100微秒(即有时它不像它看起来那样准确)
  • 日历是一种非常昂贵的计算时间的方法。(我可以想到除了XMLGregorianCalendar)有时它是最合适的解决方案,但请注意,您应该只执行较长的时间间隔。

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

如果你正在测量经过的时间,并且你希望它是正确的,你必须使用System.nanoTime()。你不能使用System.currentTimeMillis(),除非你不介意结果是错误的。

nanoTime的目的是测量运行时间,而currentTimeMillis的目的是测量墙上时钟时间。你不能用它来做别的事。原因是没有一台电脑的时钟是完美的;它总是漂移,偶尔需要纠正。这种修正可能是手动进行的,或者在大多数机器的情况下,有一个进程运行并不断对系统时钟(“挂钟”)发出小的修正。这些往往经常发生。每当有闰秒时,就会发生另一种这样的修正。

由于nanoTime的目的是测量经过的时间,因此它不受任何这些小修正的影响。它是你想要使用的。当前正在进行的currentTimeMillis的任何计时都将关闭——甚至可能为负。

您可能会说,“这听起来似乎并没有那么重要”,对此我说,也许不是,但总的来说,正确的代码不就是比错误的代码好吗?此外,nanoTime无论如何都更短。

以前发布的关于nanoTime的免责声明通常只有微秒精度是有效的。此外,它可能需要超过一微秒的时间来调用,这取决于环境(另一个也是如此),所以不要期望对非常非常小的间隔进行正确的计时。

博士tl;

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

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

所以从晚上11点到凌晨1点意味着将向后在时间上移动22小时,逆时针方向运行时钟指针。请看下面的结果,a 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()捕获UTC的当前时刻。
  2. 以后再捕捉这样的时刻。
  3. 将两者都传递给Duration.between
  4. (a)从结果的Duration对象中,通过调用各种to…Part方法,提取一个24小时的日、时、分、秒和以纳秒为单位的小数秒。
    (b)或者调用toStringPnYnMnDTnHnMnSISO 8601标准格式中生成String

示例代码,使用一对Instant对象。

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

PT3M27.602197S

Java 8+中的新技术

我们现在在Java 8和以后的版本中内置了新的技术,java.time框架。

java.time

java。时间框架由JSR 310定义,灵感来自非常成功的Joda-Time项目,由ThreeTen-Extra项目扩展,并在甲骨文教程中描述。

旧的日期时间类,如java.util.Date/。与早期版本的Java捆绑在一起的日历被证明设计得很糟糕,令人困惑,而且很麻烦。它们被爪哇取代了。时间类。

决议

其他答案讨论解决方案。

java。time类具有纳秒分辨率,最多可达秒的小数部分的9位。例如,2016-03-12T04:29:39.123456789Z

旧的java.util.Date/。Calendar类和Joda-Time类具有毫秒级分辨率(3位分数)。例如,2016-03-12T04:29:39.123Z

在Java 8中,由于遗留问题,当前时刻只能以毫秒的分辨率获取。在Java 9及以后版本中,如果您的计算机硬件时钟运行得非常好,则可以以纳秒的分辨率确定当前时间。

1)

如果你真的想只使用一天中的时间,而不使用任何日期或时区,请使用LocalTime类。

LocalTime sooner = LocalTime.of ( 17, 00 );
LocalTime later = LocalTime.of ( 19, 00 );

Duration表示一段时间,它以秒加纳秒的形式表示。

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

转储到控制台。

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

sooner: 17:00 | later: 19:00 | duration: PT2H

ISO 8601

注意,Duration::toString的默认输出是标准的ISO 8601格式。在这种格式中,P标记开始(如在'Period'中),并且T将任何年-月-日部分与小时-分-秒部分分开。

过午夜

不幸的是,只有当你绕过午夜时,处理一天中的时间才会变得棘手。LocalTime类通过假设你想回到一天的早些时候来处理这个问题。

使用与上面相同的代码,但从23:0001:00会导致负22小时(PT-22H)。

LocalTime sooner = LocalTime.of ( 23, 0 );
LocalTime later = LocalTime.of ( 1, 0 );

早:23:00 |晚:01:00 |持续时间:PT-22H

日期-时间

如果您打算越过午夜,那么使用日期时间值而不是仅使用日期时间值可能更有意义。

时区

时区对约会至关重要。因此,我们指定三个项目:(1)所需的日期,(2)所需的时间,以及(3)时区作为解释该日期和时间的上下文。这里我们任意选择了蒙特利尔区域的时区。

如果只使用offset-from-UTC定义日期,则使用ZoneOffsetOffsetDateTime。如果你有一个完整的时区(偏移量加上处理异常(如日光节约时间)的规则),使用ZoneIdZonedDateTime

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

我们指定较晚的时间为第二天凌晨1点。

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

我们用上面看到的相同方式计算Duration。现在我们有两个小时的时间来回答这个问题。

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

转储到控制台。

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

早:2016-01-23T23:00-05:00[美国/蒙特利尔]|晚:2016-01-24T01:00-05:00[美国/蒙特利尔]|时长:PT2H

日光节约时间

如果手边的日期时间涉及日光节约时间(DST)或其他类似的异常情况,java。时间类别将根据需要调整。详细信息请阅读类文档。


关于java.time

< em > java.time < / em >框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗产日期-时间类,如java.util.DateCalendar, &SimpleDateFormat

现在在维护模式中的< em > Joda-Time < / em >项目建议迁移到java.time类。

要了解更多信息,请参见< < em >甲骨文教程/ em >。搜索Stack Overflow可以找到很多例子和解释。规格是JSR 310

你可以直接与你的数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

从哪里获取java。时间类?

我发现这段代码在计时时很有用:

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;
}

用这个:

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。)

“else”部分可以得到正确的答案

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

我建立了一个格式化函数,基于我偷来的东西。我需要一种方法来“分析”日志消息中的内容,所以我需要一个固定长度的持续时间消息。

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));


}


}