Period between dates using Java/Joda - java

I have 2 joda dates as follows:
org.joda.time.DateTime a;
org.joda.time.DateTime b;
I want the difference between the 2 dates in terms of Years, months and days (eg. 0 years, 2 months, 5 days).
I can use the org.joda.time.Days.daysBetween(a, b) or monthsBetween(a, b) or yearsBetween(a, b) to get the whole values of each respectively.
Since a month does have number of fixed number of days, how can I calculate this?
Eg. If I get monthsbetween = 2 and daysbetween = 65, how can I write this as "2 months and x days"
Is there any other way to get this?

Try this:
Calendar ca = Calendar.getInstance();
ca.setTime(a);
Calendar cb = Calendar.getInstance();
cb.setTime(b);
System.out.printf("%d months and %d days"
, ca.get(Calendar.MONTH) - cb.get(Calendar.MONTH)
, ca.get(Calendar.DAY_OF_MONTH) - cb.get(Calendar.DAY_OF_MONTH));

Im not too familiar with Joda, but it looks like your best option is to divide the days left by 30.5, and then round it up back to a whole integer. Like so:
double daysDivided = daysbetween / 30.5;
int daysbetweenFixed = (int) daysDivided;
System.out.printf("%d months and %d days", monthsbetween, daysbetweenFixed);
//optional output ^
I'm sure you would know i chose 30.5 because it seems like the a good average month length, excluding February. Because there is no set length of a month this is the best we can do with only these integers.

Related

CodenameOne - fixing Calendar get(Calendar.DAY_OF_WEEK) bigger value than expected

I am creating a workaround to fix the SimpleDateFormat "clone" class of CN1 in my app.
I cannot use other classes from pure Java.
I used this instruction in my CN1 app
int dayNumber=c.get(Calendar.DAY_OF_WEEK);
for handling the u letter in the format string.
At the time of the creation of this post it's Wednesday
dayNumber happens to have value of 4.
So I replaced that instruction with
int dayNumber=c.get(Calendar.DAY_OF_WEEK)-1;
because I found in Oracle documentation
Day number of week (1 = Monday, ..., 7 = Sunday)
I would like to know if it is correct
so that I have the 7 days of the week covered so it is just as
(1 = Monday, ..., 7 = Sunday)
and I can have the right u value for Java and Android compatibility.
I understand that java.time, the modern Java date and time API, is not yet part of CodeName One, and that therefore you cannot use it in your case. Apart from special situations like yours no one should use Calendar since it is poorly designed and long outdated. Everyone should use java.time.
To get (1 = Monday, ..., 7 = Sunday) from Calendar (the numbers that you would get by default from java.time):
int dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
Seen under modulo 7 I am first adding 5, then 1, so 6 in total, which is the same as subtracting 1. I am using this trickery to make sure I get a number in the 1 through 7 interval (which we don’t always by simply subtracting 1 from the return value from Calendar).
I am demonstrating the edge cases and using java.time for it:
LocalDate ld = LocalDate.of(2021, Month.SEPTEMBER, 5);
ZonedDateTime startOfDay = ld.atStartOfDay(ZoneId.of("Etc/UTC"));
Calendar c = GregorianCalendar.from(startOfDay);
int dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
System.out.format("%s: %s = %d%n", ld, ld.getDayOfWeek(), dayNumber);
ld = ld.plusDays(1);
startOfDay = ld.atStartOfDay(ZoneId.of("Etc/UTC"));
c = GregorianCalendar.from(startOfDay);
dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
System.out.format("%s: %s = %d%n", ld, ld.getDayOfWeek(), dayNumber);
Output is:
2021-09-05: SUNDAY = 7
2021-09-06: MONDAY = 1
Disclaimer: I don’t know the CodeName One Calendar in particular. I strongly expect it to behave exactly the same as the original java.util.Calendar, and my answer is based on the assumption that it does.

Compare date Range by Month (Integer) and Year (Integer)

I'm having a problem comparing the date range. I have to validate dates that are within a certain month and year. The month and year are integer values.
NOTE: I´m using OUTSYSTEMS aggregates using Oracle DataBase
Example for two results of a query:
Start Date End Date
1 2020-08-16 2020-10-14
2 2019-11-01 2020-08-15
Case 1
Input:
Month = 9
Year = 2020
Expected Result:
Start Date End Date
1 2020-08-16 2020-10-14
Case 2
Input:
Month = 8
Year = 2020
Expected Result:
Start Date End Date
1 2020-08-16 2020-10-14
2 2019-11-01 2020-08-15
Case 3
Input:
Month = 3
Year = 2020
Expected Result:
Start Date End Date
2 2019-11-01 2020-08-15
Case 4
Input:
Month = 10
Year = 2019
Expected Result: No Row
The selection is in Java Way. I´m using a system function like Month() and Year() to convert the rows to the integers.
Like this
((Month(StartDate) <= Month and Month(EndDate) = Month)
and
(Year(StartDate) <= Year and Year(EndDate) = Year))
or
((Month(StartDate) <= Month and Month(EndDate) = Month)
and
(Year(StartDate) <= Year and Year(EndDate) = Year))
The code above won't work. I try many combinations without success. I have no special comparison functions. For my analysis, I have four scenarios to create to bring the dates that are included in the month and year that I am researching. But I'm not getting the code to work. Someone can light the way for me
A simple approach uses arithmetics:
where year * 100 + month
between year(startdate) * 100 + month(startdate)
and year(enddate) * 100 + month(enddate)
However this probably isn't the most efficient method. In general, you want to avoid applying functions on the column you filter on. A better alternative woul be to convert the year/month parameter to a date - unfortunately you did not tag your database, and date functions are highly vendor-specific, so it is not really possible to suggest.
If you don't want between:
where year * 100 + month >= year(startdate) * 100 + month(startdate)
and year * 100 + month <= year(enddate) * 100 + month(enddate)
Does this work? Considering your inputs m for month and y for year:
StartDate <= AddDays(AddMonths(NewDate(Year(y), Month(m), 1),1)-1)
and
EndDate >= NewDate(Year(y), Month(m), 1))
The thinking is like: filter by all start dates that are lower than the last day of input month and all the end dates that are greater than the first day of input month.
Regarding performance, with this approach you don't have to do any logic/filter on the columns you're filtering on.
The vendor-independent solution
The answer by GMB is nice, I might go with it if it were me. As GMB says, it is vendor specific because the date functions are. If you want a solution that works across database vendors, do the date math in Java so you only need simple date comparisons in the database.
int month = 8;
int year = 2020;
YearMonth ym = YearMonth.of(year, month);
LocalDate monthStart = ym.atDay(1);
LocalDate monthEnd = ym.atEndOfMonth();
When you pass these dates to your query, your search condition may be put simply:
where startDate <= monthEnd and endDate >= monthStart

How to calculate the number of days in a period?

For the following Period calculation:
Period.between(LocalDate.of(2015, 8, 1), LocalDate.of(2015, 9, 2))
the result is:
P1M1D
This is equivalent to 31 days + 1 day = 32 days.
For this Period:
Period.between(LocalDate.of(2015, 8, 1), LocalDate.of(2015, 10, 2))
the result is:
P2M1D
This is equivalent to: 31 days (in August) + 30 days (in September) + 1 (in October) = 62 days
Is there a method in the java.time package which will give the number of days in a Period? I can't find one. Not sure if I have overlooked anything or if it is just plain not there.
From the documentation:
To define an amount of time with date-based values (years, months,
days), use the Period class. The Period class provides various get
methods, such as getMonths, getDays, and getYears.To present the amount >of time measured in a single unit of time, such as days, you can use the
ChronoUnit.between method.
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.println("You are " + p.getYears() + " years, " + p.getMonths() +
" months, and " + p.getDays() +
" days old. (" + p2 + " days total)");
The code produces output similar to the following:
You are 53 years, 4 months, and 29 days old. (19508 days total)
There is no way to do what you ask. The reason is that it is not possible from a Period to deduce the actual number of calendar days in the period. A Period is not tied to specific dates, once constructed in the way you show, it loses track of the actual calendar dates.
For example your first period represents a period of 1 month and 1 day. But the period does not care which month. It is simply a concept of "a month and a day".
If you need the number of days between two dates you should use ChronoUnit.DAYS.between as Saket Mittal writes.
There's a specific object depending at the amount of time you'd like to deal with.
This page here is very useful explaining which is best for your scenario.
The ChronoUnit.between method is useful when you want to measure an amount of time in a single unit of time only, such as days or seconds
LocalDate localDateStartDate = LocalDate.of(2016, 06, 10);
LocalDate localDateEndDate = LocalDate.of(2016,06,23);
long days = ChronoUnit.DAYS.between(localDateStartDate, localDateEndDate);

Is there a way to have a Java8 duration of one year that accounts for leap years?

I need the number of days in a year and I wanted to use Java8's new time api.
However, I can't do Duration.ofDays(365) because it doesn't account for leap years. And Duration.of(1, ChronoUnit.YEARS) doesn't fly because of java.time.temporal.UnsupportedTemporalTypeException: Unit must not have an estimated duration
I looked into Period, but it doesn't appear useful for going from years to days.
I feel like I'm missing something here? I could write something to add a day if the year is a leap year, but it seems like I should be able to handle this out of the box.
As per the response in Getting Duration using the new dateTime API you should be using
Period p = Period.ofYears(1);
It's important to understand the difference between Duration (exact number of nanoseconds < 1 day) and Period (variable > 1 day).
Duration won't account for leap days, daylight savings time or leap seconds, for example, and is intended for durations of less than a day, at most a few days.
So you should use Period instead.
Because different years have different number of days, if you want to find the number of days in a year, you need to specify which year you're talking about.
If you want the number of days in a specific year, you can use
Year.of(year).length()
If you want the date one year from now, you can use
LocalDate.now().plusYears(1)
or
LocalDate.now().plus(Period.ofYears(1))
If you need the number of days between two dates, you can use
ChronoUnit.DAYS.between(start, end)
So to find the number of days to the date a year from now, you can use
LocalDate today = LocalDate.now();
long days = ChronoUnit.DAYS.between(today, today.plusYears(1));
If you want to see whether a membership of one year is still valid, you can use
Period membershipLength = Period.ofYears(1);
LocalDate membershipStart = ...;
LocalDate membershipEnd = membershipStart.plus(membershipLength);
LocalDate today = LocalDate.now();
boolean memberShipEnded = today.isAfter(membershipEnd);
boolean membershipValid = !membershipEnded;
It seems clear you do not want a duration (= between two dates), but the year length of a specific date.
LocalDate dateLeap = LocalDate.of(2004, Month.MARCH, 1);
System.out.println("leap year of " + dateLeap
+ " has days: " + dateLeap.lengthOfYear());
leap year of 2004-03-01 has days: 366
Java 8 Date & Time is astonishing complete.
If you mean, in January 5th 2004 to January 5th 2005 = 366 and March 2nd 2004 to March 2rd 2005 = 365:
int lengthOfYear(LocalDate date) {
return date.getMonthValue() <= 2
? date.lengthOfYear() // Count Feb in this year
: date.plusYears(1).lengthOfYear(); // Count Feb in next year
}
Explanation: basically the length is 365. But if date is >= March, the February in the next year is counted, otherwise this year's February.
Mind that plusYears(1) will not change DAY or MONTH.
Also neither leap second nor hour/minuts on February, 29th are considered.

how to find the total number of months between the two dates including extra days?

I have a requirement where I need to find out number of months between two dates including extra days.
example:
start date:01/01/2014
end date:21/02/2014
LocalDate startDate = new LocalDate(startDate1);
LocalDate endDate = new LocalDate(endDate1);
PeriodType monthDay =PeriodType.yearMonthDay().withYearsRemoved();
Period difference = new Period(startDate, endDate, monthDay);
int months = difference.getMonths();
int days = difference.getDays()
the out put I will get is:
months:1 days:20
but my requirement is I want get total months including that extra day.
like:1.66 months.
How to get this one in java?
In order to be able to say 1.66 months you need to define how long a month is. It's not always the same. If you assume that a month is 30 days long then you can solve this by using:
Date startDate = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2014");
Date endDate = new SimpleDateFormat("dd/MM/yyyy").parse("21/02/2014");
double result = (endDate.getTime() - startDate.getTime()) / (1000D*60*60*24*30);
This gives us 1.7 and if you divide with 31 you get 1.6451612903225807.
If you want a better (but not perfect) approximation of how long a month is you can try 365/12 which will give you 1.6767123287671233 but still this is not perfect because leap years have 366 days.
The problem though is not with the formula, but with the problem definition. Nobody in real life says "I'll be there in exactly 1.66 months" and nobody will ever ask you to convert 1.66 months in days.
This is my own answer, a variation on cherouvim's
final Date startDate = new GregorianCalendar (2014, 0, 1).getTime ();
final Date endDate = new GregorianCalendar (2014, 1, 21).getTime ();
System.out.println ((endDate.getTime () - startDate.getTime ()) / (float) (1000L * 60 * 60 * 24 * 30));

Categories

Resources