ZetCode

Java Clock类

最后修改时间:2025 年 4 月 16 日

java.time.Clock类提供了使用时区访问当前瞬间、日期和时间的功能。它被设计用作基于时间的服务的依赖注入点。Clock可以代替System.currentTimeMillis()使用。

Clock是抽象类并且是不可变的。它支持多种时间源,包括用于测试的固定时钟。该类通过允许在单元测试中控制时间来帮助使代码更易于测试。

Clock类概述

Clock提供了获取各种格式的当前时间的方法。关键操作包括获取瞬间、毫秒和时区感知日期。该类支持系统和自定义时间源。

public abstract class Clock {
    public static Clock systemUTC();
    public static Clock systemDefaultZone();
    public static Clock system(ZoneId zone);
    public static Clock fixed(Instant fixedInstant, ZoneId zone);
    public static Clock offset(Clock baseClock, Duration offsetDuration);
    public abstract ZoneId getZone();
    public abstract Clock withZone(ZoneId zone);
    public long millis();
    public abstract Instant instant();
    public boolean equals(Object obj);
    public int hashCode();
    public String toString();
}

上面的代码展示了Clock提供的关键方法。这些方法允许创建不同的时钟实现并访问时间值。该类专为在对时间敏感的应用程序中提供灵活性而设计。

创建Clock对象

Clock对象可以使用各种工厂方法创建。最常见的是用于不同时区的系统时钟和用于测试的固定时钟。每种时钟类型都服务于不同的用例。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

public class Main {

    public static void main(String[] args) {
        
        // System clock in UTC
        Clock utcClock = Clock.systemUTC();
        System.out.println("UTC time: " + utcClock.instant());
        
        // System clock in default timezone
        Clock defaultClock = Clock.systemDefaultZone();
        System.out.println("Default zone time: " + defaultClock.instant());
        
        // System clock in specific timezone
        Clock parisClock = Clock.system(ZoneId.of("Europe/Paris"));
        System.out.println("Paris time: " + parisClock.instant());
        
        // Fixed clock for testing
        Clock fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
        System.out.println("Fixed time: " + fixedClock.instant());
    }
}

此示例演示了创建Clock对象的不同方法。输出显示了每种时钟类型如何提供时间值。固定时钟对于测试时间敏感的代码特别有用。

获取当前时间

Clock提供了几种访问当前时间值的方法。这些包括获取瞬间、毫秒或特定于时区的日期。这些方法提供了根据所需精度的灵活性。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {

    public static void main(String[] args) {

        Clock clock = Clock.systemDefaultZone();
        
        // Get current instant
        Instant instant = clock.instant();
        System.out.println("Current instant: " + instant);
        
        // Get milliseconds since epoch
        long millis = clock.millis();
        System.out.println("Milliseconds: " + millis);
        
        // Get zoned date time
        ZonedDateTime zdt = ZonedDateTime.now(clock);
        System.out.println("Zoned date time: " + zdt);
        
        // Get time zone
        ZoneId zone = clock.getZone();
        System.out.println("Time zone: " + zone);
    }
}

此示例演示了如何从Clock提取各种时间值。instant()方法提供了最精确的时间测量。其他方法为不同的用例提供了便利。

使用固定时钟进行测试

固定时钟对于测试依赖于时间的代码非常宝贵。它们允许模拟特定的时间点,使测试可预测且可重复。这对于可靠的单元测试至关重要。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.Duration;

public class Main {

    public static void main(String[] args) {

        // Create fixed clock for testing
        Instant testTime = Instant.parse("2025-01-01T00:00:00Z");
        Clock fixedClock = Clock.fixed(testTime, ZoneId.of("UTC"));
        
        System.out.println("Fixed time: " + fixedClock.instant());
        
        // Simulate time passing (won't actually change)
        System.out.println("After waiting: " + fixedClock.instant());
        
        // Create offset clock
        Clock offsetClock = Clock.offset(fixedClock, Duration.ofHours(2));
        System.out.println("Offset time: " + offsetClock.instant());
    }
}

此示例演示了使用固定时钟和偏移时钟进行测试场景。固定时钟始终返回相同的瞬间,而偏移时钟提供一致的时间差。两者都对测试用例有用。

具有不同时区的Clock

Clock可以配置不同的时区以提供本地化时间值。这对于需要在多个区域显示或处理时间的应用程序很有用。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {

    public static void main(String[] args) {

        // Clocks with different time zones
        Clock newYorkClock = Clock.system(ZoneId.of("America/New_York"));
        Clock tokyoClock = Clock.system(ZoneId.of("Asia/Tokyo"));
        Clock londonClock = Clock.system(ZoneId.of("Europe/London"));
        
        // Get zoned date times
        ZonedDateTime nyTime = ZonedDateTime.now(newYorkClock);
        ZonedDateTime tokyoTime = ZonedDateTime.now(tokyoClock);
        ZonedDateTime londonTime = ZonedDateTime.now(londonClock);
        
        System.out.println("New York: " + nyTime);
        System.out.println("Tokyo: " + tokyoTime);
        System.out.println("London: " + londonTime);
        
        // Change time zone of existing clock
        Clock adjustedClock = londonClock.withZone(ZoneId.of("Australia/Sydney"));
        System.out.println("Sydney: " + ZonedDateTime.now(adjustedClock));
    }
}

此示例展示了Clock如何为不同的时区提供时间值。withZone方法使用不同的时区创建一个新的时钟。这允许在应用程序中灵活地处理时区。

比较Clocks

可以根据其配置来比较时钟是否相等。这在验证时钟设置或确保在整个应用程序中使用一致的时间源时很有用。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

public class Main {

    public static void main(String[] args) {

        Clock clock1 = Clock.systemUTC();
        Clock clock2 = Clock.system(ZoneId.of("UTC"));
        Clock clock3 = Clock.systemDefaultZone();
        Clock fixedClock = Clock.fixed(Instant.now(), ZoneId.of("UTC"));
        
        System.out.println("UTC clocks equal: " + clock1.equals(clock2));
        System.out.println("UTC vs default: " + clock1.equals(clock3));
        System.out.println("System vs fixed: " + clock1.equals(fixedClock));
        
        // Hash code comparison
        System.out.println("UTC clock hash: " + clock1.hashCode());
        System.out.println("Default clock hash: " + clock3.hashCode());
    }
}

此示例演示了时钟比较操作。请注意,仅当两个时钟具有相同的配置时,才认为它们相等。具有不同时区的系统时钟不相等,即使它们表示相同的瞬间也是如此。

在时间敏感代码中使用Clock

Clock被设计为注入到时间敏感的类中,使其更易于测试。此模式允许在测试期间用固定时钟替换系统时钟。

Main.java
package com.zetcode; 

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.time.Duration;

class EventLogger {
    private final Clock clock;
    
    public EventLogger(Clock clock) {
        this.clock = clock;
    }
    
    public void logEvent(String message) {
        System.out.println(clock.instant() + ": " + message);
    }
    
    public boolean isEventRecent(Instant eventTime) {
        return Duration.between(eventTime, clock.instant())
                      .compareTo(Duration.ofMinutes(5)) <= 0;
    }
}

public class Main {

    public static void main(String[] args) {
        
        // Production use with system clock
        EventLogger productionLogger = new EventLogger(Clock.systemUTC());
        productionLogger.logEvent("System started");
        
        // Test use with fixed clock
        Instant testTime = Instant.parse("2025-01-01T12:00:00Z");
        Clock testClock = Clock.fixed(testTime, ZoneId.of("UTC"));
        EventLogger testLogger = new EventLogger(testClock);
        
        testLogger.logEvent("Test event");
        Instant recentEvent = Instant.parse("2025-01-01T11:55:00Z");
        System.out.println("Is event recent? " + 
            testLogger.isEventRecent(recentEvent));
    }
}

此示例展示了如何使用Clock使时间敏感的代码可测试。EventLogger类在其构造函数中接受一个Clock,允许测试使用固定时钟。建议在生产代码中使用此模式。

来源

Java Clock类文档

在本文中,我们介绍了Java Clock类的基本方法和功能。理解这些概念对于在Java应用程序中编写可测试的、时间敏感的代码至关重要。

作者

我的名字是Jan Bodnar,是一位经验丰富的程序员,在这个领域拥有多年的经验。我从2007年开始撰写编程文章,至今已撰写了1400多篇文章和8本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程