Analog of ORACLE function MONTHS_BETWEEN in Java - java

Does Java have some analog of Oracle's function MONTHS_BETWEEN?

I've run into the same need and started from #alain.janinm answer which is good but doesn't give the exact same result in some cases.
ex :
Consider months between 17/02/2013 and 11/03/2016 ("dd/MM/yyyy")
Oracle result : 36,8064516129032
Java method from #Alain.janinm answer : 36.74193548387097
Here's the changes i made, to get a closer result to Oracle's months_between() function :
public static double monthsBetween(Date startDate, Date endDate){
Calendar cal = Calendar.getInstance();
cal.setTime(startDate);
int startDayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
int startMonth = cal.get(Calendar.MONTH);
int startYear = cal.get(Calendar.YEAR);
cal.setTime(endDate);
int endDayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
int endMonth = cal.get(Calendar.MONTH);
int endYear = cal.get(Calendar.YEAR);
int diffMonths = endMonth - startMonth;
int diffYears = endYear - startYear;
int diffDays = endDayOfMonth - startDayOfMonth;
return (diffYears * 12) + diffMonths + diffDays/31.0;
}
With this function the result of the call for the dates 17/02/2013 and 11/03/2016 is : 36.806451612903224
Note : From my understanding Oracle's months_between() function considers that all months are 31 days long

You can do that with :
public static int monthsBetween(Date minuend, Date subtrahend){
Calendar cal = Calendar.getInstance();
cal.setTime(minuend);
int minuendMonth = cal.get(Calendar.MONTH);
int minuendYear = cal.get(Calendar.YEAR);
cal.setTime(subtrahend);
int subtrahendMonth = cal.get(Calendar.MONTH);
int subtrahendYear = cal.get(Calendar.YEAR);
return ((minuendYear - subtrahendYear) * (cal.getMaximum(Calendar.MONTH)+1)) +
(minuendMonth - subtrahendMonth);
}
Edit :
According to this documentation MONTHS_BETWEEN return a fractional result, I think this method do the same :
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date d = sdf.parse("02/02/1995");
Date d2 = sdf.parse("01/01/1995");
System.out.println(monthsBetween(d, d2));
}
public static double monthsBetween(Date baseDate, Date dateToSubstract){
Calendar cal = Calendar.getInstance();
cal.setTime(baseDate);
int baseDayOfYear = cal.get(Calendar.DAY_OF_YEAR);
int baseMonth = cal.get(Calendar.MONTH);
int baseYear = cal.get(Calendar.YEAR);
cal.setTime(dateToSubstract);
int subDayOfYear = cal.get(Calendar.DAY_OF_YEAR);
int subMonth = cal.get(Calendar.MONTH);
int subYear = cal.get(Calendar.YEAR);
//int fullMonth = ((baseYear - subYear) * (cal.getMaximum(Calendar.MONTH)+1)) +
//(baseMonth - subMonth);
//System.out.println(fullMonth);
return ((baseYear - subYear) * (cal.getMaximum(Calendar.MONTH)+1)) +
(baseDayOfYear-subDayOfYear)/31.0;
}

I had to migrate some Oracle code to java and haven't found the analog for months_between oracle function. While testing listed examples found some cases when they produce wrong results.
So, created my own function. Created 1600+ tests comparing results of db vs my function, including dates with time component - all work fine.
Hope, this can help someone.
public static double oracle_months_between(Timestamp endDate,Timestamp startDate) {
//MONTHS_BETWEEN returns number of months between dates date1 and date2.
// If date1 is later than date2, then the result is positive.
// If date1 is earlier than date2, then the result is negative.
// If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer.
// Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endDateString = sdf.format(endDate), startDateString = sdf.format(startDate);
int startDateYear = Integer.parseInt(startDateString.substring(0,4)), startDateMonth = Integer.parseInt(startDateString.substring(5,7)), startDateDay = Integer.parseInt(startDateString.substring(8,10));
int endDateYear = Integer.parseInt(endDateString.substring(0,4)), endDateMonth = Integer.parseInt(endDateString.substring(5,7)), endDateDay = Integer.parseInt(endDateString.substring(8,10));
boolean endDateLDM = is_last_day(endDate), startDateLDM = is_last_day(startDate);
int diffMonths = -startDateYear*12 - startDateMonth + endDateYear * 12 + endDateMonth;
if (endDateLDM && startDateLDM || extract_day(startDate) == extract_day(endDate)){
// If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer.
return (double)(diffMonths);
}
double diffDays = (endDateDay - startDateDay)/31.;
Timestamp dStart = Timestamp.valueOf("1970-01-01 " + startDateString.substring(11)), dEnd = Timestamp.valueOf("1970-01-01 " + endDateString.substring(11));
return diffMonths + diffDays + (dEnd.getTime()-dStart.getTime())/1000./3600./24./31.;
}
public static boolean is_last_day(Timestamp ts){
Calendar calendar = Calendar.getInstance();
calendar.setTime(ts);
int max = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
return max == Integer.parseInt((new SimpleDateFormat("dd").format(ts)));
}

Actually, I think the correct implementation is this one:
public static BigDecimal monthsBetween(final Date start, final Date end, final ZoneId zone, final int scale ) {
final BigDecimal no31 = new BigDecimal(31);
final LocalDate ldStart = start.toInstant().atZone(zone).toLocalDate();
final LocalDate ldEnd = end.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
final int endDay = ldEnd.getDayOfMonth();
final int endMonth = ldEnd.getMonthValue();
final int endYear = ldEnd.getYear();
final int lastDayOfEndMonth = ldEnd.lengthOfMonth();
final int startDay = ldStart.getDayOfMonth();
final int startMonth = ldStart.getMonthValue();
final int startYear = ldStart.getYear();
final int lastDayOfStartMonth = ldStart.lengthOfMonth();
final BigDecimal diffInMonths = new BigDecimal((endYear - startYear)*12+(endMonth-startMonth));
final BigDecimal fraction;
if(endDay==startDay || (endDay==lastDayOfEndMonth && startDay==lastDayOfStartMonth)) {
fraction = BigDecimal.ZERO;
}
else {
fraction = BigDecimal.valueOf(endDay-startDay).divide(no31, scale, BigDecimal.ROUND_HALF_UP);
}
return diffInMonths.add(fraction);
}
public static BigDecimal monthsBetween(final Date start, final Date end) {
return monthsBetween(start, end, ZoneId.systemDefault(), 20);
}

In Joda Time there is a monthsBetween in the org.joda.time.Months class.

I've the same problem and following the Oracle MONTHS_BETWEEN I have made some changes to #alain.janinm and #Guerneen4 answers in order to correct some cases:
Consider months between 31/07/1998 and 30/09/2013 ("dd/MM/yyyy") Oracle result : 182 Java method from #Guerneen4 answer : 181.96774193548387
The problem is that according to specification if date1 and date2 are both last days of months, then the result is always an integer.
For easy understanding here you can find Oracle MONTHS_BETWEEN specifications: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions089.htm. I copy here to summarize:
"returns number of months between dates date1 and date2. If date1 is later than date2, then the result is positive. If date1 is earlier than date2, then the result is negative. If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer. Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2."
Here's the changes that I've done get the closest result to the Oracle's months_between() function :
public static double monthsBetween(Date startDate, Date endDate) {
Calendar calSD = Calendar.getInstance();
Calendar calED = Calendar.getInstance();
calSD.setTime(startDate);
int startDayOfMonth = calSD.get(Calendar.DAY_OF_MONTH);
int startMonth = calSD.get(Calendar.MONTH);
int startYear = calSD.get(Calendar.YEAR);
calED.setTime(endDate);
int endDayOfMonth = calED.get(Calendar.DAY_OF_MONTH);
int endMonth = calED.get(Calendar.MONTH);
int endYear = calED.get(Calendar.YEAR);
int diffMonths = endMonth - startMonth;
int diffYears = endYear - startYear;
int diffDays = calSD.getActualMaximum(Calendar.DAY_OF_MONTH) == startDayOfMonth
&& calED.getActualMaximum(Calendar.DAY_OF_MONTH) == endDayOfMonth ? 0 : endDayOfMonth - startDayOfMonth;
return (diffYears * 12) + diffMonths + diffDays / 31.0;
}

java.time
The other Answers use the troublesome old Calendar class that is now legacy, supplanted by the java.time classes.
MONTHS_BETWEEN
The doc says:
MONTHS_BETWEEN returns number of months between dates date1 and date2. If date1 is later than date2, then the result is positive. If date1 is earlier than date2, then the result is negative. If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer. Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
Retrieve a LocalDate from the database using JDBC 4.2 and later. The java.sql.Date class is now legacy, and can be avoided.
LocalDate start = myResultSet.getObject( … , LocalDate.class ) ; // Retrieve a `LocalDate` from database using JDBC 4.2 and later.
For our demo here, let’s simulate those retrieved dates.
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 23 );
LocalDate stop = start.plusDays( 101 );
Period
Calculate the elapsed time as a span of time unattached to the timeline, a Period.
Period p = Period.between( start , stop );
Extract the total number of months.
long months = p.toTotalMonths() ;
Extract the number of days part, the days remaining after calculating the months.
int days = p.getDays() ;
BigDecimal
For accuracy, use BigDecimal. The double and Double types use floating-point technology, trading away accuracy for fast execution performance.
Convert our values from primitives to BigDecimal.
BigDecimal bdDays = new BigDecimal( days );
BigDecimal bdMaximumDaysInMonth = new BigDecimal( 31 );
Divide to get our fractional month. The MathContext provides a limit to resolving the fractional number, plus a rounding mode to get there. Here we use the constant MathContext.DECIMAL32, because I am guessing the Oracle function is using 32-bit math. The rounding mode is RoundingMode.HALF_EVEN, the default specified by IEEE 754, and also known as “Banker’s rounding” which is more mathematically fair than “schoolhouse rounding” commonly taught to children.
BigDecimal fractionalMonth = bdDays.divide( bdMaximumDaysInMonth , MathContext.DECIMAL32 );
Add this fraction to our number of whole months, for a complete result.
BigDecimal bd = new BigDecimal( months ).add( fractionalMonth );
To more closely emulate the behavior of the Oracle function, you may want to convert to a double.
double d = bd.round( MathContext.DECIMAL32 ).doubleValue();
Oracle did not document the gory details of their calculation. So you may need to do some trial-and-error experimentation to see if this code has results in line with your Oracle function.
Dump to console.
System.out.println( "From: " + start + " to: " + stop + " = " + bd + " months, using BigDecimal. As a double: " + d );
See this code run live at IdeOne.com.
From: 2018-01-23 to: 2018-05-04 = 3.3548387 months, using BigDecimal. As a double: 3.354839
Caveat: While I answered the Question as asked, I must remark: Tracking elapsed time as a fraction as seen here is unwise. Instead use the java.time classes Period and Duration. For textual representation, use the standard ISO 8601 format: PnYnMnDTnHnMnS. For example, the Period seen in our example above: P3M11D for three months and eleven days.
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.

The previous answers are not perfect because they do not handle dates such as Feb 31.
Here is my iterative interpretation of MONTHS_BETWEEN in Javascript...
// Replica of the Oracle function MONTHS_BETWEEN where it calculates based on 31-day months
var MONTHS_BETWEEN = function(d1, d2) {
// Don't even try to calculate if it's the same day
if (d1.getTicks() === d2.getTicks()) return 0;
var totalDays = 0;
var earlyDte = (d1 < d2 ? d1 : d2); // Put the earlier date in here
var laterDate = (d1 > d2 ? d1 : d2); // Put the later date in here
// We'll need to compare dates using string manipulation because dates such as
// February 31 will not parse correctly with the native date object
var earlyDteStr = [(earlyDte.getMonth() + 1), earlyDte.getDate(), earlyDte.getFullYear()];
// Go in day-by-day increments, treating every month as having 31 days
while (earlyDteStr[2] < laterDate.getFullYear() ||
earlyDteStr[2] == laterDate.getFullYear() && earlyDteStr[0] < (laterDate.getMonth() + 1) ||
earlyDteStr[2] == laterDate.getFullYear() && earlyDteStr[0] == (laterDate.getMonth() + 1) && earlyDteStr[1] < laterDate.getDate()) {
if (earlyDteStr[1] + 1 < 32) {
earlyDteStr[1] += 1; // Increment the day
} else {
// If we got to this clause, then we need to carry over a month
if (earlyDteStr[0] + 1 < 13) {
earlyDteStr[0] += 1; // Increment the month
} else {
// If we got to this clause, then we need to carry over a year
earlyDteStr[2] += 1; // Increment the year
earlyDteStr[0] = 1; // Reset the month
}
earlyDteStr[1] = 1; // Reset the day
}
totalDays += 1; // Add to our running sum of days for this iteration
}
return (totalDays / 31.0);
};

Related

"is this date the third thursday of the month?" - Java Library?

I've got a few dozen backlog requests in the pipeline like
'I need this functionality to run on the third Thursday of every month, and the first Wednesday of every other month...'
I've already got a function that runs every day, i just need the: isThirdSundayOfMonth(date) bit to append onto then end.
The less time I spend considering the nuances of the Gregorian calendar and timezones, the better my life is.
Anyone know a Java library that simplifies this sort of calculation? No xml config or frameworks or anything. Just a .Jar and a documented, readable API would be perfect.
Any help would be much appreciated.
Complete overview:
In Java-8 (new standard):
LocalDate input = LocalDate.now(); // using system timezone
int ordinal = 3;
DayOfWeek weekday = DayOfWeek.SUNDAY;
LocalDate adjusted =
input.with(TemporalAdjusters.dayOfWeekInMonth(ordinal, weekday));
boolean isThirdSundayInMonth = input.equals(adjusted);
In Joda-Time (popular 3rd-party-library):
LocalDate input = new LocalDate(); // using system timezone
int ordinal = 3;
int weekday = DateTimeConstants.SUNDAY;
LocalDate start = new LocalDate(input.getYear(), input.getMonthOfYear(), 1);
LocalDate date = start.withDayOfWeek(weekday);
LocalDate adjusted = (
date.isBefore(start))
? date.plusWeeks(ordinal)
: date.plusWeeks(ordinal - 1);
boolean isThirdSundayInMonth = input.equals(adjusted);
Using java.util.GregorianCalendar (old standard):
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
GregorianCalendar input = new GregorianCalendar();
int ordinal = 3;
int weekday = Calendar.SUNDAY;
GregorianCalendar start =
new GregorianCalendar(input.get(Calendar.YEAR), input.get(Calendar.MONTH), 1);
int dow = start.get(Calendar.DAY_OF_WEEK); // Sun=1, Mon=2, ...
int delta = (weekday - dow);
if (delta < 0) {
delta += 7;
}
start.add(Calendar.DAY_OF_MONTH, delta + (ordinal - 1) * 7);
String comp1 = sdf.format(input.getTime());
String comp2 = sdf.format(start.getTime());
boolean isThirdSundayInMonth = comp1.equals(comp2);
Even with the ugliest library a solution is possible ;-) I have used a string comparison in order to get rid of any timezone effects or time-of-day-parts including milliseconds. A field-wise comparison based only on year, month and day-of-month is also a good idea.
Using Time4J (my own 3rd-party-library):
PlainDate input =
SystemClock.inLocalView().today(); // using system timezone
Weekday weekday = Weekday.SUNDAY;
PlainDate adjusted =
input.with(PlainDate.WEEKDAY_IN_MONTH.setToThird(weekday));
boolean isThirdSundayInMonth = input.equals(adjusted);
The canonical library for all things date and time related is Joda Time. Adopt that and purge all the standard java classes like Date, Calendar, etc.
It will make your life much better.
As for "How do I use joda-time to find the third Thursday of the month", there's a stackoverflow answer for that already. I'd suggest using the code that the question asker posted and then the question "is it now the third Thursday of the month" is answered by:
LocalDate today = new LocalDate();
if (today.equals(calcDayOfWeekOfMonth(DateTimeConstants.THURSDAY, 3, today))) {
// do special third-Thursday processing here
}

Java: when setting a date in a Calendar object, can I use day/month/year references?

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.

Java, get days between two dates

In java, I want to get the number of days between two dates, excluding those two dates.
For example:
If first date = 11 November 2011 and the second date = 13 November 2011
then it should be 1.
This is the code I am using but doesn't work (secondDate and firstDate are Calendar objects):
long diff=secondDate.getTimeInMillis()-firstDate.getTimeInMillis();
float day_count=(float)diff / (24 * 60 * 60 * 1000);
daysCount.setText((int)day_count+"");
I even tried rounding the results but that didn't help.
How do I get the number of days between dates in java excluding the days themselves?
I've just tested on SDK 8 (Android 2.2) the following code snippet:
Calendar date1 = Calendar.getInstance();
Calendar date2 = Calendar.getInstance();
date1.clear();
date1.set(
datePicker1.getYear(),
datePicker1.getMonth(),
datePicker1.getDayOfMonth());
date2.clear();
date2.set(
datePicker2.getYear(),
datePicker2.getMonth(),
datePicker2.getDayOfMonth());
long diff = date2.getTimeInMillis() - date1.getTimeInMillis();
float dayCount = (float) diff / (24 * 60 * 60 * 1000);
textView.setText(Long.toString(diff) + " " + (int) dayCount);
it works perfectly and in both cases (Nov 10,2011 - Nov 8,2011) and (Nov 13,2011 - Nov 11,2011) gives dayCount = 2.0
Get Days between java.util.Dates, ignoring daylight savings time
Quick and dirty hack:
public int get_days_between_dates(Date date1, Date date2)
{
//if date2 is more in the future than date1 then the result will be negative
//if date1 is more in the future than date2 then the result will be positive.
return (int)((date2.getTime() - date1.getTime()) / (1000*60*60*24l));
}
This function will work 99.99% of the time, except when it surprises you later on in the edge cases during leap-seconds, daylight savings, timezone changes leap years and the like. If you are OK with the calculation being off by 1 (or 2) hours once in a while, this will suffice.
Get Days between Dates taking into account leapseconds, daylight savings, timezones, etc
If you are asking this question you need to slap yourself. What does it mean for two dates to be at least 1 day apart? It's very confusing. What if one Date is midnight in one timezone, and the other date is 1AM in another timezone? Depending on how you interpret it, the answer is both 1 and 0.
You think you can just force the dates you pass into the above function as Universal time format; that will fix some of your problems. But then you just relocate the problem into how you convert your local time to a universal time. The logical conversion from your timezone to universal time may not be what is intuitive. In some cases you will get a day difference when the dates passed in are obviously two days apart.
And you think you can deal with that? There are some simplistic calendar systems in the world which are constantly changing depending on the harvest season and installed political rulers. If you want to convert their time to UTC, java.util.Date is going to fail you at the worst moment.
If you need to calculate the days between dates and it is critical that everything come out right, you need to get an external library called Joda Time: (They have taken care of all the details for you, so you can stay blissfully unaware of them): http://joda-time.sourceforge.net/index.html
java.time
The java.time API, released with Java-8 in March 2014, supplanted the error-prone legacy date-time API. Since then, using this modern date-time API has been strongly recommended.
Solution using modern date-time API
Using Calendar#toInstant, convert your java.util.Calendar instances into java.time.Instant and then into java.time.ZonedDateTime instances and then use ChronoUnit.DAYS.between to get the number of days between them.
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
// Sample start and end dates as java.util.Date
Calendar startCal = Calendar.getInstance();
startCal.set(2011, 10, 11); // 11 November 2011
Calendar endCal = Calendar.getInstance();
endCal.set(2011, 10, 13); // 13 November 2011
// Convert the java.util.Calendar into java.time.ZonedDateTime
// Replace ZoneId.systemDefault() with the applicable ZoneId
ZonedDateTime startDateTime = startCal.toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime endDateTime = endCal.toInstant().atZone(ZoneId.systemDefault());
// The end date is excluded by default. Subtract 1 to exclude the start date
long days = ChronoUnit.DAYS.between(startDateTime, endDateTime) - 1;
System.out.println(days);
}
}
Output:
1
Learn more about the modern Date-Time API from Trail: Date Time.
Don't use floats for integer calculations.
Are you sure your dates are days? The precision of the Date type is milliseconds. So the first thing you need to do is round the date to something which doesn't have hours. Example: It's just one hour from 23:30 2011-11-01 to 00:30 2011-11-02 but the two dates are on different days.
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);
if (year < 1900 || year > 2099) {
throw new IllegalArgumentException("daysSince1900 - Date must be between 1900 and 2099");
}
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, to get the difference in days between two dates, you calculate their days since 1900 and calc the difference. Our daysBetween method looks like this:
public static Integer getDaysBetween(Date date1, Date date2) {
if (date1 == null || date2 == null) {
return null;
}
int days1 = daysSince1900(date1);
int days2 = daysSince1900(date2);
if (days1 < days2) {
return days2 - days1;
} else {
return days1 - days2;
}
}
In your case you would need to subtract an extra day (if the days are not equal).
And don't ask me where this calculation came from because we've used it since the early '90s.
I have two suggestions:
Make sure your float day_count is calculated correctly
float day_count = ((float)diff) / (24f * 60f * 60f * 1000f);
If it's rounding error, try using floor method
daysCount.setText("" + (int)Math.floor(day_count));

How to write a method that returns number (int) of days from provided day to the todays date?

Please help me to write a method that returns number (int) of days from a provided day to the todays date.
So let's say, I am providing into a method an int 110515 (for May 15, 2011). It should return 9 (inclusive or exclusive is not important to me).
If you can use Joda, this is super simple:
Days d = Days.daysBetween(startDate, endDate);
int days = d.getDays();
Of course you could combine these.
int days = Days.daysBetween(startDate, endDate).getDays();
Joda objects can go back and forth between the JDK's date class pretty easily.
For the first part, make a DateFormatter then parse the string based on it, like this:
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyyMMdd");
DateTime dt = fmt.parseDateTime(strInputDateTime);
(After turning the int into a string of course.)
Should dates in the future include the current day? Meaning if today is May 24th 2011, should 110529 result in 4 or 5?
public static long numberOfDays(final long date) throws ParseException {
final Calendar compare = Calendar.getInstance();
compare.setTime(new SimpleDateFormat("yyMMdd").parse(String.valueOf(date)));
final int dstOffset = compare.get(Calendar.DST_OFFSET);
final long currentTimeMillis = System.currentTimeMillis();
final long compareTimeInMillis = compare.getTimeInMillis();
long difference = 0;
if (currentTimeMillis >= compareTimeInMillis) {
difference = currentTimeMillis - compareTimeInMillis - dstOffset;
} else {
difference = compareTimeInMillis - currentTimeMillis + dstOffset;
}
return difference / (24 * 60 * 60 * 1000);
}
Since this seems like a homework question I will help you out. You will want to use Calendar.getTimeInMillis. Then you will want to create a constant that is NUMBER_OF_MILLIS_IN_DAY . From there you subtract the initialDate from the currentDate (both time in millis) and divide by the constant.

Java Date month difference

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;

Categories