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");
Related
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.
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
I am trying to parse following date time string
2018-01-30T23:59:59.000
I am not able to understand which standard format it is like UTC or ISO_8601
while parsing in the following manner:
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD'T'HH:MM:SS:MS");
Date date = null;
try {
date = sdf.parse("2018-01-30T23:59:59.000");
} catch (ParseException e) {
e.printStackTrace();
}
But It is throwing following exception:
java.text.ParseException: Unparseable date: "2018-01-30T23:59:59.000"
Any help is appreciated.
See the doc of SimpleDateFormat and try this:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
LocalDateTime dateTime = LocalDateTime.parse("2018-01-30T23:59:59.000");
System.out.println(dateTime);
This prints:
2018-01-30T23:59:59
Your string is in ISO 8601 format. UTC or Coordinated Universal Time is not a format, it is a standard time used to define the time the rest of use in our respective time zones.
The date-time classes you were using, SimpleDateFormat and Date, are long outdated and the former in particular notoriously troublesome. I recommend that you instead use java.time, the modern Java date and time API. It is so much nicer to work with.
A LocalDateTime is a date with time of day and without time zone or offset from UTC. Its one-argument parse method parses ISO 8601, which is why no explicit formatter is needed.
What went wrong in your code
Your format pattern string has a number of issues to it. Which is one reason why you should appreciate the above solution without any explicit formatter. The first thing that goes wrong is: Your format pattern string has a colon, :, between seconds and milliseconds, whereas your date-time string has a dot, .. This is why you get the exception.
However, fixing this, your code yields the following Date:
Sun Dec 31 23:00:00 CET 2017
It’s one month off from the expected, and the minutes and seconds are missing. Because:
Uppercase YYYY is for week-based year and only useful with a week number. You need lowercase yyyy for year.
Uppercase DD is for day of year. You need lowercase dd for day of month.
You correctly used uppercase MM for month. Trying the same again for minutes won’t work. Maybe you can guess by now: it’s lowercase mm.
Not surprising you need lowercase ss for seconds.
UsingMS for milliseconds is interesting. SimpleDateFormat takes it as M for month (which we’ve already had twice before) and uppercase S for millisecond. Instead you needed uppercase SSS for the three digits of milliseconds.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Wikipedia article: Coordinated Universal Time on UTC
You need to escape the literal T:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:SS");
See This SO Answer for more examples
Update: Your string is in the format
yyyy-MM-dd'T'HH:mm:ss.SSS
but you are trying to parse it with a completely uppercase format string.
This does not do what you want it to do and you should read the documentation on SimpleDateFormat and the format string placeholders
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"
I'm working on taking a date value (createWhen) from Active Directory, and translating it into a Java date, for the purposes of getting a list of accounts created between two dates. Everything is working fine, save for one method: the method where I go from the AD Date to the Java date. The method looks like this:
private Date getParsedDate(String givenString) {
System.out.println("Value from AD is: " + givenString);
Date parsedDate = null;
String formattedString = this.formatDateString(givenString);
System.out.println("Formatted String is: " + formattedString);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/DD");
try {
parsedDate = sdf.parse(formattedString);
System.out.println("Final date string is: " + parsedDate.toString());
} catch (ParseException ex) {
ex.printStackTrace();
}
return parsedDate;
}
And, for a single piece of arbitrary data from AD:
Value from AD is: 20050912190509.0Z
Formatted String is: 2005/09/12
Final date string is: Wed Jan 12 00:00:00 EST 2005
Obviously, it's picking up the day and year correctly (and if I choose to include hours/minutes/seconds it includes those correctly as well), but every single date is being placed in January for some reason.
Now, I'm sure that my error is a pretty simple one, but I've rechecked my formatting about ten times, and I'm at the point where I just can't see it any more. Can a second pair of eyes hopefully look over my code and point out where I'm going wrong to get the month so grossly incorrect?
Thanks.
Change the pattern string from "yyyy/MM/DD" to "yyyy/MM/dd"
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Make sure you don't use 'mm' instead of 'MM' or 'MMM'. As small m denotes minutes and caps M denotes month.
TL;DR
LocalDate parsedDate = OffsetDateTime
.parse("20050912190509.0Z", DateTimeFormatter.ofPattern("uuuuMMddHHmmss.SX"))
.toLocalDate();
This yields a LocalDate of 2005-09-12.
java.time
I am contributing the modern answer. Suhas Phartale’s answer is correct and was a good answer when it was written 7 years ago. Now the notoriously troublesome SimpleDateFormat class is long outdated and we have so much better in java.time, the modern Java date and time API. I warmly recommend you use this instead of the old date-time classes.
Details
It seems from your code that you reformat your string from AD before parsing it. There’s no need for that, the string from AD can be parsed directly. We might have parsed it directly into a LocalDate, but I recommend parsing it into an OffsetDateTime to grab the time and offset from the string; as you can see, this can be directly converted to a LocalDate afterwards. A LocalDate is a date without time of day, so it seems to match your requirements better than the old Date class.
The string is in UTC (denoted by the Z in the end). The above gives you the date from the string, that is the date in UTC. If instead you wanted the date it was in your local time zone when it was September 12 19:05 in UTC:
LocalDate parsedDate = OffsetDateTime.parse(givenString, adDateTimeFormatter)
.atZoneSameInstant(ZoneId.of("America/Coral_Harbour"))
.toLocalDate();
I assumed we have declared the formatter a static field:
private static final DateTimeFormatter adDateTimeFormatter
= DateTimeFormatter.ofPattern("uuuuMMddHHmmss.SX");
In this case the result is the same, for other time zones it will not be. Please substitute your own desired time zone for America/Coral_Harbour. To use the JVM’s time zone setting, specify ZoneId.systemDefault(). Beware, however, that the setting may be changed by other parts of your program or other programs running in the same JVM, so this is fragile.
And the point from Suhas Phartale’s answer is valid in java.time too: format pattern strings are case sensitive, and I needed to use lowercase dd for day of month.
Tutorial
Learn more about java.time in the Oracle tutorial and/or search for other resources on the net.
I am posting this answer because i was redirected from here and above solutions did not resolve my issue
For me the scenario was that after parsing this date "2020-03-01T07:00:00+0530" i was getting the result as 1/2 [dd/MM] which is the format that i wanted, but that result contained the wrong month since the date string clearly indicates the month is 3 [MARCH].
So basically cal.get(Calendar.DAY_OF_MONTH) was returning me 2 instead of actual 3.
And as per docs in MONTH section
"the first month of the year in the Gregorian and Julian calendars is
JANUARY which is 0; the last depends on the number of months in a
year."
so we just need to add a +1 and we would get the actual month. Guess this behavior is there may be to return the names of month from month array or so ?! [January,February,etc..]
Below is a sample of my implementation (my date format in string is "yyyy-MM-dd'T'HH:mm:ssZ"):
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(Constant.DATE_FORMAT_WITH_TIMEZONE,Locale.ENGLISH);
try {
cal.setTime(Objects.requireNonNull(sdf.parse(forecastList.get(listPosition).fcst_valid_local)));
} catch (ParseException e) {
e.printStackTrace();
}
String s = "%s/%d";
String output = String.format(s,cal.get(Calendar.DAY_OF_MONTH),(cal.get(Calendar.MONTH)+1)));
hope this helps some one.