我想要类似于这样的东西:
public class Stream
{
public startTime;
public endTime;
public getDuration()
{
return startTime - endTime;
}
}
在Java中,要实现这个目标,重要的是要考虑好以下情况:如果开始时间是23:00,结束时间是1:00,则需要获得2:00的持续时间。
很不幸,到目前为止发布的10个答案都不太正确。
如果你要测量经过的时间,并且想要结果准确,你必须使用System.nanoTime()
。你不能使用System.currentTimeMillis()
,除非你不在意结果是否正确。
nanoTime
的目的是测量 经过的 时间,而 currentTimeMillis
的目的是测量 墙上挂钟 时间。你不能把一个用于另一个目的。原因是没有任何一台计算机的时钟是完美的;它总是会漂移并且偶尔需要校正。这种校正可能是手动进行的,或者在大多数机器上,有一个运行的进程会持续发出小的修正以调整系统时钟(“墙上挂钟”)。这些修正经常发生。还有一种这样的校正是在出现闰秒的时候发生。
由于nanoTime
的目的是测量经过的时间,所以它不受这些微小校正的影响。它是你想要使用的。任何正在使用currentTimeMillis
进行的计时都会出错--甚至可能为负数。
你可能会说,“这听起来好像永远不会真正有那么大的影响”,对此我要说,也许不会,但总体上,正确的代码难道不比错误的代码更好吗?此外,nanoTime
打起来也更短。
先前发布的声明关于nanoTime
通常只具有微秒精度是正确的。此外,根据情况(其他方法也可能如此),它可能需要超过整个微秒才能调用,因此请不要期望正确计时非常非常小的时间间隔。
在Java中要实现这个,使用
long
类型就可以了。下面是如何测量时间的更多信息...
实现传统方法,确实需要使用System.currentTimeMillis()
:
long startTime = System.currentTimeMillis();
// ... do something ...
long estimatedTime = System.currentTimeMillis() - startTime;
注意,Commons Lang库中有一个StopWatch类可用于以毫秒为单位测量执行时间。它具有像split()
、suspend()
、resume()
等方法,允许在执行的不同点上进行测量,这可能会更方便。请查看一下。
如果您正在寻找精确度极高的经过时间测量,则可以使用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();
请阅读 这篇文章 以了解Java性能调优。
最后,如果您不想在代码中添加这些度量(或者无法更改现有的代码),那么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的完美应用案例。
你的新类:
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);
之后,经过的时间可以转换为你喜欢的任何格式,例如使用日历。TimeUnit
的提及。我不知道它的存在(为什么它不在 java.util
包中?) - OscarRyz例如,如果startTime是23:00,endTime是1:00,则持续时间为2:00。
不可能。如果只有时间,那么时钟会在午夜停止。没有日期的上下文,我们怎么知道你是指下一天、下一周还是下一个十年的1 AM?
因此,从晚上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中测量经过的时间?
Instant.now()
以UTC时区捕获当前时间。Duration.between
方法。to…Part
方法,从生成的 Duration
对象中提取一定数量的24小时天数、小时、分钟、秒和纳秒分数部分。toString
生成一个符合 标准ISO 8601格式 的 PnYnMnDTnHnMnS
字符串。示例代码,使用一对 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.time框架。
java.time框架由JSR 310定义,灵感来自于非常成功的Joda-Time项目,由ThreeTen-Extra项目扩展,并在Oracle Tutorial中描述。
旧的日期时间类(例如java.util.Date/.Calendar)随Java最早版本捆绑在一起,已被证明设计不良,令人困惑和麻烦。它们被java.time类所取代。
其他答案讨论了分辨率。
java.time类具有纳秒的分辨率,最多可达小数点后九位。例如:2016-03-12T04:29:39.123456789Z
。
旧的java.util.Date/.Calendar类和Joda-Time类都具有毫秒分辨率(3位小数)。例如,2016-03-12T04:29:39.123Z
。
在Java 8中,由于传统问题,当前时刻仅以毫秒分辨率获取。在Java 9及更高版本中,如果您的计算机硬件时钟运行得如此精细,则可以确定当前时间的纳秒分辨率。
如果您真正想只使用缺少任何日期或时区的时间,请使用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 );
早于:17:00 | 晚于:19:00 | 持续时间:PT2H
请注意,Duration::toString
的默认输出是使用标准 ISO 8601 格式。在这种格式中,P
表示开始(如“周期”),而 T
将任何年份-月份-日期部分与小时-分钟-秒钟部分分开。
不幸的是,当您跨越午夜时只考虑一天内的时间会变得棘手。通过假设您想要回到一天内较早的时间点,LocalTime
类处理此问题。
使用上面相同的代码,但从 23:00
到 01:00
结果为负二十二小时 (PT-22H
)。
LocalTime sooner = LocalTime.of ( 23, 0 );
LocalTime later = LocalTime.of ( 1, 0 );
早于:23:00 | 晚于:01:00 | 持续时间:PT-22H
如果您打算跨越午夜,那么使用日期和时间值而不是仅有时间的值可能更有意义。
时区对于日期非常重要。因此,我们指定三个项目:(1)所需日期,(2)所需时间,以及(3)作为解释该日期和时间的上下文的时区。在这里,我们任意选择了蒙特利尔地区的时区。
如果您仅通过偏移量定义日期,请使用ZoneOffset
和OffsetDateTime
。 如果您有一个完整的时区(偏移加上处理异常规则,例如夏令时),请使用ZoneId
和ZonedDateTime
。
LocalDate localDate = LocalDate.of ( 2016, 1, 23 );
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime sooner = ZonedDateTime.of ( localDate, LocalTime.of ( 23, 0 ), zoneId );
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.time类将根据需要进行调整。请阅读类文档以了解详情。
java.time 框架是内置于 Java 8 及更高版本中的。这些类取代了老旧且麻烦的 传统 日期时间类,例如 java.util.Date
、Calendar
和 SimpleDateFormat
。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
要了解更多信息,请参见Oracle教程。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。无需字符串,也不需要java.sql.*
类。
如何获取java.time类?
ThreeTen-Extra 项目扩展了 java.time 的附加类。该项目是 java.time 可能未来添加的一个试验场。您可能会在这里找到一些有用的类,例如 Interval
, YearWeek
, YearQuarter
和 更多。
Duration.between (sooner, later)
结合对跨越午夜问题的解释是对“如果 startTime 是 23:00,endTime 是 1:00,要获得 2:00 的持续时间”的完美回答。虽然我欣赏建设性的批评,但我期望更多的勤奋。 - Basil Bourque如果你的目的只是为了在程序日志中打印粗略的时间信息,那么对于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()
仅适用于 getStartTime()
属性。时间计算完全基于 System.nanoTime()
(至少在 org.apache.commons.lang3
版本中)。 - Ilya SerbisJava提供了静态方法System.currentTimeMillis()
。它返回一个long值,因此是一个很好的参考。许多其他类也接受一个名为'timeInMillis'的参数,它也是长整型。
许多人发现使用Joda Time库来计算日期和时间更容易。
在Java中要实现这个功能需要使用哪些类型?
答案: long
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.
这是我对你的第一个问题的直接答案。
至于最后的“note”,我建议你使用Joda Time。它包含一个适合你需要的interval类。
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://istack.dev59.com/hYo8y.webp
[2]: https://istack.dev59.com/xPjCK.webp
如果你更喜欢使用Java的日历API,你可以尝试这个方法:
Date startingTime = Calendar.getInstance().getTime();
//later on
Date now = Calendar.getInstance().getTime();
long timeElapsed = now.getTime() - startingTime.getTime();
System.currentTimeMillis()
,你不需要知道时区和在 Calendar#toString()
中看到的所有内容。请注意,Calendar
类包含了更多的功能,可能会给你带来额外的复杂性和开销。 - BalusC
System.currentTimeMillis()
和System.nanoTime()
。这里的问题侧重于计算两个时刻之间经过的时间,而不考虑获取当前时间。 - Basil Bourque