I have following code which checks for the valid format date
private void validateDate(String date){
try {
String validDate = "MM/dd/yyyy";
SimpleDateFormat format = new SimpleDateFormat(validDate);
format.setLenient(false);
Date theDate = new Date();
theDate = format.parse(date);
}
catch (Exception e) {
}
}
I am passing the date value as 06/25/20014. In this year format is wrong and I was expecting that it will throw exception and will go inside catch, but it never happens and it successfully passes the code format.parse(date); due to which my application is not throwing error.
I also debugged the line format.parse(date); and it returns "Fri Jul 31 00:00:00 MST 20015". I am not sure why that line is not throwing any exception.
Thanks for your help!!
In the standard date formats for SimpleDateFormat, the number 'y' 's doesn't necessarily correspond to the number of digits (it isn't a regex). One or two y's is an indicator for a 2 digit year (15, 98, etc.) and 3 or more y's is an indicator for the full year.
If this line did throw an exception, then your program would stop working after year 9999, which is not usually what you want. Sure, you do not expect your program to last this long, but people did not expect to last up to y2k either; so Java's choice not to block at any date seems reasonable.
If you want to check the year is between 1 and 9999, you can just write a if: (see related question I want to get Year, Month, Day, etc from Java Date to compare with Gregorian Calendar date in Java. Is this possible?)
Calendar cal = Calendar.getInstance();
cal.setTime(date);
if (cal.get(Calendar.YEAR) > 9999)
throw new RuntimeException("Blah");
This is actually documented behaviour (at least for the Gregorian calendar, which I assume you will be using unless you explicitely set it to a different calendar):
Year: If the formatter's Calendar is the Gregorian calendar, the
following rules are applied.
For formatting, if the number of pattern
letters is 2, the year is truncated to 2 digits; otherwise it is
interpreted as a number. For parsing, if the number of pattern letters
is more than 2, the year is interpreted literally, regardless of the
number of digits.
And yes, 20015 will probably be a valid year some day ;) If you want to check for exactly 4 digits, you might want to consider using a regular expression matching before parsing.
I can see that the provided date is valid (although 18k years ahead)
public static void validateDate(String date){
try {
String validDate = "MM/dd/yyyy";
SimpleDateFormat format = new SimpleDateFormat(validDate);
format.setLenient(false);
Date theDate = format.parse(date);
Calendar c = new GregorianCalendar();
c.setTime(theDate);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
int pos = c.get(Calendar.DAY_OF_WEEK);
if (Calendar.WEDNESDAY == pos)
System.out.print("day is Wednesday on " + year + "/" + (month+1) + "/" + day);
}
catch (Exception e) {
System.out.print("Inside exception block " + e);
}
}
prints: day is Wednesday on 20014/6/25
If you need some year validation you can add additional check
if (year > MAX_YEAR) {
// add error here
}
Here is DEMO
Related
I'm trying to validate a given date, but it's not working how I would like it. The user inputs a date, and it gets parsed and passed to an array, and I'm trying to validate the date is a correct date(taking into account leap years, Feb, etc) without making the code extremely lengthy.
Calendar calendar = new GregorianCalendar(getValidYear(), getValidMonth() - 1, getValidDay());
if( validYear < 2010 )
{
throw new InvalidDateException("Year must be greater than 2009");
} else {
if(validMonth < 1 || validMonth > 12 )
{
throw new InvalidDateException("Month value must be greater than or equal to 1 or less than or eqaul to 12");
} else {
if(validDay > calendar.getActualMaximum(Calendar.DAY_OF_MONTH) || validDay <= 0)
{
throw new InvalidDateException(String.format ("Day value must be greater than 0 and less than or equal to %s ",
calendar.getActualMaximum(Calendar.DAY_OF_MONTH) ) );
}
}//end nested if month
}//end nested IF year
}
If I put 2018/02/33 in and have it print for the exception it shows the date as March 05, 2018 and Im not exactly sure where these numbers are coming from. The code to parse the date is
String dateGiven[] = validDate.split("/");
validYear = Integer.parseInt(dateGiven[0]);
validMonth = Integer.parseInt( dateGiven[1] );
validDay = Integer.parseInt( dateGiven[2] );
And when I build the string to show the date it prints correctly, but it is not working with Calendar and Im not sure what Im doing wrong. Any help is appreciated!
It would be easier to use Java 8 time features:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date = LocalDate.parse("2018/02/33", formatter);
will result in a DateTimeException when date is invalid:
java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 33
Calendar is lenient by default. Quoting from the documentation of its method setLenient:
With lenient interpretation, a date such as "February 942, 1996" will
be treated as being equivalent to the 941st day after February 1,
1996. With strict (non-lenient) interpretation, such dates will cause an exception to be thrown. The default is lenient.
If you want to receive an exception if an invalid date is set on the Calendar then use setLenient(false);.
Otherwise 2018/02/33 is going to be interpreted as 32 days after February 1, which is March 5.
I googled for a while and the most commonly used method seems to be
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
However, this method seems to fail for dates before 1893-04-01
The following test fails on my machine with an outcome of 1893-03-31 instead of 1893-04-01:
#Test
public void testBeforeApril1893() throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("1893-04-01");
System.out.println(date);
LocalDate localDate2 = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
System.out.println(localDate2);
assertEquals(1893, localDate2.getYear());
assertEquals(4, localDate2.getMonth().getValue());
assertEquals(1, localDate2.getDayOfMonth());
}
The System.out.prinlns are for me to double check the created dates. I see the following output:
Sun Apr 02 00:00:00 CET 1893
1893-04-02
Sat Apr 01 00:00:00 CET 1893
1893-03-31
For 1400-04-01 I even get an output of 1400-04-09.
Is there any method to convert dates before 1893-04 correctly to LocalDate?
As some helpfully pointed out, the reason for this shift is explained in this question. However, I don't see how I can deduce a correct conversion based on this knowledge.
If you're just parsing a String input, it's straighforward:
LocalDate d1 = LocalDate.parse("1893-04-01");
System.out.println(d1); // 1893-04-01
LocalDate d2 = LocalDate.parse("1400-04-01");
System.out.println(d2); // 1400-04-01
The output is:
1893-04-01
1400-04-01
But if you have a java.util.Date object and need to convert it, it's a little bit more complicated.
A java.util.Date contains the number of milliseconds from unix epoch (1970-01-01T00:00Z). So you can say "it's in UTC", but when you print it, the value is "converted" to the system's default timezone (in your case, it's CET). And SimpleDateFormat also uses the default timezone internally (in obscure ways that I must admit I don't fully understand).
In your example, the millis value of -2422054800000 is equivalent to the UTC instant 1893-03-31T23:00:00Z. Checking this value in Europe/Berlin timezone:
System.out.println(Instant.ofEpochMilli(-2422054800000L).atZone(ZoneId.of("Europe/Berlin")));
The output is:
1893-03-31T23:53:28+00:53:28[Europe/Berlin]
Yes, it's very strange, but all places used strange offsets before 1900 - each city had its own local time, before UTC standard took place. That explains why you get 1893-03-31. The Date object prints April 1st probably because the old API (java.util.TimeZone) doesn't have all the offsets history, so it assumes it's +01:00.
One alternative to make this work is to always use UTC as the timezone:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // set UTC to the format
Date date = sdf.parse("1893-04-01");
LocalDate d = date.toInstant().atZone(ZoneOffset.UTC).toLocalDate();
System.out.println(d); // 1893-04-01
This will get the correct local date: 1893-04-01.
But for dates before 1582-10-15, the code above doesn't work. That's the date when the Gregorian Calendar was introduced. Before it, the Julian Calendar was used, and dates before it need an adjustment.
I could do it with the ThreeTen Extra project (an extension of java.time classes, created by the same guy BTW). In the org.threeten.extra.chrono package there are the JulianChronology and JulianDate classes:
// using the same SimpleDateFormat as above (with UTC set)
date = sdf.parse("1400-04-01");
// get julian date from date
JulianDate julianDate = JulianChronology.INSTANCE.date(date.toInstant().atZone(ZoneOffset.UTC));
System.out.println(julianDate); // Julian AD 1400-04-01
The output will be:
Julian AD 1400-04-01
Now we need to convert the JulianDate to a LocalDate. If I do LocalDate.from(julianDate) it converts to Gregorian calendar (and the result is 1400-04-10).
But if you want to create a LocalDate with exactly 1400-04-01, you'll have to do this:
LocalDate converted = LocalDate.of(julianDate.get(ChronoField.YEAR_OF_ERA),
julianDate.get(ChronoField.MONTH_OF_YEAR),
julianDate.get(ChronoField.DAY_OF_MONTH));
System.out.println(converted); // 1400-04-01
The output will be:
1400-04-01
Just be aware that dates before 1582-10-15 have this adjustment and SimpleDateFormat can't handle these cases properly. If you need to work just with 1400-04-01 (year/month/day values), use a LocalDate. But if you need to convert it to a java.util.Date, be aware that it might not be the same date (due to Gregorian/Julian adjustments).
If you don't want to add another dependency, you can also do all the math by hand. I've adapted the code from ThreeTen, but IMO the ideal is to use the API itself (as it can cover corner cases and other things I'm probably missing by just copying a piece of code):
// auxiliary method
public LocalDate ofYearDay(int prolepticYear, int dayOfYear) {
boolean leap = (prolepticYear % 4) == 0;
if (dayOfYear == 366 && leap == false) {
throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + prolepticYear + "' is not a leap year");
}
Month moy = Month.of((dayOfYear - 1) / 31 + 1);
int monthEnd = moy.firstDayOfYear(leap) + moy.length(leap) - 1;
if (dayOfYear > monthEnd) {
moy = moy.plus(1);
}
int dom = dayOfYear - moy.firstDayOfYear(leap) + 1;
return LocalDate.of(prolepticYear, moy.getValue(), dom);
}
// sdf with UTC set, as above
Date date = sdf.parse("1400-04-01");
ZonedDateTime z = date.toInstant().atZone(ZoneOffset.UTC);
LocalDate d;
// difference between the ISO and Julian epoch day count
long julianToIso = 719164;
int daysPerCicle = (365 * 4) + 1;
long julianEpochDay = z.toLocalDate().toEpochDay() + julianToIso;
long cycle = Math.floorDiv(julianEpochDay, daysPerCicle);
long daysInCycle = Math.floorMod(julianEpochDay, daysPerCicle);
if (daysInCycle == daysPerCicle - 1) {
int year = (int) ((cycle * 4 + 3) + 1);
d = ofYearDay(year, 366);
} else {
int year = (int) ((cycle * 4 + daysInCycle / 365) + 1);
int doy = (int) ((daysInCycle % 365) + 1);
d = ofYearDay(year, doy);
}
System.out.println(d); // 1400-04-01
The output will be:
1400-04-01
Just reminding that all this math is not needed for dates after 1582-10-15.
Anyway, if you have an input String and want to parse it, don't use SimpleDateFormat - you can use LocalDate.parse() instead. Or LocalDate.of(year, month, day) if you already know the values.
But converting these local dates from/to a java.util.Date is more complicated, because Date represents the full timestamp millis and dates can vary according to the calendar system in use.
Seems to be a known bug that won't get fixed: https://bugs.openjdk.java.net/browse/JDK-8061577
After a lot of research I gave up with every simple API method and just convert it by hand. You could wrap the date in a sql.Date and call toLocalDate() or you just use the same deprecated methods as sql.Date does.
Without deprecated methods you need to convert your util.Date to Calendar and get the fields one by one:
Calendar calendar = Calendar.getInstance();
calendar.setTime(value);
return LocalDate.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1,
calendar.get(Calendar.DAY_OF_MONTH));
If you futher want to have a two digit year conversion like in SimpleDateFormat (convert the date in range of now - 80 years till now + 19 years) you could use this implementation:
Calendar calendar = Calendar.getInstance();
calendar.setTime(value);
int year = calendar.get(Calendar.YEAR);
if (year <= 99) {
LocalDate pivotLocalDate = LocalDate.now().minusYears(80);
int pivotYearOfCentury = pivotLocalDate.getYear() % 100;
int pivotCentury = pivotLocalDate.minusYears(pivotYearOfCentury).getYear();
if (year < pivotYearOfCentury) {
year += 100;
}
year += pivotCentury;
}
return LocalDate.of(year, calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH));
Conclusion: it is realy ugly and I can't believe that there isn't any simple API!
This code works for me:
#Test
public void oldDate() throws ParseException {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("1893-04-01");
assertEquals("1893-04-01", String.format("%tF", date));
}
I have verified that the date is read correctly from a file, but once I use SimpleDateFormat.format with the pattern "dd/MM/yy" it suddenly adds a month. This leads me to believe lenient mode is calculating the wrong value. But I have no idea what would make it add a full month.
Some example dates I read:
16/09/2013
23/09/2013
30/09/2013
07/10/2013
14/10/2013
21/10/2013
The code used to parse the date (it's a wrapper around Calendar I made):
public static SimpleDateTime parseDate(String date)
{
String[] dateParts = date.split("[-\\.:/]");
int day = Integer.parseInt(dateParts[0]);
int month = Integer.parseInt(dateParts[1]);
int year = Integer.parseInt(dateParts[2]);
return new SimpleDateTime(dag, maand, jaar);
}
The constructor used here:
public SimpleDateTime(int day, int month, int year)
{
date = Calendar.getInstance();
date.setLenient(true);
setDay(day);
setMonth(month);
setYear(year);
}
The setters for day, month and year:
public void setYear(int year)
{
date.set(Calendar.YEAR, year);
}
public void setMonth(int month)
{
date.set(Calendar.MONTH, month);
}
public void setDay(int day)
{
date.set(Calendar.DAY_OF_MONTH, day);
}
And the code used to format the date:
public String toString(String pattern)
{
String output = new SimpleDateFormat(pattern, Locale.getDefault()).format(date.getTime());
return output;
}
where the pattern passed is:
"dd/MM/yy"
Intended to print a date as:
16/09/13
23/09/13
Instead I get:
16/10/13
23/10/13
January is 0 in Java; February is 1 and so on.
See Calendar.JANUARY, Calendar.FEBRUARY.
So when you're reading 1 from the file
you think you read JAN but you read FEB.
You should do: date.set(Calendar.MONTH, month-1); to fix this.
Months are indexed from 0 not 1 so 10 is November and 11 will be December.
Calendar.MONTH
From documentation:
Field number for get and set indicating the month. This is a calendar-specific value. The first month of the year is JANUARY; the last depends on the number of months in a year.
So if you check JANUARY you see it starts in zero.
Make sure your month is in the interval 0-11. Possibly it is in 1-12.
The reason for this is that the counting starts at 0.
January == 0
February == 1
and so on. See the documentation.
THe problem is that you pass 9 to SimpleDateFormat and since month are indexed from 0 to 11 it will parse month '9' as the 10th month.
You need to subtract 1 from the month :)
Calendar class in Java holds months starting from 0, hence when you set the month as 0, it would consider it as January. SimpleDateFormat provides for a way to correctly display the value as 01.
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 0);
System.out.println(new SimpleDateFormat("dd/MM/yy").format(cal.getTime()));
Output:
29/01/14
The workaround for you to align you file that Calendar can work with (since December - or 12 would trickle over to the next year) or modify your logic to pick Constants like:
cal.set(Calendar.MONTH, Calendar.JANUARY);
The answer by peter.petrov is almost correct, except for one major problem. Like your question, it neglects to account for time zone.
For your information, this kind of work is much easier in Joda-Time (or new java.time.* classes in Java 8). Joda-Time is so much cleaner you won't even feel the need to create a wrapper class.
// Specify the time zone for which the incoming date is intended.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Brussels" );
String input = "16/09/2013";
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy").withZone( timeZone );
DateTime dateTime = formatter.parseDateTime( input );
String output = formatter.print( dateTime );
Dump to console…
System.out.println( "dateTime: " + dateTime );
System.out.println( "output: " + output );
System.out.println( "millis since Unix epoch: " + dateTime.getMillis() );
When run…
dateTime: 2013-09-16T00:00:00.000+02:00
output: 16/09/2013
millis since Unix epoch: 1379282400000
I'm trying to compare two dates with the current date. It seems not to work when I try to know if a date is the same as the current date. Here's what I do in my code :
//BeginDate is set earlier
Date myDate= new SimpleDateFormat("dd/MM/yyyy").parse(BeginDate);
Date now = new Date();
System.out.println("Now : " + now);
System.out.println("myDate : " + myDate);
System.out.println("equals : " + myDate.equals(now));
System.out.println(myDate.compareTo(now));
And I get this in the console :
Now : Thu Dec 29 00:28:45 CET 2011
myDate : Thu Dec 29 00:00:00 CET 2011
equals : false
-1
The first comparison should return true and the second "0" right ? Or am I missing something ?
Comparing dates with either equals() or compareTo() compares the times (hours, minutes, seconds, millis) as well as the dates. Your test is failing because myDate is midnight today, whereas now is a little later than that.
Your comparison is failing because you need to format now so that both dates have the same format and thus may be compared.
Or, if you prefer, you can convert dates into strings and perform the comparison:
String beginDate = "28/12/2011";
Calendar now = Calendar.getInstance();
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
String nowStr = df.format(new Date());
System.out.println("equals : " + beginDate.equals(nowStr));
Are you specifying the milliseconds when creating the dates? If you are, don't. So when creating the dates earlier, only specify the Day, Hour etc, not seconds/milliseconds.
And, change the SimpleDateFormat respectively. That "should" work.
Date object in Java is nothing but a number that represents milliseconds since January 1, 1970, 00:00:00 GMT. It doesn't have any attribute called day, date, month, year etc. That's because date, month, year varies based on the type of calendar and timezone. These attributes belong to Calendar instance.
So, if you have 2 Date objects and you want to compare day of month, month and year then you should create corresponding Calendar instance and compare them separately.
// Parse begin date
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date beginDate = dateFormat.parse(beginDateAsString);
// Create calendar instances
Calendar beginDateCalendar = Calendar.getInstance();
beginDateCalendar.setTime(beginDate);
Calendar todayCalendar = Calendar.getInstance();
// Check Equals
boolean dayEquals = todayCalendar.get(Calendar.DAY_OF_MONTH) == beginDateCalendar
.get(Calendar.DAY_OF_MONTH);
boolean monthEquals = todayCalendar.get(Calendar.MONTH) == beginDateCalendar
.get(Calendar.MONTH);
boolean yearEquals = todayCalendar.get(Calendar.YEAR) == beginDateCalendar
.get(Calendar.YEAR);
// Print Equals
System.out.println(dayEquals && monthEquals && yearEquals);
Above code is cumbersome for the current problem but explains how date operations must be done in JAVA.
If you just want to solve the equals problem you have mentioned then the code below will suffice:
String todayAsString = (new SimpleDateFormat("dd/MM/yyyy")).format(new Date());
System.out.println(beginDateAsString.equals(todayAsString));
If you are only going to be dealing with dates between the years 1900 and 2100, there is a simple calculation which will give you the number of days since 1900:
public static int daysSince1900(Date date) {
Calendar c = new GregorianCalendar();
c.setTime(date);
int year = c.get(Calendar.YEAR) - 1900;
int month = c.get(Calendar.MONTH) + 1;
int days = c.get(Calendar.DAY_OF_MONTH);
if (month < 3) {
month += 12;
year--;
}
int yearDays = (int) (year * 365.25);
int monthDays = (int) ((month + 1) * 30.61);
return (yearDays + monthDays + days - 63);
}
Thus, Date (only) comparison can be achieved by checking if the number of days since 1900 of the 2 dates are equal.
NOTE: The above method should have code added to check if the dates are outside the valid range (1/1/1900 - 31/12/2099) and throw an IllegalArgumentException.
And don't ask me where this calculation came from because we've used it since the early '90s.
I need to get the number of minutes between two dates. I know Joda is the best to use, but this is for an Android project, so I prefer to use a little external libraries as possible, and the app I'm building doesn't require the calculation to be surgically precise.
However, the code I'm using doesn't seem to be working. I'm trying to get the number of minutes between "11/21/2011 7:00:00 AM" and "11/21/2011 1:00:00 PM" (which should be 360 minutes), but the code I'm using returns 0:
int diff = minutesDiff(GetItemDate("11/21/2011 7:00:00 AM"), GetItemDate("11/21/2011 1:00:00 PM"));
public static Date GetItemDate(final String date)
{
final Calendar cal = Calendar.getInstance(TimeZone.getDefault());
final SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a");
format.setCalendar(cal);
try {
return format.parse(date);
} catch (ParseException e) {
return null;
}
}
public static int minutesDiff(Date earlierDate, Date laterDate)
{
if( earlierDate == null || laterDate == null ) return 0;
return (int)((laterDate.getTime()/60000) - (earlierDate.getTime()/60000));
}
If your GetItemDate is failing for either date, you will get zero. On the catch statement, place a println or debug to output the issue and see if that tells you something.
Also, just as a matter of practice, I'd change:
return (int)((laterDate.getTime()/60000) - (earlierDate.getTime()/60000));
to:
long result = ((laterDate.getTime()/60000) - (earlierDate.getTime()/60000));
return (int) result;
This gives you the opportunity to view the result of the math in an IDE.
I debugged through your code: there is ParseException in GetItemDate() for both of the date strings like:
Unparseable date: 11/21/2011 07:00:00 AM
The problem is that parsing AM/PM hours only works with "hh" (small caps!) or "KK". At least that is the case in my phone (it seems some of us didn't see this issue at all). I tested it also works with a single "h" too.
hh = 1-12
KK = 0-11
HH = 0-23
kk = 1-24
Change your SimpleDateFormat line to this:
final SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a", Locale.US);
This post explains the "a" and Locale: Unable to parse DateTime-string with AM/PM marker
PS. I'm from Finland so I must use the "Locale.US" part for this to work. US residents may test the code without it, I think.