I have received a string in format "yyyy-MM-dd'T'HH:mm:ssXXX"
e.g. "2020-06-01T11:04:02+02:00"
I want to convert it into "yyyy-MM-dd'T'HH:mm:ss:SSSZ"
e.g "2020-06-01T11:04:02.000+0200"
I don't know the time zone actually. It should take from that last part of string as it is.
I have tried but it is taking my local time and time zone when I convert string to date(i.e IST).
SimpleDateFormat sd1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
//sd1.setTimeZone(TimeZone.getTimeZone("PST"));
Date dt = sd1.parse("2020-06-01T11:04:02+02:00");
SimpleDateFormat sd2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
System.out.println(sd2.format(dt));
Output:
2020-06-01T14:34:02:000+0530
Only date is right, time and timezone has changed.
I know I am doing it wrong, it will be really helpful if someone can tell me how can I do this.
Thanks for the help.
OffsetDateTime
You said:
I have received a string in format "yyyy-MM-dd'T'HH:mm:ssXXX"
e.g. "2020-06-01T11:04:02+02:00"
No need to define a formatting pattern. Your input complies with the ISO 8601 standard.
These standard formats are used by default in the java.time classes when parsing/generating strings.
Your input should be parsed as a OffsetDateTime.
String input = "2020-06-01T11:04:02+02:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;
odt.toString(): 2020-06-01T11:04:02+02:00
Offset-from-UTC versus time zone
You said:
I don't know the time zone actually.
That +02:00 on the end is not a time zone. That text represents a mere offset-from-UTC. An offset is just a number of hours-minutes-seconds, positive or negative. 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. A time zone has a name in the format of Continent/Region, such as Europe/Brussels or Africa/Cairo.
You can adjust from a mere offset to a specific time zone. Apply a ZoneId to get a ZonedDateTime.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
zdt.toString(): 2020-06-01T14:34:02+05:30[Asia/Kolkata]
You said:
It should take from that last part of string as it is.
I am not sure what you meant by that. If you parse your input as an OffsetDateTime, that object knows its offset, accessible as a ZoneOffset.
ZoneOffset offset = odt.getOffset() ;
See the code shown in this Answer run live at IdeOne.com.
offset.toString(): +02:00
Formatting strings
You said:
I want to convert it into "yyyy-MM-dd'T'HH:mm:ss:SSSZ"
e.g "2020-06-01T11:04:02.000+0200"
Not sure what you mean here. Do you mean to force the display of milliseconds even if the value is zero? Firstly, you should know that java.time objects have a resolution of nanoseconds for up to nine decimal digits, much finer that the milliseconds shown in 3 digits of a decimal fraction. Secondly, forcing display of fractional second has been covered on Stack Overflow, such as here. Always search Stack Overflow before posting.
Or do you mean displaying the offset without a COLON character as a delimiter between minutes and seconds?
I advise against this. While dropping the COLON is technically allowed by the ISO 8601 standard, I have seen more than one software library or system fail to handle an offset without that delimiter. Ditto for using an offset of hours without the minutes. I advise always using the hours, the minutes, and the delimiter.
If you insist, use DateTimeFormatter with a formatting pattern. Study the Javadoc, keeping mind that the formatting codes are (a) case-sensitive, and (b) sensitive to repeating the character 0, 1, or more times. Here we use xx to get the hours and minutes of an offset without the COLON character delimiting. (Again, I do not recommend that format.)
Code shown in that same IdeOne.com page.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSxx" ) ;
String output = odt.format( f ) ;
output: 2020-06-01T11:04:02.000+0200
Date::toString injects time zone
You said:
I have tried but it is taking my local time and time zone when I convert string to date(i.e IST).
The java.util.Date::toString method tells a lie. While well-intentioned, that method unfortunately applies the JVM’s current default time zone to the Date value as it generates the text. The Date class actually represents a moment in UTC. This is one of many reasons to never use Date. That class nowadays is replaced by java.time.Instant.
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. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The first suggestion I would make to you is to switch from using the Date object to LocalDateTime (java 8+)
Using the new API would work in this way
String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
LocalDateTime dateTime = LocalDateTime.parse(input_date, formatter);
//Then you can set your timezone in this way - remember to replace the values with the proper timezone you want.
ZonedDateTime zonedUTC = dateTime.atZone(ZoneId.of("UTC"));
ZonedDateTime zonedIST = zonedUTC.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
let me know if that works for you
To elaborate the comment and picking up on #Daniel Vilas-Boas answer, you should go for Java8 and I think what you want is something like:
public static void main(String[] args) {
String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
String YOUR_NEW_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss:SSSZ";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern(YOUR_NEW_DATE_TIME_PATTERN);
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-06-01T11:04:02+02:00", formatter);
ZoneId from = ZoneId.from(zonedDateTime);
System.out.println(from);
ZonedDateTime zonedIST = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
System.out.println(zonedDateTime.format(newFormatter));
System.out.println(zonedIST.format(newFormatter));
}
The prints should print:
2020-06-01T11:04:02:000+0000
2020-06-01T16:34:02:000+0530
EDIT
Included ZoneId to allow handling different timezones.
Related
I'm a little bit frustrated of java 8 date format/parse functionality. I was trying to find Jackson configuration and DateTimeFormatter to parse "2018-02-13T10:20:12.120+0000" string to any Java 8 date, and didn't find it.
This is java.util.Date example which works fine:
Date date = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ")
.parse("2018-02-13T10:20:12.120+0000");
The same format doesn't work with new date time api
ZonedDateTime dateTime = ZonedDateTime.parse("2018-02-13T10:20:12.120+0000",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'hh:mm:ss.SSSZZZ"));
We should be able to format/parse date in any format suitable for FE UI application. Maybe I misunderstand or mistake something, but I think java.util.Date gives more format flexibility and easier to use.
tl;dr
Until bug is fixed:
OffsetDateTime.parse(
"2018-02-13T10:20:12.120+0000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
)
When bug is fixed:
OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" )
Details
You are using the wrong classes.
Avoid the troublesome old legacy classes such as Date, Calendar, and SimpleDateFormat. Now supplanted by the java.time classes.
The ZonedDateTime class you used is good, it is part of java.time. But it is intended for a full time zone. Your input string has merely an offset-from-UTC. A full time zone, in contrast, is a collection of offsets in effect for a region at different points in time, past, present, and future. For example, with Daylight Saving Time (DST) in most of North America, the offsets change twice a year growing smaller in the Spring as we shift clocks forward an hour, and restoring to a longer value in the Autumn when we shift clocks back an hour.
OffsetDateTime
For only an offset rather than a time zone, use the OffsetDateTime class.
Your input string complies with the ISO 8601 standard. The java.time classes use the standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.
OffsetDateTime odt = OffsetDateTime.parse( "2018-02-13T10:20:12.120+0000" );
Well, that should have worked. Unfortunately, there is a bug in Java 8 (at least up through Java 8 Update 121) where that class fails to parse an offset omitting the colon between hours and minutes. So the bug bites on +0000 but not +00:00. So until a fix arrives, you have a choice of two workarounds: (a) a hack, manipulating the input string, or (b) define an explicit formatting pattern.
The hack: Manipulate the input string to insert the colon.
String input = "2018-02-13T10:20:12.120+0000".replace( "+0000" , "+00:00" );
OffsetDateTime odt = OffsetDateTime.parse( input );
DateTimeFormatter
The more robust workaround is to define and pass a formatting pattern in a DateTimeFormatter object.
String input = "2018-02-13T10:20:12.120+0000" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" );
OffsetDateTime odt = OffsetDateTime.parse( input , f );
odt.toString(): 2018-02-13T10:20:12.120Z
By the way, here is a tip: I have found that with many protocols and libraries, your life is easier if your offsets always have the colon, always have both hours and minutes (even if minutes are zero), and always use a padding zero (-05:00 rather than -5).
DateTimeFormatterBuilder
For a more flexible formatter, created via DateTimeFormatterBuilder, see this excellent Answer on a duplicate Question.
Instant
If you want to work with values that are always in UTC (and you should), extract an Instant object.
Instant instant = odt.toInstant();
ZonedDateTime
If you want to view that moment through the lens of some region’s wall-clock time, apply a time zone.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );
See this code run live at IdeOne.com.
All of this has been covered many times in many Answers for many Questions. Please search Stack Overflow thoroughly before posting. You would have discovered many dozens, if not hundreds, of examples.
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?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
Short: Not a bug, just your pattern is wrong.
Please use the type OffsetDateTime which is especially designed for time zone offsets and use a pattern this way:
OffsetDateTime odt =
OffsetDateTime.parse(
"2018-02-13T10:20:12.120+0000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSZZZ" )
)
Problems in detail:
a) 12-hour-clock versus 24-hour-clock
"h" indicates the hour of AM/PM on a 12-hour-clock but you obviously need "H" for the 24-hour-clock as required by ISO-8601.
b) The form of zero offset
If you want to parse zero offset like "+0000" instead of "Z" (as described in ISO-paper) you should not use the pattern symbol "X" but "ZZZ". Citing the pattern syntax:
Offset Z: This formats the offset based on the number of pattern
letters. One, two or three letters outputs the hour and minute,
without a colon, such as '+0130'. The output will be '+0000' when the
offset is zero.
c) Your input is NOT ISO-8601-compatible therefore no bug in Java
Your assumption that "2018-02-13T10:20:12.120+0000" shall be valid ISO is wrong because you are mixing basic format (in the offset part) and extended format which is explicitly prohibited in ISO-paper (see sections 4.3.2 (example part) and 4.3.3d). Citing ISO-8601:
[...]the expression shall either be completely in basic format, in which
case the minimum number of separators necessary for the required
expression is used, or completely in extended format[...]
The statement of B. Bourque that java.time has a bug is based on the same wrong expectation about ISO-compatibility. And the documentation of let's say ISO_OFFSET_DATE_TIME describes the support of the extended ISO-format only. See also the related JDK issue. Not all ISO-8601-variants are directly supported hence a pattern-based construction of the parser in the right way is okay.
if offset +0000 try this
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" )
LocalDate from =LocalDate.parse("2018-02-13T10:20:12.120+0000",f);
just a question what i am doing wrong. I have this code:
public static int berechneSekundenwert(String datum, String zeit) throws ParseException {
Date dt = new Date();
SimpleDateFormat df = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss" );
dt = df.parse( datum+" "+ zeit);
int gesamtzeit = (int)dt.getTime();
return gesamtzeit;
}
Now my import format is:
09.11.2019 01:30:17
What i want to do is calculate the time passed for these dates, so i
can later sort them by time. But i get negative values?!
Example output (passed time, date, daytime):
-2120215336 30.09.2019 12:03:35
1757321960 25.09.2019 16:06:25
-2111322336 30.09.2019 14:31:48
-1281127040 21.08.2019 12:05:36
-1280681040 21.08.2019 12:13:02
377782960 09.09.2019 16:54:06
1301386664 09.11.2019 01:30:17
710621960 13.09.2019 13:21:25
712564960 13.09.2019 13:53:48
Shouldn't they all be positive, since java states, that the getTime function measures the time since 01.01.1970
Anyone knows what i did wrong?
Computers use something called a timestamp to represent dates. In Java, Date::getTime() returns the milliseconds passed since 1970-01-01T00:00:00.000Z up to the date in question as long (64-bit integer).
In the code presented, this value is narrowed down to an int (32-bit integer). By narrowing the long to an int, the highest 32 bits get cut of. The largest value representable by an int is 2^31 - 1. A quick calculation shows that:
(2^31 - 1) (milliseconds)
/ 1000 (milliseconds per second)
/ 60 (seconds per minute)#
/ 60 (minutes per hour)
/ 24 (hours per day)
= 24.8551348032 (days)
This means that after roughly 25 days, the int will overflow (as it is defined in the Two's compliment). Not to mention that a later point in time could have a lower value than an earlier point in time, thus the negative values.
To fix this issue1, I would suggest to define gesamtzeit as long.
Two remarks on your code:
java.util.Date is regarded as outdated. I would suggest to use java.time.Instant instead.
I would suggest to use English in the source code, only exception being you use domain-specific words that cannot (well) be translated to English.
1 This is only a temporary fix. All representation with a fixed number of bits will eventually overflow. In fact, all representation with any memory constraint at all will overflow eventually. I leave it up to the reader to find out when a 64-bit integer will overflow
tl;dr
See correct Answer by Turing85 about 32-bit versus 64-bit integers.
Use only modern java.time classes, never Date/SimpleDateFormat.
Consider the crucial issue of time zone or offset-from-UTC.
Educate the publisher of your data about the importance of (a) including zone/offset info, and (b) using ISO 8601 standard formats.
Code:
LocalDateTime.parse(
"09.11.2019 01:30:17" ,
DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" )
)
.atOffset(
ZoneOffset.UTC
)
.toInstant()
.toEpochMilli()
See this code run live at IdeOne.com.
1573263017000
Details
The correct Answer by Turing85 addresses your specific question as to why the invalid negative numbers. But you have other problems.
ISO 8601
Now my import format is: 09.11.2019 01:30:17
I suggest you educate the publisher of this data about the ISO 8601 standard defining formats to use when communicating date-time values as text.
Legacy date-time classes
You are use terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310. Never use Date or SimpleDateFormat.
Moment
Apparently you want to get a count of milliseconds since the epoch reference of first moment of 1970 in UTC. But doing that requires a moment, a specific point on the timeline.
Your input does not meet this requirement. Your input is a date and a time-of-day but lacks the context of an offset-from-UTC or a time zone.
So, take your example of 09.11.2019 01:30:17. We cannot know if this is 1:30 in the afternoon of Tokyo Japan, or 1:30 PM in Paris France, or 1:30 in Toledo Ohio US — which are all very different moments, several hours apart on the timeline.
So we must first parse your input as a LocalDateTime. This class represent a date and time without any concept of offset or zone.
String input = "09.11.2019 01:30:17" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
Perhaps you know for certain the offset or zone intended by the publisher of this data. If so:
Suggest to the publisher of this data that they include the zone/offset info within their data.
Apply a ZoneOffset to get an OffsetDateTime, or a ZoneId to get a ZonedDateTime.
Perhaps you know for certain this input was intended for UTC, that is, an offset of zero hours-minutes-seconds.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
To get a count of milliseconds since 1970-01-01T00:00Z convert to the basic building-block class Instant.
Instant instant = odt.toInstant() ;
Interrogate for a count of milliseconds since epoch.
long millisSinceEpoch = instant.toEpochMilli() ;
Understand that your original code ignored the crucial issue of time zone & offset-from-UTC. So your code implicitly applies the JVM's current default time zone. This means your results will vary at runtime, and means you likely have incorrect results too.
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?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
why you downcast the return value ofgetTime()?
just make you method return long instead of int
and replace this line
int gesamtzeit = (int)dt.getTime();
with
long gesamtzeit = dt.getTime();
I'm working on a project which takes rrule to generate next occurrences. But I'm not able to understand what i need to put in UNTIL tag of rrule.
String str="RRULE:FREQ=MONTHLY;UNTIL=20190625T000000Z;INTERVAL=2;";
Idk how to convert date into "20190625T000000Z".I'm using rfc 2445 java library. If user enters the date as a string for example :25/06/2019......i need to set this value in UNTIL tag as shown above. If I set the default value in UNTIL then it works but not when i make it user friendly.. I'm taking all the values from user as start date, end date, interval, Byday,Until... But idk what value to set in UNTIL.
If someone can help.. Thanks in advance.
Parsing basic ISO 8601 format
Your input 20190625T000000Z is the “basic” variation of standard ISO 8601 format to represent a moment in UTC. The word “basic” means minimizing the use of delimiters (I do not recommend this, as it makes the string less readable by humans).
Defining a formatting pattern to match input.
String input = "20190625T000000Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmssX" );
OffsetDateTime odt = OffsetDateTime.parse( input , f );
Dump to console.
System.out.println("odt.toString(): " + odt);
See this code run live at IdeOne.com.
odt.toString(): 2019-06-25T00:00Z
Translating date to moment
If user enters the date as a string for example :25/06/2019......i need to set this value in UNTIL tag as shown above
First, parse that input string into a LocalDate, representing a date-only value, without time-of-day and without time zone.
DateTimeFormatter fDateOnly = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );
LocalDate ld = LocalDate.parse( "25/06/2019" , fDateOnly );
ld.toString(): 2019-06-25
As for translating that date into a moment (a date with time-of-day in a zone or offset-from-UTC), that is trickier than it sounds intuitively.
A date such as the 25th of June 2019 represents an entire day. And a theoretical date at that. The moments when a day begins and ends varies around the globe by time zone. A new day begins much earlier in Tokyo Japan than in Paris France, and even later in Montréal Québec.
Another issue is that the day does not always begin at 00:00:00. Because of anomalies such as Daylight Saving Time (DST), the first moment of a day on some dates in some zones may be something like 01:00:00. Let the java.time classes determine first moment.
ZoneId z = ZoneId.of( "Africa/Tunis" );
ZonedDateTime zdt = ld.atStartOfDay( z );
zdt.toString(): 2019-06-25T00:00+01:00[Africa/Tunis]
That ZonedDateTime object represents a specific moment. But it uses the wall-clock time adopted by the people of a particular region (a time zone). Your goal is a moment in UTC. Fortunately, we can adjust from the zone to UTC by converting to an OffsetDateTime (a date and time with a context of offset-from-UTC rather than a time zone). We can specify UTC (an offset of zero) by the ZoneOffset.UTC constant.
OffsetDateTime odt = zdt.toOffsetDateTime().withOffsetSameInstant( ZoneOffset.UTC );
odt.toString(): 2019-06-24T23:00Z
Note how 00:00 on the 25th in Tunisia is 11 PM “yesterday” the 24th in UTC. Same moment, same simultaneous point on the timeline, but two different wall-clock times.
Lastly, we need a string in that “basic” ISO 8601 format. Use the same formatter we defined above.
DateTimeFormatter fIso8601DateTimeBasic = DateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmssX" );
String output = odt.format( fIso8601DateTimeBasic );
output: 20190624T230000Z
See this code run live at IdeOne.com.
Just what is the difference between a time zone and an offset-from-UTC? An offset is merely a number of hours-minutes-seconds. Nothing more, nothing less, just a number (well, three numbers). A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. For example, in most of North America, the offset changes twice a year, springing ahead an hour and then falling back an hour (the lunacy of Daylight Saving Time (DST)).
Tip: Date-time handling is surprisingly tricky and slippery. If you are working with calendars and the iCalendar spec for data exchange, I suggest you take a long while to study the concepts and practice with the industry-leading java.time classes.
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?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
I am very new to OffsetDateTime usage and I am trying to compare OffsetDateTime strings with OffsetDateTime.now() in java this way,
import java.time.OffsetDateTime;
public class OffsetDateTimeDemo {
public static void main(String[] args) {
OffsetDateTime one = OffsetDateTime.parse("2017-02-03T12:30:30+01:00");
System.out.println("First ::" + OffsetDateTime.now().compareTo(one));
OffsetDateTime date1 = OffsetDateTime.parse("2019-02-14T00:00:00");
System.out.println("Second ::" + OffsetDateTime.now().compareTo(date1));
OffsetDateTime date3 = OffsetDateTime.parse("Mon Jun 18 00:00:00 IST 2012");
System.out.println(" Third :: " +OffsetDateTime.now().compareTo(date3));
}
}
But I am getting java.time.format.DateTimeParseException in all the 3 cases.
However if i compare 2 OffsetDateTime Strings with CompareTo method its working fine.
Can someone shed some light to me in this regard and kindly guide me through my mistake.
Thanks in Advance.
Your compareTo coding is a distraction. Your exception is about parsing the string inputs into objects.
Another problem: You are using wrong classes on the 2nd and 3rd inputs.
Another problem: You are relying implicitly on your JVM’s current default time zone when calling now(). Poor practice as any programmer reading will not know if you intended the default or if you were unaware of the issue as are so many programmers. Furthermore, the current default can be changed at any moment during runtime by any code in any thread of any app within the JVM. So better to always specify explicitly your desired/expected zone or offset.
OffsetDateTime.now(
ZoneOffset.UTC
)
Or better yet, use a ZonedDateTime to capture more information than a OffsetDateTime.
ZonedDateTime.now(
ZoneId.of( "Pacific/Auckland" )
)
First: OffsetDateTime works
Your first string input is proper, and parses successfully.
OffsetDateTime.parse( "2017-02-03T12:30:30+01:00" )
Full line of code:
OffsetDateTime odt = OffsetDateTime.parse( "2017-02-03T12:30:30+01:00" ) ;
See this code run live at IdeOne.com.
odt.toString(): 2017-02-03T12:30:30+01:00
To compare, extract an Instant. Doing so effectively adjusts your moment from some offset to an offset of zero, or UTC itself. An Instant is always in UTC, by definition.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
boolean odtIsPast = odt.toInstant().isBefore( instant ) ;
Second: LocalDateTime
Your second string input lacks any indicator of offset-from-UTC or time zone. So an OffsetDateTime is the wrong class to use. Instead use LocalDateTime which lacks any concept of offset or zone.
This means a LocalDateTime cannot represent a moment. For example, noon on the 23rd of January this year could mean noon on Asia/Tokyo which would be hours earlier than noon in Europe/Paris, or it could mean noon in America/Montreal which would be a moment even more hours later. Without the context of a zone or offset, a LocalDateTime has no real meaning. So comparing a LocalDateTime to the current moment is senseless.
LocalDateTime.parse( "2019-02-14T00:00:00" )
See this code run live at IdeOne.com.
ldt.toString(): 2019-02-14T00:00
To compare, you can’t — illogical as discussed above. You must assign a time zone (or offset) to determine a moment on the timeline. If you know for certain this date and time were meant for a specific time zone, assign ZoneId to get a ZonedDateTime. Then extract a Instant to compare.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ; // India time.
ZonedDateTime zdt = ldt.atZone( z ) ;
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
boolean zdtIsPast = zdt.toInstant().isBefore( instant ) ; // Compare.
By the way, I noticed the time-of-day is zero. If your goal was to represent the date only, without any time-of-day and without any zone, use LocalDate class.
Third: Don’t bother, ambiguous input
Your third string input carries a time zone indicator. So it should be parsed as a ZonedDateTime.
Unfortunately, you’ve chosen a terrible string format to parse. Never use the 2-4 character pseudo-zones like IST. They are not standardized. And they are not unique! Your IST could mean Ireland Standard Time or India Standard Time or others.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
See this code run live at IdeOne.com.
zdt.toString(): 2019-02-20T22:34:26.833+01:00[Africa/Tunis]
You could try to parse this. ZonedDateTime will make a guess as to which zone was meant by IST. But it would be just a guess, and so is unreliable given the inherently ambiguous input. Personally, I would refuse to code that, rejecting this input data back to its source.
If you insist on making this unreliable parse attempt, see the correct Answer to a similar Question you asked recently.
Educate your source about always using standard ISO 8601 formats to exchange date-time values as human-readable text.
The java.time classes use these ISO 8601 formats by default when parsing/generating strings. The ZonedDateTime class wisely extends the standard to append the standard name of the time zone in square brackets.
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?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
For a REST web service, I need to return dates (no time) with a time zone.
Apparently there is no such thing as a ZonedDate in Java (only LocalDate and ZonedDateTime), so I'm using ZonedDateTime as a fallback.
When converting those dates to JSON, I use DateTimeFormatter.ISO_OFFSET_DATE to format the date, which works really well:
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE;
ZonedDateTime dateTime = ZonedDateTime.now();
String formatted = dateTime.format(formatter);
2018-04-19+02:00
However, attempting to parse back such a date with...
ZonedDateTime parsed = ZonedDateTime.parse(formatted, formatter);
... results in an Exception:
java.time.format.DateTimeParseException: Text '2018-04-19+02:00' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {OffsetSeconds=7200},ISO resolved to 2018-04-19 of type java.time.format.Parsed
I also tried ISO_DATE and ran into the same problem.
How can I parse such a zoned date back?
Or is there any other type (within the Java Time API) I'm supposed to use for zoned dates?
The problem is that ZonedDateTime needs all the date and time fields to be built (year, month, day, hour, minute, second, nanosecond), but the formatter ISO_OFFSET_DATE produces a string without the time part.
When parsing it back, there are no time-related fields (hours, minutes, seconds) and you get a DateTimeParseException.
One alternative to parse it is to use a DateTimeFormatterBuilder and define default values for the time fields. As you used atStartOfDay in your answer, I'm assuming you want midnight, so you can do the following:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date and offset
.append(DateTimeFormatter.ISO_OFFSET_DATE)
// default values for hour and minute
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.toFormatter();
ZonedDateTime parsed = ZonedDateTime.parse("2018-04-19+02:00", fmt); // 2018-04-19T00:00+02:00
Your solution also works fine, but the only problem is that you're parsing the input twice (each call to formatter.parse will parse the input again). A better alternative is to use the parse method without a temporal query (parse only once), and then use the parsed object to get the information you need.
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE;
// parse input
TemporalAccessor parsed = formatter.parse("2018-04-19+02:00");
// get data from the parsed object
LocalDate date = LocalDate.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime restored = date.atStartOfDay(zone); // 2018-04-19T00:00+02:00
With this solution, the input is parsed only once.
tl;dr
Use a time zone (continent/region) rather than a mere offset-from-UTC (hours-minutes-seconds). For any particular zone, the offset is likely to change over time.
Combine the two to determine a moment.
LocalDate.parse(
"2018-04-19"
)
.atStartOfDay(
ZoneId.of( "Europe/Zurich" )
) // Returns a `ZonedDateTime` object.
2018-04-19T00:00+02:00[Europe/Zurich]
From your REST service, either:
Return the date and zone separately (either with a delimiter or as XML/JSON), or,
Return the start of day as that is likely the intended outcome of a date with a time zone.
Separate your text inputs
The solution in the Answer by Walser is effectively treating the string input as a pair of string inputs. First the date-only part is extracted and parsed. Second, the offset-from-UTC part is extracted and parsed. So, the input is parsed twice, each time ignoring the opposite half of the string.
I suggest you make this practice explicit. Track the date as one piece of text, track the offset (or, better, a time zone) as another piece of text. As the code in that other Answer demonstrates, there is no real meaning to a date with zone until you take the next step of determining an actual moment such as the start of day.
String inputDate = "2018-04-19" ;
LocalDate ld = LocalDate.parse( inputDate ) ;
String inputOffset = "+02:00" ;
ZoneOffset offset = ZoneOffset.of( inputOffset) ;
OffsetTime ot = OffsetTime.of( LocalTime.MIN , offset ) ;
OffsetDateTime odt = ld.atTime( ot ) ; // Use `OffsetDateTime` & `ZoneOffset` when given a offset-from-UTC. Use `ZonedDateTime` and `ZoneId` when given a time zone rather than a mere offset.
odt.toString(): 2018-04-19T00:00+02:00
As you can see, the code is simple, and your intent is obvious.
And no need to bother with any DateTimeFormatter object nor formatting patterns. Those inputs conform with ISO 8601 standard formats. The java.time classes use those standard formats by default when parsing/generating strings.
Offset versus Zone
As for applying the date and offset to get a moment, you are conflating a offset-from-UTC with a time zone. An offset is simply a number of hours, minutes, and seconds. No more, no less. In contrast, a time zone is a history of the past, present, and future changes in offset used by the people of a particular region.
In other words, the +02:00 happens to be used by many time zones on many dates. But in a particular zone, such as Europe/Zurich, other offsets may be used on other dates. For example, adopting the silliness of Daylight Saving Time (DST) means a zone will be spending half the year with one offset and the other half with a different offset.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Europe/Zurich" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
zdt.toString(): 2018-04-19T00:00+02:00[Europe/Zurich]
So I suggest you track two strings of input:
Date-only (LocalDate): YYYY-MM-DD such as 2018-04-19
Proper time zone name (ZoneId): continent/region such as Europe/Zurich
Combine.
ZonedDateTime zdt =
LocalDate.parse( inputDate )
.atStartOfDay( ZoneId.of( inputZone ) )
;
Note: The ZonedDateTime::toString method generates a String in a format that wisely extends the standard ISO 8601 format by appending the name of the time zone in square brackets. This rectifies a huge oversight made by the otherwise well-designed standard. But you can only return such a string by your REST service if you know your clients can consume it.
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?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
I found the solution (using TemporalQueries):
parse the date and zone separately, and restore the zoned date using that information:
LocalDate date = formatter.parse(formatted, TemporalQueries.localDate());
ZoneId zone = formatter.parse(formatted, TemporalQueries.zone());
ZonedDateTime restored = date.atStartOfDay(zone);