Divya Singh
Divya Singh

Reputation: 167

Unwanted timezone conversion for an incoming timestamp in spring boot app

I'm trying to format the incoming timestamp in my spring boot app, but the code that I wrote is changing the timezone of the incoming timestamp. It is adding 5:30 hrs on its own which I don't want.

Here is my snippet:

String startTime = searchParams.getDeploymentStartDate();
System.out.println("startTime from req body in utc:"+startTime);
DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd' HH:mm:ss"
             , Locale.ENGLISH).ISO_OFFSET_DATE_TIME;
Date startDate = Date.from(ZonedDateTime.parse(startTime, targetFormatter).toInstant());
System.out.println("startDate converted to desired format in IST:"+startDate);

Actual output:

startTime from req body in utc:2023-03-22T11:27:04Z
startDate converted to desired format in IST:Wed Mar 22 16:57:04 IST 2023 

Desired output:

startTime from req body in utc:2023-03-22T11:27:04Z
startDate converted to desired format in IST:Wed Mar 22 11:27:04 IST 2023

Please help me find a way to convert the format without converting timezone.

Upvotes: 1

Views: 2889

Answers (2)

Anish B.
Anish B.

Reputation: 16469

Please don't use java.util.Date as it's poorly designed and may be deprecated in future.

Instead use java.time.LocalDateTime or java.time.ZonedDateTime.

Update:

You have to use ZonedDateTime.withZoneSameLocal(ZoneId zone) if you want to keep the same time with the change in timezone only.

From ZonedDateTime.withZoneSameLocal(ZoneId zone) JDK 8 doc :

Returns a copy of this date-time with a different time-zone, retaining the local date-time if possible. This method changes the time-zone and retains the local date-time.

So, update the code (without using java.util.Date) to this:

String startTime = searchParams.getDeploymentStartDate();
System.out.println("startTime from req body in utc:" + startTime);
ZonedDateTime ist = ZonedDateTime.parse(startTime)
                .withZoneSameLocal(ZoneId.of("Asia/Kolkata"));
System.out.println("startDate converted to desired format in IST:" 
             + ist.format(DateTimeFormatter.ofPattern("E MMM dd HH:mm:ss z u")));

Output:

startTime from req body in utc:2023-03-22T11:27:04Z
startDate converted to desired format in IST:Wed Mar 22 11:27:04 IST 2023

Note: If you want the timestamp in a different timezone, then just pass that timezone in ZoneId.of(...)

I'm providing a list of ZoneIds from the official doc that are supported :

EST - -05:00
HST - -10:00
MST - -07:00
ACT - Australia/Darwin
AET - Australia/Sydney
AGT - America/Argentina/Buenos_Aires
ART - Africa/Cairo
AST - America/Anchorage
BET - America/Sao_Paulo
BST - Asia/Dhaka
CAT - Africa/Harare
CNT - America/St_Johns
CST - America/Chicago
CTT - Asia/Shanghai
EAT - Africa/Addis_Ababa
ECT - Europe/Paris
IET - America/Indiana/Indianapolis
IST - Asia/Kolkata
JST - Asia/Tokyo
MIT - Pacific/Apia
NET - Asia/Yerevan
NST - Pacific/Auckland
PLT - Asia/Karachi
PNT - America/Phoenix
PRT - America/Puerto_Rico
PST - America/Los_Angeles
SST - Pacific/Guadalcanal
VST - Asia/Ho_Chi_Minh

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 338564

tldr

java.util.Date juDate = 
    Date                                   // Legacy class. Best to avoid wherever possible. Represents a moment as seen in UTC (zero offset) but it’s poorly designed `toString` applies the JVM’s current default time zone while generating text. 
    .from(                                 // A new method on old legacy class to bridge between legacy and modern classes. 
        java.time.Instant                  // The modern class for representing a moment as seen with an offset of zero. 
        .parse( "2023-03-22T11:27:04Z" )   // Parsing text in standard ISO 8601 format. 
    )
;

Best to avoid the terribly flawed legacy date-time class java.util.Date. Use only java.time classes.

Instant
.parse( "2023-03-22T11:27:04Z" )  // Returns an `Instant` object. 
.atZone(                          // Adjust from UTC to a specific time zone. 
    ZoneId.of( "Asia/Kolkata" )   // Returns a `ZoneId` object. 
)                                 // Returns a `ZonedDateTime` object. 

Incorrect formatting pattern, also unnecessary

2023-03-22T11:27:04Z

DateTimeFormatter.ofPattern("yyyy-MM-dd' HH:mm:ss" , Locale.ENGLISH).ISO_OFFSET_DATE_TIME;

Your formatting pattern for parsing does not match the input data. The Z on the end of the input indicates the date and time represent a moment as seen with an offset from UTC of zero hours-minutes-seconds. Your formatter ignores that crucial part.

And, the formatting pattern needs other fixes: a stray single-quote mark, and a SPACE that should instead be T.

Furthermore, there is no need to define this formatting pattern at all. Your input text happens to comply with the ISO 8601 format. The java.time classes use ISO 8601 formats by default when parsing/generating text.

Instant instant = Instant.parse( "2023-03-22T11:27:04Z" ) ;

Avoid legacy date-time types

Date startDate = …

Both Date classes bundled with Java are terribly flawed, designed by people who did not understand date-time handling. Avoid these classes. Use only the java.time classes defined in JSR 310.

If you must have an object of the legacy classes to interoperate with old code not yet updated to java.time, convert using new methods added to the old classes.

java.util.Date juDate = java.util.Date.from( instant ) ;

Beware: The toString method of java.util.Date unfortunately applies the JVM’s current time zone while generating its resulting text. This design flaw may be adding to your confusion. A java.util.Date actually represents a moment with an offset from UTC of zero hours-minutes-seconds.

Adjusting to a time zone

An Instant represents a moment as seen with an offset from UTC of zero hours-minutes-seconds.

If you want to view that moment through the lens of a particular time zone, apply a ZoneId to get a ZonedDateTime.

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time. 

Upvotes: 1

Related Questions