I am facing very strange issue..
Here is the code, which generates a new Date object:
Date result = DateUtils.setYears(new Date(), year);
result = DateUtils.setMonths(result, month);
return DateUtils.setDays(result, day);
If I pass any value for month starting from 1 till 11 - everything works fine, 1 means January, 2 - February ... 11 - November. But with 12 it always fails with java.lang.IllegalArgumentException: MONTH exception..
When I try to pass 0-based values, the first one 0 means December of previous year..
Any ideas?
Thank you in advance
The method setMonths look like
public static Date setMonths(Date date, int amount) {
return set(date, Calendar.MONTH, amount);
}
As you can notice that internally it uses Calendar.MONTH from java. Months in Calendar class starts from 0 till 12(12 value mean UNDECIMBER i.e. thireteenth month of the year Although GregorianCalendar does not use this value, lunar calendars do). So when you are passing 0 it means January, 1 it means February,... and 11 means December. For invalid month value calendar class throw
java.lang.IllegalArgumentException
Let's trace it.
The setMonths method in DateUtils is defined as follows:
public static Date setMonths(Date date, int amount) {
return set(date, Calendar.MONTH, amount);
}
Let's check out the set method. This methods throws the same exception class but for a different reason.
private static Date set(Date date, int calendarField, int amount) {
if (date == null) {
throw new IllegalArgumentException("The date must not be null");
}
// getInstance() returns a new object, so this method is thread safe.
Calendar c = Calendar.getInstance(); //returns an "empty" Calendar instance using default TimeZone and Local. Does not throw any exception
c.setLenient(false); // Just set the leniency value of the Calendar.
c.setTime(date); // set the time of the Calendar to the reference time by converting the date input into milliseconds
c.set(calendarField, amount); // this one looks interesting, but not quite
return c.getTime(); //returns the Date Object, possible source of the thrown Exception
}
The getTime method in Calendar.java looks like:
public final Date getTime() {
return new Date(getTimeInMillis());
}
The method getTimeInMillis in Calendar.java is defined as follows:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
The only statement in this method that looks interesting is updateTime which in turn is defined as follows:
private void updateTime() {
computeTime();
// The areFieldsSet and areAllFieldsSet values are no longer
// controlled here (as of 1.5).
isTimeSet = true;
}
The computeTime method in Calendar.java is an abstract method which for this case is concretely implemented in GregorianCalendar.java. I'll only show statements in the method that can throw that exception because the whole method is very long.
protected void computeTime() {
// In non-lenient mode, perform brief checking of calendar
// fields which have been set externally. Through this
// checking, the field values are stored in originalFields[]
// to see if any of them are normalized later.
if (!isLenient()) {
if (originalFields == null) {
originalFields = new int[FIELD_COUNT];
}
for (int field = 0; field < FIELD_COUNT; field++) {
int value = internalGet(field);
if (isExternallySet(field)) {
// Quick validation for any out of range values
**This is the part of the code that has thrown that Exception**
if (value < getMinimum(field) || value > getMaximum(field)) {
throw new IllegalArgumentException(getFieldName(field));
}
}
originalFields[field] = value;
}
//After this part, code that computes the time in milliseconds follows
.............................
.............................
}
As you can see, the value being supplied for a particular field is compared to the field's predefined minimum and maximum value. For the MONTH field, the minimum value is 0 (January) and the maximum value is 11 (December). You can verify these values from here.
Now as for your other question, the information you have provided is limited for us to provide a concrete answer. By the implementation of the Calendar API, if the leniency mode is set to false, the value 0 for the month should correspond to January and 11 to December. The only way for a 0 month value to correspond to December is when you set the leniency mode to true and you have a day value that "wraps around (roll over)" to December e.g. month = 0 but day = 369.
The best guess here as mentioned in one of the comments above is perhaps you are modifying the value of month somehow, somewhere.
Related
how to set DATE using constructor?
public Date(int day, int month, int year)
{
this.day=day; // set day to 1-31
this.month=month; // set month to 1-12
this.year=year; // set year to 1900-9999
}
if i use
if(day<=1 || day >=31)
this.day=day;
else if(month<=1 || month>=12)
this.month=month;
else if(year<=1900 || year>=9999)
this.year=year
the problem is if I do this it will only result to 0 however if I removed the conditional statements the day will accept until 32 and months will be accept 13 up and so on
Well, you tell me. What do you want to happen? Here are some common options:
(recommended): Decree that a given date that clearly cannot exist, is not a legal invocation of the constructor. In other words, new Date(32, 2, 2051) isn't valid. To do that, throw an exception. For example:
if (day > 31) throw new IllegalArgumentException("Days must be between 1 and 31, inclusive");
Use rollover behaviour, and round off years. This is a 'I don't care it makes no sense do SOMETHING and just don't crash' attitude, and is not recommended. For example, you'd make that date as above act like march 4th, 2051.
Something else of your choosing. You are the programmer, after all.
Note that you are reinventing a very common wheel, and thus, unless this is homework or a pure learning exercise ('pure' in the sense of: This code will be tossed in the garbage once you have completed it), it is a mistake. use LocalDate instead.
NB: Dates are HARD. Harder than you think.
You don't have to create date attributes for holding date information. You can just use either java 8 LocalDate or Date API . LocalDate is thread safe and immutable, most recommended way of handling date use cases.
If you insist on creating your own Date class you can just easily do the following.
import java.time.*;
public class Date {
final LocalDate date;
public Date(int day, int month, int year) {
date = LocalDate.of(year, month, day);
}
public int getYear() {
return date.getYear();
}
public Month getMonth() {
return date.getMonth();
}
public int getDay() {
return date.getDayOfMonth();
}
}
Using the Date class.
public class Main {
public static void main(String[] args) {
try{
Date date1 = new Date(2020, 13, 33);
} catch(DateTimeException ex){
System.out.println("Invalid input");
}
}
}
I am asking this question cause actually I have absolutely no way to test this case, and maybe someone could explain it to me :)
I have been working on a piece of code that was written by a person who is very new to programming. This code looks like this:
List<Date> dateList = infoFacade.getDateFrom(documentId);
for(Date from : dateList) {
LocalDate now1 = LocalDate.now();
int year = now1.getYear();
int previousyear = now1.getYear()-1;
int yearfrom = from.getYear()+1900;
if((yearfrom == year )|| (yearfrom == previousyear )){
idoc.setBauinfoArzvon(from);
}
}
I have rewritten it a little bit, so we stop using a deprecated method. It looks like this:
for (Date from : infoFacade.getDateFrom(documentId))
{
cal.setTime(from);
int yearfrom = cal.get(Calendar.YEAR);
if ((yearfrom == LocalDate.now().getYear())
|| (yearfrom == (LocalDate.now().getYear() - 1)))
{
idoc.setDateFrom(from);
}
}
I am worried about all that +1900 or -1900 thing. Should I add or substract something from the yearfrom variable to get the same results as in the code before refactoring?
Assuming you cannot change the return type of infoFacade.getDateFrom() my suggestion would be:
ZoneId zone = ZoneId.systemDefault();
LocalDate now1 = LocalDate.now(zone);
int year = now1.getYear();
int previousYear = year - 1;
List<Date> dateList = infoFacade.getDateFrom(documentId);
for (Date from : dateList) {
int yearfrom = from.toInstant().atZone(zone).getYear();
if (yearfrom == year || yearfrom == previousYear) {
idoc.setBauinfoArzvon(from);
}
}
Both versions of your code implicitly rely on the JVM’s time zone (which is fragile). I have made this dependency explicit. I am reading the default time zone and the current date only once to ensure consistent results. And by converting the Date first to an Instant and then to ZonedDateTime I am avoiding both the deprecated method and the old and outdated Calendar class. And any considerations about whether to add or subtract 1900 or not, which gives clearer code and fewer doubts on the part of the reader.
To answer your question more directly too: No, in your rewritten version of the code you should neither add nor subtract 1900 (or any other number). The code does give the same result. This is because Date uses a “1900-based year” (where 2018 is given as 118, for example), while the also outdated Calendar class numbers the years the same way humans do. My worry is different: If either the default time zone changes while the code is running or (unlikely, but possible) New Year passes, LocalDate.now() will not give the same result each time, so your results will be inconsistent. The JVM’s default time zone can be changed at any time from another part of your program or another program running in the same JVM.
I have written a simple test:
public static void main(String[] args) {
Date date = new GregorianCalendar().getTime();
Calendar cal = new GregorianCalendar();
cal.setTime(date);
System.out.println(cal.get(Calendar.YEAR));
System.out.println((LocalDate.now().getYear() - 1));
System.out.println(LocalDate.now().getYear());
LocalDate now1 = LocalDate.now();
int year = now1.getYear();
int previousyear = now1.getYear()-1;
int yearfrom = date.getYear()+1900;
System.out.println(year);
System.out.println(previousyear);
System.out.println(yearfrom);
}
The output of this test is:
2018
2017
2018
2018
2017
2018
So both code samples are giving the same result.
BUT i will try to use the #Ole V.V. answer tomorrow to see what will happen.
I have a Java problem where I need to check if an item has expired. This is supposed to check if the item is at least x (x is an integer and can be set to any integer value) months old.
Just to reclarify Supposing I have a pack of eggs, I want to check if it has been 1 months since I added them (dateAdded).
I wrote a simple comparison but it doesn't seem to give the correct response. Here is the code.
public Boolean isEndOfLine() {
Calendar today = Calendar.getInstance();
if(today.compareTo(dateAdded) >= END_OF_LINE) {
return true;
} else {
return false;
}
}
The value of end of line is an integer 12 i.e 12 months.
I do not hold javadoc in my head, but along the lines of:
dateAdded.add(Calendar.Month, END_OF_LINE).compareTo(today) > 0
Here's some similar example code, but using the Joda-Time 2.3 library.
FYI:
A Joda-Time DateTime instance knows its own time zone.
The minusMonths method is smart, handles Daylight Saving Time and other issues. You may want to read its source code to verify its logic follows your business rules as to what "x number of months ago" means.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
// Better to specify a time zone explicitly rather than rely on default.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html (not quite up-to-date, read page for details)
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
int countMonths = 2;
DateTime now = new DateTime( timeZone );
// If you want to include the entire day, get first moment of the day by calling "withTimeAtStartOfDay".
DateTime someMonthsAgo = now.minusMonths( countMonths ).withTimeAtStartOfDay();
DateTime dateAdded = new DateTime( 2013, 5, 6, 7, 8, 9, timeZone ); // Arbitrary values for example.
// If 'dateAdded' happened prior to our target date-time 'someMonthsAgo', the pack of eggs is expired.
Boolean isEndOfLine = dateAdded.isBefore( someMonthsAgo );
Dump to console…
System.out.println( "now: " + now );
System.out.println( "someMonthsAgo: " + someMonthsAgo );
System.out.println( "dateAdded: " + dateAdded );
System.out.println( "isEndOfLine: " + isEndOfLine );
When run…
now: 2014-01-08T21:36:11.179+01:00
someMonthsAgo: 2013-11-08T00:00:00.000+01:00
dateAdded: 2013-05-06T07:08:09.000+02:00
isEndOfLine: true
as mentioned in the Calendar docs
You should not rely on the number returned by compareTo - you just know that if it is greater than 0 that the original date is greater.
So create a new date (x months in the passed) and compare to that one.
The method returns 0 if the time represented by the argument is equal to the time represented by this Calendar object; or a value less than 0 if the time of this Calendar is before the time represented by the argument; or a value greater than 0 if the time of this Calendar is after the time represented.
import java.util.*;
public class CalendarDemo {
public static void main(String[] args) {
// create two calendar at the different dates
Calendar cal1 = new GregorianCalendar(2015, 8, 15);
Calendar cal2 = new GregorianCalendar(2008, 1, 02);
// compare the time values represented by two calendar objects.
int i = cal1.compareTo(cal2);
// return positive value if equals else return negative value
System.out.println("The result is :"+i);
// compare again but with the two calendars swapped
int j = cal2.compareTo(cal);
// return positive value if equals else return negative value
System.out.println("The result is :" + j);
}
}
Here is the working solution. Tested with JUNIT to confirm results.
public Boolean isEndOfLine() {
Calendar today = Calendar.getInstance();
today.add(Calendar.MONTH, -END_OF_LINE);
return today.compareTo(dateAdded) >= 0;
}
I subtracted the END_OF_LINE from today using the add method. Notice the minus on line 3. I then compared to see if it is greater than 0. Thanks for all your suggestions.
I need to write the high performance function which calculates the new datetime based on given datetime and timeshift. It accept 2 arguments:
String, representing the date in format YYYYMMDDHHmm
Integer, representing the timeshift in hours
Function returns the string in format of 1st argument which is composed as result of applying the timeshift to 1st argument
It is known in advance that the first argument is always the same during the program lifetime.
My implementation has the following steps:
parsing 1st argument to extract the year,month,date, hours,min
creating GregorianCalendar(year, month, date, hours, min) object
applying method GregorianCalendar.add(HOUR,timeshift)
applying SimpleDateFormat to convert result back into string
Issue is that I do not take advantage from the fact that 1st argument is always the same.
If I will create a class member GregorianCalendar(year, month, date, hours, min), then after the 1st call to my function this object will be modified, which is not good, because I cannot reuse it for the following calls.
If you can, use the Joda-Time library, which makes date arithmetic very simple:
DateTime dt = new DateTime();
DateTime twoHoursLater = dt.plusHours(2);
They have a DateTimeFormatter class that you'd use to do the parsing of your input date-time string into a DateTime, eg:
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMddHHmm");
DateTime dt = fmt.parseDateTime(myDateString);
DateTime result = dt.plusHours(myTimeshiftInHours);
And Joda-Time interoperates well with java.util.Date too. I love it!
If the first argument is a value that will not change often, perhaps use a cache :
static private Map<String,Calendar> dateCache = new HashMap<String,Calendar>();
Then, in your method, check of the first argument (ex: String dateStr) is a key in the cache
Calendar cal;
if (dateCache.containsKey(dateStr)) {
cal = (Calendar)(dateCache.get(dateStr)).clone();
} else {
// parse date
cal = new GregorianCalendar(...);
dateCache.put(dateStr, (Calendar)cal.clone());
}
And add your timeshift value.
How about this,
Parse and hold on to your fixed date, call it fixedDate
Let timeShift be a time shift in hours, then Date shiftedDate = new Date(fixedDate.getTime() + (timeShift * 3600000)) would be your calculated shifted date (see this and this for understanding)
Apply SimpleDateFormat to convert shiftedDate to string.
Repeat steps 2 and 3 indefinitely, fixedDate is not modified and can be reused.
I'd try simple memoisation:
// This is not thread safe. Either give each thread has its own
// (thread confined) converter object, or make the class threadsafe.
public class MyDateConverter {
private String lastDate;
private int lastShift;
private String lastResult;
public String shiftDate(String date, int shift) {
if (shift == lastShift && date.equals(lastDate)) {
return lastResult;
}
// Your existing code here
lastDate = date;
lastShift = shift
lastResult = result;
return result;
}
}
Note this simple approach is most effective if the shift and date values rarely change. If either changes frequently, you'd need a more complicated cache, the code will be more complicated and the overheads (for a cache miss) will be higher.
If you simply want to avoid repeating step 1 (and maybe 2) again and again, parse the date once, then save the Date you get. You can then apply this date to your Calendar (with setDate()) before each add step again (or create a new GregorianCalendar, measure if it matters).
This question already has answers here:
How do I check if a date is within a certain range?
(17 answers)
Closed 7 years ago.
One thing I want to know is how to calculate what date will it be 10 days from today.
Second thing is to check if one Date is between two other Dates.
For example, let's say I have an app that shows what events I need to do in the next 10 days (planner). Now how can I see if the date I assigned to an event is between today and the date that is 10 days from today?
Manipulating and comparing dates using java.util.Date and java.util.Calendar is pretty a pain, that's why JodaTime exist. None of the answers as far have covered the time in question. The comparisons may fail when the dates have a non-zero time. It's also unclear whether you want an inclusive or exclusive comparison. Most of the answers posted so far suggest exclusive comparision (i.e. May 24 is not between May 20 and May 24) while in real it would make more sense to make it inclusive (i.e. May 24 is between May 20 and May 24).
One thing I want to know is how to calculate what date will it be 10 days from today.
With the standard Java SE 6 API, you need java.util.Calendar for this.
Calendar plus10days = Calendar.getInstance();
plus10days.add(Calendar.DAY_OF_YEAR, 10);
With JodaTime you would do like this:
DateTime plus10days = new DateTime().plusDays(10);
Second thing is to check if one Date is between two other Dates. For example, let's say I have an app that shows what events I need to do in the next 10 days (planner). Now how can I see if the date I assigned to an event is between today and the date that is 10 days from today?
Now comes the terrible part with Calendar. Let's prepare first:
Calendar now = Calendar.getInstance();
Calendar plus10days = Calendar.getInstance();
plus10days.add(Calendar.DAY_OF_YEAR, 10);
Calendar event = Calendar.getInstance();
event.set(year, month - 1, day); // Or setTime(date);
To compare reliably using Calendar#before() and Calendar#after(), we need to get rid of the time first. Imagine it's currently 24 May 2010 at 9.00 AM and that the event's date is set to 24 May 2010 without time. When you want inclusive comparison, you would like to make it return true at the same day. I.e. both the (event.equals(now) || event.after(now)) or -shorter but equally- (!event.before(now)) should return true. But actually none does that due to the presence of the time in now. You need to clear the time in all calendar instances first like follows:
calendar.clear(Calendar.HOUR);
calendar.clear(Calendar.HOUR_OF_DAY);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Alternatively you can also compare on day/month/year only.
if (event.get(Calendar.YEAR) >= now.get(Calendar.YEAR)
&& event.get(Calendar.MONTH) >= now.get(Calendar.MONTH)
&& event.get(Calendar.DAY_OF_MONTH) >= now.get(Calendar.DAY_OF_MONTH)
{
// event is equal or after today.
}
Very verbose all.
With JodaTime you can just use DateTime#toLocalDate() to get the date part only:
LocalDate now = new DateTime().toLocalDate();
LocalDate plus10days = now.plusDays(10);
LocalDate event = new DateTime(year, month, day, 0, 0, 0, 0).toLocalDate();
if (!event.isBefore(now) && !event.isAfter(plus10days)) {
// Event is between now and 10 days (inclusive).
}
Yes, the above is really all you need to do.
public static boolean between(Date date, Date dateStart, Date dateEnd) {
if (date != null && dateStart != null && dateEnd != null) {
if (date.after(dateStart) && date.before(dateEnd)) {
return true;
}
else {
return false;
}
}
return false;
}
EDIT: Another suggested variant:
public Boolean checkDate(Date startDate, Date endDate, Date checkDate) {
Interval interval = new Interval(new DateTime(startDate),
new DateTime(endDate));
return interval.contains(new DateTime(checkDate));
}
Use JodaTime calendar replacement classes: http://joda-time.sourceforge.net/
You can use before, after and compareTo methods of Date class.
Here're some examples
http://www.roseindia.net/java/example/java/util/CompareDate.shtml
http://www.javafaq.nu/java-example-code-287.html
http://www.esus.com/javaindex/j2se/jdk1.2/javautil/dates/comparingdates.html
And here's API on Date class
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Date.html
Good Luck!
To add ten days:
Date today = new Date();
Calendar cal = new GregorianCalendar();
cal.setTime(today);
cal.add(Calendar.DAY_OF_YEAR, 10);
To check if between two dates:
myDate.after(firstDate) && myDate.before(lastDate);
To check if date is between two dates, here is simple program:
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
String oeStartDateStr = "04/01/";
String oeEndDateStr = "11/14/";
Calendar cal = Calendar.getInstance();
Integer year = cal.get(Calendar.YEAR);
oeStartDateStr = oeStartDateStr.concat(year.toString());
oeEndDateStr = oeEndDateStr.concat(year.toString());
Date startDate = sdf.parse(oeStartDateStr);
Date endDate = sdf.parse(oeEndDateStr);
Date d = new Date();
String currDt = sdf.format(d);
if((d.after(startDate) && (d.before(endDate))) || (currDt.equals(sdf.format(startDate)) ||currDt.equals(sdf.format(endDate)))){
System.out.println("Date is between 1st april to 14th nov...");
}
else{
System.out.println("Date is not between 1st april to 14th nov...");
}
}
I took the initial answer and modified it a bit. I consider if the dates are equal to be "inside"..
private static boolean between(Date date, Date dateStart, Date dateEnd) {
if (date != null && dateStart != null && dateEnd != null) {
return (dateEqualOrAfter(date, dateStart) && dateEqualOrBefore(date, dateEnd));
}
return false;
}
private static boolean dateEqualOrAfter(Date dateInQuestion, Date date2)
{
if (dateInQuestion.equals(date2))
return true;
return (dateInQuestion.after(date2));
}
private static boolean dateEqualOrBefore(Date dateInQuestion, Date date2)
{
if (dateInQuestion.equals(date2))
return true;
return (dateInQuestion.before(date2));
}