Java ZonedDate Time

5 minute read

Current Formatted Date, from the server running JVM, using ZonedDateTime

String formattedCurrentTimeStamp = ZonedDateTime.now()
        .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS a z(O)"));
//2024-03-23 23:14:34.984 PM MDT(GMT-6)

Convert from UTC to EST/EDT

  • First convert the Input String into ZonedDateTime using parse
  • Manipulate the zoneDateTime object
  • Convert the zonedDateTimeObject into the Output String using the required format

Difference between Parse and Format

//Convert the input time to city timeZone ()
ZonedDateTime zdtBasedOnCity = zonedDateTimeInUtc.withZoneSameInstant(ZoneId.of(cityTimeZone));

//Or
zdtBasedOnCity = zonedDateTime
        .with(LocalTime.MAX)
        .withZoneSameInstant(ZoneId.of(ZoneOffset.UTC.getId()))
        .truncatedTo(ChronoUnit.MILLIS);
String inputDateTimePattern = "yyyy-MM-dd HH:mm:ssX"; //"yyyy-MM-dd HH:mm:ss.SSSSSSX";
String outputDateTimeFormat = "MM/dd/yyyy HH:mm z";
String toTimeZone = "America/New_York";

String startTime = "2024-03-10 04:00:00+00"; // Start time in GMT
String endTime = "2024-03-10 07:00:00+00";   // End time in GMT

//03/09/2024 23:00 EST
System.out.println(getFormattedOutputDateTimeString
        (startTime,inputDateTimePattern, outputDateTimeFormat, toTimeZone));

//03/10/2024 03:00 EDT
System.out.println(getFormattedOutputDateTimeString
        (endTime, inputDateTimePattern, outputDateTimeFormat,toTimeZone ));

Modern Java Date Time Calendar Library

From java.time package

The LocalDateTime class represents a date and time without a time zone, while the ZonedDateTime class represents a date and time with a time zone.

  • ZonedDateTime : A date-time with a time-zone in the ISO-8601 calendar system, eg. 2007-12-03T10:15:30+01:00 Europe/Paris}.

When would you use OffsetDateTime instead of ZonedDateTime?

If you are writing complex software that models its own rules for date and time calculations based on geographic locations, or if you are storing time-stamps in a database that track only absolute offsets from Greenwich/UTC time, then you might want to use OffsetDateTime.

LocalDate

LocalDateTime

ZonedDateTime

Parse Input String into ZonedDate time

Format ZonedDateTime into desired output String format (to be used as json strings)

ZoneId vs ZoneOffset

//TimeZone
ZoneId zone    = ZoneId.systemDefault();//Uses Z for UTC
ZoneId india   = ZoneId.of("Asia/Kolkata");//UTC+05:30
ZoneId chicago = ZoneId.of("US/Central");
ZoneId ny      = ZoneId.of("UTC-05:00");

ZoneId

  • Represents a time zone identifier, such as “America/New_York” or “ Europe/London”.
  • It provides a way to identify regions with distinct rules for adjusting time, including daylight saving time (DST) rules.
  • ZoneId is used to create ZonedDateTime instances, which represent a specific date and time in a particular time zone.

ZoneOffset

  • Represents a fixed offset from UTC, such as +03:00 or -08:00.
  • It does not handle daylight saving time or historical changes in time zone rules; it simply represents a constant time difference from UTC.
String ist = ZoneOffset.SHORT_IDS.get("IST");//Asia/Kolkata
//Get zoneOffset from Zone Id
ZoneOffset standardOffset =  zoneId.getRules().getStandardOffset(Instant.now()).toString();//+05:30

A case with Z

As per Java 17 documentation

Capital Z would format the date with timezone in the end with + or - sign indicating the timezone compared to UTC.

For ZoneDateTime yyyy-MM-dd'T'HH:mm:ss.SSSZ format would look like 2022-03-03T09:08:56.064+0000

Date comparison with ZonedDateTime

It is easy to compare the dates with ZonedDateTime

Convertors

Working with Legacy DB that uses Timestamp

toInstant connects ZonedDateTime with sql Timestamp

Java provides a way to convert between ZonedDateTime and Timestamp using the toInstant()

//converts a ZonedDateTime to a Timestamp
Timestamp timestamp = Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());

//converts a Timestamp to a ZonedDateTime
ZonedDateTime zonedDateTime = timestamp.toInstant().atZone(ZoneOffset.UTC);

from SQL Timestamp

SQL Timestamp to LocalDate

SQL Timestamp to LocalDateTime

SQL Timestamp to ZonedDateTime

Often the timestamp conversion is needed to and from DB timestamp column

to SQL Timestamp

SQL Timestamp has Date and Time component. Thus, LocalDateTime is the connecting medium.

LocalDate to SQL Timestamp

Instead of just using Date, convert the Date into DateTime by using startOfDay or endOfDay to properly convert into SQL Timestamp

LocalDateTime to SQL Timestamp

ZonedDateTime to SQL Timestamp

public static Timestamp fromDate(ZonedDateTime date) {
    return Optional.of(Timestamp.valueOf(date.toLocalDateTime())).orElse(null);
}

SQL Timestamp from current time with UTC timezone

Often, to save current time stamp in DB (ex. updateTime column in a table), with multiple timezones, it is a good idea to save in UTC

public static Timestamp currentTimestamp() {
        return Timestamp.from(ZonedDateTime.now(ZoneOffset.UTC).toInstant());
    }

Letters Meaning

Symbol Examples Meaning
G AD; Anno Domini; A era
u 2004; 04 year
y 2004; 04 year-of-era
Y 1996; 96 week-based-year
D 189 day-of-year
d 10 day-of-month
M/L 7; 07; Jul; July; J month-of-year
     
Q/q 3; 03; Q3; 3rd quarter quarter-of-year
w 27 week-of-week-based-year
W 4 week-of-month
E Tue; Tuesday; T day-of-week
e/c 2; 02; Tue; Tuesday; T localized day-of-week
F 3 week-of-month
     
a PM am-pm-of-day
h 12 clock-hour-of-am-pm (1-12)
K 0 hour-of-am-pm (0-11)
k 0 clock-hour-of-am-pm (1-24)
     
H 0 hour-of-day (0-23)
m 30 minute-of-hour
s 55 second-of-minute
S 978 fraction-of-second
A 1234 milli-of-day
n 987654321 nano-of-second
N 1234000000 nano-of-day
     
v America/Los_Angeles time-zone ID
Z -08:30  
z Pacific Standard Time; PST time-zone name
O GMT+8; GMT+08:00; UTC-08:00; localized zone-offset
X Z; -08; -0830; -08:30; -083015; -08:30:15; zone-offset ‘Z’ for zero
x +0000; -08; -0830; -08:30; -083015; -08:30:15; zone-offset
Z +0000; -0800; -08:00; zone-offset

Troubleshooting LocalDate.now() Discrepancy in Cloud GKE

If your application uses LocalDate.now() and returns an outdated date after deployment to GKE, follow these steps to resolve the issue:

Potential Causes and Solutions

1. Verify Node Time Synchronization

Ensure that the time on your Kubernetes nodes is accurate and synchronized:

  • Check Node Time
    • SSH into a Kubernetes node.
    • Use commands like date or timedatectl to verify the current date and time.
    • Ensure nodes are synchronized with NTP servers.
  • GKE and NTP
    • GKE nodes should be managed to sync time automatically. Verify if there are any issues with NTP configuration.

2. Review Container Time Configuration

Containers inherit the time settings from the host system but may have specific configurations:

  • Check Container Time
    • Run a command like date inside the container to check if the time is correct.
    • Ensure no container-specific settings are overriding the time.

3. Inspect Deployment Scripts and Configurations

Your deployment scripts or Kubernetes configurations might influence the observed time:

  • Review Jenkins Pipeline
    • Ensure Jenkins doesn’t inject any deployment-specific time settings.
  • Check Kubernetes Configurations
    • Verify that no environment variables or configurations are affecting time.

4. Consider Timezone Differences

Timezone differences can affect the date and time values:

  • Verify Timezone Settings
    • Ensure that timezone settings are consistent across your local environment, Kubernetes nodes, and containers.
    • Check both system and application timezone settings.

5. Update and Restart Pods

If time discrepancies are due to stale data or configurations:

  • Update Pods
    • Apply any changes to configurations.
    • Restart the affected pods to ensure they pick up the latest settings.

6. Use Consistent Time Source

For critical applications, consider using a consistent time source:

  • External Time Services
    • Integrate an external time service or API if you need to ensure accurate and consistent time across environments.

Example Code

If using LocalDate.now() is crucial, ensure it’s called at the point of execution:

import java.time.LocalDate;

public class DateProvider {
    public static LocalDate getCurrentDate() {
        return LocalDate.now();
    }

    public static void main(String[] args) {
        System.out.println("Current Date: " + getCurrentDate());
    }
}

Tags:

Categories:

Updated: