I am using the following code for validating date.the code is working fine for days and months but its accepting the more than 5 digit year value but in the format i have specified "yyyy".
Question: why it is returning true in case of 21114 because its not a valid date with respect to my specified format.?
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class raju {
public static void main(String[] args) {
boolean a=raju.isThisDateValid("12/12/21114","MM/dd/yyyy");
System.out.println(a);
}
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;
}
}
From the SimpleDateFormat javadoc,
Otherwise, calendar system specific forms are applied. For both formatting and parsing, if the number of pattern letters is 4 or more, a calendar specific long form is used. Otherwise, a calendar specific short or abbreviated form is used.
For the behavior you seem to want you'll need to check the year is less then 10000.
Looking at the documentation, it appears that if the exact number of digits you specify isn't provided, the value is treated literally.
For example:
"yyyy" with 12 passed in will be successfully parsed as 12 A.D. (not 2012)
"yyyy" with 21114 passed in will be successfully parsed as 21114 A.D.
Since you are using the parse(String) method of the DateFormat class which is extended by the SimpleDateFormat here is what the javadoc of parse method reads:
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.
The javadoc of parse(String,ParsePosition) reads:
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).
This parsing operation uses the calendar to produce a Date. As a result, the calendar's date-time fields and the TimeZone value may have been overwritten, depending on subclass implementations. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
The format when used for parsing is more about the order of the components and the separators.
I would expect it not to mind if the input omitted leading 0s on the days or months even though it explicitly says to expect 2 digits. And so the year is also not a strict count of digits.
In practice if you want to check the year is valid you need to do another step. After all in most applications even the year 9999 would not make sense. So if you allow that then why not 10k?
Aside
Java is Y10k compliant. In fact it is Y1,000,000 complient. It won't parse 1/1/10000000 (10 million) though!
The problem is with the way dates work in java, 21114 is a valid year according to java in the format YYYY. As its already well explianed by Westos and bstar55, about java and date formats. A solution to this might be adding your own validations to make sure date format is the way you need. For example if you need the date to be like this dd (not more than 2 digits), month (not more than 2 digits ) and year (not more than 4 digits)
public static void main(String[] args) {
boolean a= isThisDateValid("12/12/21114","MM/dd/yyyy");
System.out.println(a);
}
public static boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
String[] Arr=dateToValidate.split("/");
try {
if(Arr[0].length()>2 || Arr[1].length()>2 || Arr[2].length()>4){
throw new ParseException("Error in date: "+dateToValidate,0);
}
//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;
}
Related
This question already has answers here:
SimpleDateFormat leniency leads to unexpected behavior
(3 answers)
SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa?
(7 answers)
Closed 2 years ago.
I'm using this SimpleDateFormat yyyy/MM/dd to pass a date value from String to Date. But there is this issue: when I input a non-date value, like 2020/12/2x, it will return to 2020/12/02. And when I try 2020/12/x2, the function will return an error. Anyone know why this is happening? Thank you so much.
Here is my code.
Declare SimpleDateFormat
SimpleDateFormat df2 = new SimpleDateFormat("yyyy/MM/dd");
df2.setLenient(false);
Function I used to check if the string I input is a valid date or not
private static Boolean validDate(DateFormat df, String s) {
Boolean valid=false;
try {
Date d= df.parse(s);
valid=true;
} catch (Exception e) {
valid=false;
}
return valid;
}
I used input:
2020/12/2x
The SimpleDateFormat class contains many corner cases with unexpected behavior (as pointed out in a comment the DateFormat.parse(String) Javadoc says the method may not use the entire text of the given string), and has long been deprecated in favor of classes in the java.time package. For example,
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate d = LocalDate.parse("2020/12/2x", formatter);
System.out.println(formatter.format(d));
Is consistent, and throws java.time.format.DateTimeParseException: Text '2020/12/2x' could not be parsed at index 8
I think the parse(string) method just travel from index zero to the farthest possible index where it can parse the source string to a correct date and does not care about remaining characters. It makes result of
df2.parse("2020/12/2x")
equivalent to result of
df2.parse("2020/12/2xxxxxxx_anything_goes_here")
In case of df2.parse("2020/12/x2"). When the method travels to x character, it realize that there is no date satisfying "2020/12/x", so it immediately throws an exception.
I need to make sure that the users enters a date in the follow format: mm/dd/yyy
I've tried using a simple method to verify that if the parameter given is not null or empty, to check if it matches the required formatting, then if so, set that as the new date.
public void setDate(String dateIn) {
if ((dateIn != null) && !(dateIn.equals(""))) {
if (dateIn.equals("%2d" + "/" + "%2d" + "/" + "%4d")) {
validDate = dateIn;
}
}
}
The problem is in my second if statement, I just need to figure out how to make the method take numbers for the day, month, and year.
Just try to parse the date. I am using the "new" java.time api introduced in Java 8. This api has been thouroughly tested in the JDK and is the way to go (Generally try to do the least amount of work yourself, with a very high probability you're having a problem that has been encountered before):
try {
DateTimeFormatter.ofPattern("MM/dd/yyyy").parse(dateIn);
// valid date
} catch(DateTimeParseException e) {
// invalid date
}
This question already has an answer here:
What is the difference between JAVA date format "dd/MM/yyyy" and "dd/mm/yyyy"? [duplicate]
(1 answer)
Closed 6 years ago.
public static void main(String [] args){
String date = "01/08/2017";
Date todaydate = new Date();
SimpleDateFormat myFormat = new SimpleDateFormat("dd/mm/yyyy");
try {
Date expiry = myFormat.parse(date);
if(expiry.compareTo(todaydate) >0){
System.out.println("false");
}
else{
System.out.println("true");
}
} catch (ParseException e) {
e.printStackTrace();
}
}
I am comparing date 01/08/2017 to current date. It should return false. However, it is returning true, even if I use isAfter in the if condition.
This should do the trick
SimpleDateFormat myFormat = new SimpleDateFormat("dd/MM/yyyy");
besides the format, compareTo() is returning true, always that the date is after the argument, which is right. If the dates where equals it would return 0.
Check the docs: http://docs.oracle.com/javase/6/docs/api/java/util/Date.html#compareTo%28java.util.Date%29
The value 0 if the argument is a string lexicographically equal to this string; a value less than 0 if the argument is a string lexicographically greater than this string; and a value greater than 0 if the argument is a string lexicographically less than this string.
you need to change the condition
if(expiry.compareTo(todaydate) <0)
Go to this link n see the methods of the dates
https://www.tutorialspoint.com/java/util/date_compareto.htm
I've been given an assignment where I need to create a set of interfaces and classes for a car rental company. I'm busy working on implementing a LicenceNumber class which must match the following spec:
The licence number has three components. The first component is the concatenation of the initial of the first name of the driver with the initial of the last name of the driver. The second component is the year of issue of the licence. The third component is an arbitrary serial number. For example, the string representation of the licence number for a licence issued to Mark Smith in 1990 would have the form, MS-1990-10, where the 10 is a serial number that, with the initials and year, guarantees the uniqueness of the licence number as a whole.
You should use the java.util.Date class to represent dates. However, you must not use deprecated methods of the Date class. So, for example, in your test classes use java.util.Calendar to construct dates of birth and dates of issue of licences. You can assume default time zone and locale. (Note, there are now better classes available in the java.time package which was introduced in Java 1.8 but it will be good experience to work with classes
which are written less well).
So far I've got the following implementation for the LicenceNumber class:
import java.util.Calendar;
import java.util.Date;
public class LicenceNumber {
private String licenceNo;
public LicenceNumber(Name driverName, Date issueDate){
setLicenceNo(driverName, issueDate);
}
public String getLicenceNo() {
return licenceNo;
}
public void setLicenceNo(Name driverName, Date issueDate) {
String initials;
initials = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0,1);
System.out.println(initials);
int issueYear = issueDate.getYear(); //Deprecated
}
}
I want to be able to get only the year from issueDate, but the only way I can figure out how to do it, is by using the deprecated method, getYear(). This is obviously against the criteria, so can anyone shed some light on how to get the Year from a Date object without using deprecated methods?
Thanks ahead.
Try this
Date date = new Date();
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
int year = localDate.getYear();
Here is fix
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
System.out.println(calendar.get(Calendar.YEAR));
Substitute the new date with the date you are wanting to get the year from.
I can think of three ways to obtain the year from a Date object but avoiding the deprecated methods. Two of the approaches use other Objects (Calendar and SimpleDateFormat), and the third parses the .toString() of the Date object (and that method is not deprecated). The .toString() is potentially locale specific, and there could be issues with the approach in other locales, but I am going to assume (famous last words) that the year is always the only sequence of 4 digits. One could also understand the specific locale and use other parsing approaches. For example, standard US/English puts the year at the end (e.g., "Tue Mar 04 19:20:17 MST 2014"), one could use the .lastIndexOf(" ") on the .toString().
/**
* Obtains the year by converting the date .toString() and
* finding the year by a regular expression; works by assuming that
* no matter what the locale, only the year will have 4 digits
*/
public static String getYearByRegEx(Date dte) throws IllegalArgumentException
{
String year = "";
if (dte == null) {
throw new IllegalArgumentException("Null date!");
}
// match only a 4 digit year
Pattern yearPat = Pattern.compile("^.*([\\d]{4}).*$");
// convert the date to its String representation; could pass
// this directly, but I prefer the intermediary variable for
// potential debugging
String localDate = dte.toString();
// obtain a matcher, and then see if we have the expected value
Matcher match = yearPat.matcher(localDate);
if (match.matches() && match.groupCount() == 1) {
year = match.group(1);
}
return year;
}
/**
* Constructs a Calendar object, and then obtains the year
* by using the Calendar.get(...) method for the year.
*/
public static String getYearFromCalendar(Date dte) throws IllegalArgumentException
{
String year = "";
if (dte == null) {
throw new IllegalArgumentException("Null date!");
}
// get a Calendar
Calendar cal = Calendar.getInstance();
// set the Calendar to the specific date; the reason why
// Calendar is deprecated is this mutability
cal.setTime(dte);
// get the year using the .get method, and convert to a String
year = String.valueOf(cal.get(Calendar.YEAR));
return year;
}
/**
* Uses the SimpleDateFormat with a format for only a year.
*/
public static String getYearByFormatting(Date dte)
throws IllegalArgumentException
{
String year = "";
if (dte == null) {
throw new IllegalArgumentException("Null date!");
}
// set a format only for the year
SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
// format the date; the result is the year
year = sdf.format(dte);
return year;
}
public static void main(String[] args)
{
Calendar cal = Calendar.getInstance();
cal.set(2014,
Calendar.MARCH,
04);
Date dte = cal.getTime();
System.out.println("byRegex: "+ getYearByRegEx(dte));
System.out.println("from Calendar: "+ getYearFromCalendar(dte));
System.out.println("from format: " + getYearByFormatting(dte));
}
All three approaches return the expected output based upon the test input.
Take a look at https://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html and related classes. That can yield a string with just the "year" field from a given 'Date'.
I'm looking for a regex string that will validate dates formatted as yyyyMMdd (no delimiters) and works for leap years. The closest thing I found so far only validates dates since 2000.
import java.util.regex.Pattern;
public class TestDate {
public static final Pattern datePattern = Pattern.compile("** Need RegEx **");
public static void main(String[] args) {
System.out.println(datePattern.matcher("19960229").matches());
}
}
I suggest using the java.text.DateFormat as shown in this page :
public static boolean isValidDateStr(String date) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.setLenient(false);
sdf.parse(date);
}
catch (ParseException e) {
return false;
}
catch (IllegalArgumentException e) {
return false;
}
return true;
}
I thing using regex for this is almost impossible, due to the complex calculation required for leap years. Jon Skeet is right: use new SimpleDateFormat("yyyyMMdd") for this.
the regex for a leap year is very complex: case in point: (this also incorporates full date checking)
"(((\\d\\d)(0[48]|[2468][048]|[13579][26])|([02468][048]|[13579][26])(00))(02)([012]\\d))|(\\d\\d([02468][1235679]|[13579][01345789])(02)([01]\\d|2[012345678]))|(\\d\\d\\d\\d((0[13578]|1[02])([012]\\d|3[01])|((0[46]|11)([012]\\d|30))))"
first section separates out the the general case with non ending on 00
the second section handles the century year so only years divisable by 400 are leap years (1900 is not a leap year)
then in the last section it handles all the other months
(I didn't really test this but it should work close enough)