Java Date month difference - java

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;

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.

How to know if now time is between two hours?

I have a now time:
new Date();
And I have some hour constants, for example, 23 and 8 (it's 11pm or 23:00, 8am or 08:00).
How I can know is now time between it's two hour constants?
It need to run some code of program or not to run if now time is between in two hours, for example, do not run some code if its already evening and while it is not a morning.
Here the image to better explain:
Some situations when silent mode does not fire:
00:00 20.06.13 - 23:00 20.06.13 // after 23.00 can loud!!
23:00 20.06.13 - 15:00 20.06.13 // after 15.00 can loud!!
01:00 20.06.13 - 08:00 20.06.13 // after 08.00 can loud!!
21:00 20.06.13 - 08:00 20.06.13 // after 08.00 can loud!!
try this
int from = 2300;
int to = 800;
Date date = new Date();
Calendar c = Calendar.getInstance();
c.setTime(date);
int t = c.get(Calendar.HOUR_OF_DAY) * 100 + c.get(Calendar.MINUTE);
boolean isBetween = to > from && t >= from && t <= to || to < from && (t >= from || t <= to);
Calendar cal = Calendar.getInstance(); //Create Calendar-Object
cal.setTime(new Date()); //Set the Calendar to now
int hour = cal.get(Calendar.HOUR_OF_DAY); //Get the hour from the calendar
if(hour <= 23 && hour >= 8) // Check if hour is between 8 am and 11pm
{
// do whatever you want
}
java.time
The modern way is with the java.time classes built into Java 8 and later. Much of the functionality is back-ported to Java 6 & 7 in the ThreeTen-Backport project and further adapted to Android in ThreeTenABP project.
Time zone is crucial here. For any given moment, the date and time-of-day both vary around the world by zone.
ZoneId z = ZoneId.of( "America/Montreal" );
Get your current moment.
ZonedDateTime zdt = ZonedDateTime.now( z );
Extract the time-of-day. The Local part of the name means there is no concept of time zone contained within the object.
LocalTime lt = zdt.toLocalTime();
Define the limits of the evening.
LocalTime start = LocalTime.of( 23 , 0 ); // 11 PM.
LocalTime stop = LocalTime.of( 8 , 0 ); // 8 AM.
Compare.
We need to figure out if we are straddling over a new day or within the same day. A LocalTime has no concept of date, only a single generic day of 24 hours. So we must test if the start is before or after the stop as we need different comparison algorithm for each case. And we should consider if the start equals the stop, as that may be a special case depending on your business rules.
In date-time work, we usually define spans of time as Half-Open, where the beginning is inclusive while the ending is exclusive.
Here's one way to do it.
Boolean silentRunning = null ;
if( start.equals( stop ) ) {
silentRunning = Boolean.FALSE ;
} else if( stop.isAfter( start ) ) { // Example 3 PM to 6 PM.
silentRunning = ( ! lt.isBefore( start ) ) && lt.isBefore( stop ) ;
} else if ( stop.isBefore( start ) ) { // Example 11 PM to 8 AM.
silentRunning = ( lt.equals( start ) || lt.isAfter( start ) ) && lt.isBefore( stop ) ;
} else {
// Error. Should not reach this point. Paranoid check.
}
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 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….
UPDATE: The above is a later version of this Answer. Below is the old.
Joda-Time
The Joda-Time library is vastly superior to the java.util.Date and .Calendar classes for date-time work.
Time zone is crucial for determine the time of day. Obviously "now" is later in the day in Paris than Montréal.
Definig a range of time is usually best done as half-open, [), where the beginning is inclusive but the ending is exclusive.
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime now = DateTime.now( zone );
Integer hour = now.getHourOfDay();
Boolean isNight = ( ( hour >= 23 ) && ( hour < 8 ) );
I think that this is more cleaner solution and it`s works. I have tested it with different time parameters.
/**
* #param fromHour Start Time
* #param toHour Stop Time
* #param now Current Time
* #return true if Current Time is between fromHour and toHour
*/
boolean isTimeBetweenTwoHours(int fromHour, int toHour, Calendar now) {
//Start Time
Calendar from = Calendar.getInstance();
from.set(Calendar.HOUR_OF_DAY, fromHour);
from.set(Calendar.MINUTE, 0);
//Stop Time
Calendar to = Calendar.getInstance();
to.set(Calendar.HOUR_OF_DAY, toHour);
to.set(Calendar.MINUTE, 0);
if(to.before(from)) {
if (now.after(to)) to.add(Calendar.DATE, 1);
else from.add(Calendar.DATE, -1);
}
return now.after(from) && now.before(to);
}
You can see a tutorial here with Date.before and you can do with Date.after
Also you can get his milliseconds and compare it.
here is a function that checks is now(current time) is either between
1 to 4 OR
4 to 8 OR
8 to 12 OR
12 to 16 OR
16 to 20 OR
20 to 1 And returns next accuring time.
private Calendar GetTimeDiff() throws ParseException {
Calendar now = Calendar.getInstance();
Calendar one = Calendar.getInstance();
one.set(Calendar.HOUR_OF_DAY, 1);
one.set(Calendar.MINUTE, 0);
one.set(Calendar.SECOND, 0);
Calendar four = Calendar.getInstance();
four.set(Calendar.HOUR_OF_DAY, 4);
four.set(Calendar.MINUTE, 0);
four.set(Calendar.SECOND, 0);
Calendar eight = Calendar.getInstance();
eight.set(Calendar.HOUR_OF_DAY, 8);
eight.set(Calendar.MINUTE, 0);
eight.set(Calendar.SECOND, 0);
Calendar twelve = Calendar.getInstance();
twelve.set(Calendar.HOUR_OF_DAY, 12);
twelve.set(Calendar.MINUTE, 0);
twelve.set(Calendar.SECOND, 0);
Calendar sixteen = Calendar.getInstance();
sixteen.set(Calendar.HOUR_OF_DAY, 16);
sixteen.set(Calendar.MINUTE, 0);
sixteen.set(Calendar.SECOND, 0);
Calendar twenty = Calendar.getInstance();
twenty.set(Calendar.HOUR_OF_DAY, 20);
twenty.set(Calendar.MINUTE, 0);
twenty.set(Calendar.SECOND, 0);
if(now.getTime().after(one.getTime()) && now.getTime().before(four.getTime())) {
return four;
}
if(now.getTime().after(four.getTime()) && now.getTime().before(eight.getTime())) {
return eight;
}
if(now.getTime().after(eight.getTime()) && now.getTime().before(twelve.getTime())) {
return twelve;
}
if(now.getTime().after(twelve.getTime()) && now.getTime().before(sixteen.getTime())) {
return sixteen;
}
if(now.getTime().after(sixteen.getTime()) && now.getTime().before(twenty.getTime())) {
return twenty;
}
if(now.getTime().after(twenty.getTime()) && now.getTime().before(one.getTime())) {
return one;
}
return now;
}
PHP Solution
I wasn't able to find a solution for this in PHP, but #sytolk answer helped. heres the PHP version.
// $current = Date('H:i:s');
$current = "01:00:00";
$start = "23:00:00";
$end = "02:00:00";
$current = DateTime::createFromFormat('H:i:s', $current);
$start = DateTime::createFromFormat('H:i:s', $start);
$end = DateTime::createFromFormat('H:i:s', $end);
if ($end < $start) {
if ($current > $end) {
$end->modify('+1 day');
} else {
$start->modify('-1 day');
}
}
$inTime = $current > $start && $current < $end;
You could also convert your input string to an integer and compare it against your constants. This way you don't even need to work with the Calendar and Date objects.
public class testDateRange {
static final int START_HOUR = 8;
static final int END_HOUR = 23;
public static void main(String[] args) {
String now_time = new SimpleDateFormat("HH:mm").format(new Date());
System.err.println(isInRange(Integer.parseInt(now_time.replace(":","")),START_HOUR*100,END_HOUR*100));
}
private static boolean isInRange(int now_time, int start_time, int end_time) {
if ((now_time>start_time)&&
(now_time<end_time) )
{
return true;
}
return false;
}
}

Analog of ORACLE function MONTHS_BETWEEN in 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);
};

pointing to previous working day using java

I am new with using java.calendar.api.
I want to point to the previous working day for a given day using java.
BUT the conditions goes on increasing when i am using calendar.api to manipulate dates
since I had to consider the usual weekends and the pointing to the previous month and also i had to consider the regional holidays in my region......
for ex:say i had to consider the U.S holidays and point to the day before that.
Is there any way i can define my own calendar and use it so that date manipulation senses all those usual changes?
While you should consider using the Joda Time library, here's a start with the Java Calendar API:
public Date getPreviousWorkingDay(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int dayOfWeek;
do {
cal.add(Calendar.DAY_OF_MONTH, -1);
dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
} while (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY);
return cal.getTime();
}
This only considers weekends. You'll have to add additional checks to handle days you consider holidays. For instance you could add || isHoliday(cal) to the while condition. Then implement that method, something like:
public boolean isHoliday(Calendar cal) {
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
if (month == 12 && dayOfMonth == 25) {
return true;
}
// more checks
return false;
}
tl;dr
LocalDate.now ( ZoneId.of ( "America/Montreal" ) )
.with ( org.threeten.extra.Temporals.previousWorkingDay () )
java.time
Java 8 and later has the java.time framework built-in. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.
These new classes replace the notoriously troublesome old date-time classes bundled with the earliest versions of Java, java.util.Date/.Calendar. Avoid the old classes where possible. When you must interface look for newly added conversion methods to switch into java.time for most of your work. Also, the makers of Joda-Time have told us to move to java.time as soon as is convenient.
Basics of java.time… An Instant is a moment on the timeline in UTC. Apply a time zone (ZoneId) to get a ZonedDateTime. For a date-only value without a time-of-day nor a time zone, use LocalDate.
First we get "today" as an example date value. Note how a time zone is required in order to determine the current date even though a LocalDate does not contain a time zone. The date is not simultaneously the same around the globe, as a new day dawns earlier in the east.
LocalDate today = LocalDate.now ( ZoneId.of ( "America/Los_Angeles" ) );
Adjustors
The ThreeTen-Extra project extends java.time with additional or experimental features. These features may or may not eventually be folded into java.time proper. This project provides a Temporals class which provides implementations of adjustors including a couple for nextWorkingDay and previousWorkingDay. Easy to use as seen here.
// The 'Temporals' class is from the ThreeTen-Extra library, not built into Java.
LocalDate previousWorkingDay = today.with ( Temporals.previousWorkingDay () );
LocalDate nextWorkingDay = today.with ( Temporals.nextWorkingDay () );
When Run
Dump to console. Notice how today is a Friday, so the previous working day is -1 (yesterday, Thursday) and the next working day is +3 (Monday).
System.out.println ( "today: " + today + " | previousWorkingDay: " + previousWorkingDay + " | nextWorkingDay: " + nextWorkingDay );
today: 2016-01-22 | previousWorkingDay: 2016-01-21 | nextWorkingDay: 2016-01-25
Saturday & Sunday
This pair of adjustors simply skips over every Saturday and Sunday. It knows nothing of holidays. Nor does it know about other definitions of the working week and weekend. The class documentation suggests writing your own java.time.temporal.TemporalAdjuster is easy if you want to handle other definitions.
Consider using Joda Time combined with a list of regional holidays in your region.
You may define a class as below:
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*
* #author j.james
*/
public class MyCalendar {
private static Map<String, String> holidays = null;
private static MyCalendar myCalendar = null;
private static final int WEEKEND_1 = Calendar.SATURDAY;
private static final int WEEKEND_2 = Calendar.SUNDAY;
private MyCalendar() {
holidays = new HashMap<String, String>();
holidays.put("7,4", "Independence Day");
holidays.put("12,25", "Christmas");
//holidays.putAll(DBUtils.readAnyDynamicHolidaysFromDB());
}
public static Date getPreviousWorkingDay(Date date) {
Date previousWorkingDate = null;
try {
if (myCalendar == null) {
myCalendar = new MyCalendar();
}
if(date != null) {
Calendar calInstance = Calendar.getInstance();
calInstance.setTime(date);
int weekDay = 0;
do {
calInstance.add(Calendar.DATE, -1);
weekDay = calInstance.get(Calendar.DAY_OF_WEEK);
} while(weekDay == WEEKEND_1 || weekDay == WEEKEND_2 ||
holidays.get((calInstance.get(Calendar.MONTH) + 1)
+ "," + calInstance.get(Calendar.DATE)) != null);
previousWorkingDate = calInstance.getTime();
}
} catch (Exception e) {
e.printStackTrace();
}
return previousWorkingDate;
}
}
You can make a call as
public static void main(String[] args) {
System.out.println(MyCalendar.getPreviousWorkingDay(new Date(2011-1900,6,5))); //July 5, 2011 which returns July 1 as the working day because July 4th 2011 is Monday
}

Categories