tl;dr
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
Semantically, we are done at this point with a OffsetDateTime
object in hand.
But you claim to be using an API that demands a ZoneDateTime
object. We have no known time zone to apply, so let’s apply UTC (an offset of zero hours-minutes-seconds).
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
.atZoneSameInstant( // Convert from `OffsetDateTime` to `ZonedDateTime` by applying a time zone.
ZoneOffset.UTC // This constant is a `ZoneOffset` object, whose class extends from `ZoneId`. So we can use it as a time zone, though semantically we are making a mess.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text in standard ISO 8601 format.
See this code run live at IdeOne.com.
2019-11-22T22:08:15Z
Caveat: The offset on your example input string looks wrong to me.
Details
You need to understand some concepts for date-time handling.
Offset
A offset-from-UTC is merely a number of hours-minutes-seconds ahead of, or behind, the meridian line drawn at the Greenwich Royal Observatory.
In Java, we represent an offset with the ZoneOffset
class. A date and time-of-day in the context of an offset is represented with the OffsetDateTime
class. Such an object represents a moment, a specific point on the timeline.
Time zone
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. These changes are determined by politicians. So these changes can be arbitrary and capricious, and happen surprisingly often, often with little or no warning. In North America, for example, most regions have adopted Daylight Saving Time (DST) nonsense, resulting in the offset changing twice a year. Currently there is a fad amongst politicians to quit DST changes while staying permanently year-round on “summer time”, one hour ahead of standard time.
There is a database cataloging these changes. The tZ data is a file maintained by IANA listing changes worldwide. You’ll likely find copies of this data in your host OS, in enterprise-quality database management systems such as Postgres, and in your Java Virtual Machine. Be sure to keep these up-to-date with changes in zones you care about.
Time zones have names in the format of Continent/Region
. For example, Africa/Tunis
, Europe/Paris
, and Asia/Kolkata
.
OffsetDateTime
So an input string like "2019-11-23T10:32:15+12:24" has no indicator of time zone, only an offset. So we must parse it an a OffsetDateTime
.
OffsetDateTime odt = OffsetDateTime.parse( "2019-11-23T10:32:15+12:24" ) ;
Asking for that as a ZonedDateTime
makes no sense. We cannot reliably determine a time zone merely from an offset. Many time zones may share an offset for some pints in time.
Also, that particular input string 2019-11-23T10:32:15+12:24
is suspect. That offset of twelve hours and twenty-four minutes does not map to any current time zone. Are you sure it is correct?
You can convert your OffsetDateTime
to a ZonedDateTime
by specifying a time zone to use in adjustment. I suggest using UTC. While this works technically, semantically it is confusing. Moments in UTC are best represented by OffsetDateTime
rather than ZonedDateTime
. But apparently you are interoperating with code that demands a ZonedDateTime
specifically, so c’est la vie.
ZonedDateTime zdt = odt.atZoneSameInstant( ZoneOffset.UTC ) ;
Instant
Tip: Generally, APIs should be written to hand off moments as an Instant
object, which is always in UTC by definition.
LocalDateTime
You present another string input, "2018-04-05 19:58:55". This input lacks any indicator of time zone or offset-from-UTC. So we cannot know if this means almost-8PM in Tokyo Japan, or almost-8PM in Toulouse France, or almost-8PM in Toledo Ohio US — which are all events happening several hours apart, different points on the time zone.
Such a value must be parsed as a LocalDateTime
. Replace the SPACE in the middle with a T
to comply with ISO 8601 standard formatting.
LocalDateTime ldt = LocalDateTime.parse( "2018-04-05 19:58:55".replace( " " , "T" ) ) ;
The resulting object does not represent a moment, is not a point in the timeline. Such an object represents potential moments along a spectrum of about 26-27 hours, the range of time zones around the globe.
ZonedDateTime
If you are certain that input string was intended for a particular time zone, apply a ZoneId
to get a ZonedDateTime
. Then you have determined a moment, a specific point on the timeline.
ZoneId z = ZonedId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

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
.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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.