How to convert months to exact number of days in java - java

I have a requirement where I have to compare 2 variables. One is difference between 2 dates i.e., "Purchase Date" and "Date Call Received" which is coming as days. And the other one is Warranty length which is coming from UI as Months. Now, I am not able to compare these 2, as one is in months and other is in days. Could someone please help me how to convert months to days so that I can move forward.
for (ModelWarranty warr : modelWarranties)
{
if (null != warr.getWarrantyType()
&& warr.getWarrantyType().equals("WARR")
&& warr.getWarrantyPeriod().equals("0"))
//WARRANTY_PERIOD "0" means value from UI saves in DB as days
{
}
if (null != warr.getWarrantyType()
&& warr.getWarrantyType().equals("WARR")
&& warr.getWarrantyPeriod().equals("1"))
//WARRANTY_PERIOD "1" means value from UI saves in DB as months
{
Integer months = warr.getWarrantyLength();
//how to convert this months into days?
}
if (null != warr.getWarrantyType()
&& warr.getWarrantyType().equals("WARR")
&& warr.getWarrantyPeriod().equals("2"))
//WARRANTY_PERIOD "2" means value from UI saves in DB as years
{
Integer years = warr.getWarrantyLength();
//how to convert this years into days?
}
}

If all you’ve got are those two numbers, you can’t. Say you’ve got 29 days until call was received, and 1 month warranty length. The 29 days could be from January 15 to February 13, less than 1 month. Or they could be from February 15, 2018, to March 16, more than a month. You need to know the purchase date or something else to anchor your days and month to the calendar.
If that were me, I might hand code a conversion table that shows the maximum number of days in a certain number of months, so as to be sure always to give the customer the credit they are entitled to. 1 month can be 31 days. Two months may be 62 days (for example July and August). Three months cannot be more than 92 days (31 + 31 + 30). 12 months may be 366 days, but 24 months can only be 731 days since there are never two leap years in a row. Fill out the rest yourself, please.
Nerdy edit: I believe that you can build your conversion table by counting backward from January 2017, inclusive. So 1 month is January = 31 days. Two months are December 2016 + January 2017 = 31 + 31 = 62. Three months are November 2016 through January 2017. The trick about this way is: You get a group of two 31 days months first. You get two such groups as early as possible (July–August 2016 and December 2016–January 2017). You get the short month, February, as late as possible, and the first time you get it, it’s in a leap year (February 2016). Count up to 48 months. If the warranty is longer, say, 100 months, take that as 48 + 48 + 4 months, look those month counts up individually and sum. Because the leap year cycle is 48 months long (= 4 years). This is not always true, for example year 2100 will not be a leap year; but if the warranty cannot be longer than 199 years, the numbers you get will be correct.

In order to take in count that months can have 30/31/28 days, I would make the comparison in months in the next way:
double numMonths; //number of months you get from your program
double numDays; //number of days you get from your program for the second date
double daysInMonths = numDays * (12/365.25);
double difference = Math.abs(daysInMonths-numMonths);
That way, you can compare the date in months with the date in days without any kind of problem.
Note that a year has 365,25 days, exactly!

Related

How can I find all week numbers of month that are the # week of month in a year with Java?

I am looking to build up a function that return an array with all week numbers of the previous months in a year that are the same week number of one particular month.
I am using as first day of week Monday and I am taking as first week of month week with the first Monday of current month.
Input: week of year and year. For example, 27 and 2019. The first week of July (7).
Output: array of week of months. For example, [2, 6, 10, 14, 19, 23, 27].
What I try:
private void getResult(int weekYear)
{
LocalDate date = LocalDate.now();
final int weekNumber = 27;
LocalDate newDate = date.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber);
int month = newDate.getMonthValue();;
int weekMonth = LocalDate.from(newDate).get(WeekFields.ISO.weekOfMonth());
System.out.println(newDate);
System.out.println(month);
System.out.println(weekMonth);
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= month; i++)
{
LocalDate tempDate = date.withYear(2019).withMonth(i).with(WeekFields.ISO.weekOfMonth(), weekMonth);
int tempYear = LocalDate.from(tempDate).get(WeekFields.ISO.weekOfWeekBasedYear());
list.add(tempYear);
}
list.forEach((e) -> System.out.print(e + " "));
}
int weekYear = 27;
getResult(weekYear);
What I get: [1 6 10 14 18 23 27].
What I am looking for: I have two question:
First one: the results obtained are different from those expected. I think the problem is due to the fact that I didn't specify how to calculate the first week of the month (first Monday of the month). Is it right? How can I solve that?
Second one: What is a better solution?
The key here is understanding a few points:
You are numbering weeks in two different ways. For the week of year you are using ISO numbering: the first week of the year is the one that includes at least 4 days of the new year. For week of month you are counting the Mondays (you may say that the first week of the month is the one that includes seven days of the month, not four).
The week number may not always exist. If your starting point is in 0th or the 5th week of the month, a preceding month may not have that week in it.
the results obtained are different from those expected. I think the
problem is due to the fact that I didn't specify how to calculate the
first week of the month (first Monday of the month). Is it right? How
can I solve that?
You are correct. To count the Mondays of the month you may use:
LocalDate tempDate = date.withYear(2019)
.withMonth(i)
.with(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
.with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekMonth);
(DayOfWeek.MONDAY.getValue() is just of wordy way of saying 1, of course, but conveys the intention better, so I prefer it.)
With this change to your code the output is the expected:
2 6 10 14 19 23 27
The key is ChronoField.ALIGNED_WEEK_OF_MONTH. The aligned weeks of a month start from the 1st of the month and are always 7 days regardless of the days of the week. The first aligned week is from the 1st through the 7th of the month, the 2nd aligned week if from 8th through 14th, etc. Since we have set the day of week to Monday, setting the aligned week to 1 gives us the 1st Monday of the month, etc.
We’re not done yet. If I set weekNumber to 40, I get:
2 6 10 14 14 23 27 27 36 41
I had expected 40 to be the last number in the list, but it is not there. Week 40 of 2019 is from Monday September 30 through October 6, so if I understand correctly you want the 5th week of those months that have a 5th week. This brings us back to the issue of not all month having a week 5 (because they don’t have 5 Mondays). What happened was that since I ran your code on a Tuesday, it took Tuesday in week 40, which is October 1, as a starting point, and therefore gave me the 1st rather than the 5th week of every month.
are there better solutions? Can you suggest one?
I can’t really. What you’ve got is fine.
Only you’re not using the int weekYear parameter. You may want to use it in place of your weekNumber local variable. In any case you should delete one of them and use the other.
And this unrelated tip: Your use of LocalDate.from(someLocalDate) is redundant since it just gives you the same LocalDate again (either the same object or an equal one, I don’t know or care). Just use someLocalDate in those situations.

Finding the number of TAI seconds since 00:00:00 UTC, 1 January, 2004 in Java

As the title states, I'm required to find the number of TAI seconds since 00:00:00 UTC, January 1st, 2004 (in Java). I've just recently learned what TAI is and my attempts at working out the above have left me a bit confused.
What I've tried:
I know in Java you can use System.currentTimeMillis() to get the number of milliseconds since January 1st, 1970 UTC (Javadocs).
Additionally, from my brief research of atomic time I understand that currently TAI is exactly 37 (leap) seconds ahead of UTC.
Therefore, my thought process was to:
Find the number of seconds between 1970 and 2004 (34 years)
Subtract that from the current UTC time to get the number of since 2004
Add 37 to get the actual number of seconds in TAI
I wasn't certain of the math here (1 day = 86400 seconds):
Option 1: 86400 (seconds) x 365.25 (days (1 Julian Year)) x 34 (years) = 1,072,958,400
Option 2: 86400 (seconds) x 365 (days (1 Common Year)) x 34 (years) = 1,072,224,000
At this point I started questioning whether the 37 leap seconds added to TAI were to account for leap years when comparing to UTC and thus I should use Option 2. Unfortunately, I'm not certain whether my thought process is correct and I figured it'd be better to ask here to make certain.
Also, I found this cite claiming that 1,072,915,200 (seconds) is equivalent to 01/01/2004 # 12:00am (UTC). Which kind of threw me off because it's not equal to either of my calculations.
Tai-seconds are essentially atomic SI-seconds including leap seconds. My library Time4J supports this feature out of the box. For more details about TAI-support see also the javadoc of class Moment:
Moment m2004 = PlainTimestamp.of(2004, 1, 1, 0, 0).atUTC();
Moment now = SystemClock.currentMoment(); // other clocks based on NTP are possible
long seconds = SI.SECONDS.between(m2004, now);
System.out.println(seconds); // 425222084L
System.out.println(now); // 2017-06-22T13:15:24,570000000Z

In java.time, how is the result of adding a month calculated?

In the JSR-310 java.time API in JDK 8, what are the rules for calculating the result of adding a month to a date. In particular, what happens when you add 1 month to a date like January 31st?
LocalDate initial = LocalDate.of(2012, 1, 31); // 31st January 2012
LocalDate result = initial.plusMonths(1);
// what is the result?
Short answer:
In the example, the result will be the last day of February, 2012-02-29.
Explanation:
The question, "what date do you get if you add a month", is one which could be open to interpretation. To avoid this, the java.time API has a clear rule. The result will have the same day-of-month as the input, unless that would be an invalid date, in which case the result is the last day of the month.
Thus, the 31st January plus one month would result in the 31st February, but since that is an invalid date, the result is the last valid date in February, which is 28th or 29th February depending on whether it is a leap year:
// normal case
2011-01-15 plus 1 month = 2011-02-15 // 15 Jan -> 15 Feb
// special rule choosing the last valid day-of-month
2011-01-31 plus 1 month = 2011-02-28 // 31 Jan -> 28 Feb (2011 is normal year)
2012-01-31 plus 1 month = 2012-02-29 // 31 Jan -> 29 Feb (2012 is leap year)
// same rule applies for months other than February
2013-03-31 plus 1 month = 2013-04-30 // 31 Mar -> 30 Apr (only 30 days in April)
The same rule applies whether adding one month or many months and is always based on the resulting month. ie. the month is added first (adjusting the year if necessary), and only then is the day-of-month considered. The same rule also applies when subtracting.
// multiple months works on the month of the result
2013-10-31 plus 4 months = 2014-02-28 // last day of February
2013-10-31 minus 4 months = 2013-06-30 // last day of June
The same rules also apply when adding/subtracting years to/from a date - the years are added, and only then is the day-of-month checked for validity within the month.
// years use the same rule
2012-02-29 plus 1 year = 2013-02-28 // 29th February invalid so adjusted to 28th
If your business logic needs a different rule for month addition, the best approach is to write a TemporalAdjuster or TemporalAmount that packages up your special logic.

calculate frequency on certain range

I have maths problem ... (at the moment i solved it using manual iteration which is pretty slow) ...
For example if an employee got paid weekly (it can be fortnightly / every 2 weeks and monthly) with certain date (let's call the employee got paid every tuesday and for monthly the employee paid on certain date).
I have date range between 10th August 2009- 31 December 2009, now how to get frequency the employee got paid ?
is it possible to calculate this using jodatime ?
Example to make this question clear:
I have date range between Friday 14 August - Monday 14 Sept 2009 (31 days)
the employee got paid on every Tuesday
so he got paid on 18 & 25 August, 1 & 8 August we got 4 times payment
(frequency)
another example:
with the same date range Friday 14 August - Monday 14 Sept 2009 (31 days)
but different pay date .. for example on Sunday
so he got paid on : 15, 22 & 29 August , 5 & 12 September ... we got 5 times payment.
same date range but different pay day .. will result different.
So my question is, are there any formula to solve this case ?
at the moment I calculate using manual iterator .. which is very slow (because the range could be some years or months)
thank you
ps: I am using groovy .. any solutions using java or groovy or just algorithm are welcome :)
Oftentimes pay periods are on the 15th and the end of every month, so in that case you'd count the number of months and multiply by 2, checking the end conditions (if start is before the 15th, subtract one pay period; if end is after end of the month subtract one pay period).
It's possible to get counts of days, weeks, and months, but you'll have to add in the logic to handle the dodgy end conditions. It's probably not a simple formula, as the case I described demonstrates.
abosolutely, using the Weeks class is very simple:
DateTime start = new LocalDate(2009, 8, 10).toDateTimeAtStartOfDay();
DateTime end = new LocalDate(2009, 12, 31).toDateTimeAtStartOfDay();
int numberOfWeeks = Weeks.weeksBetween(start, end).getWeeks();
this code give 20 as result. It is right?
EDIT
maybe this is better:
DateMidnight start = new DateMidnight(2009, 8, 10);
DateMidnight end = new DateMidnight(2009, 12, 31);
int numberOfWeeks = Weeks.weeksBetween(start, end).getWeeks();
System.out.println(numberOfWeeks);
Subtracting one date from the other to get the "number of days" (or weeks) is generally the wrong way to go for these kinds of calculations. For example, if someone is 365 days old, they are exactly one year old, unless there was a February 29 during that time. In any (modern) 7-day period, there is always exactly one Tuesday; but for 8 days, it's either one or two. The calendar often figures into the calculations.
If they're paid once or twice a month, you do the easy calculation on the whole months -- starting on the first and ending on the last day of the month, which varies -- and then you have to consider partial months at the beginning and/or end. (Don't forget what happens if the 15th or last day of the month falls on a weekend.) If they're paid every one or two weeks, you can sync on a known payday, and then do the simpler math to figure the whole weeks before and/or since. (Don't forget holidays that fall on the payday.)
There are two tricks here: One is that the rules are different depending on the time frame. I mean, if a person is paid once a week, then in 7 days he gets paid once, in 14 days he gets paid twice, etc. But if a person is paid on the 1st and 16th of every month, I can't tell you how many times he was paid in 60 days without knowing what months were included: where they short months or long months?
The second is that you have to worry about the start and end of the time period. If a person is paid every Monday, then the number of times he gets paid in 8 days depends on whether the first day of the 8 is Monday.
Thus, I think you need to have different logic for schedules that are a fixed number of days and those that are tied to months or something else where the intervals can vary.
For the fixed number of days, the problem is fairly simple. The only complexity is if the time frame is not an exact multiple of the interval. So I'd say, find the first date in the interval on which a payday occurs. Then find the number of days between there and the end of the time period, divide by the interval and drop any fractions.
For example: A person is paid every Monday. How many pay days between March 1 and April 12? Find the first Monday in that range. Say it falls on March 4. Then calculate the number of days from March 4 to April 12. That would be 39. 39/7=5 and a fraction. Therefore he gets paid 5 more paychecks, for a total of 6.
For monthly pay, I think you'd have to separate out the first and last month. You could then count the number of months in the middle and multiply by the number of pays per month. Then for the first and last count how many are in them the hard way.
Just got solutions please check if I did something wrong
import org.joda.time.* ;
def start = new Date().parse("dd/MM/yy","14/08/2009");
def end = new Date().parse("dd/MM/yy","14/09/2009");
println("date range ${start} - ${end}");
def diff = end - start ;
println("diff : ${diff} days ");
println("how many weeks : ${diff/7}");
def payDay = 2 ; // Monday = 1 Sunday = 0
def startDay = new DateTime(start).dayOfWeek ; // 5 = Thursday
def startDayDiff = payDay - startDay ;
if(startDay > payDay){
startDayDiff = 7 + payDay - startDay ;
}
// for example if end on Friday (5) while Pay day is day 1 (Monday) then
// make sure end date is on Monday (same week )
// end date = end - ( endDay - payDay)
def endDay = new DateTime(end).dayOfWeek;
println("original end day: ${endDay}");
def endDayDiff = endDay - payDay ;
// otherwise ... if endDay < payDay (for example PayDay = Friday but End day is on Monday)
// end date = end - 7 + payDay
if(endDay < payDay){
endDayDiff = 7 - endDay - payDay ;
}
println("endDayDiff : ${endDayDiff}");
println("startDayDiff: ${startDayDiff}");
def startedOn = new DateTime(start).plusDays(startDayDiff);
println("started on : ${startedOn.toDate()}");
def endOn = new DateTime(end).minusDays(endDayDiff);
println("End on : ${endOn.toDate()}");
println("occurences : ${Weeks.weeksBetween(startedOn,endOn).getWeeks()+1}");
Tested using groovyConsole with Joda Time help .. :)

Calendar.getActualMaximum(Calendar.WEEK_OF_YEAR) weirdness

Either I don't understand the method getActualMaximum(int) or the field WEEK_OF_YEAR, or there's a Sun bug involved (or all three)...could someone explain to me why (at least in a German locale...) the following code:
Locale.setDefault( Locale.GERMAN );
Calendar c = Calendar.getInstance();
c.set( Calendar.YEAR, 2010 );
c.set( Calendar.MONTH, 0 );
c.set( Calendar.DAY_OF_MONTH, 1 );
System.out.println("max: "+c.getActualMaximum( Calendar.WEEK_OF_YEAR ));
System.out.println("actual: "+c.get( Calendar.WEEK_OF_YEAR ));
produces the following output:
max: 52
actual: 53
Here's the Javadoc of getActualMaximum(int):
Returns the maximum value that the
specified calendar field could have,
given the time value of this Calendar.
For example, the actual maximum value
of the MONTH field is 12 in some
years, and 13 in other years in the
Hebrew calendar system.
Edit
The plot thickens. In an English locale (-Duser.language=en -Duser.country=us) the output is:
max: 52
actual: 1
Seems to point to it being a Sun bug for German locales?
This information is correct:
max: 52
actual: 53
The year 2010 has a maximum of 52 weeks. The actual week is 53, since 2009 has maximum 53 weeks and most weeks start on sunday or monday. Week 1 is in most cases the first week of the year with 4 days in january. Since the week of the january 1st has only 2 or 3 days in 2010, the week is considered part of 2009.
Most likely the english locale has different rules for determing week 1, like the first week is the week of january 1st.
Wikipedia explains it correctly: wikipedia week article
The problem is, that January 1st 2010 is in week 53 of 2009 (in Germany), but year 2010 only has 52 weeks (December 31st 2010 is in week 52). The Java Calendar object unfortunately does not have a field for the year, to which the week number relates.

Categories

Resources