Regex pattern for date format yyyy/MM/dd - java

I need to validate a date with format yyyy/MM/dd using a regex pattern. I already have a regex for the format dd/MM/yyyy.
(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)
I'm using the validation from this link http://www.mkyong.com/regular-expressions/how-to-validate-date-with-regular-expression. I need the validation for yyyy/MM/dd. Can anyone help me?

I have found the answer:
((?:19|20)\\d\\d)/(0?[1-9]|1[012])/([12][0-9]|3[01]|0?[1-9])

Old question, I know, but as I've just had cause to do this:
\d{4}\\\d{2}\\\d{2}
Looks a lot neater to my eyes than the other suggestions, and works outside of the 20th/21st century (unclear if OP required this or not).
However, my implementation will match for dates such as 9999/99/99 (clearly invalid) - but as a commenter suggested, SimpleDateFormat is the tool for that kind of validation, in my opinion.
If OP were able to use SimpleDateFormat as well as regex, then this would suffice:
public static final boolean isDateValid(String date) {
if (!date.matches("\d{4}\\\d{2}\\\d{2}"))
return false;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
try {
sdf.parse(date);
return true;
} catch (ParseExceptione) {
return false;
}
}
NB. Usual caveats about SimpleDateFormat not being thread safe etc.

Here is my take on it:
((19|20)[0-9]{2})/((0?[1-9])|1[012])/((0?[1-9])|(1[0-9])|(3[01]))

i think this helpful for u...
var regdate = /^(19[0-9][0-9]|20[0-9][0-9])\/(0[1-9]|1[012])\/(0[1-9]|[12][0-9]|3[01])$/;

A simple regex plus a SimpleDateFormat doesn't filter a String date like "2014\26\26", then it would not suffice.
So maybe the best approach is a fully strict regex pattern.
If you turn over your regex (Java) expression from the mkyong website you should be done. As you were already suggested:
String pattern = "((?:19|20)\\d\\d)/(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])";
And for extra assurance you can add a SimpleDateFormat. Here is my extra verbose approach:
if(stringDate.length()==10 && stringDate.matches("((?:19|20)\\d\\d)/(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])")){
Date d=null;
String checkDate=null;
DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
try {
//Also parses the String as a Date with the format "yyyy/MM/dd"
//to be sure that the passed string is correct
d=df.parse(stringDate);
} catch (ParseException e) {
e.printStackTrace();
}
if (d != null) {
//Transforms the Date into a String with the format "yyyy/MM/dd"
checkDate=df.format(d);
System.out.println(checkDate);
}
}

Here is an another simple approach, write a small method that converts the string input to date and convert back the date to string. Then compare both string are equal. If equal then date is correct else its incorrect. All validations are taken care by this approach.
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {
public static boolean isValidDateFormat(String value, String format) {
Date date = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
date = sdf.parse(value);
if (!value.equals(sdf.format(date))) {
date = null;
}
} catch (Exception ex) {
return false;
}
return date != null;
}
public static void main(String[] args) {
String format = "yyyy/MM/dd";
System.out.println("2017/02/31 isValidDateFormat: " + isValidDateFormat("2017/02/31", format));
System.out.println("2017/03/31 isValidDateFormat: " + isValidDateFormat("2017/03/31", format));
System.out.println("2017/04/31 isValidDateFormat: " + isValidDateFormat("2017/04/31", format));
System.out.println("2017/13/31 isValidDateFormat: " + isValidDateFormat("2017/04/31", format));
System.out.println("2017/01/35 isValidDateFormat: " + isValidDateFormat("2017/01/35", format));
System.out.println("017/01/30 isValidDateFormat: " + isValidDateFormat("017/01/30", format));
// output :
//2017/02/31 isValidDateFormat: false
// 2017/03/31 isValidDateFormat: true
// 2017/04/31 isValidDateFormat: false
// 2017/13/31 isValidDateFormat: false
// 2017/01/35 isValidDateFormat: false
// 017/01/30 isValidDateFormat: false
}
}

Just for fun, I wanted to see how the regex Jon Skeet's very valid comment mentioned (that a regex handling leap years correctly would be horrendous) would look like. And look - he was right ;)
^
(?:(?:19|2[01])\d\d\/(?:1[02]|0[13578])\/(?:[0-2]\d|3[01])) # 31 day months
|
(?:(?:19|2[01])\d\d\/(?:(?:11|0[469])\/(?:[0-2]\d|30))) # 30 day months
|
(?:(?:19|2[01])(?:[02468][1235679]|[13579][01345789])|1900|2100)\/02\/(?:[01]\d|2[0-8]) # Non leap year
|
(?:(?:(?:19|21)(?!00)|20)(?:[02468][048]|[13579][26]))\/02\/(?:[01]\d|2[0-9]) # Leap year
$
See it here at regex101.
It tests using four alternations:
31 day months
30 day months
February non-leap years
February leap years
Note that leap years are the years that are a multiple of four. An exception to that rule are multiples of 100 except if it's a multiple of 400. (Phew... This is what makes it horrendous.)

It's regexp date format(dd.MM.yyyy). You can adapt this one for you.
(((0[1-9]{1}|1[0-9]|2[0-9]).(0[1-9]|1[0-2]))|(30.(04|06|09|11))|((30|31).(01|03|05|07|08|10|12))).[0-9]{4}

Related

How do I ensure that the date wrongly introduced by user (ex. 20222, or month 15, or day 44) doesn't translate into a later date [duplicate]

I find it curious that the most obvious way to create Date objects in Java has been deprecated and appears to have been "substituted" with a not so obvious to use lenient calendar.
How do you check that a date, given as a combination of day, month, and year, is a valid date?
For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.
Key is df.setLenient(false);. This is more than enough for simple cases. If you are looking for a more robust (I doubt that) and/or alternate libraries like joda-time, then look at the answer by user "tardate"
final static String DATE_FORMAT = "dd-MM-yyyy";
public static boolean isDateValid(String date)
{
try {
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
df.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
As shown by #Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.
However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:
Invalid characters in the date string
Surprisingly, 2008-02-2x will "pass" as a valid date with locale format = "yyyy-MM-dd" for example. Even when isLenient==false.
Years: 2, 3 or 4 digits?
You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret "12-02-31" differently depending on whether your format was "yyyy-MM-dd" or "yy-MM-dd")
A Strict Solution with the Standard Library
So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.
Date parseDate(String maybeDate, String format, boolean lenient) {
Date date = null;
// test date string matches format structure using regex
// - weed out illegal characters and enforce 4-digit year
// - create the regex based on the local format string
String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {
// date string matches format structure,
// - now test it can be converted to a valid date
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
sdf.applyPattern(format);
sdf.setLenient(lenient);
try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
}
return date;
}
// used like this:
Date date = parseDate( "21/5/2009", "d/M/yyyy", false);
Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: "d/MM/yy", "yyyy-MM-dd", and so on. The format string for the current locale could be obtained like this:
Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();
Joda Time - Better Alternative?
I've been hearing about joda time recently and thought I'd compare. Two points:
Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
Can't see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)
It's quite simple to use:
import org.joda.time.format.*;
import org.joda.time.DateTime;
org.joda.time.DateTime parseDate(String maybeDate, String format) {
org.joda.time.DateTime date = null;
try {
DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
date = fmt.parseDateTime(maybeDate);
} catch (Exception e) { }
return date;
}
tl;dr
Use the strict mode on java.time.DateTimeFormatter to parse a LocalDate. Trap for the DateTimeParseException.
LocalDate.parse( // Represent a date-only value, without time-of-day and without time zone.
"31/02/2000" , // Input string.
DateTimeFormatter // Define a formatting pattern to match your input string.
.ofPattern ( "dd/MM/uuuu" )
.withResolverStyle ( ResolverStyle.STRICT ) // Specify leniency in tolerating questionable inputs.
)
After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Avoid legacy date-time classes
Avoid using the troublesome old date-time classes shipped with the earliest versions of Java. Now supplanted by the java.time classes.
LocalDate & DateTimeFormatter & ResolverStyle
The LocalDate class represents a date-only value without time-of-day and without time zone.
String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
LocalDate ld = LocalDate.parse ( input , f );
System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
System.out.println ( "ERROR: " + e );
}
The java.time.DateTimeFormatter class can be set to parse strings with any of three leniency modes defined in the ResolverStyle enum. We insert a line into the above code to try each of the modes.
f = f.withResolverStyle ( ResolverStyle.LENIENT );
The results:
ResolverStyle.LENIENTld: 2000-03-02
ResolverStyle.SMARTld: 2000-02-29
ResolverStyle.STRICTERROR: java.time.format.DateTimeParseException: Text '31/02/2000' could not be parsed: Invalid date 'FEBRUARY 31'
We can see that in ResolverStyle.LENIENT mode, the invalid date is moved forward an equivalent number of days. In ResolverStyle.SMART mode (the default), a logical decision is made to keep the date within the month and going with the last possible day of the month, Feb 29 in a leap year, as there is no 31st day in that month. The ResolverStyle.STRICT mode throws an exception complaining that there is no such date.
All three of these are reasonable depending on your business problem and policies. Sounds like in your case you want the strict mode to reject the invalid date rather than adjust 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.
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.
You can use SimpleDateFormat
For example something like:
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
The current way is to use the calendar class. It has the setLenient method that will validate the date and throw and exception if it is out of range as in your example.
Forgot to add:
If you get a calendar instance and set the time using your date, this is how you get the validation.
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
java.time
With the Date and Time API (java.time classes) built into Java 8 and later, you can use the LocalDate class.
public static boolean isDateValid(int year, int month, int day) {
try {
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
return false;
}
return true;
}
Building on Aravind's answer to fix the problem pointed out by ceklock in his comment, I added a method to verify that the dateString doesn't contain any invalid character.
Here is how I do:
private boolean isDateCorrect(String dateString) {
try {
Date date = mDateFormatter.parse(dateString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return matchesOurDatePattern(dateString); //added my method
}
catch (ParseException e) {
return false;
}
}
/**
* This will check if the provided string matches our date format
* #param dateString
* #return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
*/
private boolean matchesDatePattern(String dateString) {
return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
An alternative strict solution using the standard library is to perform the following:
1) Create a strict SimpleDateFormat using your pattern
2) Attempt to parse the user entered value using the format object
3) If successful, reformat the Date resulting from (2) using the same date format (from (1))
4) Compare the reformatted date against the original, user-entered value. If they're equal then the value entered strictly matches your pattern.
This way, you don't need to create complex regular expressions - in my case I needed to support all of SimpleDateFormat's pattern syntax, rather than be limited to certain types like just days, months and years.
I suggest you to use org.apache.commons.validator.GenericValidator class from apache.
GenericValidator.isDate(String value, String datePattern, boolean strict);
Note: strict - Whether or not to have an exact match of the datePattern.
I think the simpliest is just to convert a string into a date object and convert it back to a string. The given date string is fine if both strings still match.
public boolean isDateValid(String dateString, String pattern)
{
try
{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
if (sdf.format(sdf.parse(dateString)).equals(dateString))
return true;
}
catch (ParseException pe) {}
return false;
}
Assuming that both of those are Strings (otherwise they'd already be valid Dates), here's one way:
package cruft;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateValidator
{
private static final DateFormat DEFAULT_FORMATTER;
static
{
DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
DEFAULT_FORMATTER.setLenient(false);
}
public static void main(String[] args)
{
for (String dateString : args)
{
try
{
System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
}
catch (ParseException e)
{
System.out.println("could not parse " + dateString);
}
}
}
public static Date convertDateString(String dateString) throws ParseException
{
return DEFAULT_FORMATTER.parse(dateString);
}
}
Here's the output I get:
java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011
Process finished with exit code 0
As you can see, it does handle both of your cases nicely.
This is working great for me. Approach suggested above by Ben.
private static boolean isDateValid(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
Date d = asDate(s);
if (sdf.format(d).equals(s)) {
return true;
} else {
return false;
}
} catch (ParseException e) {
return false;
}
}
looks like SimpleDateFormat is not checking the pattern strictly even after setLenient(false); method is applied on it, so i have used below method to validate if the date inputted is valid date or not as per supplied pattern.
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
boolean valid = true;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
Two comments on the use of SimpleDateFormat.
it should be declared as a static instance
if declared as static access should be synchronized as it is not thread safe
IME that is better that instantiating an instance for each parse of a date.
Above methods of date parsing are nice , i just added new check in existing methods that double check the converted date with original date using formater, so it works for almost each case as i verified. e.g. 02/29/2013 is invalid date.
Given function parse the date according to current acceptable date formats. It returns true if date is not parsed successfully.
public final boolean validateDateFormat(final String date) {
String[] formatStrings = {"MM/dd/yyyy"};
boolean isInvalidFormat = false;
Date dateObj;
for (String formatString : formatStrings) {
try {
SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
sdf.applyPattern(formatString);
sdf.setLenient(false);
dateObj = sdf.parse(date);
System.out.println(dateObj);
if (date.equals(sdf.format(dateObj))) {
isInvalidFormat = false;
break;
}
} catch (ParseException e) {
isInvalidFormat = true;
}
}
return isInvalidFormat;
}
Here's what I did for Node environment using no external libraries:
Date.prototype.yyyymmdd = function() {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return zeroPad([yyyy, mm, dd].join('-'));
};
function zeroPad(date_string) {
var dt = date_string.split('-');
return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}
function isDateCorrect(in_string) {
if (!matchesDatePattern) return false;
in_string = zeroPad(in_string);
try {
var idate = new Date(in_string);
var out_string = idate.yyyymmdd();
return in_string == out_string;
} catch(err) {
return false;
}
function matchesDatePattern(date_string) {
var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
return dateFormat.test(date_string);
}
}
And here is how to use it:
isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
int daysInMonth;
boolean leapYear;
leapYear = checkLeap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
daysInMonth = 30;
else if (month == 2)
daysInMonth = (leapYear) ? 29 : 28;
else
daysInMonth = 31;
return daysInMonth;
}
// to check a year is leap or not
private boolean checkLeap(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
Here is I would check the date format:
public static boolean checkFormat(String dateTimeString) {
return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
|| dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
public static String detectDateFormat(String inputDate, String requiredFormat) {
String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
String dateFormat;
if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMddyyyy";
} else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
dateFormat = "ddMMyyyy";
} else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
dateFormat = "yyyyMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
dateFormat = "yyyyddMM";
} else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
dateFormat = "ddMMMyyyy";
} else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMMddyyyy";
} else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
dateFormat = "yyyyMMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
dateFormat = "yyyyddMMM";
} else {
return "Pattern Not Added";
//add your required regex
}
try {
String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));
return formattedDate;
} catch (Exception e) {
//
return "";
}
}
setLenient to false if you like a strict validation
public boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try {
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
return true;
}
With 'legacy' date format, we can format the result and compare it back to the source.
public boolean isValidFormat(String source, String pattern) {
SimpleDateFormat sd = new SimpleDateFormat(pattern);
sd.setLenient(false);
try {
Date date = sd.parse(source);
return date != null && sd.format(date).equals(source);
} catch (Exception e) {
return false;
}
}
This execerpt says 'false' to source=01.01.04 with pattern '01.01.2004'
We can use the org.apache.commons.validator.GenericValidator's method directly without adding the whole library:
public static boolean isValidDate(String value, String datePattern, boolean strict) {
if (value == null
|| datePattern == null
|| datePattern.length() <= 0) {
return false;
}
SimpleDateFormat formatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
formatter.setLenient(false);
try {
formatter.parse(value);
} catch(ParseException e) {
return false;
}
if (strict && (datePattern.length() != value.length())) {
return false;
}
return true;
}
A simple and elegant way for Android developers (Java 8 not required):
// month value is 1-based. e.g., 1 for January.
public static boolean isDateValid(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
try {
calendar.setLenient(false);
calendar.set(year, month-1, day);
calendar.getTime();
return true;
} catch (Exception e) {
return false;
}
}
Below code works with dd/MM/yyyy format and can be used to check NotNull,NotEmpty as well.
public static boolean validateJavaDate(String strDate) {
if (strDate != null && !strDate.isEmpty() && !strDate.equalsIgnoreCase(" ")) {
{
SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
date.setLenient(false);
try {
Date javaDate = date.parse(strDate);
System.out.println(strDate + " Valid Date format");
}
catch (ParseException e) {
System.out.println(strDate + " Invalid Date format");
return false;
}
return true;
}
} else {
System.out.println(strDate + "----> Date is Null/Empty");
return false;
}
}

SimpleDateFormat doesn't work as expected [duplicate]

This question already has answers here:
SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa?
(7 answers)
Closed 4 years ago.
I try to use this function but it doesn't work with this case '12/05/201a' somebody knows why happen this?
In my test I use this System.out.println(isThisDateValid("12/05/201a", "dd/MM/yyyy")); and the answer was true but I'm expected the result would be false because year contains letters.
public static boolean isThisDateValid(String dateToValidate, String dateFromat)
{
if (dateToValidate == null)
{
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try
{
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e)
{
e.printStackTrace();
return false;
}
return true;
}
DateFormat#parse doesn't necessarily use the entire string:
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
(my emphasis)
SimpleDateFormat's docs tell us that yyyy doesn't necessarily mean it will require four digits for a year:
Year:
...
For parsing, if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
So it's correct (if possibly surprising) that it parses that string in the year 201.
You can use parse(String,ParsePosition) to figure out whether the entire string has been consumed, or validate it with a regular expression before parsing. Here's a version that will check that the whole string has been parsed, and not just the first characters:
public static boolean isThisDateValid(String dateToValidate, String dateFormat) {
if (dateToValidate == null) {
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
sdf.setLenient(false);
ParsePosition position = new ParsePosition(0);
Date date = sdf.parse(dateToValidate, position);
return date != null && position.getIndex() == dateToValidate.length();
}

Lenient SimpleDateFormat acting strange

I understand that, in order to properly validate date strings, one must make DateFormat instances non-lenient to get all ParseExceptions from malformed dates. But consider
String dubiousDate = "2014-04-01";
DateFormat sdf = new SimpleDateFormat( "yyyyMMdd");
Date d;
try {
d = sdf.parse( dubiousDate);
System.out.println( dubiousDate + " -> " + d);
} catch ( ParseException e) {
e.printStackTrace();
System.err.println( dubiousDate + " failed");
}
this will give
2014-04-01 -> Wed Dec 04 00:00:00 CET 2013
Now I can understand that the lenient calendars try to be nice and accept funny negative numbers, but this interpretation looks like the -01 is considered as month, even though it appears last, where the days are. And the -04 months become 04 days, with the minus ignored.
In all leniency, why would this make sense to anyone?
I see another possible interpretation:
In the pattern yyyyMMdd the month part is limited to exact two chars because there are no separators between the different numerical fields. So "-0" will be seen as month which is just zero and is one month behind January yielding December in previous year.
After having "parsed" the fake month, the day part comes with "4" stopping before the second minus char. The result is then the fourth of December.
Finally, the remaining chars "-01" are simply ignored. This is typical for the class SimpleDateFormat about how to handle non-digit trailing chars, for example see this code:
String dubiousDate = "2014-04-01xyz";
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d;
try {
d = sdf.parse(dubiousDate);
System.out.println(dubiousDate + " -> " + d);
// output: Tue Apr 01 00:00:00 CEST 2014
} catch (ParseException e) {
e.printStackTrace();
System.err.println(dubiousDate + " failed");
}
As thumb rule, with only two equal symbol chars MM or dd the parser will only consume up to at most two chars (if digits are found).
Some research about Java 8:
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.parseLenient();
builder.append(DateTimeFormatter.ofPattern("yyyyMMdd"));
DateTimeFormatter dtf = builder.toFormatter();
String dubiousDate = "2014-04-01";
LocalDate date = LocalDate.parse(dubiousDate, dtf);
System.out.println(date);
According to JDK-8-documentation the formatter constructed this way should behave leniently, but unfortunately still throws an exception:
"Exception in thread "main" java.time.format.DateTimeParseException: Text '2014-04-01' could not be parsed at index 3"
Best option would be in lenient case - theoretically - if the parser just ignores the minus chars. But obviously this is not possible with JSR-310 (still too strict). Well, SimpleDateFormat is lenient, but in rather a wrong way.
This doesn't make sense. It sounds like a bug to me.
I think the right answer is to wait for Java 8 where dates are finally done right. Your code, for example, could now change to something like what is below. And, Java will throw an exception, like it should.
import java.util.*;
import java.lang.*;
import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String dubiousDate = "2014-04-01";
LocalDate d;
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
d = LocalDate.parse(dubiousDate, formatter);
System.out.println(dubiousDate + " -> " + d);
}
catch (Exception e) {
e.printStackTrace();
System.err.println(dubiousDate + " failed");
}
}
}
}

What is the regular expression for Date format dd\mm\yyyy?

What is the regular expression for Date format dd\mm\yyyy? I am unable to find out regex for this format?
regex for pattern dd/mm/yyyy
String regex = "^([0-2][0-9]||3[0-1])/(0[0-9]||1[0-2])/([0-9][0-9])?[0-9][0-9]$";
In my opinion, it's best to verify the formatting using a regular expression, but verify the validity with code (Java, in your case). It would be absurdly verbose to try to check things like differing days per month and leap years with a regular expression.
I suggest parsing the date using a regex like ([0-9]{2})\\([0-9]{2})\\([0-9]{4}), then extract each piece (dd, mm, and yyyy, respectively), and try to create a java.util.Date object out of them.
Note the following:
Dates are typically written with forward slashes (/) not backslashes (\),
In Java strings, to write a backslash character in a regular expression, you will have to actually write \\\\ for two backslash (\\) characters. In java strings to write a backslash we need the escape character (which is again a \).
/**
* Created with IntelliJ IDEA.
* User: S34N
* Date: 2013/07/30
* Time: 8:21 AM
* To change this template use File | Settings | File Templates.
*/
//Import the required classes/packages
import javax.swing.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class dateInputScan {
public static void main(String args[]) {
dateInputScan run = new dateInputScan();
run.dateInputScan();
}
public void dateInputScan() {
//Instantiating variables
String lvarStrDateOfTransaction = null;
DateFormat formatter = null;
Date lvarObjDateOfTransaction = null;
//Use one of the following date formats.
lvarStrDateOfTransaction = "29/07/2013";
//lvarStrDateOfTransaction = "29-07-2013";
//lvarStrDateOfTransaction = "20130729";
//lvarStrDateOfTransaction = "2013-07-29";
//lvarStrDateOfTransaction = "29/07/2013";
//You can also add your own regex (Regular Expression)
if (lvarStrDateOfTransaction.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})")) {
formatter = new SimpleDateFormat("dd/MM/yyyy");
} else if (lvarStrDateOfTransaction.matches("([0-9]{2})-([0-9]{2})-([0-9]{4})")) {
formatter = new SimpleDateFormat("dd-MM-yyyy");
} else if (lvarStrDateOfTransaction.matches("([0-9]{4})([0-9]{2})([0-9]{2})")) {
formatter = new SimpleDateFormat("yyyyMMdd");
} else if (lvarStrDateOfTransaction.matches("([0-9]{4})-([0-9]{2})-([0-9]{2})")) {
formatter = new SimpleDateFormat("yyyy-MM-dd");
} else if (lvarStrDateOfTransaction.matches("([0-9]{4})/([0-9]{2})/([0-9]{2})")) {
formatter = new SimpleDateFormat("yyyy/MM/dd");
}
try {
lvarObjDateOfTransaction = formatter.parse(lvarStrDateOfTransaction);
JOptionPane.showMessageDialog(null, "Date: " + lvarObjDateOfTransaction);
} catch (Exception ex) { //Catch the Exception in case the format is not found.
JOptionPane.showMessageDialog(null, ex);
}
}
}
I tried this ^[0-3]{1}[0-9]{1}/[0-1]{1}[1-2]{1}/[1-9]{1}[0-9]{3}$ and it works
String dateRegEx="^[0-3]{1}[0-9]{1}/[0-1]{1}[1-2]{1}/[1-9]{1}[0-9]{3}$";
System.out.println(Pattern.matches(dateRegEx, "01/01/1990"));
([0-2][0-9]||3[0-1])\\(0[0-9]||1[0-2])\\((19|20)\d\d)
Above regex will validate
1st 2 character in between range of 0-31
then next group must contain character in between range 0-12
then next group contain year in between range 1900-2099
It is working perfectly fine for me
I have done this one for format DDMMYY
([0-2]?[0-2][0-9]|3[0-1])(0?0[1-9]|1[0-2])([0-9][0-9])
If you wanna fixed version use:
private static final String FIXED_DATE_PATTERN =
"(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/((19|20)\\d\\d)";
This force zero in days, and month values, and dont allow inputs like 00/12/2018 or 07/00/2018 in DD/MM/YYYY
for "dd/MM/yyyy" You can use :
(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[1-9])/(0[1-9]|1[0-2]|[1-9])/([0-9]{4})
Day (dd): it accept 1 or 01, 9 or 09 (until 31)
Month (MM): it accept 1 or 01 (until 12)
Year (yyyy): It accept 4 digit

forcing 4 digits year in java's simpledateformat

I want to validate and parse dates using a simpleDateFormat with the format "yyyymmdd"
This also allows 100624, which is parsed to the year 10 (54 years after Julius Ceasar died). The dates will also be something like 1970, so I don't want to settle with SimpleDateFornat("yymmdd").
I'm wondering is there a way to force a four digit year format using the SimpleDateFormat? I'm close to do a regexp test upfront but maybe there is a smart way to use the (Simple)DateFormat()?
As requested the code, things are getting more complicate and my research was half. The Format used was yyyy-MM-dd to start with (it came from a variable, which had a wrong javadoc). However as indicated in an answer below yyyyMMdd does force a four year digit. So my question is changed to How to force a four digit year for the "yyyy-MM-dd" format. And why does "yyyyMMdd" behave different?
public void testMaturity() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
System.out.println(" " + sdf.format(sdf.parse("40-12-14")));
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
sdf.setLenient(false);
System.out.println(" " + sdf2.format(sdf2.parse("401214")));
fail();
} catch (ParseException pe) {
assertTrue(true);
}
Which prints 0040-12-14
Simply use yyyyMMdd (note: upper case M is used to indicate month, otherwise you're parsing minutes!) and then check if the year is greater some cutoff date (for example, when parsing birth dates, greater 1800 is a safe bet, when parsing dates for upcomming dates greater than or equal the current year would be good).
Hmm. I suspect you should be using "MM" instead of "mm" to start with... but "100624" doesn't parse anyway when I try it - even in lenient mode:
import java.util.*;
import java.text.*;
public class Test
{
public static void main(String[] args) throws Exception
{
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
format.setLenient(true);
tryParse(format, "100624");
format.setLenient(false);
tryParse(format, "100624");
}
static void tryParse(DateFormat format, String text)
{
try
{
Date date = format.parse(text);
System.out.println("Parsed " + text + " to " + date);
}
catch (ParseException pe)
{
System.out.println("Failed to parse " + text);
}
}
}
(And even using "mm" instead of "MM" it still fails to parse.)
Prints:
Failed to parse 100624
Failed to parse 100624
Perhaps you could show the code which is managing to parse this?
There is no easy way for this. If you're saying that dates can be like 1970 the main question would be what 700624 means - 1970 or 2070? You should either implement some cutoff date like Joachim proposed or move entirely to 4 digits year.
Well it is quite straight and forward. Follow these simple steps:
Step 1:
try {
String dates = "40-12-14";
String patterns = "yy-MM-dd";
SimpleDateFormat format3 = new SimpleDateFormat(patterns);``
java.util.Date dates1 = format3.parse(dates);
SimpleDateFormat sdfDates = new SimpleDateFormat("dd/MM/yyyy");
String strDate1 = sdfDate.format(dates1);
System.out.println("Date1: " + strDate1);
}
catch(ParseException e){
e.printStackTrace();
}
This will output:
Date1: 14/12/1940

Categories