Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - ZonedDateTime does not correctly convert to Date object?

I have the following Java code:

Date convertedDate = Date.from(zonedDateTime.toInstant());

The Issue I am having is that convertedDateis not correct in comparison to the zonedDateTime object.

For Example when I have a zonedDateTime object of:

2021-09-16T12:00

with zone:

Africa/Abidjan

The code above converts this to:

Thu Sep 16 13:00:00 BST 2021

What I would be expecting here is

 Thu Sep 16 10:00:00 BST 2021

As the Africa/Abidjan timezone is 2 hours ahead of BST.

How can I solve this?

like image 404
java12399900 Avatar asked Jul 19 '21 15:07

java12399900


People also ask

How do I get datetime from ZonedDateTime?

You can convert ZonedDateTime to an instant, which you can use directly with Date. No, it will be the current Date on your zone system default.

What is the format of ZonedDateTime?

Class ZonedDateTime. A date-time with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris . ZonedDateTime is an immutable representation of a date-time with a time-zone.

Should I use ZonedDateTime or OffsetDateTime?

Use OffsetDateTime to store unique instants in the universal timelines irrespective of the timezones, such as keeping the timestamps in the database or transferring information to remote systems worldwide. Use ZonedDateTime for displaying timestamps to users according to their local timezone rules and offsets.

Is ZonedDateTime a UTC?

A ZonedDateTime represents a date-time with a time offset and/or a time zone in the ISO-8601 calendar system. On its own, ZonedDateTime only supports specifying time offsets such as UTC or UTC+02:00 , plus the SYSTEM time zone ID.

How to convert from zoneddatetime to old-school Java time?

The Answer by ssoltanid correctly addresses your specific question, how to convert a new-school java.time object ( ZonedDateTime) to an old-school java.util.Date object. Extract the Instant from the ZonedDateTime and pass to java.util.Date.from ().

Is zoneddatetime the same as the current date?

You can convert ZonedDateTime to an instant, which you can use directly with Date. No, it will be the current Date on your zone system default. @MilenKovachev Your question does not make sense - a Date does not have a time zone - it only represents an instant in time. @assylias Actually, your statement doesn't make sense.

Why doesn't Java support time zones in datetime?

Date is based on UTC, unfortunately, Java does some stupid things and doesn't treat it as such, and on top of it, considers the time to be local TZ instead of UTC. The way Data,LocalDateTime,ZonedDateTime store data implies a timezone.

How to create a zoneddatetime with a time zone of UTC?

First, we’ll start with a ZonedDateTime with a time zone of UTC. There are several ways we can accomplish this. We can specify the year, month, day, etc: We can also create a ZonedDateTime from the current date and time: Or, we can create a ZonedDateTime from an existing LocalDateTime: 3. ZonedDateTime to String


Video Answer


2 Answers

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Solution using java.time, the modern Date-Time API:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        // The given ZonedDateTime
        ZonedDateTime zdtAbidjan = ZonedDateTime.of(
                                        LocalDateTime.of(LocalDate.of(2021, 9, 16),
                                        LocalTime.of(12, 0)),
                                        ZoneId.of("Africa/Abidjan")
                                    );
        System.out.println(zdtAbidjan);

        ZonedDateTime zdtLondon = zdtAbidjan.withZoneSameInstant(ZoneId.of("Europe/London"));
        System.out.println(zdtLondon);
    }
}

Output:

2021-09-16T12:00Z[Africa/Abidjan]
2021-09-16T13:00+01:00[Europe/London]

ONLINE DEMO

  • The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

  • From the output, it is clear that 2021-09-16T12:00Z[Africa/Abidjan] is equal to 2021-09-16T13:00+01:00[Europe/London].

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

like image 140
Arvind Kumar Avinash Avatar answered Oct 24 '22 23:10

Arvind Kumar Avinash


Answer by Avinash is correct. Here are a few more thoughts.

Time zone names

BST is not a real time zone name. Perhaps you meant Europe/London. And that is not GMT/UTC. The London time zone can vary in its offset, because of Daylight Saving Time (DST) and perhaps other reasons.

UTC

Let's look at your moment in each of the three different time zones.

First we parse your input as a LocalDateTime, lacking the context of a time zone or offset-from-UTC. Then we assign a time zone for Abidjan as the context to produce a ZonedDateTime object. We adjust to another time zone, resulting in a second ZonedDateTime that represents the same moment, the same point on the timeline, but a different wall-clock time. Lastly, we extract a Instant to effectively adjust into UTC. A Instant represents a moment in UTC, always in UTC.

LocalDateTime ldt = LocalDateTime.parse( "2021-09-16T12:00" ) ;
ZonedDateTime zdtAbidjan = ldt.atZone( ZoneId.of( "Africa/Abidjan" ) ) ;
ZonedDateTime zdtLondon = zdtAbidjan.withZoneSameInstant( ZoneId.of( "Europe/London" ) ) ;
Instant instant = zdtAbidjan.toInstant() ;  // Adjust to UTC by extracting an `Instant` object.

See this code run live at IdeOne.com.

ldt: 2021-09-16T12:00
zdtAbidjan: 2021-09-16T12:00Z[Africa/Abidjan]
zdtLondon: 2021-09-16T13:00+01:00[Europe/London]
instant: 2021-09-16T12:00:00Z

The Z at the end means an offset of zero hours-minutes-seconds, pronounced “Zulu”. So we can zee that noon on that September day in Côte d'Ivoire is the same as in UTC, having an offset of zero. In contrast, the +01:00 tells us that London time is an hour ahead. So the clock reads 1 PM (13:00) rather than noon.

Fetch the offset

You can determine the offset in effect at a particular moment via the ZoneRules class. The offset info is represented by the ZoneOffset class.

ZoneId z = ZoneId.of( "Africa/Abidjan" ) ;
ZoneRules rules = z.getRules() ;
ZoneOffset offset = rules.getOffset( LocalDateTime.parse( "2021-09-16T12:00" ) ) ;
int offsetInSeconds = offset.getTotalSeconds() ;

Or condense that to:

ZoneId
.of( "Africa/Abidjan" )
.getRules()
.getOffset( LocalDateTime.parse( "2021-09-16T12:00" ) )
.getTotalSeconds()

When run we see again that Côte d'Ivoire is using an offset of zero at that date-time.

rules: ZoneRules[currentStandardOffset=Z]
offset: Z
offsetInSeconds: 0

Table of date-time types in Java, both modern and legacy

like image 20
Basil Bourque Avatar answered Oct 24 '22 22:10

Basil Bourque