Correct way to parse date - java

I'm trying to parse the following date ut get a runtime error saying:
java.time.format.DateTimeParseException: Text '2019-11-21-05:00' could not be parsed, unparsed text found at index 10
My input:
String inpDate = "2019-11-20-05:00"
I also tried the following date formats but no luck.
yyyy-MM-ddZ
yyyy-MM-dd Z
Code:
public static final String DATE_FORMAT = "yyyy-MM-dd";
public static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
LocalDate localDate = LocalDate.parse(inpDate, dateFormatter);
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
How can I get my input to parse correctly?

Use the following format:
public static final String DATE_FORMAT = "yyyy-MM-ddZZZZZ";
It has 5 times the letter "Z". To parse a timezone offset with a colon, you need to provide the letter "Z" 5 times. This may be somewhat hidden away in the Javadoc.
From the Javadoc:
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.
Four letters outputs the full form of localized offset, equivalent to four letters of Offset-O. The output will be the corresponding localized offset text if the offset is zero.
Five letters outputs the hour, minute, with optional second if non-zero, with colon.
It outputs 'Z' if the offset is zero. Six or more letters throws IllegalArgumentException.

It’s easier than you think. The formatter you need is built in. So don’t struggle with writing your own format pattern string.
String inpDate = "2019-11-20-05:00";
LocalDate localDate = LocalDate.parse(inpDate, DateTimeFormatter.ISO_OFFSET_DATE);
System.out.println(Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
Output in my time zone:
Wed Nov 20 00:00:00 EST 2019
I am assuming that you are only converting to a Date because you need one for a legacy API that you cannot afford to upgrade to java.time just now. Otherwise you should not use Date but stick to the modern API.
Start of day in your time zone or at the offset given in the string?
Edit: Basil Bourque in a comment asked this very knowledgeable question:
Perhaps it would be more true to the intent of the publisher of this
input string to get the first moment of the day as seen in that offset
of 5 hours behind UTC. Is it possible to parse as an OffsetDateTime
with the time-of-day defaulting to first moment of the day (00:00:00)?
(And then convert to java.util.Date if required.)
It’s definitely true. I have chosen to give code that gives the result that I think the code in the question was trying to obtain. If we understand 2019-11-20-05:00 as some unspecified time in the half-open interval from 2019-11-20T00:00-05:00 to 2019-11-21T00:00-05:00, then for some offsets and some default time zones the result from the above code snippet will actually lie outside that interval (typically before it, may also in corner cases fall after it). So this is true to the question (if I understood it correctly) and untrue to the publisher of the original string. If instead we want the start of day at the UTC offset given in the string I would go like this:
TemporalAccessor parsed = DateTimeFormatter.ISO_OFFSET_DATE.parse(inpDate);
Instant startOfDayAtSpecifiedOffset = LocalDate.from(parsed)
.atStartOfDay(ZoneOffset.from(parsed))
.toInstant();
System.out.println(Date.from(startOfDayAtSpecifiedOffset));
Output in my time zone:
Wed Nov 20 06:00:00 CET 2019
I am at offset +01:00 in November, which is why the time here is 06:00 when the day begins at offset -05:00. To illustrate that the choice of time zone or offset may make a great difference, here’s the output from running the latter snippet with Pacific/Kiritimati as default time zone:
Wed Nov 20 19:00:00 LINT 2019
Or in Pacific/Pago_Pago time zone:
Tue Nov 19 18:00:00 SST 2019
So pick carefully what you really want.
What went wrong in your code?
First, LocalDate.parse() and similar parsing methods insist on parsing the entire string or they will throw the exception you saw mentioning unparsed text found at index (some index) (you may use the overloaded DateTimeFormatter.parse​(CharSequence, ParsePosition) method for parsing only as much as possible of the string without that exception).
Second, one Z in the format pattern string matches offset without colon, so -0500, not -05:00.
Link: Documentation of DateTimeFormatter.parse​(CharSequence, ParsePosition)

String inpDate = "2019-11-20-05:00";
System.out.println(LocalDate.parse(inpDate, DateTimeFormatter.ofPattern("yyyy-MM-dd-hh:mm")));
Will print 2019-11-20.

Related

Converting a string pattern of format 1/1/2010 3:23:12 PM +00:00 to a Java.util.Date

I am trying to convert a String with format 1/1/2010 3:23:12 PM +00:00 to a Java.util.Date
Unable to convert the String format to a Java Date.
It is not identifying the time asAM/PM
String s = "1/1/2010 3:23:12 PM +00:00";
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss a",Locale.ENGLISH);
Date date = sdf.parse(s));
Need the date converted with time identified as AM/PM
OffsetDateTime is what you're looking for.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy h:mm:ss a XXX");
OffsetDateTime time = OffsetDateTime.parse(str, formatter);
System.out.println(time);
Your pattern has some problems:
Your day-of-the-month is without a leading zero, yet you are using dd;
Same for month with MM;
Same for hour with HH;
You are using AM/PM in conjunction with a 24-hour hour format specifier (H); you should use h instead.
I don't know exactly how SimpleDateFormat handles the timezone part of the string, but no formatting specifiers for the timezone are given.
That's one of the reasons why I like this Date and Time API: it's pretty straightforward.
Ideone example
Unable to convert the String format to a Java Date. It is not
identifying the time as AM/PM
You are asking the impossible. A Date is a point in time (internally implemented as a count of milliseconds since the so-called epoch), so it “knows” nothing about AM and PM in your time zone.
That’s just the same, though, because the Date class was always poorly designed and is fortunately long outdated. You should not use it at all.
java.time
java.time, the modern Java date and time API that we should use instead of Date, comes closer to fulfilling your requirement. MC Emperor has already shown the basic code you need for parsing your datetime string. The output from his code is:
2010-01-01T15:23:12Z
There’s no AM or PM here. When we print an OffsetDateTime in this way, its toString method is implicitly called. It produces an ISO 8601 formatted string. ISO 8601 prescribes a 24 hour clock (no AM or PM). But! With assistance from the correct TemporalField object the OffsetDateTime is able to calculate and return whether it is in AM or PM. time.get(ChronoField.AMPM_OF_DAY) returns 0 for AM or 1 for PM:
System.out.println("AM or PM? 0 for AM. 1 for PM. time: "
+ time.get(ChronoField.AMPM_OF_DAY));
AM or PM? 0 for AM. 1 for PM. time: 1
So in this case we got 1 for PM as expected since your original string had PM in it.
I have deliberately not answered all of your question because much of it has been covered in other Stack Overflow questions and their answers already. So it’s better to keep the information there. I include links to a couple of relevant questions below.
What went wrong in your code?
There are at least two bugs in your code that each cause you to get an incorrect result. I tried running your code in America/Los_Angeles time zone and got
Fri Jan 01 03:23:12 PST 2010
The time printed is on a 24 hour clock (Date always does that), so we got 03:23:12 AM instead of PM. And we got the time in the default time zone (PST is for Pacific Standard Time), so the point in time corresponds to 11:23:12 AM at offset +00:00, the offset in the string.
The wrong clock hour comes from conflicting indications in your code: HH in the format pattern string is for hour of day from 00 through 23, so 3 is taken to mean 03 AM and apparently “wins” over the PM marker (for hour within AM or PM, from 1 through 12, you would have needed lowercase h).
The default time zone comes from the fact that you are making no attempt to parse the offset from the string (in conjunction with SimpleDateFormat being satisfied with not parsing all of the string).
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Question: want current date and time in “dd/MM/yyyy HH:mm:ss.SS” format
Question: Unable to parse DateTime-string with AM/PM marker
Question: Display current time in 12 hour format with AM/PM
Question: Convert String to java.util.Date

Java Date object parsing precision is off on SimpleDateFormat

I have a date as a string 2016-10-07T12:46:23Z and after parsing to Date object using SimpleDateFormat is converted to Fri Oct 07 08:46:22 EDT 2016 which is 1 sec precision off. Debugging that code it came that it was parsed to Fri Oct 07 08:46:22.998 EDT 2016
SimpleDateFormat to parse as looks like
DATE_FORMAT_ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
DATE_FORMAT_ISO8601.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));
and the code to parse looks like
String dateStr = valuesArray.getString(0);
values[0] = RESTUtils.DATE_FORMAT_ISO8601.parse(dateStr);
Any tips how to get proper seconds value after parsing?
The expression new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC")) defines an offset not of zero, but 2 milliseconds, see Javadoc, thus explaining the observed result of "Fri Oct 07 08:46:22.998 EDT 2016".
The constant SimpleTimeZone.UTC is not intended to indicate an offset (as mandated as first argument to SimpleTimeZone-constructor). Its numerical value of "2" is rather a mode to denote how to interprete start or end time parameters for other constructors.
Therefore the correct solution to interprete the trailing "Z" in your input (ISO-8601-notation for zero offset) is:
DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("GMT"));
Alternatively, if you really want to use the class SimpleTimeZone:
DATE_FORMAT_ISO8601.setTimeZone(new SimpleTimeZone(0, "UTC"));
And if you are on Java-8, you could also do this:
Instant instant = Instant.parse("2016-10-07T12:46:23Z");
System.out.println(instant); // 2016-10-07T12:46:23Z
System.out.println(Date.from(instant)); // Fri Oct 07 14:46:23 CEST 2016
You are seeing the second difference when you add the time zone as UTC using SimpleTimeZone, if you comment that part out you will get the exact seconds.
String str = "2016-10-07T12:46:24Z";
SimpleDateFormat DATE_FORMAT_ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
// DATE_FORMAT_ISO8601.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));
System.out.println(DATE_FORMAT_ISO8601.parse(str));
SimpleTimeZone is not meant to be used to define the time zone. Per the documentation
SimpleTimeZone is a concrete subclass of TimeZone that represents a
time zone for use with a Gregorian calendar. The class holds an offset
from GMT, called raw offset, and start and end rules for a daylight
saving time schedule. Since it only holds single values for each, it
cannot handle historical changes in the offset from GMT and the
daylight saving schedule, except that the setStartYear method can
specify the year when the daylight saving time schedule starts in
effect.
The correct way to set the timezone is by doing the following
DATE_FORMAT_ISO8601.setTimeZone(TimeZone.getTimeZone("UTC"));

Android SimpleDateFormat Parsing Error

I know there are many other questions on stackoverflow that deal with SimpleDateFormatter in Android or Java, but I have been unable to find any questions that help me answer my own question.
I have a String with this time format 2014-06-28T21:00:00-05:00 and I am trying to convert it to 9:00 PM (or at least 9:00). I'm pretty sure my issue is with actually writing out the correct notation for the above time, but here is my code:
String input = '2014-06-28T21:00:00-05:00';
DateFormat fromFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+|-hh:mm");
fromFormat.setLenient(false);
DateFormat toFormat = new SimpleDateFormat("hh:mm");
toFormat.setLenient(false);
try{
String output = toFormat.format(fromFormat.parse(input));
return output;
} catch(ParseException pe){
pe.printStackTrace();
}
return "No Date Listed";
If I look at the stack trace, it tells me unparseable date at offset #19.
I am fairly certain the logic behind the code does work because I switched the SimpleDateFormats to something a little simpler like yyyy-MM-dd and MMMM dd, yy and it worked perfectly. So, can anyone point me in the right direction and help me figure out the proper time notation?
I appreciate all of your help.
The main problem you're having is that you are being given a time with a time zone format which is not supported by SimpleDateFormat.
There are two supported time zone formats that it can parse,
General time zones:
General time zone: Time zones are interpreted as text if they have names. For time zones representing a GMT offset value, the following syntax is used:
GMTOffsetTimeZone:
GMT Sign Hours : Minutes
Sign: one of
+ -
Hours:
Digit
Digit Digit
Minutes:
Digit Digit
Digit: one of
0 1 2 3 4 5 6 7 8 9
Hours must be between 0 and 23, and Minutes must be between 00 and 59. The format is locale independent and digits must be taken from the Basic Latin block of the Unicode standard.
...and RFC 822 time zones:
RFC 822 time zone: For formatting, the RFC 822 4-digit time zone format is used:
RFC822TimeZone:
Sign TwoDigitHours Minutes
TwoDigitHours:
Digit Digit
TwoDigitHours must be between 00 and 23. Other definitions are as for general time zones.
As you can see, the general time zone has a colon in it, but must be prefixed with "GMT", whereas the RFC 822 format has no colon. What you are trying to parse is a sort of bastardization of the two.
One of the following would work, depending on the time zone format, if you had a legal syntax:
String generalInput = "2014-06-28T21:00:00GMT-05:00"; // legal General time zone
String rfcInput = "2014-06-28T21:00:00-0500"; // legal RFC 822 time zone
DateFormat generalFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); // format for general time zone
DateFormat rfcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); // format for RFC 822 time zone
Since your input is malformed, I would suggest that you simply don't try to parse the time zone part of it at all, and treat it as a local time. Since you're trying to convert 21:00 to 9:00 pm anyway, this should work for you:
String input = "2014-06-28T21:00:00-05:00"; // not a legal time zone format
DateFormat fromFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); // don't even try to parse time zone
You can try following -
String input = '2014-06-28T21:00:00.000-0500';
DateFormat fromFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

java.text.ParseException: Unparseable date: yyyy-MM-dd HH:mm:ss.SSSSSS

I am getting ParseException for the following code
String dateStr = "2011-12-22 10:56:24.389362";
String formatStr = "yyyy-MM-dd HH:mm:ss.SSSSSS";
Date testDate = null;
SimpleDateFormat sdf= new SimpleDateFormat(formatStr);
sdf.setLenient(false);
testDate = sdf.parse(dateStr);
System.out.println("CHECK DATE " + sdf.format(testDate));
Exception in thread "main" java.text.ParseException: Unparseable date: "2011-12-22 10:56:24.389362"
at java.text.DateFormat.parse(DateFormat.java:337)
If I comment out the line sdf.setLenient(false), then I see a time difference in the ouput
CHECK DATE 2011-12-22 11:02:53.000362
What am I doing wrong??
'S' is for millisecond. There are 1000 (0 to 999) milliseconds in a second. 389362 is greater than 999. The extra 389000 milliseconds are getting converted to 389 seconds, or 6 minutes 29 seconds and added to the time.
The S format specifier refers to milliseconds. When you allow lenient parsing, the last part is interpreted as 389362 milliseconds. When that's added to the date so far, the last 3 digits (actually, the value % 1000) become the actual milliseconds, and you wind up with a date about 389 seconds (~6 1/2 minutes) later than you're expecting. (With strict parsing, the parser knows that 389362 milliseconds doesn't make sense, so it throws an error.)
The simplest way around that, if you can guarantee the date will always look like that, would be to chop the last 3 digits off. (This will about half the time give you a date that's off by a millisecond. But that's better than having to write a date parser...)
Your date input for milliseconds is incorrect. It should be:-
String dateStr = "2011-12-22 10:56:24.389";
You also do not need the extra number of "S"s in the pattern. The following should suffice:
String formatStr = "yyyy-MM-dd HH:mm:ss.S";
It is clearly mentioned in the java docs for presentation type of Number:
Number: For formatting, the number of pattern letters is the minimum
number of digits, and shorter numbers are zero-padded to this amount.
For parsing, the number of pattern letters is ignored unless it's
needed to separate two adjacent fields.
It works when you set lenient to be true (or comment out the line which defaults it true) since you are asking the parser to be not strict about the parsing. From java docs on setLenient():-
Specify whether or not date/time parsing is to be lenient. With
lenient parsing, the parser may use heuristics to interpret inputs
that do not precisely match this object's format. With strict parsing,
inputs must match this object's format.
S is only to be used for milliseconds. If you want microseconds, you will have to write your own parser.
Use toISOString('HH:mm:ss.S') to get milliseconds (3 digits), then complete as you need with 0.
For example:
new Date().toISOString('HH:mm:ss.S')
returns "2012-02-10T12:16:39.124Z"

Joda Time subtracting 24 hours from an instance of MutableDateTime, I would like to know why

I do not understand why MutableDateTime.setDate() is setting the time to "yesterday" (see the log timestamp hours - it is 20:28). Is this timezone related? Do I need to set something on the formatter?
I would expect that after calling setDate with "10/27/2010", the date would be the same as the parsed date 00:00 EDT 10/27/10, instead of 20:28 EDT 10/26/10. This is 24 hours ago from "now".
What am I missing here, or how should I edit the code to get the desired result? I am new to Joda Time, and would like to solve this mystery.
DateTimeFormatter dateFormatterJ = DateTimeFormat.forPattern("MM/dd/yyyy");
DateTimeFormatter timestampFormatJ = DateTimeFormat.forPattern("HH:mm zzz MM/dd/yy");
MutableDateTime startDate = new MutableDateTime();
log.info("parsed date " +
timestampFormatJ.print(dateFormatterJ.parseMutableDateTime(startDateString)));
startDate.setDate((dateFormatterJ.parseMutableDateTime(startDateString)));
log.info("startDate: " + timestampFormatJ.print(startDate));
In this case, startDateString is simply "10/27/2010".
here is the log output:
10-27 20:28:55 INFO parsed date: 00:00 EDT 10/27/10
10-27 20:28:55 INFO startDate: 20:28 EDT 10/26/10
Thanks
The simple answer would be, because the javadoc says so.
public void setDate(ReadableInstant
instant)
Set the date from another
instant. The time part of this object
will be unaffected.
Parameters:
instant - an instant to copy the date
from, time part ignored
Throws:
IllegalArgumentException - if the
object is invalidobject is invalid
When Joda says 'Date' it means the human meaning of the word Date. "The year-month-day portion of this value", not the logical equivalent of a java.util.Date. (the whole point of joda being to introduce some natural, sensible, semantics to handling date and time.)
EDIT:
To answer your 'how to fix' question, simply do:
MutableDateTime startDate = new MutableDateTime(dateFormatterJ.parseMutableDateTime(startDateString));
Or else manually zero out the time portions of course.
EDIT 2: Hmm, I apparently did not read carefully enough, this is only half of the answer. Will check.
EDIT 3: well this bugged me so much that I took a minute to look for it.
public void setDate(final ReadableInstant instant) {
long instantMillis = DateTimeUtils.getInstantMillis(instant);
Chronology instantChrono = DateTimeUtils.getInstantChronology(instant);
DateTimeZone zone = instantChrono.getZone();
if (zone != null) {
instantMillis = zone.getMillisKeepLocal(**DateTimeZone.UTC**, instantMillis);
}
setDate(instantMillis);
}
For some reason, it's rolling your absolute time forward into UTC before setting the date. So you give it 10/27/2010 00:00 EDT and it sets the absolute magnitude of time to the number of milliseconds that represent 10/27/2010 00:00 UTC, which of course is only 6 or 7 PM the day before. Then it finds the EDT date value of that to be 10/26.
Couldn't say if that's somehow intended or if it's a bug that's been there for 2 years or what.)
When parsing a string that does not contain a GMT offset or time-zone ID, you must do one of three things:
do nothing, and accept that the string is parsed in the default time zone
specify the time zone to parse in using withZone() on the formatter
use parseLocalDate() instead of parseMutableDateTime()
The last is the preferred solution, as it correctly parses the data that was actually input, which was a date without time, offset or zone.
Using parseLocalDate() in the test code correctly parses the date.

Categories

Resources