Related
Been wrestling with this problem for a while, would love some input.
The problem I want to solve collects all of the dates in one specific year which fall on a particular day of the week, for example, every Tuesday in 2014. The dates are stored in an ArrayList<Date>. This list is then returned.
Also have to validate to make sure the year is not 0 and the day of the week submitted must be a number between 1-7.
If there are any issues, I would love to know what I have screwed up.
public List<Date> getDatesforDayOfWeek(int year, int dayOfWeek) throws InvalidDateException, ParseException {
List<Date> dateList = new ArrayList<>();
if (year <= 0 || (1 > dayOfWeek && dayOfWeek > 7)) {
throw new InvalidDateException("Year or day of week is invalid.");
} else {
Calendar newCal = Calendar.getInstance();
newCal.set(YEAR, year);
newCal.set(DAY_OF_YEAR, 1);
while (newCal.get(YEAR) < year + 1) {
int currentDayOfWeek = newCal.get(DAY_OF_WEEK);
Date newDate = null;
if (currentDayOfWeek >= dayOfWeek) {
int dayOfMonth = newCal.get(DAY_OF_MONTH);
String strDayOfMonth = String.valueOf(dayOfMonth);
String strYear = String.valueOf(year);
DateUtility d1 = new DateUtility();
Date passDate = newCal.getTime();
String weekDay = d1.getWeekDayNameAbbreviation(passDate);
String monthAbbreviation = d1.getMonthAbbreviation(passDate);
String finalString = new String();
finalString.concat(weekDay).concat(" ").
concat(monthAbbreviation).concat(" ").
concat(strDayOfMonth).concat(" ").
concat(strYear);
SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd YYYY");
Date theDate = format.parse(finalString);
dateList.add(theDate);
}
newCal.add(Calendar.DATE, 1);
}
}
return (dateList);
}
Your question fails to specify which is first day of the week, but things are further complicated by your method for testing the current day of the week. Let's start with validating days of the week by using the Calendar standard,
private static boolean isValidDayOfWeek(int dayOfWeek) {
switch (dayOfWeek) {
// Seven days of the week.
case Calendar.SUNDAY: case Calendar.MONDAY: case Calendar.TUESDAY:
case Calendar.WEDNESDAY: case Calendar.THURSDAY: case Calendar.FRIDAY:
case Calendar.SATURDAY:
return true;
}
return false;
}
It then follows that we can do something like,
public static List<Date> getDatesforDayOfWeek(int year, int dayOfWeek) {
List<Date> dateList = new ArrayList<>();
if (year <= 0 || !isValidDayOfWeek(dayOfWeek)) {
return null;
} else {
Calendar newCal = Calendar.getInstance();
newCal.set(Calendar.YEAR, year);
newCal.set(Calendar.DAY_OF_YEAR, 1);
// First, let's loop until we're at the correct day of the week.
while (newCal.get(Calendar.DAY_OF_WEEK) != dayOfWeek) {
newCal.add(Calendar.DAY_OF_MONTH, 1);
}
// Now, add the Date to the List. Then add a week and loop (stop
// when the year changes).
do {
dateList.add(newCal.getTime());
newCal.add(Calendar.DAY_OF_MONTH, 7);
} while (newCal.get(Calendar.YEAR) == year);
}
return dateList;
}
Leaving us with main(). So, to get every Tuesday in 2014 you could then use -
public static void main(String[] args) {
List<Date> tuesdays = getDatesforDayOfWeek(2014, Calendar.TUESDAY);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
for (Date d : tuesdays) {
System.out.println(df.format(d));
}
}
tl;dr
startOfYear // `Year.of( 2019 ).atDay( 1 )` gets the first day of the year.
.datesUntil( startOfYear.plusYears( 1 ) ) // Generate a stream of incrementing `LocalDate` objects.
.filter( // Pull out the dates that are a Tuesday.
t -> t.getDayOfWeek().equals( DayOfWeek.TUESDAY )
)
.collect( Collectors.toList() ) // Return results in a `List` of `LocalDate` objects.
ISO 8601
The ISO 8601 standard for date-time work defines Monday as the first day of week, identified by number 1. Sunday is 7.
Avoid j.u.Date & .Calendar
The java.util.Date and .Calendar classes bundled with java are notoriously troublesome. Avoid them. They have been supplanted in Java 8 by the new java.time package. That package was inspired by Joda-Time, an alternative that remains an active viable project with some advantages.
Both Joda-Time and java.time use ISO 8601 by default.
Date-Only
For this Question, we need only dates, not time-of-day or time zones. Both Joda-Time and java.time offer a LocalDate class for this purpose.
java.time
Use Year.of and LocalDate::plusYears to determine the bounds of a year, yielding a pair of LocalDate objects for each first-day-of-year.
LocalDate startOfYear = Year.of( 2019 ).atDay( 1 ); // Determine first day of the year.
LocalDate startOfFollowingYear = startOfYear.plusYears( 1 );
Loop, incrementing the date one day at a time. If that date happens to be a Tuesday, add it to our collection.
LocalDate localDate = startOfYear;
List < LocalDate > tuesdays = new ArrayList <>( 55 ); // Set initialCapacity to maximum number of tuesdays in a year. Probably 53, but I'll go with 55 for good measure.
while ( localDate.isBefore( startOfFollowingYear ) )
{
if ( localDate.getDayOfWeek().equals( DayOfWeek.TUESDAY ) )
{
tuesdays.add( localDate );
}
// Set up the next loop.
localDate = localDate.plusDays( 1 );
}
System.out.println( tuesdays );
See this code run live at IdeOne.com.
[2019-01-01, 2019-01-08, 2019-01-15, 2019-01-22, 2019-01-29, 2019-02-05, 2019-02-12, 2019-02-19, 2019-02-26, 2019-03-05, 2019-03-12, 2019-03-19, 2019-03-26, 2019-04-02, 2019-04-09, 2019-04-16, 2019-04-23, 2019-04-30, 2019-05-07, 2019-05-14, 2019-05-21, 2019-05-28, 2019-06-04, 2019-06-11, 2019-06-18, 2019-06-25, 2019-07-02, 2019-07-09, 2019-07-16, 2019-07-23, 2019-07-30, 2019-08-06, 2019-08-13, 2019-08-20, 2019-08-27, 2019-09-03, 2019-09-10, 2019-09-17, 2019-09-24, 2019-10-01, 2019-10-08, 2019-10-15, 2019-10-22, 2019-10-29, 2019-11-05, 2019-11-12, 2019-11-19, 2019-11-26, 2019-12-03, 2019-12-10, 2019-12-17, 2019-12-24, 2019-12-31]
Or get fancy with functional lambda syntax. The LocalDate::datesUntil method generates a stream, in Java 9 and later. Then filter the stream by a match on DayOfWeek.TUESDAY.
LocalDate startOfYear = Year.of( 2019 ).atDay( 1 );
Stream < LocalDate > stream = startOfYear.datesUntil( startOfYear.plusYears( 1 ) );
List < LocalDate > tuesdays = stream.filter( t -> t.getDayOfWeek().equals( DayOfWeek.TUESDAY ) ).collect( Collectors.toList() );
Joda-Time
Here is some example code in Joda-Time 2.4 for collecting all the Tuesdays in a year.
int year = 2014;
String input = year + "-01-01";
LocalDate localDateInput = LocalDate.parse( input );
LocalDate firstTuesday = localDateInput.withDayOfWeek ( DateTimeConstants.TUESDAY );
LocalDate tuesday = firstTuesday; // for incrementing by week.
List<LocalDate> list = new ArrayList<>();
while ( tuesday.getYear() == year ) {
list.add( tuesday );
tuesday.plusWeeks( 1 );
}
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I think your main problem lies in this condition
if (currentDayOfWeek >= dayOfWeek) {
since that will count any day that is "higher" than the day you want. If you pass 3, it will also count any day that is higher than 3, which isn't what you want.
the condition should be
if (currentDayOfWeek == dayOfWeek) {
I also recommend you use Calendar getTime method instead of parsing a String to get your Date.
I'm pretty new to Java world, and I'm practicing a lot.
My last exercize is about an Apartment Renting program. My question refers to the "booking" part in the Manager class, in which I have to check if the requested arrival date is linked to the low, medium or high season lot in the array double tariffs[].
Here is the portion of code with the bookApartment() method, where code and id are the keys in the HashMaps of Apartments and Clients (booking is correct only if the arrival date is a Saturday):
public Booking bookAppartment(String code, String id, int day, int month, int year, int nweeks) throws WrongDay {
Calendar date = Calendar.getInstance();
date.set(year, month-1, day);
int weekday = date.get(Calendar.DAY_OF_WEEK);
Booking book=null;
if(code!="" && id!=""){
if(weekday!=Calendar.SATURDAY)
throw new WrongDay(date);
else{
for(Map.Entry<String , Apartment> apt : apts.entrySet()){
for(Map.Entry<String, Client> client : clients.entrySet()){
if(apt.getKey()==code && client.getKey()==id && weekday==Calendar.SATURDAY){
book = new Booking(client.getValue(), apt.getValue(), d, m, y, nweeks);
bookings.add(book);
book.setPrice(d, m, y, apt.getValue().getTariffs(), nweeks);
break;
}
}
}
}
}
return book;
}
And here I attach the constructor of the Booking object and my personal override of the setPrice() method, which calculates the entire booking price selecting the correct tariffs[] lot:
public class Booking {
private Client client;
private Apartment apt;
private double price;
private int numweeks;
private static int day, month, year;
public Booking(Client client, Apartment apt, int day, int month, int year, int numweeks){
this.client = client;
this.apt = apt;
Booking.day = day;
Booking.month = month;
Booking.year = year;
this.numweeks = numweeks;
}
// other stuff
public void setPrice(int day, int month, int year, double[] tariff, int numweeks){
tariff = apt.getTariffs();
Booking.day=day;
Booking.month=month;
Booking.year=year;
Calendar date = Calendar.getInstance();
date.set(year, month-1, day);
Calendar date1 = Calendar.getInstance();
date1.set(2008, 6, 1);
Calendar date2 = Calendar.getInstance();
date2.set(2008, 6, 31);
Calendar date3 = Calendar.getInstance();
date3.set(2008, 7, 1);
Calendar date4 = Calendar.getInstance();
date4.set(2008, 7, 31);
Calendar date5 = Calendar.getInstance();
date5.set(2008, 11, 20);
Calendar date6 = Calendar.getInstance();
date6.set(2009, 0, 1);
if(date.equals(date1) || date.equals(date2) || (date.after(date1) && date.before(date2))){
this.price = tariff[1] * numweeks;
} else if(date.equals(date3) || date.equals(date4) || (date.after(date3) && date.before(date4))){
this.price = tariff[2] * numweeks;
} else if(date.equals(date5) || date.equals(date6) || (date.after(date5) && date.before(date6))){
this.price = tariff[2] * numweeks;
} else{
this.price = tariff[0] * numweeks;
}
}
}
I encounter the problem when setting the price of a Booking object with arrival date on the 20th December 2008 (considered high season): it skips the third if check (expected) and goes directly to the last else.
But if I run my own program to check if the dates are the same, passing directly the values to day, month and year, the test is passed.
So it seems to me that I cannot pass only references not pointing to an int value not manually setted.
Is it possible I am right?
If so, I really don't know how to go on.
Thanks in advance: I hope I used all the right words in the right places.
When you get a calendar instance, it defaults to using the current time (right down to the millisecond). Thus, when set your date in it:
Calendar date = Calendar.getInstance();
date.set(year, month-1, day);
... the date is still left with "random" values for the hours, minutes, seconds and milliseconds. The same goes for date1 through to date6.
In your code, you create all the dates one right after the other, so the speed of executing those instructions may mean that the first few dates end up with identical values for hours, minutes, seconds and milliseconds. However there is no guarantee of this.
What you're finding is that when you do, for example, date.equals(date3), the year month and day match, but the other fields potentially don't.
To solve this, call clear() first:
Calendar date = Calendar.getInstance();
date.clear();
date.set(year, month-1, day);
Also, you probably don't actually want to compare calendars for equality. You can, but if you look at the Javadoc for it, it says:
* Compares this <code>Calendar</code> to the specified
* <code>Object</code>. The result is <code>true</code> if and only if
* the argument is a <code>Calendar</code> object of the same calendar
* system that represents the same time value (millisecond offset from the
* Epoch) under the same
* <code>Calendar</code> parameters as this object.
*
* <p>The <code>Calendar</code> parameters are the values represented
* by the <code>isLenient</code>, <code>getFirstDayOfWeek</code>,
* <code>getMinimalDaysInFirstWeek</code> and <code>getTimeZone</code>
* methods. If there is any difference in those parameters
* between the two <code>Calendar</code>s, this method returns
* <code>false</code>.
*
* <p>Use the {#link #compareTo(Calendar) compareTo} method to
* compare only the time values.
You're probably better off using:
if (date.getTime().equals(date1.getTime()))
{
...
}
... and comparing the returned Date objects, or doing as the Javadoc suggests and using compareTo():
if (date.compareTo(date1) == 0)
{
...
}
I understand you are doing an exercise, but you should know:
(a) Avoid java.util.Calendar
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them. Use either the new java.time package bundled with Java 8, or the Joda-Time library which inspired java.time. Both java.time and Joda-Time have some pros and cons over each other, both are active projects, and you can even use them both in a project.
(b) Date-Only
The old .Date & .Calendar classes lack a representation of date-only without a time-of-day. But that is what your Question demands, a class that is date-only without time and time zones. Fortunately both Joda-Time and java.time have such a class, both called LocalDate.
(c) Half-Open
The best approach to spans of time is called "Half-Open" where the beginning is inclusive and the ending exclusive. For example the month of June would be June 1 and going up to, but not including, July 1. This simplifies things whether doing date-only or date-time work. Joda-Time and java.time adopt this approach.
The other answer by Greg Kopff seems to be correct, the time-of-day portion is throwing you off.
Here is some example code in Joda-Time 2.4 to get you headed in the right direction.
LocalDate target = new LocalDate( 2008, 12, 20 );
LocalDate highSummerStart = new LocalDate( 2008, 6, 1 ); // Half-Open. Inclusive.
LocalDate highSummerStop = new LocalDate( 2008, 7, 1 ); // Exclusive.
LocalDate lateSummerStart = new LocalDate( 2008, 7, 1 ); // Half-Open. Inclusive.
LocalDate lateSummerStop = new LocalDate( 2008, 8, 1 ); // Exclusive.
LocalDate holidaysStart = new LocalDate( 2008, 11, 20 ); // Half-Open. Inclusive.
LocalDate holidaysStop = new LocalDate( 2009, 1, 2 ); // Exclusive.
if ( this.rangeContainsTarget( highSummerStart, highSummerStop, target ) ) {
System.out.println( "Apply High Summer rates." );
} else if ( this.rangeContainsTarget( lateSummerStart, lateSummerStop, target ) ) {
System.out.println( "Apply Late Summer rates." );
} else if ( this.rangeContainsTarget( holidaysStart, holidaysStop, target ) ) {
System.out.println( "Apply Holidays rates." );
} else { // Else not in special season.
System.out.println( "Apply default rates." );
}
And the comparison method.
private boolean rangeContainsTarget( LocalDate start, LocalDate stop, LocalDate target )
{
// Half-Open approach. If the Target is GREATER THAN OR EQUAL TO Start AND Target is LESS THAN Stop.
if ( start.isAfter( stop ) ) {
return false; // Or throw exception.
}
boolean startGood = ( target.isEqual( start ) || target.isAfter( start ) );
boolean stopGood = target.isBefore( stop );
boolean containsTarget = ( startGood && stopGood );
return containsTarget;
}
The old .Date/.Calendar classes lack a way to represent a span of time. Joda-Time offers three classes to define a span of time in various ways: Interval, Period, and Duration. Unfortunately they work only with DateTime, not LocalDate. So I did not use them in the example above, where Interval would have been handy.
By the way, if in Joda-Time you do need a date plus time-of-day yet want to focus on days, call the withTimeAtStartOfDay() method to get a DateTime object set to the first moment of the day. That first moment is not always the time 00:00:00.000 because of Daylight Saving Time and perhaps other anomalies.
I am trying to get the date of Monday or Thurday in this format YYYYMMDD
For Monday it should give me this - 20130224 (as an example)
For Thursday it should give me this - 20130227 (as an example)
Now, if I am running my program after Thursday or on Thursday, it should print date for Thursday in this format YYYYMMDD which can be 20130227 (coming thursday in this week).
And If I am running my program after Monday or on Monday, then it should print date for Monday in the same format YYYYMMMDD which can be 20130224 (yesterday Monday date as an example)
How would I do this in Java?
Below is what I have tried -
Calendar cal = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat("EEE");
String text = formatter.format(cal.getTime());
System.out.println(text);
// but how do I check if it is Tuesday but less than Thursday
if(text.equalsIgnoreCase("Tue")) {
// get previous Monday date in YYYYMMDD
}
// and how do I check if it is thursday or greater than Thursday?
else if(text.equalsIgnoreCase("Thur")) {
// get previous Thursday date in YYYYMMDD
}
Update:-
In a particular week, if I am running my program on Thursday or after Thursday then it should return me date for Thursday in the same week in YYYYMMDD format, but if I am running my program on Monday or after Monday, then it should return me date for Monday in the same week in YYYYMMDD format.
For example, In this week, if I am running my program on Thursday or after Thursday, then it should return date for Thursday. But if I am running my program on Monday or Tuesday or Wednesday in this same week, then it should return me date for Monday.
Code:-
Below is my code -
public static void main(String[] args) {
try {
Calendar cal = Calendar.getInstance();
SimpleDateFormat toDateFormat = new SimpleDateFormat("yyyyMMdd");
int dow = cal.get(Calendar.DAY_OF_WEEK);
switch (dow) {
case Calendar.THURSDAY:
case Calendar.FRIDAY:
case Calendar.SATURDAY:
case Calendar.SUNDAY:
while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.THURSDAY) {
cal.add(Calendar.DATE, -1);
}
break;
case Calendar.MONDAY:
case Calendar.TUESDAY:
case Calendar.WEDNESDAY:
while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
cal.add(Calendar.DATE, -1);
}
break;
}
System.out.println(date);
System.out.println(cal.getTime());
System.out.println(toDateFormat.format(cal.getTime()));
} catch (ParseException exp) {
exp.printStackTrace();
}
}
Start by parsing the text value to a Date value...
String dateText = "20130224";
SimpleDateFormat toDateFormat = new SimpleDateFormat("yyyyMMdd");
Date date = toDateFormat.parse(dateText);
This gives you the starting point. Next you need to use a Calendar which will allow you move backwards and forwards, automatically rolling the date internally for you (so if you roll over week, month or year boundaries)
For example...
try {
String dateText = "20130227";
SimpleDateFormat toDateFormat = new SimpleDateFormat("yyyyMMdd");
Date date = toDateFormat.parse(dateText);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int dow = cal.get(Calendar.DAY_OF_WEEK);
switch (dow) {
case Calendar.THURSDAY:
case Calendar.FRIDAY:
case Calendar.SATURDAY:
case Calendar.SUNDAY:
while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.THURSDAY) {
cal.add(Calendar.DATE, -1);
}
break;
case Calendar.MONDAY:
case Calendar.TUESDAY:
case Calendar.WEDNESDAY:
while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
cal.add(Calendar.DATE, -1);
}
break;
}
System.out.println(date);
System.out.println(cal.getTime());
} catch (ParseException exp) {
exp.printStackTrace();
}
So, based on this example, it would output...
Wed Feb 27 00:00:00 EST 2013
Mon Feb 25 00:00:00 EST 2013
For 20130224 (which is a Sunday) it will give
Sun Feb 24 00:00:00 EST 2013
Thu Feb 21 00:00:00 EST 2013
I should also add, there's probably a much easier way to do this with JodaTime, but this is what I was able to wipe up quickly. Yes, I know the case statement is little winded, but SUNDAY is equal to 0, which is a little annoying ;)
What? Moving to a new question with the same contents?
String[] weeks = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
DateFormat df = new SimpleDateFormat("yyyyMMdd");
Calendar c = Calendar.getInstance();
c.setTime(new Date()); // Now use today date.
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK); // Sun=1, Mon=2, ... Sat=7
System.out.println("Today " + df.format(c.getTime()) + " is " + weeks[dayOfWeek-1]);
c.add(Calendar.DATE, 7); // Adding 7 days
System.out.println("Next " + weeks[dayOfWeek-1] + " is " + df.format(c.getTime()));
// Should display:
// Today 20140225 is Tuesday
// Next Tuesday is 20140304
I would use the calendar class day to get the day of the week. Calendar.DAY_OF_WEEK function returns 1 - 7 for Sunday - Saturday. This way you can do numeric comparison and not mess around with comparing the strings for the weekdays (which would be a mess if your app needs to support multiple languages).
See:
http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html
If I understood what you're after, then this should work
private static final java.text.SimpleDateFormat sdf =
new java.text.SimpleDateFormat("yyyyMMdd");
public static String calculateCorrectDate(
java.util.Date d) {
java.util.Calendar cal = java.util.Calendar
.getInstance();
cal.setTime(d);
int dayOfWeek = cal
.get(java.util.Calendar.DAY_OF_WEEK);
if (dayOfWeek >= java.util.Calendar.MONDAY
&& dayOfWeek < java.util.Calendar.THURSDAY) {
cal.set(java.util.Calendar.DAY_OF_WEEK,
java.util.Calendar.MONDAY);
} else {
cal.set(java.util.Calendar.DAY_OF_WEEK,
java.util.Calendar.THURSDAY);
}
return sdf.format(cal.getTime());
}
public static void main(String[] args) {
java.util.List<java.util.Date> dates = new java.util.ArrayList<java.util.Date>();
java.util.Calendar cal = java.util.Calendar
.getInstance();
String today = sdf.format(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_WEEK, -10);
for (int i = 0; i < 20; i++) {
dates.add(cal.getTime());
cal.add(java.util.Calendar.DAY_OF_WEEK, 1);
}
for (java.util.Date d : dates) {
if (sdf.format(d).equals(today)) {
System.out.println("TODAY!");
}
System.out.println(calculateCorrectDate(d));
}
}
Which gives the output
20140213
20140220
20140217
20140217
20140217
20140220
20140220
20140220
20140227
20140224
TODAY!
20140224
20140224
20140227
20140227
20140227
20140306
20140303
20140303
20140303
20140306
or with a few import(s),
// Import Static
// This simplifies accessing the Calendar fields. Use sparingly.
import static java.util.Calendar.DAY_OF_WEEK;
import static java.util.Calendar.MONDAY;
import static java.util.Calendar.THURSDAY;
// The other imports.
import java.util.Calendar;
import java.util.Date;
Then you can use,
public static String calculateCorrectDate(Date d) {
Calendar cal = Calendar.getInstance();
cal.setTime(d);
// By using import static this remains concise and correct.
int dayOfWeek = cal.get(DAY_OF_WEEK);
if (dayOfWeek >= MONDAY && dayOfWeek < THURSDAY) {
cal.set(DAY_OF_WEEK, MONDAY);
} else {
cal.set(DAY_OF_WEEK, THURSDAY);
}
return sdf.format(cal.getTime());
}
Joda-Time
UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. This Answer is left intact as history. See my modern Answer instead.
Yes, Joda-Time is the solution.
Or the new java.time package in Java 8. Inspired by Joda-Time but re-architected. Defined by JSR 310.
Next Monday/Thursday
Search StackOverflow for getting first or last day of week. That will show you how to get next monday or thursday. I won't cover that part of your question here.
Testing Day Of Week
Testing for the day of week by the English word is prone to break if you ever happen to run where English is not the default locale. Instead, get day of week by number. Joda-Time uses ISO 8601 as its defaults, so Monday = 1, Sunday = 7. Constants are provided, so you needn't memorize the numbers.
Date Without Time
If you truly don't care about time-of-day, only date, then we can use LocalDate rather than DateTime.
Example Code
Some code to get you started, using Joda-Time 2.3.
String input = "20130224";
DateTimeFormatter formatterInput = DateTimeFormat.forPattern( "yyyyMMdd" );
LocalDate localDate = formatterInput.parseLocalDate( input );
int dayOfWeek = localDate.getDayOfWeek();
boolean isMonday = ( localDate.getDayOfWeek() == DateTimeConstants.MONDAY );
boolean isThursday = ( localDate.getDayOfWeek() == DateTimeConstants.THURSDAY );
Dump to console…
System.out.println( "localDate: " + localDate );
System.out.println( "dayOfWeek: " + dayOfWeek );
System.out.println( "isMonday: " + isMonday );
System.out.println( "isThursday: " + isThursday );
When run…
localDate: 2013-02-24
dayOfWeek: 7
isMonday: false
isThursday: false
tl;dr
If:
EnumSet.range( DayOfWeek.MONDAY , DayOfWeek.WEDNESDAY ) // Monday, Tuesday, & Wednesday.
contains( LocalDate.now().getDayOfWeek() ) // If today’s date is a day-of-week that happens to be a member of that `Set`.
…then, apply:
TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) // Adjust into an earlier `LocalDate` that is a Monday, unless today already is Monday.
…else apply:
TemporalAdjusters.previousOrSame( DayOfWeek.THURSDAY ) // Otherwise move to a Thursday.
java.time
The modern approach uses the java.time classes. So much easier to solve this Question.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Or specify a date. You may set the month by a number, with sane numbering 1-12 for January-December.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Or, better, use the Month enum objects pre-defined, one for each month of the year. Tip: Use these Month objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety.
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
DayOfWeek enum
The DayOfWeek enum defines a set of seven objects, one for each day of the week.
An EnumSet is a highly-optimized implementation of Set for collecting enum objects. So we can make a pair of EnumSet objects to hold a collection of DayOfWeek objects to define your two conditions: (Monday & Tuesday) versus (Thursday…Sunday).
Despite mysteriously failing to implement SortedSet, an EnumSet is indeed sorted in the natural order (declared order) of the enum. For DayOfWeek that would be Monday-Sunday, numbered 1-7 though you may never need those numbers.
Set < DayOfWeek > mondayDays = EnumSet.range( DayOfWeek.MONDAY , DayOfWeek.WEDNESDAY ); // Monday, Tuesday, & Wednesday.
Set < DayOfWeek > thursdayDays = EnumSet.range( DayOfWeek.THURSDAY , DayOfWeek.SUNDAY ); // Thursday, Friday, Saturday, Sunday.
Get the day-of-week for our source date. Prepare to match that against our enum sets.
LocalDate ld = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ; // Get today’s current date in a particular time zone.
DayOfWeek dow = ld.getDayOfWeek();
LocalDate target = LocalDate.EPOCH; // Or null. The `LocalDate.EPOCH` is first moment of 1970 in UTC, 1970-01-01T00:00:00Z.
See which Set has the day-of-week of our date. From there, adjust into a previous or same date for the desired day-of-week (Monday or Thursday).
if ( mondayDays.contains( dow ) )
{
target = ld.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) );
} else if ( thursdayDays.contains( dow ) )
{
target = ld.with( TemporalAdjusters.previousOrSame( DayOfWeek.THURSDAY ) );
} else
{
System.out.println( "ERROR - Unexpectedly reached IF-ELSE. " );
}
Generate string in your desired format. Your chosen format happens to be the “basic” version of standard ISO 8601 format where the use of delimiters is minimized.
String output = target.format( DateTimeFormatter.BASIC_ISO_DATE ) ; // YYYY-MM-DD
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
What this code does is print the dates of the current week from Monday to Friday. It works fine, but I want to ask something else: If today is Saturday or Sunday I want it to show the next week. How do I do that?
Here's my working code so far (thanks to StackOverflow!!):
// Get calendar set to current date and time
Calendar c = Calendar.getInstance();
// Set the calendar to monday of the current week
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
// Print dates of the current week starting on Monday to Friday
DateFormat df = new SimpleDateFormat("EEE dd/MM/yyyy");
for (int i = 0; i <= 4; i++) {
System.out.println(df.format(c.getTime()));
c.add(Calendar.DATE, 1);
}
Thanks a lot! I really appreciate it as I've been searching for the solution for hours...
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
// Set the calendar to monday of the current week
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
// Print dates of the current week starting on Monday to Friday
DateFormat df = new SimpleDateFormat("EEE dd/MM/yyyy");
for (int i = 0; i <= 10; i++) {
System.out.println(df.format(c.getTime()));
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
if (dayOfWeek == Calendar.FRIDAY) { // If it's Friday so skip to Monday
c.add(Calendar.DATE, 3);
} else if (dayOfWeek == Calendar.SATURDAY) { // If it's Saturday skip to Monday
c.add(Calendar.DATE, 2);
} else {
c.add(Calendar.DATE, 1);
}
// As Cute as a ZuZu pet.
//c.add(Calendar.DATE, dayOfWeek > Calendar.THURSDAY ? (9 - dayOfWeek) : 1);
}
}
Output
Mon 03/01/2011
Tue 04/01/2011
Wed 05/01/2011
Thu 06/01/2011
Fri 07/01/2011
Mon 10/01/2011
Tue 11/01/2011
Wed 12/01/2011
Thu 13/01/2011
Fri 14/01/2011
Mon 17/01/2011
If you want to be cute you can replace the if/then/else with
c.add(Calendar.DATE, dayOfWeek > 5 ? (9 - dayOfWeek) : 1);
but I really wanted something easily understood and readable.
tl;dr
Core code concept:
EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) // Instantiate a n implementation of `Set` highly optimized in both memory usage and execution speed for collecting enum objects.
.contains( // Ask if our target `DayOfWeek` enum object is in our `Set`.
LocalDate.now( ZoneId.of( "America/Montreal" ) ) // Determine today’s date as seen by the people of a particular region (time zone).
.getDayOfWeek() // Determine the `DayOfWeek` enum constant representing the day-of-week of this date.
)
java.time
The modern way is with the java.time classes.
The DayOfWeek enum provides seven objects, for Monday-Sunday.
The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );
DayOfWeek dow = today.getDayOfWeek();
Define the weekend as a set of DayOfWeek objects. Note that EnumSet is an especially fast and low-memory implementation of Set designed to hold Enum objects such as DayOfWeek.
Set<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
Now we can test if today is a weekday or a weekend.
Boolean todayIsWeekend = weekend.contains( dow );
The Question said we want to jump to the start of next week if this is a weekend. To do that, use a TemporalAdjuster which provides for classes that can manipulate date-time objects. In java.time we have immutable objects. This means we produce new instances based on the values within an existing object rather than alter ("mutate") the original. The TemporalAdjusters class (note the plural 's') provides several handy implementations of TemporalAdjuster including next( DayOfWeek ).
DayOfWeek firstDayOfWeek = DayOfWeek.MONDAY ;
LocalDate startOfWeek = null ;
if( todayIsWeekend ) {
startOfWeek = today.with( TemporalAdjusters.next( firstDayOfWeek ) );
} else {
startOfWeek = today.with( TemporalAdjusters.previousOrSame( firstDayOfWeek ) );
}
We soft-code the length of the week in case our definition of weekend ever changes.
LocalDate ld = startOfWeek ;
int countDaysToPrint = ( DayOfWeek.values().length - weekend.size() );
for( int i = 1 ; i <= countDaysToPrint ; i++ ) {
System.out.println( ld );
// Set up the next loop.
ld = ld.plusDays( 1 );
}
See live code in IdeOne.com.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Here is short answer using Java 8, all you need to do is to convert your Calendar instance to LocalDateTime and leverage DayOfWeek enum to check if it's Saturday or Sunday, here you go...
Calendar c = Calendar.getInstance();
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
DateFormat df = new SimpleDateFormat("EEE dd/MM/yyyy");
for (int i = 0; i <= 20; i++) {
//following line does all magic for you
if(LocalDateTime.ofInstant(c.toInstant(), ZoneId.systemDefault()).getDayOfWeek()!=DayOfWeek.SATURDAY && LocalDateTime.ofInstant(c.toInstant(), ZoneId.systemDefault()).getDayOfWeek()!=DayOfWeek.SUNDAY)
System.out.println(df.format(c.getTime()));
c.add(Calendar.DATE, 1);
}
I have start date and end date.
I need the number of months between this two dates in Java.
For example
From date: 2009-01-29
To date: 2009-02-02
It has one jan date and one Feb date.
It should return 2.
As the rest say, if there's a library that will give you time differences in months, and you can use it, then you might as well.
Otherwise, if y1 and m1 are the year and month of the first date, and y2 and m2 are the year and month of the second, then the value you want is:
(y2 - y1) * 12 + (m2 - m1) + 1;
Note that the middle term, (m2 - m1), might be negative even though the second date is after the first one, but that's fine.
It doesn't matter whether months are taken with January=0 or January=1, and it doesn't matter whether years are AD, years since 1900, or whatever, as long as both dates are using the same basis. So for example don't mix AD and BC dates, since there wasn't a year 0 and hence BC is offset by 1 from AD.
You'd get y1 etc. either from the dates directly if they're supplied to you in a suitable form, or using a Calendar.
Apart from using Joda time which seems to be the the favorite suggestion I'd offer the following snippet:
public static final int getMonthsDifference(Date date1, Date date2) {
int m1 = date1.getYear() * 12 + date1.getMonth();
int m2 = date2.getYear() * 12 + date2.getMonth();
return m2 - m1 + 1;
}
EDIT: Since Java 8, there is a more standard way of calculating same difference. See my alternative answer using JSR-310 api instead.
I would strongly recommend Joda-Time (and as of Java 8, the Java Time apis) for this.
It makes this sort of work very easy (check out Periods)
It doesn't suffer from the threading issues plaguing the current date/time objects (I'm thinking of formatters, particularly)
It's the basis of the new Java date/time APIs to come with Java 7 (so you're learning something that will become standard)
Note also Nick Holt's comments below re. daylight savings changes.
Now that JSR-310 has been included in the SDK of Java 8 and above, here's a more standard way of getting months difference of two date values:
public static final long getMonthsDifference(Date date1, Date date2) {
YearMonth m1 = YearMonth.from(date1.toInstant().atZone(ZoneOffset.UTC));
YearMonth m2 = YearMonth.from(date2.toInstant().atZone(ZoneOffset.UTC));
return m1.until(m2, ChronoUnit.MONTHS) + 1;
}
This has a benefit of clearly spelling out the precision of the calculation and it is very easy to understand what is the intent of the calculation.
Java 8 solution:
#Test
public void monthBetween() {
LocalDate d1 = LocalDate.of(2013, Month.APRIL, 1);
LocalDate d2 = LocalDate.of(2014, Month.APRIL, 1);
long monthBetween = ChronoUnit.MONTHS.between(d1, d2);
assertEquals(12, monthBetween);
}
Based on the above suggested answers I rolled my own which I added to my existing DateUtils class:
public static Integer differenceInMonths(Date beginningDate, Date endingDate) {
if (beginningDate == null || endingDate == null) {
return 0;
}
Calendar cal1 = new GregorianCalendar();
cal1.setTime(beginningDate);
Calendar cal2 = new GregorianCalendar();
cal2.setTime(endingDate);
return differenceInMonths(cal1, cal2);
}
private static Integer differenceInMonths(Calendar beginningDate, Calendar endingDate) {
if (beginningDate == null || endingDate == null) {
return 0;
}
int m1 = beginningDate.get(Calendar.YEAR) * 12 + beginningDate.get(Calendar.MONTH);
int m2 = endingDate.get(Calendar.YEAR) * 12 + endingDate.get(Calendar.MONTH);
return m2 - m1;
}
And the associatiated unit tests:
public void testDifferenceInMonths() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
assertEquals(12, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2015/03/22")).intValue());
assertEquals(11, DateUtils.differenceInMonths(sdf.parse("2014/01/01"), sdf.parse("2014/12/25")).intValue());
assertEquals(88, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2021/07/05")).intValue());
assertEquals(6, DateUtils.differenceInMonths(sdf.parse("2014/01/22"), sdf.parse("2014/07/22")).intValue());
}
using joda time would be like this (i compared how many months between today and 20/dec/2012)
import org.joda.time.DateTime ;
import org.joda.time.Months;
DateTime x = new DateTime().withDate(2009,12,20); // doomsday lol
Months d = Months.monthsBetween( new DateTime(), x);
int monthsDiff = d.getMonths();
Result: 41 months (from july 6th 2009)
should be easy ? :)
ps: you can also convert your date using SimpleDateFormat
like:
Date x = new SimpleDateFormat("dd/mm/yyyy").parse("20/12/2009");
DateTime z = new DateTime(x);
If you don't want to use Joda (for whatever reason), you can convert your date to TimeStamp and then do the differences of milli seconds between both date and then calculate back to months. But I still prefer to use Joda time for the simplicity :)
tl;dr
ChronoUnit.MONTHS.between(
YearMonth.from( LocalDate.of( 2009 , 1 , 29 ) ) ,
YearMonth.from( LocalDate.of( 2009 , 2 , 2 ) )
)
Time Zone
The Answer by Roland Tepp is close but ignores the crucial issue of time zone. Determining a month and date requires a time zone, as for any given moment the date varies around the globe by zone.
ZonedDateTime
So his example of converting java.util.Date objects to java.time.Instant objects implicitly uses UTC. Values in either of those classes is always in UTC by definition. So you need to adjust those objects into the desired/intended time zone to be able to extract a meaningful date.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtStart = myJavaUtilDate1.toInstant().atZone( z );
ZonedDateTime zdtStop = myJavaUtilDate2.toInstant().atZone( z );
YearMonth
Since you want to know how many calendar months were touched by your date range rather than the number of 30-day chunks elapsed, convert to YearMonth objects.
YearMonth start = YearMonth.from( zdtStart );
YearMonth stop = YearMonth.from( zdtStop );
ChronoUnit
Calculate months between by calling on ChronoUnit enum.
long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
Half-Open
You desired a result of 2 but we get 1 here. The reason is that in date-time work the best practice is to define spans of time by the Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive. I suggest you stick to this definition throughout your date-time work as doing so ultimately makes sense, eliminates confusing ambiguities, and makes your work easier to parse mentally and less error-prone. But if you insist on your definition, simply add 1 to the result assuming you have positive numbered results (meaning your spans of time go forward in time rather than backward).
LocalDate
The original Question is not clear but may require date-only values rather than date-time values. If so, use the LocalDate class. The LocalDate class represents a date-only value without time-of-day and without time zone.
LocalDate start = LocalDate.of( 2009 , 1 , 29 ) ;
LocalDate stop = LocalDate.of( 2009 , 2 , 2 ) ;
long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Joda Time is a pretty cool library for Java Date and Time and can help you achieve what you want using Periods.
You can use a Calendar or Joda time library for this.
In Joda time you can use the Days.daysBetween() method. You can then calculate the months difference. You can also use DateTime.getMonthOfYear() and do a subtraction (for dates in the same year).
It depends on your definition of a month, but this is what we use:
int iMonths = 0;
Calendar cal1 = GregorianCalendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = GregorianCalendar.getInstance();
cal2.setTime(date2);
while (cal1.after(cal2)){
cal2.add(Calendar.MONTH, 1);
iMonths++;
}
if (cal2.get(Calendar.DAY_OF_MONTH) > cal1.get(Calendar.DAY_OF_MONTH)){
iMonths--;
}
return iMonths;
I had to write this implementation, becoz I had custom defined periods, which i had to look for within two dates.
Here you can define you custom period and put the logic, for calculation.
Here TimePeriod is a POJO which has start, end, period start, period End
public class Monthly extends Period {
public int getPeriodCount(String startDate, String endDate, int scalar) {
int cnt = getPeriods(startDate, endDate, scalar).size();
return cnt;
}
public List getPeriods(String startDate, String endDate, int scalar) {
ArrayList list = new ArrayList();
Calendar startCal = CalendarUtil.getCalendar(startDate);
Calendar endCal = CalendarUtil.getCalendar(endDate);
while (startCal.compareTo(endCal) <= 0) {
TimePeriod period = new TimePeriod();
period.setStartDate(startCal.getTime());
period.setPeriodStartDate(getPeriodStartDate((Calendar) startCal.clone()).getTime());
Calendar periodEndCal = getPeriodEndDate((Calendar) startCal.clone(), scalar);
period.setEndDate(endCal.before(periodEndCal) ? endCal.getTime() : periodEndCal.getTime());
period.setPeriodEndDate(periodEndCal.getTime());
periodEndCal.add(Calendar.DATE, 1);
startCal = periodEndCal;
list.add(period);
}
return list;
}
private Calendar getPeriodStartDate(Calendar cal) {
cal.set(Calendar.DATE, cal.getActualMinimum(Calendar.DATE));
return cal;
}
private Calendar getPeriodEndDate(Calendar cal, int scalar) {
while (scalar-- > 0) {
cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
if (scalar > 0)
cal.add(Calendar.DATE, 1);
}
return cal;
}
}
it is not the best anwer but you can use unixtimestamp
First you find the unixtime's of the dates
then eject each other
Finally you should convert the unixtime(sum) to String
That's because the classes Java Date and Calendar use the Month indices from 0-11
January = 0
December = 1
Is recommended to use Joda Time!
Here's a solution using java.util.Calendar object:
private static Integer getMonthsBetweenDates(Date d1, Date d2) {
Calendar todayDate = getCalendar(d1);
Calendar pastDate = getCalendar(d2);
int yearDiff = todayDate.get(Calendar.YEAR) - pastDate.get(Calendar.YEAR);
if (pastDate.get(Calendar.MONTH) < 11 && pastDate.get(Calendar.DAY_OF_MONTH) < 31){ //if pastDate is smaller than 31/12
yearDiff++;
}
int monthCount = 0;
for (int year = 0 ; year < yearDiff ; year++){
if (year == 0) {
monthCount += 12 - pastDate.get(Calendar.MONTH);
} else if (year == yearDiff - 1){ //last year
if (todayDate.get(Calendar.MONTH) < pastDate.get(Calendar.MONTH)){
monthCount += todayDate.get(Calendar.MONTH) + 1;
} else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) < pastDate.get(Calendar.DAY_OF_MONTH)){
monthCount += todayDate.get(Calendar.MONTH);
} else if (todayDate.get(Calendar.MONTH) >= pastDate.get(Calendar.MONTH) && todayDate.get(Calendar.DAY_OF_MONTH) >= pastDate.get(Calendar.DAY_OF_MONTH)){
monthCount += todayDate.get(Calendar.MONTH) + 1;
}
}
for (int months = 0 ; months < 12 ; months++){
if (year > 0 && year < yearDiff -1){
monthCount++;
}
}
}
return monthCount;
}
Why not calculate with full timedate
public static Integer calculateMonthDiff(Date begining, Date end) throws Exception {
if (begining.compareTo(end) > 0) {
throw new Exception("Beginning date is greater than the ending date");
}
if (begining.compareTo(end) == 0) {
return 0;
}
Calendar cEndCheckDate = Calendar.getInstance();
cEndCheckDate.setTime(begining);
int add = 0;
while (true) {
cEndCheckDate.add(Calendar.MONTH, 1);
add++;
if (cEndCheckDate.getTime().compareTo(end) > 0) {
return add - 1;
}
}
}
A full code snippet for finding the difference of months between two date is as follows:
public String getContractMonth(String contractStart, String contractEnd) {
SimpleDateFormat dfDate = new SimpleDateFormat("yyyy-MM-dd");
String months = "0";
try {
Date startDate = dfDate.parse(contractStart);
Date endDate = dfDate.parse(contractEnd);
Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(startDate);
Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(endDate);
int diffYear = endCalendar.get(Calendar.YEAR) - startCalendar.get(Calendar.YEAR);
int diffMonth = diffYear * 12 + endCalendar.get(Calendar.MONTH) - startCalendar.get(Calendar.MONTH);
months = diffMonth + "";
} catch (ParseException e) {
e.printStackTrace();
} catch (java.text.ParseException e) {
e.printStackTrace();
}
return months;
}
below logic will fetch you difference in months
(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))
you can by 30 days or by months :
public static void main(String[] args) throws IOException {
int n = getNumbertOfMonth(LocalDate.parse("2016-08-31"),LocalDate.parse("2016-11-30"));
System.out.println("number of month = "+n);
n = getNumbertOfDays(LocalDate.parse("2016-08-31"),LocalDate.parse("2016-11-30"));
System.out.println("number of days = "+n);
System.out.println("number of 30 days = "+n/30);
}
static int getNumbertOfMonth(LocalDate dateDebut, LocalDate dateFin) {
LocalDate start = dateDebut;
LocalDate end = dateFin;
int count = 0 ;
List<String> lTotalDates = new ArrayList<>();
while (!start.isAfter(end)) {
count++;
start = start.plusMonths(1);
}
return count;
}
static int getNumbertOfDays(LocalDate dateDebut, LocalDate dateFin) {
LocalDate start = dateDebut;
LocalDate end = dateFin;
int count = 0 ;
List<String> lTotalDates = new ArrayList<>();
while (!start.isAfter(end)) {
count++;
start = start.plusDays(1);
}
return count;
}
long monthsBetween = ChronoUnit.MONTHS.between(LocalDate.parse("2016-01-29").minusMonths(1),
LocalDate.parse("2016-02-02").plusMonths(1));
2016-01-29 to 2016-01-02 = months 1
2016-02-29 to 2016-02-02 = months 1
2016-03-29 to 2016-05-02 = months 5
Here a complete implementation for monthDiff in java without iterations. It returns the number of full month between two dates. If you want to include the number of incomplete month in the result (as in the initial question), you have to zero out the day, hours, minutes, seconds and millisecondes of the two dates before calling the method, or you could change the method to not compare days, hours, minutes etc.
import java.util.Date;
import java.util.Calendar;
...
public static int monthDiff(Date d1, Date d2) {
int monthDiff;
Calendar c1, c2;
int M1, M2, y1, y2, t1, t2, h1, h2, m1, m2, s1, s2, ms1, ms2;
c1 = Calendar.getInstance();
c1.setTime(d1);
c2 = Calendar.getInstance();
c2.setTime(d2);
M1 = c1.get(Calendar.MONTH);
M2 = c2.get(Calendar.MONTH);
y1 = c1.get(Calendar.YEAR);
y2 = c2.get(Calendar.YEAR);
t1 = c1.get(Calendar.DAY_OF_MONTH);
t2 = c2.get(Calendar.DAY_OF_MONTH);
if(M2 < M1) {
M2 += 12;
y2--;
}
monthDiff = 12*(y2 - y1) + M2 - M1;
if(t2 < t1)
monthDiff --; // not a full month
else if(t2 == t1) { // perhaps a full month, we have to look into the details
h1 = c1.get(Calendar.HOUR_OF_DAY);
h2 = c2.get(Calendar.HOUR_OF_DAY);
if(h2 < h1)
monthDiff--; // not a full month
else if(h2 == h1) { // go deeper
m1 = c1.get(Calendar.MINUTE);
m2 = c2.get(Calendar.MINUTE);
if(m2 < m1) // not a full month
monthDiff--;
else if(m2 == m1) { // look deeper
s1 = c1.get(Calendar.SECOND);
s2 = c2.get(Calendar.SECOND);
if(s2 < s1)
monthDiff--; // on enleve l'age de mon hamster
else if(s2 == s1) {
ms1 = c1.get(Calendar.MILLISECOND);
ms2 = c2.get(Calendar.MILLISECOND);
if(ms2 < ms1)
monthDiff--;
// else // it's a full month yeah
}
}
}
}
return monthDiff;
}
So many answers with long code when you can just do it with 1 line and some math:
LocalDate from = yourdate;
LocalDate to = yourotherdate;
int difference = to.getMonthValue() - from.getMonthValue()) + ((to.getYear() - from.getYear()) * 12) + 1;