Related
I am trying to get a user to input YR MON DAY and then system calculate the current age based on the provided input and show the age on the screen.
Here is the beginning to my code:
static void checkAgeFormat(int current_date, int current_month,
int current_year, int birth_date,
int birth_month, int birth_year) {
int f = 0;
if(current_date <= 01 && current_date => 31) {
System.out.println("Invalid current_date");
f = 1;
}
I am getting a "Bad Operand types for binary operator &&" I cannot figure out why, and I am fairly new to coding.
Thanks for any help
It is >= not => so if(current_date<=1 && current_date>=31)
(don't use 0 as prefix for numbers, it causes them to be interpreted as octal)
What are you trying to return here? The amount of days left before someones birthdate? You could use the between() method of Period class for that.
static void checkAgeFormat(int current_date, int current_month,
int current_year, int birth_date,
int birth_month, int birth_year) {
LocalDate birthDate = LocalDate.of(birth_year, birth_month, birth_date);
long daysLeft = Period.between(LocalDate.now(), birthDate).get(ChronoUnit.DAYS);
}
As I have already mentioned in the comment, the issue is because of the bad symbol, => for the operator. It should be >=. Check this to learn more about operators.
Apart from that, I can see a serious problem with your logic. The way you are validating the date values is a naive way to do it. I suggest you do it using OOTB APIs as shown below:
import java.time.DateTimeException;
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
// Tests
checkAgeFormat(45, 10, 2017, 10, 10, 2010);
checkAgeFormat(30, 2, 2017, 10, 10, 2010);
checkAgeFormat(31, 4, 2017, 10, 10, 2010);
checkAgeFormat(30, 15, 2017, 10, 10, 2010);
checkAgeFormat(30, 4, 2020, 10, 10, 2010);
}
static void checkAgeFormat(int current_day, int current_month, int current_year, int birth_day, int birth_month,
int birth_year) {
int f = 0;
LocalDate currentDate, birthDate;
try {
currentDate = LocalDate.of(current_year, current_month, current_day);
birthDate = LocalDate.of(birth_year, birth_month, birth_day);
System.out.println("If you see this line printed, the date values are correct.");
// ...Rest of the code
} catch (DateTimeException e) {
System.out.println(e.getMessage());
f = 1;
}
// ...Rest of code
}
}
Output:
Invalid value for DayOfMonth (valid values 1 - 28/31): 45
Invalid date 'FEBRUARY 30'
Invalid date 'APRIL 31'
Invalid value for MonthOfYear (valid values 1 - 12): 15
If you see this line printed, the date values are correct.
isn't it easier to write a function where the user will provide year, month and the day and it will return how old is the user? Below is an example of a function that does it.
public static int getYears(int year, int month, int day)
{
LocalDate yearOfBirth = LocalDate.of(year, month, day);
LocalDate currentDate = LocalDate.now();
Period period = Period.between(yearOfBirth, currentDate);
return period.getYears();
}
you are getting the error because
you are using if (current_date <= 01 && current_date =>31) instead of
if (current_date <= 01 && current_date >=31)
after editing your code will be:
static void checkAgeFormat(int current_date, int current_month, int current_year, int birth_date, int birth_month, int birth_year) {
int f=0
if (current_date <= 01 && current_date >=31){
System.out.println("Invalid current_date");
f=1;
}
}
I've actually been browsing for quite a while on this site, sadly without much progress. Thanks to a lot of extremely useful answers, I've learned quite a bunch of stuff though!
I'm learning Java since ... about 4 days (I guess?) so I'm not very experienced in the methods I can use.
There's this assignment we got at our univ. We shall write a program that returns how many days have passed between two dates. The only restriction is to keep the program as simple as possible, we're not allowed to use "complicated methods".
Sadly, my program is kind of stuck. If I try it out e.g. the dates 23 01 1994 and 07 04 1997, it counts only up to 01 01 1997 and suddenly stops. I have no idea why that happens, I even doubt if I fully understood what I wrote there.
Anyways, here's my code:
import java.util.Arrays;
import java.util.Scanner;
public class Datecalc {
public static int yearcounter,monthcounter,daycounter,days;
public static int[] input() {
Scanner input = new Scanner(System.in);
//Eingabe der Daten
String day1 = input.next();
String month1 = input.next();
String year1 = input.next();
String day2 = input.next();
String month2 = input.next();
String year2 = input.next();
int day1int = Integer.parseInt(day1);
int month1int = Integer.parseInt(month1);
int year1int = Integer.parseInt(year1);
int day2int = Integer.parseInt(day2);
int month2int = Integer.parseInt(month2);
int year2int = Integer.parseInt(year2);
int [] eingabe = new int [6];
eingabe[0] = day1int;
eingabe[1] = month1int;
eingabe[2] = year1int;
eingabe[3] = day2int;
eingabe[4] = month2int;
eingabe[5] = year2int;
return eingabe;
// put everything into an array to be able to use it in the main method
}
public static void main(String[] args) {
int[] eingabe = input();
days=0;
daycounter = eingabe[0];
monthcounter = eingabe[1];
yearcounter = eingabe[2];
Integer[] einunddreissig = {1,3,5,7,8,10,12}; //months with 31 days
Integer[] dreissig = {4,6,9,11}; // months with 30 days
while (daycounter != eingabe[3] && monthcounter != eingabe[4] && yearcounter != eingabe[5] ) {
// if its a month that has 31 days
if ( Arrays.asList(einunddreissig).contains(monthcounter) ) {
for (int i = daycounter; i <= 31; i++) {
days++;
}
daycounter=1;
monthcounter++;
}
// if its a month with 30 days
if ( Arrays.asList(dreissig).contains(monthcounter) ) {
for (int i = daycounter; i <= 30; i++) {
days++;
daycounter++;
}
daycounter=1;
monthcounter++;
// february
} else if ( monthcounter == 2) {
for (int i = daycounter; i <= 28; i++) {
days++;
}
daycounter=1;
monthcounter++;
} else if (monthcounter==13) {
monthcounter=1;
yearcounter++;
}
}
// checking how many days were counted and comparing the input (eingabe[something]) to how far the daycounter reached
System.out.println(" "+days);
System.out.println(" "+daycounter+" "+monthcounter+" "+yearcounter);
System.out.println(eingabe[3]+" "+eingabe[4]+" "+eingabe[5]);
}
}
I hope there is somebody who might be so kind to give me a hint how to fix that.
I would separate this out into two different functions, one that counts the days until the end of the current year and another which counts the days since the beginning of the current year. Then you can put the total days in terms of those values.
First write a generic sum() function:
public static int sum(int[] a, int start, int end){
int sum = 0;
for(int i = start; i < end; i++) sum += a[i];
return sum;
}
Then use the sum() function to easily compute daysToEnd and daysFromStart:
static int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
public static int daysToEnd(int day, int month){
return sum(daysInMonth, month - 1, 12) - day;
}
public static int daysFromStart(int day, int month){
return sum(daysInMonth, 0, month - 1) + day;
}
public static void main(String... args){
int[] date1 = {23, 1, 1994};
int[] date2 = {1, 1, 1997};
// This actually works for same or different years
int days = daysToEnd(date1[0], date1[1]) +
365*(date2[2] - date1[2] - 1) +
daysFromStart(date2[0], date2[1]);
System.out.println(days);
}
Do you have to use loops? Here's some food for thought -- imagine this as a simple algebra problem. In your program, each date is modeled as a collection of quantities, each of a different unit. Once you obtain uniform units, you can just do the arithmetic.
In the UNIX world we use an agreed-upon reference date called the "epoch", 12:00 AM on 01 January, 1970 as a reference date. Kind of like "tare" on a balance. All other dates can be represented as some number of seconds relative to that time.
I would construct the passed in user input to java.util.Date type. But I am assuming line by line input. You can construct a string using StringBuilder if you want to get day,mth,year separately.
try{
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
DateFormat df = new SimpleDateFormat ("dd/MM/yyyy");
System.out.println("Enter Date1");
String dateSt1= br.readLine(); // Accepts date1 as string
System.out.println("Enter Date2");
String dateSt2= br.readLine(); // Accepts date2 as string
Date date1=df.parse(dateSt1); // Date String to Date
Date date2=df.parse(dateSt2);
}catch(IOException ex)
}catch(ParseExceptionOfSomesort pex)
}
then use this to get the difference in days -
int diffInDays = (int)( (date2.getTime() - date1.getTime())
/ (1000 * 60 * 60 * 24) )
If you get this in negative, that means the second date is before first date.
Think: Do I have to do it using a loop or hasn't java already added some utility method to do date compare?
What makes something "complicated" though? Writing too many lines of code or using existing java library?
I've got a String like this: 2013-04-19, and I want to change it into : April 19th 2013. I know there is some classes in Java like SimpleDateFormat, but I don't know what kind of function I should use. Maybe I need to choose the class Pattern? I need some help.
Try the following, which should give you correct "th", "st", "rd" and "nd" for days.
public static String getDayOfMonthSuffix(final int n) {
if (n >= 11 && n <= 13) {
return "th";
}
switch (n % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
public static void main(String[] args) throws ParseException {
Date d = new SimpleDateFormat("yyyy-MM-dd").parse("2013-04-19");
int day = Integer.parseInt(new java.text.SimpleDateFormat("dd").format(d));
SimpleDateFormat sdf = new SimpleDateFormat("MMMMM dd'" + getDayOfMonthSuffix(day) + "' yyyy");
String s = sdf.format(d);
System.out.println(s);
}
Which will print April 19th 2013
(Day termination adapted from this post)
Try this:
String originalDate = "2013-04-19";
Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(originalDate);
} catch (Exception e) {
}
String formattedDate = new SimpleDateFormat("MMMM dd yyyy").format(date);
Will not print the st, nd, rd, etc.
Just using parse method from SimpleDateFormat class
try this:
new SimpleDateFormat("MMM dd YYYY").parse("2013-04-19");
I'm pretty new to Java and tried the Best Before puzzle from Spotify yesterday. I get the "Wrong Answer" error when I send it in.
Checking for other solutions didn't help and I cant figure out which input gives a wrong answer.
At best you would just tell me which code lead to the wrong answer. I should be able to correct the code myself then.
import java.util.Scanner;
public class Bestbefore {
public static void main(String[] args) {
//Process Input String
Scanner scanner = new Scanner(System.in);
String dateString = scanner.nextLine();
Integer a,b,c;
a=Integer.parseInt(dateString.substring(0,dateString.indexOf("/")));
b=Integer.parseInt(dateString.substring(dateString.indexOf("/")+1,
dateString.lastIndexOf("/")));
c=Integer.parseInt(dateString.substring(dateString.lastIndexOf("/")+1));
int[][] va={ //All possible combinations
{a,b,c},
{a,c,b},
{b,a,c},
{b,c,a},
{c,a,b},
{c,b,a}
};
int i=0;
while (!checkDate(va[i][0], va[i][1], va[i][2])
&& i<5) // get the first valid date combination
i++;
//to prevent OutofBoundsException of the while loop
if (!checkDate(va[i][0], va[i][1], va[i][2]))
i++;
if (i==6)
System.out.println(dateString+" is illegal");
else
{ //compare the rest of the va-Array and save the position of the lowest Date in i
for (int k=i+1;k<6;k++)
if (checkDate(va[k][0], va[k][1], va[k][2]))
{
if (ageOfDate(va[k][0], va[k][1], va[k][2])
< ageOfDate(va[i][0], va[i][1], va[i][2]))
i=k;
}
System.out.println(getDate(va[i][0], va[i][1], va[i][2]));
}
}
public static boolean checkDate(int day,int month, int year)
{
int[] dpm={31,28,31,30,31,31,30,31,30,31,30,31}; //Days per Month
if (year<2000)
year=year+2000;
if((year%4==0 && year%100!=0) || year%400==0)
dpm[1]=29; //Leapyear correction
if (month==0 || month>12)
return false;
else
if (day==0 || day>dpm[month-1])
return false;
else
return true;
}
public static int ageOfDate(int day,int month, int year) //to compare 2 dates
{
return ((year*10000)+(month*100)+day);
}
public static String getDate(int day,int month, int year) //for the Output
{
String smonth = String.valueOf(month);
String sday = String.valueOf(day);
if (year<2000)
year=year+2000;
if (month<10)
smonth="0"+smonth;
if (day<10)
sday="0"+sday;
return (year+"-"+smonth+"-"+sday);
}
}
June has 30 days, August and July both have 31. Your array dpm is initialized in a wrong manner. So here is where you can go wrong: 31/06/20 -> earliest possible is 20-th of June 2031 instead of 31-st of June 2020.
Hope this solves your problem.
Might be that you don't handle negative numbers. -3/5/2012 would be valid. Also, your program does not handle invalid integers gracefully as it will exit with an exception. If I pass "as/5/12" output should be "as/5/12 is illegal".
FYI, the following
while (!checkDate(va[i][0], va[i][1], va[i][2])
&& i<5) // get the first valid date combination
i++;
//to prevent OutofBoundsException of the while loop
if (!checkDate(va[i][0], va[i][1], va[i][2]))
i++;
Could be reduced to
while (i <= 5 && !checkDate(va[i][0], va[i][1], va[i][2]))
i++;
I'm just wondering if there is a way (maybe with regex) to validate that an input on a Java desktop app is exactly a string formatted as: "YYYY-MM-DD".
Use the following regular expression:
^\d{4}-\d{2}-\d{2}$
as in
if (str.matches("\\d{4}-\\d{2}-\\d{2}")) {
...
}
With the matches method, the anchors ^ and $ (beginning and end of string, respectively) are present implicitly.
The pattern above checks conformance with the general “shape” of a date, but it will accept more invalid than valid dates. You may be surprised to learn that checking for valid dates — including leap years! — is possible using a regular expression, but not advisable. Borrowing from an answer elsewhere by Kuldeep, we can all find amusement and admiration for persistence in
((18|19|20)[0-9]{2}[\-.](0[13578]|1[02])[\-.](0[1-9]|[12][0-9]|3[01]))|(18|19|20)[0-9]{2}[\-.](0[469]|11)[\-.](0[1-9]|[12][0-9]|30)|(18|19|20)[0-9]{2}[\-.](02)[\-.](0[1-9]|1[0-9]|2[0-8])|(((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)[\-.](02)[\-.]29
In a production context, your colleagues will appreciate a more straightforward implementation. Remember, the first rule of optimization is Don’t!
You need more than a regex, for example "9999-99-00" isn't a valid date. There's a SimpleDateFormat class that's built to do this. More heavyweight, but more comprehensive.
e.g.
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
boolean isValidDate(string input) {
try {
format.parse(input);
return true;
}
catch(ParseException e){
return false;
}
}
Unfortunately, SimpleDateFormat is both heavyweight and not thread-safe.
Putting it all together:
REGEX doesn't validate values (like "2010-19-19")
SimpleDateFormat does not check format ("2010-1-2", "1-0002-003" are accepted)
it's necessary to use both to validate format and value:
public static boolean isValid(String text) {
if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d"))
return false;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
try {
df.parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
A ThreadLocal can be used to avoid the creation of a new SimpleDateFormat for each call.
It is needed in a multithread context since the SimpleDateFormat is not thread safe:
private static final ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() {
#Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
System.out.println("created");
return df;
}
};
public static boolean isValid(String text) {
if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d"))
return false;
try {
format.get().parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
(same can be done for a Matcher, that also is not thread safe)
This will do it regex: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"
This will take care of valid formats and valid dates. It will not validate the correct days of the month i.e. leap year.
String regex = "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$";
Assert.assertTrue("Date: matched.", Pattern.matches(regex, "2011-1-1"));
Assert.assertFalse("Date (month): not matched.", Pattern.matches(regex, "2011-13-1"));
Good luck!
I would go with a simple regex which will check that days doesn't have more than 31 days and months no more than 12. Something like:
(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\\d\\d)
This is the format "dd-MM-yyyy". You can tweak it to your needs (for example take off the ? to make the leading 0 required - now its optional), and then use a custom logic to cut down to the specific rules like leap years February number of days case, and other months number of days cases. See the DateChecker code below.
I am choosing this approach since I tested that this is the best one when performance is taken into account. I checked this (1st) approach versus 2nd approach of validating a date against a regex that takes care of the other use cases, and 3rd approach of using the same simple regex above in combination with SimpleDateFormat.parse(date).
The 1st approach was 4 times faster than the 2nd approach, and 8 times faster than the 3rd approach. See the self contained date checker and performance tester main class at the bottom.
One thing that I left unchecked is the joda time approach(s). (The more efficient date/time library).
Date checker code:
class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* #param date the date that needs to be checked.
* #return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
Date checking/testing and performance testing:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Tester {
private static final String[] validDateStrings = new String[]{
"1-1-2000", //leading 0s for day and month optional
"01-1-2000", //leading 0 for month only optional
"1-01-2000", //leading 0 for day only optional
"01-01-1800", //first accepted date
"31-12-2199", //last accepted date
"31-01-2000", //January has 31 days
"31-03-2000", //March has 31 days
"31-05-2000", //May has 31 days
"31-07-2000", //July has 31 days
"31-08-2000", //August has 31 days
"31-10-2000", //October has 31 days
"31-12-2000", //December has 31 days
"30-04-2000", //April has 30 days
"30-06-2000", //June has 30 days
"30-09-2000", //September has 30 days
"30-11-2000", //November has 30 days
};
private static final String[] invalidDateStrings = new String[]{
"00-01-2000", //there is no 0-th day
"01-00-2000", //there is no 0-th month
"31-12-1799", //out of lower boundary date
"01-01-2200", //out of high boundary date
"32-01-2000", //January doesn't have 32 days
"32-03-2000", //March doesn't have 32 days
"32-05-2000", //May doesn't have 32 days
"32-07-2000", //July doesn't have 32 days
"32-08-2000", //August doesn't have 32 days
"32-10-2000", //October doesn't have 32 days
"32-12-2000", //December doesn't have 32 days
"31-04-2000", //April doesn't have 31 days
"31-06-2000", //June doesn't have 31 days
"31-09-2000", //September doesn't have 31 days
"31-11-2000", //November doesn't have 31 days
"001-02-2000", //SimpleDateFormat valid date (day with leading 0s) even with lenient set to false
"1-0002-2000", //SimpleDateFormat valid date (month with leading 0s) even with lenient set to false
"01-02-0003", //SimpleDateFormat valid date (year with leading 0s) even with lenient set to false
"01.01-2000", //. invalid separator between day and month
"01-01.2000", //. invalid separator between month and year
"01/01-2000", /// invalid separator between day and month
"01-01/2000", /// invalid separator between month and year
"01_01-2000", //_ invalid separator between day and month
"01-01_2000", //_ invalid separator between month and year
"01-01-2000-12345", //only whole string should be matched
"01-13-2000", //month bigger than 13
};
/**
* These constants will be used to generate the valid and invalid boundary dates for the leap years. (For no leap year, Feb. 28 valid and Feb. 29 invalid; for a leap year Feb. 29 valid and Feb. 30 invalid)
*/
private static final int LEAP_STEP = 4;
private static final int YEAR_START = 1800;
private static final int YEAR_END = 2199;
/**
* This date regex will find matches for valid dates between 1800 and 2199 in the format of "dd-MM-yyyy".
* The leading 0 is optional.
*/
private static final String DATE_REGEX = "((0?[1-9]|[12][0-9]|3[01])-(0?[13578]|1[02])-(18|19|20|21)[0-9]{2})|((0?[1-9]|[12][0-9]|30)-(0?[469]|11)-(18|19|20|21)[0-9]{2})|((0?[1-9]|1[0-9]|2[0-8])-(0?2)-(18|19|20|21)[0-9]{2})|(29-(0?2)-(((18|19|20|21)(04|08|[2468][048]|[13579][26]))|2000))";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
* Keep in mind that String.matches tries to match only the whole string.
*/
private static final String DATE_REGEX_ONLY_WHOLE_STRING = "^" + DATE_REGEX + "$";
/**
* The simple regex (without checking for 31 day months and leap years):
*/
private static final String DATE_REGEX_SIMPLE = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\\d\\d)";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
*/
private static final String DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING = "^" + DATE_REGEX_SIMPLE + "$";
private static final SimpleDateFormat SDF = new SimpleDateFormat("dd-MM-yyyy");
static {
SDF.setLenient(false);
}
private static final DateChecker dateValidatorSimple = new DateChecker(DATE_REGEX_SIMPLE);
private static final DateChecker dateValidatorSimpleOnlyWholeString = new DateChecker(DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
/**
* #param args
*/
public static void main(String[] args) {
DateTimeStatistics dateTimeStatistics = new DateTimeStatistics();
boolean shouldMatch = true;
for (int i = 0; i < validDateStrings.length; i++) {
String validDate = validDateStrings[i];
matchAssertAndPopulateTimes(
dateTimeStatistics,
shouldMatch, validDate);
}
shouldMatch = false;
for (int i = 0; i < invalidDateStrings.length; i++) {
String invalidDate = invalidDateStrings[i];
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, invalidDate);
}
for (int year = YEAR_START; year < (YEAR_END + 1); year++) {
FebruaryBoundaryDates februaryBoundaryDates = createValidAndInvalidFebruaryBoundaryDateStringsFromYear(year);
shouldMatch = true;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getValidFebruaryBoundaryDateString());
shouldMatch = false;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getInvalidFebruaryBoundaryDateString());
}
dateTimeStatistics.calculateAvarageTimesAndPrint();
}
private static void matchAssertAndPopulateTimes(
DateTimeStatistics dateTimeStatistics,
boolean shouldMatch, String date) {
dateTimeStatistics.addDate(date);
matchAndPopulateTimeToMatch(date, DATE_REGEX, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegex());
matchAndPopulateTimeToMatch(date, DATE_REGEX_ONLY_WHOLE_STRING, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegexOnlyWholeString());
boolean matchesSimpleDateFormat = matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(date, dateTimeStatistics.getTimesTakenWithSimpleDateFormatParse());
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimple(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE);
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimpleOnlyWholeString(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
matchAndPopulateTimeToMatch(date, dateValidatorSimple, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimple());
matchAndPopulateTimeToMatch(date, dateValidatorSimpleOnlyWholeString, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimpleOnlyWholeString());
}
private static void matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
List<Long> times,
boolean shouldMatch, String date, boolean matchesSimpleDateFormat, String regex) {
boolean matchesFromRegex = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !((matchesSimpleDateFormat && matchesFromRegex) ^ shouldMatch) : "Parsing with SimpleDateFormat and date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, String regex, boolean shouldMatch, List<Long> times) {
boolean matches = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !(matches ^ shouldMatch) : "date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, DateChecker dateValidator, boolean shouldMatch, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = dateValidator.check(date);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
assert !(matches ^ shouldMatch) : "date:" + date + "\ndateValidator with regex:" + dateValidator.getRegex() + "\nshouldMatch:" + shouldMatch;
}
private static boolean matchAndPopulateTimeToMatchAndReturnMatches(String date, String regex, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = date.matches(regex);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
return matches;
}
private static boolean matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(String date, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches = true;
timestampStart = System.nanoTime();
try {
SDF.parse(date);
} catch (ParseException e) {
matches = false;
} finally {
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
}
return matches;
}
private static FebruaryBoundaryDates createValidAndInvalidFebruaryBoundaryDateStringsFromYear(int year) {
FebruaryBoundaryDates februaryBoundaryDates;
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(29, year),
createFebruaryDateFromDayAndYear(30, year)
);
} else {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(28, year),
createFebruaryDateFromDayAndYear(29, year)
);
}
return februaryBoundaryDates;
}
private static String createFebruaryDateFromDayAndYear(int day, int year) {
return String.format("%d-02-%d", day, year);
}
static class FebruaryBoundaryDates {
private String validFebruaryBoundaryDateString;
String invalidFebruaryBoundaryDateString;
public FebruaryBoundaryDates(String validFebruaryBoundaryDateString,
String invalidFebruaryBoundaryDateString) {
super();
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
public String getValidFebruaryBoundaryDateString() {
return validFebruaryBoundaryDateString;
}
public void setValidFebruaryBoundaryDateString(
String validFebruaryBoundaryDateString) {
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
}
public String getInvalidFebruaryBoundaryDateString() {
return invalidFebruaryBoundaryDateString;
}
public void setInvalidFebruaryBoundaryDateString(
String invalidFebruaryBoundaryDateString) {
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
}
static class DateTimeStatistics {
private List<String> dates = new ArrayList<String>();
private List<Long> timesTakenWithDateRegex = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimple = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimpleOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithSimpleDateFormatParse = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimple = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimpleOnlyWholeString = new ArrayList<Long>();
public List<String> getDates() {
return dates;
}
public List<Long> getTimesTakenWithDateRegex() {
return timesTakenWithDateRegex;
}
public List<Long> getTimesTakenWithDateRegexOnlyWholeString() {
return timesTakenWithDateRegexOnlyWholeString;
}
public List<Long> getTimesTakenWithDateRegexSimple() {
return timesTakenWithDateRegexSimple;
}
public List<Long> getTimesTakenWithDateRegexSimpleOnlyWholeString() {
return timesTakenWithDateRegexSimpleOnlyWholeString;
}
public List<Long> getTimesTakenWithSimpleDateFormatParse() {
return timesTakenWithSimpleDateFormatParse;
}
public List<Long> getTimesTakenWithdateValidatorSimple() {
return timesTakenWithdateValidatorSimple;
}
public List<Long> getTimesTakenWithdateValidatorSimpleOnlyWholeString() {
return timesTakenWithdateValidatorSimpleOnlyWholeString;
}
public void addDate(String date) {
dates.add(date);
}
public void addTimesTakenWithDateRegex(long time) {
timesTakenWithDateRegex.add(time);
}
public void addTimesTakenWithDateRegexOnlyWholeString(long time) {
timesTakenWithDateRegexOnlyWholeString.add(time);
}
public void addTimesTakenWithDateRegexSimple(long time) {
timesTakenWithDateRegexSimple.add(time);
}
public void addTimesTakenWithDateRegexSimpleOnlyWholeString(long time) {
timesTakenWithDateRegexSimpleOnlyWholeString.add(time);
}
public void addTimesTakenWithSimpleDateFormatParse(long time) {
timesTakenWithSimpleDateFormatParse.add(time);
}
public void addTimesTakenWithdateValidatorSimple(long time) {
timesTakenWithdateValidatorSimple.add(time);
}
public void addTimesTakenWithdateValidatorSimpleOnlyWholeString(long time) {
timesTakenWithdateValidatorSimpleOnlyWholeString.add(time);
}
private void calculateAvarageTimesAndPrint() {
long[] sumOfTimes = new long[7];
int timesSize = timesTakenWithDateRegex.size();
for (int i = 0; i < timesSize; i++) {
sumOfTimes[0] += timesTakenWithDateRegex.get(i);
sumOfTimes[1] += timesTakenWithDateRegexOnlyWholeString.get(i);
sumOfTimes[2] += timesTakenWithDateRegexSimple.get(i);
sumOfTimes[3] += timesTakenWithDateRegexSimpleOnlyWholeString.get(i);
sumOfTimes[4] += timesTakenWithSimpleDateFormatParse.get(i);
sumOfTimes[5] += timesTakenWithdateValidatorSimple.get(i);
sumOfTimes[6] += timesTakenWithdateValidatorSimpleOnlyWholeString.get(i);
}
System.out.println("AVG from timesTakenWithDateRegex (in nanoseconds):" + (double) sumOfTimes[0] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[1] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple (in nanoseconds):" + (double) sumOfTimes[2] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[3] / timesSize);
System.out.println("AVG from timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) sumOfTimes[4] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[2] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[3] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimple (in nanoseconds):" + (double) sumOfTimes[5] / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[6] / timesSize);
}
}
static class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* #param date the date that needs to be checked.
* #return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
}
Some useful notes:
- to enable the assertions (assert checks) you need to use -ea argument when running the tester. (In eclipse this is done by editing the Run/Debug configuration -> Arguments tab -> VM Arguments -> insert "-ea"
- the regex above is bounded to years 1800 to 2199
- you don't need to use ^ at the beginning and $ at the end to match only the whole date string. The String.matches takes care of that.
- make sure u check the valid and invalid cases and change them according the rules that you have.
- the "only whole string" version of each regex gives the same speed as the "normal" version (the one without ^ and $). If you see performance differences this is because java "gets used" to processing the same instructions so the time lowers. If you switch the lines where the "normal" and the "only whole string" version execute, you will see this proven.
Hope this helps someone!
Cheers,
Despot
java.time
The proper (and easy) way to do date/time validation using Java 8+ is to use the java.time.format.DateTimeFormatter class. Using a regex for validation isn't really ideal for dates. For the example case in this question:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate date = formatter.parse(text, LocalDate::from);
} catch (DateTimeParseException e) {
// Thrown if text could not be parsed in the specified format
}
This code will parse the text, validate that it is a valid date, and also return the date as a LocalDate object. Note that the DateTimeFormatter class has a number of static predefined date formats matching ISO standards if your use case matches any of them.
The following regex will accept YYYY-MM-DD (within the range 1600-2999 year) formatted dates taking into consideration leap years:
^((?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:(?:0[13578]|1[02])(-)31)|((0[1,3-9]|1[0-2])(-)(29|30))))$|^(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(-)02(-)29)$|^(?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:0[1-9])|(?:1[0-2]))(-)(?:0[1-9]|1\d|2[0-8])$
Examples:
You can test it here.
Note: if you want to accept one digit as month or day you can use:
^((?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:(?:0?[13578]|1[02])(-)31)|((0?[1,3-9]|1[0-2])(-)(29|30))))$|^(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(-)0?2(-)29)$|^(?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:0?[1-9])|(?:1[0-2]))(-)(?:0?[1-9]|1\d|2[0-8])$
I have created the above regex starting from this solution
The pattern yyyy-MM-dd is the default pattern used by LocalDate#parse. Therefore, all you need to do is to pass your date string to this method and check if it is successfully parsed or some exception has occurred.
Demo:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
String[] arr = { "2022-11-29", "0000-00-00", "2022-1-2", "2022-02-29" };
for (String s : arr) {
try {
System.out.println("================================");
System.out.println(LocalDate.parse(s) + " is a valid date ");
} catch (DateTimeParseException e) {
System.out.println(e.getMessage());
// Recommended; so that the caller can handle it appropriately
// throw new IllegalArgumentException("Invalid date");
}
}
}
}
Output:
================================
2022-11-29 is a valid date
================================
Text '0000-00-00' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 0
================================
Text '2022-1-2' could not be parsed at index 5
================================
Text '2022-02-29' could not be parsed: Invalid date 'February 29' as '2022' is not a leap year
Some important notes:
java.time types follow ISO 8601 standards and therefore you do need to specify a DateTimeFormatter to parse a date-time string which is in ISO 8601 format.
The java.time API, released with Java-8 in March 2014, supplanted the error-prone legacy date-time API. Since then, it has been strongly recommended to use this modern date-time API. Learn more about the modern Date-Time API from Trail: Date Time.
Construct a SimpleDateFormat with the mask, and then call:
SimpleDateFormat.parse(String s, ParsePosition p)
For fine control, consider an InputVerifier using the SimpleDateFormat("YYYY-MM-dd") suggested by Steve B.
Below added code is working for me if you are using pattern
dd-MM-yyyy.
public boolean isValidDate(String date) {
boolean check;
String date1 = "^(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-([12][0-9]{3})$";
check = date.matches(date1);
return check;
}
If you want a simple regex then it won't be accurate.
https://www.freeformatter.com/java-regex-tester.html#ad-output offers a tool to test your Java regex. Also, at the bottom you can find some suggested regexes for validating a date.
ISO date format (yyyy-mm-dd):
^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$
ISO date format (yyyy-mm-dd) with separators '-' or '/' or '.' or ' '. Forces usage of same separator accross date.
^[0-9]{4}([- /.])(((0[13578]|(10|12))\1(0[1-9]|[1-2][0-9]|3[0-1]))|(02\1(0[1-9]|[1-2][0-9]))|((0[469]|11)\1(0[1-9]|[1-2][0-9]|30)))$
United States date format (mm/dd/yyyy)
^(((0[13578]|(10|12))/(0[1-9]|[1-2][0-9]|3[0-1]))|(02/(0[1-9]|[1-2][0-9]))|((0[469]|11)/(0[1-9]|[1-2][0-9]|30)))/[0-9]{4}$
Hours and minutes, 24 hours format (HH:MM):
^(20|21|22|23|[01]\d|\d)((:[0-5]\d){1,2})$
Good luck