Caleb Rushing
Caleb Rushing

Reputation: 51

Java Time conversion from string to ZonedDateTime

Hello I am trying to convert a Date and Time from a SQL data base that is being stored in UTC time to my systems Local Time zone that will be displayed in a table in a JAVAFX application.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String appointmentStart = rs.getString("Start");

LocalDateTime ldtStart = LocalDateTime.parse(appointmentStart, formatter);
ZonedDateTime zdtStart = ZonedDateTime.of(ldtStart, ZoneId.systemDefault());
appointmentStart = zdtStart.format(formatter);


String appointmentEnd = rs.getString("End");

LocalDateTime ldtEnd = LocalDateTime.parse(appointmentEnd, formatter);
ZonedDateTime zdtEnd = ZonedDateTime.of(ldtEnd, ZoneId.systemDefault());
appointmentEnd = zdtEnd.format(formatter);

This is what I have written. The table is being populated in UTC time and is not converting. I get no errors but is this logic wrong? If so how can I do this?

Upvotes: 5

Views: 18919

Answers (4)

gufgl
gufgl

Reputation: 29

String date = "2023-1-1 00:00:00" ZonedDateTime.parse(date, DateTimeFormatter.ISO_DATE_TIME)

ISO_DATE_TIME parses with offset and zone if available like this: 2011-12-03T10:15:30+01:00[Europe/Paris]

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 340230

tl;dr

DateTimeFormatter                                               // Class for generating text that represents the value of a date-time object.
        .ofLocalizedDateTime( FormatStyle.FULL )                // Automatically localize the generated text.
        .withLocale( Locale.FRANCE )                            // Whatever human language and cultural norms are best for presentation to this particular user. Or call `Locale.getDefault`. 
        .format(                                                // Generate text.

                LocalDateTime                                   // Represent a date with a time-of-day. Does *not* represent a moment as it lacks the context of a time zone or offset from UTC.
                        .parse(                                 // Interpret text as a date-time value.
                                "2023-01-23 15:30:55"
                                        .replace( " " , "T" )   // Comply with ISO 8601 format. Then we can proceed with parsing without bothering to specify a formatting pattern.
                        )                                       // Returns a `LocalDateTime` object.
                        .atOffset( ZoneOffset.UTC )             // Returns a `OffsetDateTime` object.
                        .atZoneSameInstant(
                                ZoneId.systemDefault()
                        )                                       // Returns a `ZonedDateTime` object.

        )                                                       // Return a `String` object.

lundi 23 janvier 2023 à 07:30:55 heure normale du Pacifique nord-américain

Details

The Answer by CryptoFool and the Answer by Oliver are headed in the right direction. But I do not like that they directly inject an offset before parsing the date-time alone. I also do not like that they use ZonedDateTime where OffsetDateTime is more appropriate.

Apparently you have an input string with a date and a time-of-day but lacking the context of a time zone or offset from UTC. For example: "2023-01-23 15:30:55".

String input = "2023-01-23 15:30:55" ; 

That input nearly complies with the ISO 8601 standard format for date-time values. To fully comply, swap that SPACE in the middle with a T.

String modified = input.replace( " " , "T" ) ;

The java.time classes use ISO 8601 formats by default when parsing/generating text. So no need to specify a formatting pattern.

Parse as a date-with-time object, an LocalDateTime object.

LocalDateTime ldt = LocalDateTime.parse( modified );

You said you want to assume that date and time is intended to represent a moment as seen with an offset from UTC of zero hours-minutes-seconds. Java offers a constant for that offset, ZoneOffset.UTC. Apply that ZoneOffset to produce an OffsetDateTime object.

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );

Next you asked to adjust from that offset of zero to view the same moment in the JVM’s current default time zone. Same moment, same point on the timeline, but different wall-clock time.

Get that default zone.

ZoneId z = ZoneId.systemDefault() ;

Apply that ZoneId to the OffsetDateTime to get a ZonedDateTime.

ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

Generate text in localized format. Specify the Locale to use in localizing for human language and cultural norms.

Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale );
String output = zdt.format( f );

Pull all that code together.

String input = "2023-01-23 15:30:55";
String modified = input.replace( " " , "T" );
LocalDateTime ldt = LocalDateTime.parse( modified );

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );

ZoneId z = ZoneId.systemDefault();
ZonedDateTime zdt = odt.atZoneSameInstant( z );

Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale );
String output = zdt.format( f );

Dump to console.

input = 2023-01-23 15:30:55
modified = 2023-01-23T15:30:55
ldt = 2023-01-23T15:30:55
odt.toString() = 2023-01-23T15:30:55Z
z.toString() = America/Los_Angeles
zdt.toString() = 2023-01-23T07:30:55-08:00[America/Los_Angeles]
output = Monday, January 23, 2023 at 7:30:55 AM Pacific Standard Time

Upvotes: 7

Oliver
Oliver

Reputation: 1655

Welcome to Stack Overflow!

There are a couple issues with your code:

  • Look and code lines 3 and 4. You don't actually parse as UTC.
  • Since your appointmentStart string doesn't include the timezone, you need to specify that in your DateTimeFormatter.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    .withZone(ZoneOffset.UTC);

String appointmentStart = rs.getString("Start");

// UTC
ZonedDateTime ldtStart = ZonedDateTime.parse(appointmentStart, formatter);

// Local.
ZonedDateTime start = ldtStart.withZoneSameInstant(ZoneId.systemDefault());

Upvotes: 1

CryptoFool
CryptoFool

Reputation: 23139

There may be a slicker way to do this, but here's how I would do this:

// We have an unzoned datetime String that we assume is in UTC
String appointmentStart = "2022-12-24 11:00:00";

// Add a UTC time (+0) offset to the end of our String, and parse it to get a UTC-zoned ZonedDateTime
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ");
ZonedDateTime utcStart = ZonedDateTime.parse(appointmentStart + "+0000", formatter);

// Convert to the local timezone
ZonedDateTime localZonedStart = utcStart.withZoneSameInstant(ZoneId.systemDefault());

// Define a formatter that doesn't include the time zone
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

// Convert the local time to a String
String localZonedStartString = localZonedStart.format(formatter2);

// Print the resulting String
System.out.println(localZonedStartString);

Result:

2022-12-24 03:00:00

I'm in Pacific time zone, which is offset by -8 hours from UTC, and we see that the resulting string is correct for my local time zone.

Upvotes: 0

Related Questions