1

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?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    Welcome to Stack Overflow. Please be preciser about what you mean by *not converting*. Tell us your default time zone and give an example or two of value from database, expected result and observed result. – Ole V.V. Dec 26 '22 at 05:16
  • 1
    Don’t retrieve your date and time as string from the database. Assuming a `datetme` on the SQL side use `rs.getObject("Start", LocalDateTime.class)` to get a `LocalDateTime`, and you don’t need to parse. – Ole V.V. Dec 26 '22 at 05:18
  • 1
    What database engine? What *exactly* is the data type of your column? – Basil Bourque Dec 26 '22 at 05:21
  • Show example input and expected output. – Basil Bourque Dec 26 '22 at 05:25
  • Probably not important and not addressed at anyone in particular here, this is where the otherwise reasonable limit of 5 tags feels restrictive. I had wanted to add the zoneddatetime tag too and had ideas for even more. I did the best I could with 5, and anyone who can improve, please do. – Ole V.V. Dec 26 '22 at 05:27
  • Related and helpful answers [here](https://stackoverflow.com/questions/54108388/how-to-convert-utc-datetime-to-another-time-zone-using-java-8-library). – Ole V.V. Dec 26 '22 at 06:06

3 Answers3

1

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());
Oliver
  • 1,465
  • 4
  • 17
  • I'm glad to see your more direct way to parse a string with an unspecified zone as a UTC string. Seems, however, that you haven't answered the question. The question was how to interpret a zone-neutral string as a UTC datetime and then how to convert it to local time. – CryptoFool Dec 26 '22 at 04:35
  • Thanks for pointing that out! Your post is just as good and I wouldn't have posted mine if I saw yours. I wrote mine, stepped away and posted when I came back. – Oliver Dec 26 '22 at 04:39
  • I am glad you did post. I agree that there are now two good answers. I find `.withZone(ZoneOffset.UTC)` cleaner than the string concatenation in the answer by @CryptoFool. This answer also wins on spelling out the issues with the OP’s code. – Ole V.V. Dec 26 '22 at 05:33
1

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
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Please note that `OffsetDateTime` does not consider factors such as daylight saving time when representing a date and time with an offset from the Coordinated Universal Time (UTC). This means that it may not always accurately reflect the local time in a particular time zone. – Saif Ahmad Dec 26 '22 at 07:16
  • @SaifAhmad Yes, true. That is why `OffsetDateTime` is the appropriate choice when assigning an offset of zero to a date with time. No time zone involved. – Basil Bourque Dec 26 '22 at 08:10
  • I agree with @BasilBourque. Knowledge of the time zone is unimportant in the OP's use case and therefore, `ZonedDateTime` provides unnecessary overhead. This is definitely the best answer. – Oliver Dec 26 '22 at 15:57
0

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.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44