Featured image of post 【Java】Dates and Times

【Java】Dates and Times

java.util.Date

You’re right to ask about the Date class. The pre-Java 8 java.util.Date class is indeed still available, but it’s generally considered outdated for modern Java projects.

Issues

  • Mutable (not thread-safe)
  • Poorly designed API
  • Doesn’t handle time zones well
  • Only millisecond precision
  • Confusing method names (e.g., getYear() returns year since 1900)

Modern alternatives

  • Java 8+ java.time package (recommended)
  • Third-party libraries like Joda-Time (for pre-Java 8 projects)

Migration

  • java.time provides methods to convert between old and new date/time classes

java.time

java.time introduced in Java 8, provides a comprehensive and much-improved API for handling dates, times, and durations.

LocalDate

Represents a date without time or time zone.

LocalTime

Represents a time without date or time zone.

LocalDateTime

Combines date and time, without a time zone.

ZonedDateTime

Date and time with a time zone.

ZoneId

represents a time zone

Instant

Instant represents a point in time on the timeline, typically in UTC (Coordinated Universal Time). It’s essentially a timestamp with nanosecond precision.

Period

Represents a date-based amount of time.

Duration

Represents a time-based amount of time.

DateTimeFormatter

is used for parsing and formatting date-time objects.

Usage in Database

  • LocalDate: For date-only fields (e.g., birthdate)
  • LocalDateTime: For date and time without time zone
  • Instant: For timestamps (e.g., created_at, updated_at)
  • ZonedDateTime: If you need to store time zone information

Considerations

  • Instant is often preferred for timestamps because it’s always in UTC and avoids time zone ambiguities.
  • If using an older version of Hibernate (pre-5.0), you might need additional configuration or converters.
  • Some databases might require specific column definitions. For example, PostgreSQL might need @Column(columnDefinition = “TIMESTAMP WITH TIME ZONE”) for Instant.

Database-specific notes

  • MySQL: LocalDateTime is typically stored as DATETIME, Instant as TIMESTAMP.
  • PostgreSQL: Supports all types well, but ensure your JDBC driver is up to date.
  • Oracle: May require additional configuration for LocalDate and LocalDateTime.

Best practices

  • Use Instant for audit fields (created_at, updated_at).
  • Use LocalDateTime for user-entered date-times if time zone isn’t important.
  • Always consider time zone implications in your application logic. You can set the following property to ensure proper handling of JDBC time zones:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC

example

import javax.persistence.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private LocalDate birthDate;

    private LocalDateTime lastLogin;

    @Column(columnDefinition = "TIMESTAMP")
    private Instant createdAt;

    // getters and setters
}

Instant usage

Use Instant for:

  • System-generated timestamps (created_at, updated_at)
  • Event occurrences (lastLogin, orderPlacedAt)
  • Any time you need to record a specific moment and might deal with different time zones

Audit field

  • created_at: When a record was created
  • updated_at: When a record was last modified
  • sometimes deleted_at: When a record was soft-deleted

Benifits

Querying

  • Easy to query for records created or updated within a specific time range.

Sorting

  • Straightforward to sort records by creation or update time.

Time zone handling

  • When displaying to users, you can easily convert Instant to their local time zone.

Potential pitfall to avoid

  • Don’t use LocalDateTime for audit fields unless you’re absolutely sure all your servers and databases will always be in the same time zone.

Converting for display

  • When you need to display these times to users
    ZoneId userZone = ZoneId.of("America/New_York"); 
    Instant createdAt = entity.getCreatedAt(); 
    ZonedDateTime userTime = ZonedDateTime.ofInstant(createdAt, userZone);
    

Database considerations

  • For MySQL: Use TIMESTAMP column type
  • For PostgreSQL: Use TIMESTAMP WITH TIME ZONE

JPA Auditting

If you want to use Spring Data JPA’s automatic auditing features, such as:

  • @CreatedDate
  • @LastModifiedDate
  • @CreatedBy
  • @LastModifiedBy enable JPA Auditing.

manual Auditting

@Entity
public class MyEntity {
    // other fields

    @Column(updatable = false)
    private Instant createdAt;

    private Instant updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = Instant.now();
        updatedAt = Instant.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = Instant.now();
    }
}

example

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.Instant;

@Entity
@EntityListeners(AuditingEntityListener.class)
public class AuditedEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @CreatedDate
    @Column(updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    private Instant updatedAt;

    // getters and setters
}

@Configuration 
@EnableJpaAuditing 
public class JpaConfig { 
	// other configurations 
}

Other usage

It’s often a good practice to store times as Instant and convert to appropriate local times when presenting to users.

This ensures consistent storage and easier querying, while still allowing flexible display options.

While Instant is great for many scenarios, there are cases where other types might be more appropriate:

  • a) LocalDate: When you only need the date without time (e.g., birthDate, holidayDate).
  • b) LocalDateTime: When you need date and time, but the time zone is implicit or unnecessary.
  • c) ZonedDateTime: When you need to preserve the specific time zone information.
Licensed under CC BY-NC-SA 4.0
Last updated on Jul 12, 2024 00:00 UTC
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy