tl;dr
Do I need to set timezone using withOffsetSameInstant(ZoneOffset.of("UTC"))));, or I can skip it?
No, you do not need to specify an offset. The Z in your input string already indicates an offset of zero.
Instant.parse( "2025-01-23T01:23:45Z" ) // That's all you need.
Generate text in standard ISO 8601 format by simply calling toString.
String output = instant.toString() ; // Returns: 2025-01-23T01:23:45Z
Simplify your custom class to:
import java.time.Instant;
…
private Instant created; // (A) Use `Instant` to represent a moment as seen in UTC. (B) No need for `String creationTime`. Generate text when needed.
Do not ignore Z
"yyyy-MM-dd'T'HH:mm:ss'Z'"
Never put single-quotes around Z in a date-time formatting pattern. Doing so ignores the vital meaning of that letter as an informational signal. The Z is short for an offset of zero hours-minutes-seconds, +00:00. Your quotes are deleting information.
The T in the middle should indeed have single-quotes around it. The single-quotes mean “expect but ignore the text”. The T is merely a delimiter without semantics. So we do indeed need to expect it but then want to ignore it as meaningless. Not so with Z.
ISO 8601
Your format complies with the ISO 8601 standard.
java.time.Instant
The java.time classes use standard ISO 8601 formats by default when parsing/generating text. So no need to specify a formatting pattern at all. Just call Instant.parse. That method uses the predefined formatting pattern in constant DateTimeFormatter.ISO_INSTANT.
Instant instant = Instant.parse( "2025-01-23T01:23:45Z" ) ;
UTC-time
I tried to migrate this code to:
You are workin too hard. No need for that code.
A java.time.Instant is always “in UTC”, meaning its offset is always zero, +00:00.
Your input string with a Z on the end means an offset of zero. So it parses directly as a Instant object with no adjustment to offset.
Prefer Instant over OffsetDateTime
If your goal is to represent a moment in UTC, just use Instant class. No need for OffsetDateTime. An Instant object and a java.time.OffsetDateTime object with an offset of zero (ZoneOffset.UTC), have the same meaning.
As a data field on your custom class, you should generally use Instant as the type if your intent is a moment as seen in UTC.
So likely your line private OffsetDateTime createdDate; should be private Instant createdDate;. And I recommend simple but clear variable naming. So I would shorten to private Instant created;.
JDBC
Generally, we only need OffsetDateTime for use with databases via JDBC.
JDBC 4.2 and later requires every JDBC driver to support a subset of the java.time types. The OffsetDateTime class is one of the types in that subset, but Instant (and ZonedDateTime) are not. That is because of the peculiar limitations in the SQL Standard (written by people who did not understand date-time handling).
Some JDBC drivers may offer support for Instant. You may choose to use such a feature, but know that your code may not port to other
When you need to exchange an Instant object with a database, convert to/from OffsetDateTime. For your convenience, the ZoneOffset class offers a constant for an offset of zero, ZoneOffset.UTC.
Writing to database
When writing to database, we call Instant#atOffset to convert to OffsetDateTime.
Instant created = Instant.now() ;
OffsetDateTime odt = created.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
Abbreviated:
myPreparedStatement.setObject( … , Instant.now().atOffset( ZoneOffset.UTC ) ) ;
Or even briefer, skip Instant to directly call OffsetDateTime.now.
myPreparedStatement.setObject( … , OffsetDateTime.now( ZoneOffset.UTC ) ) ;
⚠️ Be aware that some databases such as Postgres adjust inputs to UTC values for storage. And some do not.
Reading from database
When reading, fetch a OffsetDateTime object via JDBC, then convert to Instant.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;
Abbreviated:
Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;
⚠️ Many middleware tools & SQL clients have the anti-feature of dynamically applying some default time zone after retrieving date-time values. This creates the confusing illusion that the value was stored with that time zone. JDBC clients (that I know of) do not apply some time zone. So the code shown above should always give you the truth, the actual value stored by the database.
Stringto be parsed?DateTimeFormatter.ISO_INSTANT), then you can feed that directly to:OffsetDateTime.parse("2025-02-16T16:25:00Z");Zas a literal in single quotes in the format pattern.Zis an offset of 0 from UTC and needs to be parsed and formatted as an offset.