Back to Blog
Handling Clock and Timezones Correctly in Java
Java Engineering

Handling Clock and Timezones Correctly in Java

Why LocalDateTime.now() destroys your tests and how to use java.time.Clock to handle timezones like a pro in production applications.

January 8, 20263 min readRabi
javaspring-boottestingtimezonesbest-practices

Pop quiz: What is wrong with this code?

@Service
public class TokenService {
    public boolean isExpired(Token token) {
        // ❌ The Villain
        return token.getExpiresAt().isBefore(LocalDateTime.now());
    }
}

It looks innocent. But LocalDateTime.now() is a hidden dependency on the server's system clock.

  1. Testing is a nightmare: How do you test "token expiration" without Thread.sleep()? You can't control now().
  2. Timezones are ignored: If your server is in UTC but LocalDateTime.now() picks up the system default (e.g., EST), your logic breaks.
Timezone Chaos

The Solution: java.time.Clock

Since Java 8, we have had a built-in abstraction for time: java.time.Clock.

Instead of asking the System for the time, you ask the Clock. And because the Clock is an object, you can inject it.

Step 1: Define the Bean

In your Spring Boot configuration, define a global Clock bean. best practice is to always force UTC.

@Configuration
public class TimeConfig {
 
    @Bean
    public Clock clock() {
        return Clock.systemUTC();
    }
}

Step 2: Inject It

Now, refactor your service to depend on the Clock.

@Service
@RequiredArgsConstructor
public class TokenService {
    
    // ✅ The Hero
    private final Clock clock;
 
    public boolean isExpired(Token token) {
        // Ask the clock for "now"
        return token.getExpiresAt().isBefore(LocalDateTime.now(clock));
    }
}

Step 3: Testing "Time Travel"

This is where the magic happens. In your tests, you don't use the system clock. You use a Fixed Clock.

class TokenServiceTest {
 
    @Test
    void shouldFindExpiredToken() {
        // 1. Freeze time at specific date
        Instant fixedInstant = Instant.parse("2023-10-01T10:00:00Z");
        Clock fixedClock = Clock.fixed(fixedInstant, ZoneId.of("UTC"));
        
        TokenService service = new TokenService(fixedClock);
 
        // 2. Create a token that expires 1 second BEFORE the fixed time
        Token token = new Token();
        token.setExpiresAt(LocalDateTime.ofInstant(fixedInstant.minusSeconds(1), ZoneId.of("UTC")));
 
        // 3. Assert (Value is deterministic!)
        assertThat(service.isExpired(token)).isTrue();
    }
}

No Thread.sleep(). No flaky tests. Just pure deterministic logic.

Production Rule: The "Always UTC" Architecture

Dealing with users in Tokyo, London, and New York? Follow this golden rule:

Servers, APIs, and Databases always speak UTC.

UTC Architecture Flow

1. Database

Always use TIMESTAMP WITH TIME ZONE (Postgres) or store as UTC DATETIME. In Java, map this to Instant or OffsetDateTime. Avoid LocalDateTime for storage as it lacks timezone context.

@Entity
public class Order {
    // ✅ Good: unambiguous point in timeline
    private Instant createdAt; 
    
    // ❌ Bad: ambiguous (Is this 10 AM Tokyo or 10 AM NY?)
    private LocalDateTime deliveryDue; 
}

2. API Layer

Spring Boot (via Jackson) behaves differently depending on configuration. Force it to standard ISO-8601 UTC.

# application.properties
spring.jackson.time-zone=UTC
spring.jackson.serialization.write-dates-as-timestamps=false

Now your JSON will always look like "2023-10-27T10:00:00Z".

3. The Frontend

The Browser knows the user's timezone.

  • Server sends: 2023-10-27T10:00:00Z
  • Browser JS: new Date('2023-10-27T10:00:00Z').toString() -> Displays 10:00 PM Tokyo Time.

Conversion happens at the Edge (the user's screen), never in the core logic.

Summary

  1. Never use LocalDateTime.now() in business logic.
  2. Always inject java.time.Clock.
  3. Use Clock.fixed() in tests to time-travel.
  4. Store everything as UTC (Instant).

Time is hard. But with Clock, at least it's testable.

Happy Coding! ⏱️

Join the Community

Get the latest articles on system design, frontend & backend development, and emerging tech trends delivered straight to your inbox.

No spam. Unsubscribe at any time.

Liked the blog?

Share it with your friends and help them learn something new!