Could anybody please tell me why I'm getting "10/09/2022" on the console?
String sFecha = "10/21/2021";
try {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
System.out.println(sdf.format(sdf.parse(sFecha)));
} catch (java.text.ParseException e) {
//Expected execution
}
Note: the input string is intentionally wrong - I am expecting the Exception!
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
The problem you have observed with your code is one of the weird problems that you face with SimpleDateFormat. Instead of throwing the exception because of the wrong format, SimpleDateFormat tries to parse the date string erroneously.
Solution using java.time, the modern Date-Time API:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class Main {
public static void main(String[] args) {
String sFecha = "10/21/2021";
try {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.parse(sFecha, dtf);
System.out.println(date);
} catch (DateTimeParseException e) {
System.out.println("A problem occured while parsing the date string.");
// ...Handle the exception
}
}
}
Output:
A problem occured while parsing the date string.
Now, change the format to MM/dd/yyyy and you will see that the date string will be parsed successfully.
Learn more about the modern Date-Time API from Trail: Date Time.
In case you want to use SimpleDateFormat:
Pass false to SimpleDateFormat#setLenient which is set true by deafult.
Demo:
import java.text.SimpleDateFormat;
public class Main {
public static void main(String[] args) {
String sFecha = "10/21/2021";
try {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sdf.setLenient(false);
System.out.println(sdf.format(sdf.parse(sFecha)));
} catch (java.text.ParseException e) {
System.out.println("A problem occured while parsing the date string.");
// ...Handle the exception
}
}
}
Output:
A problem occured while parsing the date string.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
When you do sdf.parse() you convert your text to date with :
10 -> days
21 -> month
2021 -> year
And 21 as month is converted to 9 (because 21 % 12 = 9).
Using setLenient(false) it will throw an exception instead:
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.
your format is day/month/year. 21 is not a valid month, It seems it subtracts 12 to get a valid one.
Related
I wanted to validate date in client side so I wrote the following code. But instead of getting an exception I am getting a proper date object for 31st of February date string, which is clearly an invalid date.
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/yyyy";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (Exception e) {
// Throw invalid date message
}
}
}
Output : 2015-02-28T11:30:59
Does anyone know why LocalDateTime is parsing this date instead of throwing an exception.
You just need a strict ResolverStyle.
Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.
Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):
import java.time.*;
import java.time.format.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/uuuu";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (DateTimeParseException e) {
// Throw invalid date message
System.out.println("Exception was thrown");
}
}
}
The Java 8 DateTimeFormatter uses yyyy to mean YEAR_OF_ERA, and uuuu to mean YEAR. You need to modify your pattern string as follows:
String dateFormat = "HH:mm:ss MM/dd/uuuu";
The DateTimeFormatter defaults to using the SMART resolver style, but you want it to use the STRICT resolver style. Modify your dateTimeFormatter initialization code as follows:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
It is not rounding down. February has never had 31 days, and it is impossible to use a validating date / time object to represent a day that doesn't exist.
As a result, it takes the invalid input and gives you the best approximation to the correct date (the last date of February that year).
SimpleDateFormat inherits from DateFormat which has a setLenient(boolean value) method on it. I would expect that if you called setLenient(true) prior to parsing, it would probably complain more, as detailed in the javadocs.
try {
SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
df.setLenient(false);
System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
System.out.println(e);
}
I found one solution to recognize date as a valid date with DateFormat.setLenient(boolean). If you try to parse any invalid date it will throws parse exception.
Edit:
Java 8, but this will raise exception if a month is not between 1 and 12, if a day is more than 32. Exactly not working. But for month its working.
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}
Output:
java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
LocalDateTime.parse will only throw an error if the String passed in contains invalid characters, a number of days exceeding 31 or a month exceeding 12.
For example, if you modified your code as such:
String dateString = "11:30:59 0zz2/31/2015";
an exception would be thrown caused by the invalid 'zz' characters within your given date. As to why it's 'rounding-down' the date so to speak, that I don't know.
Source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-
I have a question about converting from 12-hour am/pm format to 24-hour format. I have tried using SimpleDateFormat but encountered some problems.
As you can see, I printed 5 lines of original and converted time, but failed for cases ending with "PM". Note that some inputs are of the 24-hour format and others have the 12-hour am/pm format.
Below is the code I write for the conversion:
static String standardizeTime(String inputTime) throws ParseException {
SimpleDateFormat[] testInputFormat = new SimpleDateFormat[2];
SimpleDateFormat returnFormat = new SimpleDateFormat("HH:mm");
testInputFormat[0] = new SimpleDateFormat("hh:mm aa");
testInputFormat[1] = new SimpleDateFormat("HH:mm");
testInputFormat[0].setLenient(false);
testInputFormat[1].setLenient(false);
Date time;
for (int i = 0; i < testInputFormat.length; i++) {
try {
time = testInputFormat[i].parse(inputTime);
return returnFormat.format(time);
} catch (ParseException e) {
continue;
}
}
return "";
}
What should I change to fix this problem?
This works as expected.
import java.text.SimpleDateFormat;
public class HelloWorld{
public static void main(String []args) throws Exception {
final SimpleDateFormat parser = new SimpleDateFormat("hh:mm aa");
final SimpleDateFormat printer = new SimpleDateFormat("HH:mm");
System.out.println(printer.format(parser.parse("4:07 pm")));
}
}
Your code looks good so I think problem is elsewhere.
I fixed my problem by referring to this post: java.text.ParseException: Unparseable date
The problem is, the default language of my computer is not English, so take Alexander's response as an example. I need to write:
import java.text.SimpleDateFormat;
import java.util.Locale;
public class HelloWorld{
public static void main(String []args) throws Exception {
final SimpleDateFormat parser = new SimpleDateFormat("hh:mm aa", Locale.ENGLISH);
final SimpleDateFormat printer = new SimpleDateFormat("HH:mm");
System.out.println(printer.format(parser.parse("4:07 pm")));
}
}
Note the "Locale.ENGLISH" in parser!
Make sure to use Locale with date/time formatting API whenever the date/time contains letters because different locales have a different representation of letters.
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Main {
public static void main(String[] args) throws ParseException {
DateFormat stfInput = new SimpleDateFormat("h:m a", Locale.ENGLISH);// 12-hour format
DateFormat stfOutput = new SimpleDateFormat("HH:mm", Locale.ENGLISH);// 24-hour format
Date time = stfInput.parse("02:57 AM");
System.out.println(stfOutput.format(time));
time = stfInput.parse("07:05 PM");
System.out.println(stfOutput.format(time));
}
}
Output:
02:57
19:05
Note: java.util date-time classes are outdated and error-prone and so is their formatting API, SimpleDateFormat. I suggest you should stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.
If you are doing it for your Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Using the modern date-time API:
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtfInput = DateTimeFormatter.ofPattern("h:m a", Locale.ENGLISH);// 12-hour format
DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("HH:mm", Locale.ENGLISH);// 24-hour format
LocalTime time = LocalTime.parse("02:57 AM", dtfInput);
System.out.println(time.format(dtfOutput));
time = LocalTime.parse("07:05 PM", dtfInput);
System.out.println(time.format(dtfOutput));
}
}
Output:
02:57
19:05
What should I change to fix this problem?
First and most importantly: I recommend that you use java.time, the modern Java date and time API, for your time work. The date-time classes that you tried to use, SimpleDateFormat and Date, are poorly designed and long outdated. The modern API is so much nicer to work with. The last half of the answer by Arvind Kumar Avinash shows you how. There is no reason for me to repeat it. And yes, Arvind’s code does fix your problem.
What went wrong in your code?
It’s been said that your problem is not providing an English-speaking locale to your formatter. This is part of the truth, but it doesn’t fully explain why 07:05 PM was converted to 07:05 where the correct conversion would have given 19:05.
What happened was that your first input formatter, the one with pattern hh:mm aa, failed at parsing because it used a language where PM would be called something else. Next the second formatter, with pattern HH:mm succeeded in parsing the string. Surprising, isn’t it? I consider it one of the many confusing traits of SimpleDateFormat that it doesn’t necessarily parse the entire string. It parsed the 07:05 part as hour of day and minute. Then it ignored the remainder of the string. In this way you got the result 07:05, which is incorrect, instead of the correct 19:05. This is just one of the reasons why you shouldn’t use SimpleDateFormat at all.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions:
Java SimpleDateFormat throwing ParseException: Unparseable date on Windows but not on Mac [duplicate] about locale for parsing AM an PM. I obviously recommend the answer by Basil Bourque using java.time.
SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa? about SimpleDateFormat not parsing the entire string. Be inspired by the answer by David using java.time to solve the problem in an easy and straightforward way.
Force SimpleDateFormat to parse the whole string, which seems to me to be similar to yours.
Conversion from 12 hours time to 24 hours time in java. I recommend the answer by Cloud using java.time.
I am trying to format a JSON which has a date key in the format:
date: "2017-05-23T06:25:50"
My current attempt is the following:
private String formatDate(String date) {
SimpleDateFormat format = new SimpleDateFormat("MM/DD/yyyy");
String formattedDate = "";
try {
formattedDate = format.format(format.parse(String.valueOf(date)));
} catch (ParseException e) {
e.printStackTrace();
}
return formattedDate;
}
But in this occasion, the result isn't shown in my app, the TextView is invisible and I couldn't log anything.
I really tried other solutions, but no success for me. I don't know if it's because the incoming value is a string.
Use this code to format your `2017-05-23T06:25:50'
String strDate = "2017-05-23T06:25:50";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date convertedDate = new Date();
try {
convertedDate = dateFormat.parse(strDate);
SimpleDateFormat sdfnewformat = new SimpleDateFormat("MMM dd yyyy");
String finalDateString = sdfnewformat.format(convertedDate);
} catch (ParseException e) {
e.printStackTrace();
}
The converted finalDateString set to your textView
This will work for you.
String oldstring= "2017-05-23T06:25:50.0";
Date datee = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(oldstring);
This code worked for me :
public static String getNewsDetailsDateTime(String dateTime) {
#SuppressLint("SimpleDateFormat") DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = null;
try {
date = format.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
}
#SuppressLint("SimpleDateFormat") String strPublishDateTime = new SimpleDateFormat("MMM d, yyyy h:mm a").format(date);
return strPublishDateTime;
}
Out put format was : Dec 20 , 2017 2.30 pm.
Use this function :
private String convertDate(String dt) {
//String date="2017-05-23T06:25:50";
try {
SimpleDateFormat spf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); //date format in which your current string is
Date newDate = null;
newDate = spf.parse(dt);
spf = new SimpleDateFormat("MM/DD/yyyy"); //date format in which you want to convert
dt = spf.format(newDate);
System.out.println(dt);
Log.e("FRM_DT", dt);
} catch (ParseException e) {
e.printStackTrace();
}
return dt;
}
TL;DR
private static final DateTimeFormatter DATE_FORMATTER
= DateTimeFormatter.ofPattern("MM/dd/uuuu");
private static String formatDate(String date) {
return LocalDateTime.parse(date).format(DATE_FORMATTER);
}
Now formatDate("2017-05-23T06:25:50") returns the desired string of 05/23/2017.
java.time
In 2017 I see no reason why you should struggle with the long outdated and notoriously troublesome SimpleDateFormat class. java.time, the modern Java date and time API also known as JSR-310, is so much nicer to work with.
Often when converting from one date-time format to another you need two formatters, one for parsing the input format and one for formatting into the output format. Not here. This is because your string like 2017-05-23T06:25:50 is in the ISO 8601 format, the standard that the modern classes parse as their default, that is, without an explicit formatter. So we only need one formatter, for formatting.
What went wrong in your code
When I run your code, I get a ParseException: Unparseable date: "2017-05-23T06:25:50". If you didn’t notice the exception already, then you have a serious flaw in your project setup that hides vital information about errors from you. Please fix first thing.
A ParseException has a method getErrorOffset (a bit overlooked), which in this case returns 4. Offset 4 in your string is where the first hyphen is. So when parsing in the format MM/DD/yyyy, your SimpleDateFormat accepted 2017 as a month (funny, isn’t it?), then expected a slash and got a hyphen instead, and therefore threw the exception.
You’ve got another error in your format pattern string: Uppercase DD is for day-of-year (143 in this example). Lowercase dd should be used for day-of-month.
Question: Can I use java.time on Android?
Yes you can. It just requires at least Java 6.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP.
Links
Oracle tutorial: Date Time, explaining how to use java.time.
ThreeTen Backport project
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Java Specification Request (JSR) 310, where the modern date and time API was first described.
Try this:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar c = Calendar.getInstance();
try {
c.setTime(sdf.parse(dt));
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat sdf1 = new SimpleDateFormat("dd/MM/yyyy");
String output = sdf1.format(c.getTime());
I wanted to validate date in client side so I wrote the following code. But instead of getting an exception I am getting a proper date object for 31st of February date string, which is clearly an invalid date.
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/yyyy";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (Exception e) {
// Throw invalid date message
}
}
}
Output : 2015-02-28T11:30:59
Does anyone know why LocalDateTime is parsing this date instead of throwing an exception.
You just need a strict ResolverStyle.
Parsing a text string occurs in two phases. Phase 1 is a basic text parse according to the fields added to the builder. Phase 2 resolves the parsed field-value pairs into date and/or time objects. This style is used to control how phase 2, resolving, happens.
Sample code - where withResolverStyle(ResolverStyle.STRICT) is the important change, along with the use of uuuu rather than yyyy (where uuuu is "year" and "yyyy" is "year of era", and therefore ambiguous):
import java.time.*;
import java.time.format.*;
import java.util.*;
public class Test {
public static void main(String[] args) {
String dateFormat = "HH:mm:ss MM/dd/uuuu";
String dateString = "11:30:59 02/31/2015";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter
.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
System.out.println(date);
} catch (DateTimeParseException e) {
// Throw invalid date message
System.out.println("Exception was thrown");
}
}
}
The Java 8 DateTimeFormatter uses yyyy to mean YEAR_OF_ERA, and uuuu to mean YEAR. You need to modify your pattern string as follows:
String dateFormat = "HH:mm:ss MM/dd/uuuu";
The DateTimeFormatter defaults to using the SMART resolver style, but you want it to use the STRICT resolver style. Modify your dateTimeFormatter initialization code as follows:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
.withResolverStyle(ResolverStyle.STRICT);
It is not rounding down. February has never had 31 days, and it is impossible to use a validating date / time object to represent a day that doesn't exist.
As a result, it takes the invalid input and gives you the best approximation to the correct date (the last date of February that year).
SimpleDateFormat inherits from DateFormat which has a setLenient(boolean value) method on it. I would expect that if you called setLenient(true) prior to parsing, it would probably complain more, as detailed in the javadocs.
try {
SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
df.setLenient(false);
System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
System.out.println(e);
}
I found one solution to recognize date as a valid date with DateFormat.setLenient(boolean). If you try to parse any invalid date it will throws parse exception.
Edit:
Java 8, but this will raise exception if a month is not between 1 and 12, if a day is more than 32. Exactly not working. But for month its working.
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}
Output:
java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
LocalDateTime.parse will only throw an error if the String passed in contains invalid characters, a number of days exceeding 31 or a month exceeding 12.
For example, if you modified your code as such:
String dateString = "11:30:59 0zz2/31/2015";
an exception would be thrown caused by the invalid 'zz' characters within your given date. As to why it's 'rounding-down' the date so to speak, that I don't know.
Source: https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#parse-java.lang.CharSequence-
I am trying to validate a string whether it is in ISO-8601 date or not, but it is throwing a parse exception, not sure where it is going wrong.
try {
String s = "2007-03-01T13:00:00Z";
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
ft.setLenient(false);
System.out.println(ft.format(ft.parse(s)));
} catch (ParseException e) {
System.out.println(e.getMessage());
}
output is:
Unparseable date: "2007-03-01T10:00:00Z"
I suspect that Z is being interpreted as a time zone so would match -0800 but not a literal Z so you could solve that by quoting: 'Z'.
getErrorOffset should tell you where the problem is.
If you're using Java 7, use the following format string: "yyyy-MM-dd'T'HH:mm:ssXXX"
Note: X is a new code (added in Java 7) that matches ISO 8601 time zone strings; see the API documentation of SimpleDateFormat.
If you want to validate an arbitrary string, you cannot hardcode the "Z" time zone designator, as the validation would fail for a valid ISO8601 time stamp like e.g. "2007-03-01T13:00:00+01".
If you are using Java 6 or earlier, SimpleDateFormat will not support ISO8601 time zone encoding, so you cannot use it to validate time stamps either. With Java 7 or later, you can use new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");.
Your code does not work because the SDF is very limited ad was not aware of ISO 8601 at the time when it was written.
You can take this code:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
public final class JSONDateUtil {
private static final DateFormat ISO8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
static {
ISO8601_FORMAT.setLenient(false);
ISO8601_FORMAT.setTimeZone(DateUtils.UTC_TIME_ZONE);
}
public static String toJSON(Date date) {
return ISO8601_FORMAT.format(date);
}
public static String toJSON(long millis) {
return ISO8601_FORMAT.format(millis);
}
public static Date toJava(String date) {
try {
return ISO8601_FORMAT.parse(date);
} catch (ParseException e) {
return null;
}
}
}
Note the timezone, very important.
Joda-Time
FYI, if you used Joda-Time instead of the notoriously troublesome java.util.Date/Calendar classes, you could simply pass that ISO 8601 string straight into a DateTime constructor without the bother of a formatter. Joda-Time uses ISO 8601 as its defaults.
DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime = new DateTime( "2007-03-01T13:00:00Z", timeZone );
Validation
To determine if your input string was invalid, catch IllegalArgumentException.
java.util.Date
You can even get a java.util.Date back out if need be.
java.util.Date date = dateTime.toDate();