2

How do i convert ZonedDateTime to java.util.Date without changing the timezone.

In my below method when i call Date.from(datetime.toInstant()) it convert it to local time zone in my case SGT.

public static void printDate(ZonedDateTime datetime) {
    System.out.println("---> " + datetime.format(DateTimeFormatter.ofPattern(API_TIME_STAMP_PATTERN)));
    System.out.println(Date.from(datetime.toInstant()));
    System.out.println("\n");
}

Output

---> 2019-03-13_08:46:26.593
Wed Mar 13 16:46:26 SGT 2019
ETO
  • 6,970
  • 1
  • 20
  • 37
Jigar Naik
  • 1,946
  • 5
  • 29
  • 60
  • You are asling the impossible. A `Date` hasn’t got a time zone at all. See for example [Getting the current time millis from device and converting it into a new date with different timezone \[duplicate\]](https://stackoverflow.com/questions/52475409/getting-the-current-time-millis-from-device-and-converting-it-into-a-new-date-wi) (it’s sort of the opposite question, but the answer is the same). – Ole V.V. Oct 04 '21 at 20:24

4 Answers4

4

You can add offset millis by yourself. See the example using java.util.Date:

long offsetMillis = ZoneOffset.from(dateTime).getTotalSeconds() * 1000;
long isoMillis = dateTime.toInstant().toEpochMilli();
Date date = new Date(isoMillis + offsetMillis);
ETO
  • 6,970
  • 1
  • 20
  • 37
  • 1
    It’s very manual and it’s wrong. From your code I still get the `Wed Mar 13 16:46:26 SGT 2019` that the OP exactly did not want. My `offsetMillis` were 0, as I believe was the case for the OP. Had they not been 0, you would have converted to a `Date` representing a different point in time, which would have been still more wrong. – Ole V.V. Oct 04 '21 at 20:50
2

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*. However, for any reason, if you want to change java.time.ZonedDateTime to java.util.Date, I recommend you avoid any kind of manual calculations when you already have an inbuilt API to meet the requirement.

All you need to do is to add the offset to the input datetime which you can do by using ZonedDateTime#plusSeconds as shown below:

datetime = datetime.plusSeconds(datetime.getOffset().getTotalSeconds());
Date date = Date.from(datetime.toInstant());

Demo:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

public class Main {
    public static void main(String[] args) {
        // Test
        printDate(ZonedDateTime.now(ZoneId.of("Asia/Singapore")));
    }

    public static void printDate(ZonedDateTime datetime) {
        datetime = datetime.plusSeconds(datetime.getOffset().getTotalSeconds());
        Date date = Date.from(datetime.toInstant());

        // Showing date-time in Singapore timezone
        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Singapore"));
        System.out.println(sdf.format(date));
    }
}

Output:

2021-10-03T05:11:57

ONLINE DEMO

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.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

I would do as ETO states on his answer with the exception of using TimeUnit for getting the seconds converted into milliseconds:

long offsetMillis = TimeUnit.SECONDS.toMillis(ZoneOffset.from(dateTime).getTotalSeconds());
long isoMillis = dateTime.toInstant().toEpochMilli();
Date date = new Date(isoMillis + offsetMillis);

or the other option would be:

var localDate = LocalDateTime.now();
final long offSetHours = ChronoUnit.HOURS.between(localDate.atZone(ZoneId.of("YOUR_TIME_ZONE_ID")),
  localDate.atZone(ZoneId.of("UTC")));
return Date.from(Instant.parse(dateAsStringISO8601).plus(offSetHours, ChronoUnit.HOURS));
Damian229
  • 482
  • 5
  • 10
0

Stick to ZonedDateTime

To preserve the time zone simply preserve your ZonedDateTime. It can be formatted directly to the output your require. Don’t involve the outdated and poorly designed Date class.

private static final String API_TIME_STAMP_PATTERN = "yyyy-MM-dd_HH:mm:ss.SSS";

private static final DateTimeFormatter FORMATTER
        = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz y", Locale.ROOT);

public static void printDate(ZonedDateTime datetime) {
    System.out.println("---> " + datetime.format(DateTimeFormatter.ofPattern(API_TIME_STAMP_PATTERN)));
    System.out.println(datetime.format(FORMATTER));
    System.out.println("\n");
}

Try it out:

    ZonedDateTime zdt = ZonedDateTime.of(
            2019, 3, 13, 8, 46, 26, 593_000_000, ZoneId.of("Etc/UTC"));
    printDate(zdt);

Output:

---> 2019-03-13_08:46:26.593
Wed Mar 13 08:46:26 UTC 2019

Your conversion is not changing the time zone

A Date falsely pretends to have a time zone. It hasn’t got any. So there is no change of time zone going on. toInstant() discards the time zone because an Instant hasn’t got a time zone either. Date.from() performs the conversion withut any regard to time zone. System.out.println() implicitly calls Date.toString(). The toString() method uses the JVM’s default time zone for rendering the string. It’s pretty confusing alright.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161