Search code examples
javadatetimezonedate-conversionjava.util.date

Convert date to Eastern Time - Get Date object as output


I've seen many examples of converting date from one time zone to another. But all of them has output as a String. I want a date object as output.

The methods I've tried -

Approach 1

SimpleDateFormat dateTimeFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z");
dateTimeFormat.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
Date date = new Date();

System.out.println(dateTimeFormat.format(date)); // this print IST Timezone

DateFormat timeFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z");
timeFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));

String estTime = timeFormat.format(date);
try {
    date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z", Locale.ENGLISH).parse(estTime);
    System.out.println(date);

    } catch (ParseException ex) {
    Logger.getLogger(A.class.getName()).log(Level.SEVERE, null, ex);
}


System.out.println(timeFormat.format(date));

Approach 2

private static Date shiftTimeZone(Date date, TimeZone sourceTimeZone, TimeZone targetTimeZone) {
        System.out.println(sourceTimeZone.toString());
        System.out.println(targetTimeZone.toString());
        Calendar sourceCalendar = Calendar.getInstance();
        sourceCalendar.setTime(date);
        sourceCalendar.setTimeZone(sourceTimeZone);

        Calendar targetCalendar = Calendar.getInstance();
        for (int field : new int[]{Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND}) {
            targetCalendar.set(field, sourceCalendar.get(field));
        }
        targetCalendar.setTimeZone(targetTimeZone);

        return targetCalendar.getTime();
    }

Approach 1 gives me result as a String.

03/22/2018 10:16:57 AM EDT <- instanceOf String

Approach 2 gives me correct date and time of Eastern Time time zone, but the Date has the time zone of IST.

Thu Mar 22 10:16:57 IST 2018 <- instanceof Date

Can anyone please help me to obtain a Date object with Eastern Time TimeZone.

Update - My ultimate goal is to get Unix Timestamp of the current Eastern Time.


Solution

  • tl;dr

    Instant.now()                        // Capture the current moment in UTC, an `Instant` object.
        .atZone(                         // Adjust from UTC into a particular time zone.
            ZoneId.of( “Asia/Kolkata” ) 
        )                                // Returns a `ZonedDateTime` object. 
        .withZoneSameInstant(            // Adjust into yet another time zone. All three are the same moment but vowed using different wall-clock times. 
            ZoneId.of( “Africa/Tunis” ) 
        )                                // Returns another `ZonedDateTime` object. 
    

    Or…

    ZonedDateTime.now( 
        ZoneId.of( “Asia/Kolkata” )
    ).withZoneSameInstant(
        ZoneId.of( “Africa/Tunis” )
    )
    

    Avoid legacy date-time classes

    Firstly, stop using the legacy date-time classes. They are an awful wretched mess. Supplanted by the java.time classes.

    • Date is replaced by Instant.
    • Calendar is replaced by ZonedDateTime
    • SimpleDateFormat is replaced by DateTimeFormatter.

    Deceived by Date::toString

    Secondly, understand that Date has a horribly confusing feature of dynamically applying your JVM’s current default time zone while generating a String. Date always represents a moment in UTC. The toString method creates a false illusion of Date carrying a time zone, when actually its value is in UTC. While well-intentioned by the class designers, this was a disastrous decision, causing no end of confusion amongst countless programmers for decades now.

    Even worse: There actually is a time zone buried in a Date, but is irrelevant to this discussion. Confusing? Yes; as I said, an awful wretched mess of bad design.

    Instant

    The Instant class replacing Date is much clearer. An Instant represents a moment, a point on the timeline, always in UTC, with a resolution of nanoseconds.

    Use Instant to capture the current moment in UTC. The JVM’s current default time zone is irrelevant. The host OS’ assigned time zone is irrelevant.

    Instant instant = Instant.now() ;  // Capture the current moment in UTC. 
    

    Unlike Date::toString, the Instant::toString method tells the truth. A Instant is always in UTC, so toString always reports UTC. A String is generated in standard ISO 8601 format. The Z on the end is short for Zulu and means UTC.

    instant.toString(): 2018-01-23T12:34:56.123456789Z

    About capturing the current moment… In Java 8, the current moment was captured in milliseconds even though the java.time classes can represent nanoseconds. In Java 9 and later, a new implementation of Clock provides for capturing the current moment in finer granularity. In Java 9.0.4 on macOS Sierra, I see microseconds. The hardware clocks on conventional computers nowadays cannot capture the current moment with accuracy beyond microseconds.

    ZonedDateTime

    To view that same moment through the lens of a wall-clock time used by the people of a particular region, assign that region’s time zone. Applying a ZoneId to an Instant produces a ZonedDateTime. Conceptually:

    ZonedDateTime = ( Instant + ZoneId )

    In code:

    ZoneId z = ZoneId.of( “Pacific/Auckland” ) ;
    ZonedDateTime zdt = instant.atZone( z ) ;   // Same moment, same point on the timeline, different wall-clock time. 
    

    Adjusting to another time zone is easy. You can start with the Instant again.

    ZoneId zKolkata = ZoneId.of( “Asia/Kolkata” ) ;
    ZonedDateTime zdt = instant.atZone( zKolkata ) ;  
    

    Or you can adjust the ZonedDateTime object. The java.time classes use immutable objects. So rather than “mutate” (alter) the original object, the adjustment produces a new distinct object.

    ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( zKolkata ) ;  // Same moment, same point on the timeline, different wall-clock time. 
    

    You can skip the use of the Instant. I do not recommend doing so. Programmers should be doing their thinking, debugging, logging, exchanging of data, and much of their business logic in UTC. So Instant should be your go-to class whenever you start any work with date-time values.

    ZonedDateTime zdtNewYork = ZonedDateTime.now( ZoneId.of( "America/New_York" ) ) ;
    

    The ZonedDateTime::toString method wisely extends the ISO 8601 standard by appending the name of the time zone in square brackets.

    String output = zdtNewYork.toString() ;
    

    2018-01-23T07:34:56.123456789-05:00[America/New_York]


    About java.time

    The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

    The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

    To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

    You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

    Where to obtain the java.time classes?

    The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.