I have a requirement to get the amount of months between two DateTime objects and then get the number of days left over.
Here is how I get the months between:
Months monthsBetween = Months.monthsBetween(dateOfBirth,endDate);
I am unsure how to then find out how many days are left over into the next month. I have tried the following:
int offset = Days.daysBetween(dateOfBirth,endDate)
.minus(monthsBetween.get(DurationFieldType.days())).getDays();
But this does not have the desired effect.
Use a org.joda.time.Period:
// fields used by the period - use only months and days
PeriodType fields = PeriodType.forFields(new DurationFieldType[] {
DurationFieldType.months(), DurationFieldType.days()
});
Period period = new Period(dateOfBirth, endDate)
// normalize to months and days
.normalizedStandard(fields);
The normalization is needed because the period usually creates things like "1 month, 2 weeks and 3 days", and the normalization converts it to "1 month and 17 days". Using the specific DurationFieldType's above also makes it convert years to months automatically.
Then you can get the number of months and days:
int months = period.getMonths();
int days = period.getDays();
Another detail is that when using DateTime objects, the Period will also consider the time (hour, minute, secs) to know if a day has passed.
If you want to ignore the time and consider only the date (day, month and year), don't forget to convert them to LocalDate:
// convert DateTime to LocalDate, so time is ignored
Period period = new Period(dateOfBirth.toLocalDate(), endDate.toLocalDate())
// normalize to months and days
.normalizedStandard(fields);
Related
I am using this to calculate the number of days difference in hebrew calendar and it returns correct days difference. Now I want to convert those days in the months or years.
But the problem is in the Hebrew calendar
A year in the Hebrew calendar can be 353, 354, 355, 383, 384, or 385
days long. Regular common years have 12 months with a total of 354
days. Leap years have 13 months and are 384 days long. Months with
uneven numbers usually have 30 days, while months with even numbers
have 29 days.
I have tried to divide the days with total days in a year but it has a different total number of days in the year. Can you please me an idea of how to achieve this?
Looks like a loop that keeps subtracting is the easiest to be understood. I guess you know the year at either end of the difference. Then a loop like this could work:
int yearBegin = 1234;
int daysDiff = calcDaysDiff(); // your existing routine
int yearAnalyzing = yearBegin;
int years = 0;
while (daysDiff > 0) {
int daysInThatYear = calcDaysInYear (yearAnalyzing); // you calc accdg your rules
while (daysDiff >= daysInThatYear) {
++years;
daysDiff -= daysinThatYear;
yearAnalyzing++; // or --, depending which direction you want to go
}
// less than a year is left
// calculate the months (should be easy)
}
If your beginning date starts in the middle of the year you would have to work these days off your difference first, before you start the loop.
You can use my lib Time4J which offers the feature to determine differences measured in years, months or days. Example (from a JUnit test case):
HebrewCalendar start = HebrewCalendar.of(5778, HebrewMonth.HESHVAN, 6);
HebrewCalendar end = HebrewCalendar.of(5778, HebrewMonth.ELUL, 6);
assertThat(HebrewCalendar.Unit.MONTHS.between(start, end), is(10));
start = start.plus(CalendarDays.ONE);
assertThat(HebrewCalendar.Unit.MONTHS.between(start, end), is(9));
start = start.minus(3, HebrewCalendar.Unit.YEARS);
assertThat(HebrewCalendar.Unit.YEARS.between(start, end), is(3));
start = start.plus(6, HebrewCalendar.Unit.YEARS).minus(CalendarDays.of(2)); // AM-5781-HESHVAN-5
assertThat(HebrewCalendar.Unit.YEARS.between(start, end), is(-2));
start = start.with(HebrewCalendar.MONTH_OF_YEAR, HebrewMonth.ELUL); // AM-5781-ELUL-5
assertThat(HebrewCalendar.Unit.MONTHS.between(start, end), is(-36));
start = start.plus(CalendarDays.ONE);
assertThat(HebrewCalendar.Unit.MONTHS.between(start, end), is(-37));
start = start.minus(37, HebrewCalendar.Unit.MONTHS);
assertThat(start, is(end));
The logic is somehow similar to the standard logic in the java.time-package. When the day-of-month of end date is smaller than that of start date, then the calculated month delta (or year delta) will be decreased by one.
Generally I cannot recommend to try to convert days into months or years applying a fixed factor because the Hebrew calendar has sometimes 12, sometimes 13 months. My lib does not try it, too, but applies an optimized kind of counting. It is also impossible to convert a day delta to a month delta without any further informations about start or end date for principal reasons:
Imagine the SAME day delta had been determined with a start date
short before Adar I (leap month) or with another month later. Then the month
delta will be different because the second case does not include a leap month!
More examples how to handle the Hebrew calendar can be seen in the API online.
If you are on Android then please use the specialized derivate Time4A with similar API.
I only found a solution for Joda Time.
My solution works only if the last day is not in the first week:
LocalDate.now() // or any other LocalDate
.withDayOfMonth(31)
.withMonth(12)
.get(weekFields.weekOfWeekBasedYear())
So what is the correct way in Java Time (like in Joda Time)?
This information is available directly using the java.time.* API.
The key method is rangeRefinedBy(Temporal) on TemporalField. It allows you to obtain a ValueRange object that provides the minimum and maximum values for the field, refined by the temporal object passed in.
To find out how many ISO weeks there are in the year, do the following:
LocalDate date = LocalDate.of(2015, 6, 1);
long weeksInYear = IsoFields.WEEK_OF_WEEK_BASED_YEAR.rangeRefinedBy(date).getMaximum();
System.out.println(weeksInYear);
Note that the date you pass in is used to determine the answer. So when passing in dates in early January or late December ensure you understand how the ISO week-based calendar works, and the difference between the calendar year and the week-based year.
If one wants to get the week number based on 7 days no matter when the week starts and how many days the first partial week of the year has, ChronoField.ALIGNED_WEEK_OF_YEAR might be helpful.
For example, the 1st of January 2016 based on the ISO-8601 definition (where a week starts on Monday and the first week has a minimum of 4 days) falls into week number 0, but in the aligned it is week number 1.
LocalDate date = LocalDate.of(2016, 1, 1);
int iso8601 = date.get(WeekFields.ISO.weekOfYear()); // result is 0
int aligned = date.get(ChronoField.ALIGNED_WEEK_OF_YEAR); // result is 1
It seems that when the last day is in the first week, you don't want to get 1 as an answer but 52/3/4, in which case you may be looking for:
LocalDate.of(2017, 12, 31).get(WeekFields.ISO.weekOfYear());
There are several ways to define week numbers - if that doesn't do what you want you need to clarify which method you want to use.
The correct and best solution is given by #JodaStephen. Here are some alternatives anyways.
December, 28th is always in the last week of a year, because the remaining three days after can not form a major part of another week:
int weeks = LocalDate.of(2017, 12, 28).get(WeekFields.ISO.weekOfYear());
A year has 53 weeks if it starts or ends with a thursday:
Year year = Year.of(2017);
DayOfWeek firstDay = year.atDay(1).getDayOfWeek();
DayOfWeek lastDay = year.atDay(year.length()).getDayOfWeek();
int weeks = firstDay == DayOfWeek.THURSDAY || lastDay == DayOfWeek.THURSDAY ? 53 : 52;
And finally this will give you the "week number" of the last day of year. It's 53 also in cases where the last week's number is 52 iff the major part of the last day's week lies in the next year (the week is claimed by the next year).
// This will not give the correct number of weeks for a given year
Year year = Year.of(2018);
year.atDay(year.length()).get(WeekFields.ISO.weekOfYear()); // 53
That's what you actually did.
This question already has answers here:
How can I increment a date by one day in Java?
(32 answers)
Closed 6 years ago.
How to add number of days into a given date in the format mm/dd/yyyy .
If my date is 9/12/2007, I want to add 30 days into the date and the result should be 10/12/2007.
I have many frequencies like Weekly, monthly, Every 2 weeks, Twice a month, Every 4 weeks, Once in 2 months, Every 3 months, Every 6 months, Every 3 months,
Annually, etc.
If we select the different frequencies from the list, the result should vary based on the frequency. Can anyone help me on this ?
Convert your date to a LocalDate, add the required values to it and then convert it back to the format you need it.
For example adding 30 days would look like this:
LocalDate d = LocalDate.of(2007,9,12).plus(30, ChronoUnit.DAYS)
And if you look at ChronoUnit you can see there are some units defined like weeks, days, months and so on...
String dt = "9/12/2007"; // Start date
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Calendar c = Calendar.getInstance();
c.setTime(sdf.parse(dt));
c.add(Calendar.DATE, 1);
I'm trying to convert a no of months into milliseconds
For example:
6 months = X milliseconds
There's no fixed answer to that, because it depends on which months those are - and indeed which year it is. Also potentially which time zone you're in, if you want to take account of that. (I'm assuming you mean the Gregorian calendar, by the way - different calendar systems have different month lengths.)
You could get some sort of "reasonable approximation" by assuming 365.25 days in a year, and saying that 6 months is half of that, then find out that many days in milliseconds. But it would only be an approximation.
For "how many milliseconds does it take to get from date/time X to 6 months later" you'd use an API (even Calendar would work for this particular case, although I'd recommend Joda Time or java.time in Java 8):
Set your start date/time, in the appropriate calendar and time zone
Fetch the "milliseconds since the Unix epoch" (which is easy enough to retrieve in any API) and remember it
Add 6 months
Fetch the "milliseconds since the Unix epoch" again, and subtract the earlier value from it
If you know exactly from when to when those 6 months reach, you can use a variety of ways to calculate the duration, using java.util.Calendar, JodaTime, or the JDK1.8 time API.
But if you don't have particular dates in mind, you can take an average duration for your month.
No API in the world can change that fact.
For example, the JDK1.8 time API uses this for the duration of a month in seconds: (from java.time.temporal.ChronoUnit)
MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
31,556,952 is the number of a seconds in a year, based on a year that lasts 365.2425 days.
You can use the same number directly and get the same result as with the time API:
long months = 6;
long seconds = months * 31556952L / 12;
long milliseconds = seconds * 1000;
Result:
15,778,476,000
Calendar today = Calendar.getInstance();
Calendar sixMonthsAhead = Calendar.getInstance();
sixMonthsAhead.add(Calendar.MONTH, 6);
long differenceInMilis = sixMonthsAhead.getTimeInMillis() - today.getTimeInMillis();
You could also use...
sixMonthsAhead.add(Calendar.DATE, 180);
// or 183 days because 365 / 2 is approximately 183.
instead of...
sixMonthsAhead.add(Calendar.MONTH, 6);
for a more accurate result. But like Jon has mentioned, it will always vary depending on what day of the year it is.
The answer by Jon Skeet is correct.
Joda-Time
Assuming you could specify a pair of beginning and ending points on a time line, here is some example code using the Joda-Time 2.3 library.
This code grabs the current moment, adjusts to first of the month, and adjusts to first moment of that day. Then it adds 6 months. Joda-Time is smart about adding the months, taking into account leap year and various lengths of months. This span of 6 months is then represented as an Interval instance. From that we calculate the number of milliseconds. Note that count of milliseconds needs to be a long (64-bit) rather than an int (32-bit) we Java programmers more commonly use. Lastly, for fun, we see what this span of time looks like when formatted in the ISO 8601 standard’s "Duration" format.
DateTimeZone dateTimeZone = DateTimeZone.forID( "Europe/Paris" ); // Better to specify a time zone than rely on JVM’s default.
DateTime start = new DateTime( dateTimeZone ).withDayOfMonth( 1 ).withTimeAtStartOfDay();
DateTime stop = start.plusMonths( 6 );
Interval interval = new Interval( start, stop );
long milliseconds = interval.toDurationMillis(); // A long, not an int.
Period period = interval.toPeriod(); // For fun… ISO 8601 standard's Duration format.
Dump to console…
System.out.println("start: " + start );
System.out.println("stop: " + stop );
System.out.println("interval: " + interval );
System.out.println("milliseconds: " + milliseconds );
System.out.println("period: " + period );
When run…
start: 2014-04-01T00:00:00.000+02:00
stop: 2014-10-01T00:00:00.000+02:00
interval: 2014-04-01T00:00:00.000+02:00/2014-10-01T00:00:00.000+02:00
milliseconds: 15811200000
period: P6M
I'm trying to set my Period object using the "millis" constructor and have all the relevant fields be updated accordingly (years, months, weeks, days, hours, minutes, seconds)
That is, using the following code:
mPeriod = new Period(millis, PeriodType.standard());
doesn't fill all the relevant fields accordingly.
only weeks and minutes (for input millis of 1325965615539)
can someone please help me figure this thing out ?
You can normalize it using Period#normalizedStandard();
However, there's no way Period can "fill in all the relevant fields", because it can't make assumptions about the number of days in months or years. The best it can do is to turn it into weeks, days, and time fields.
The Javadoc touches on this, but not in great detail:
If the period contains years or months, then the months will be
normalized to be between 0 and 11. The days field and below will be
normalized as necessary, however this will not overflow into the
months field. Thus a period of 1 year 15 months will normalize to 2
years 3 months. But a period of 1 month 40 days will remain as 1 month
40 days.
(Emphasis mine)
If you need it to normalize into years and months, you need to construct the Period with values in those fields.
Alternatively, you could use a Duration:
Construct a Duration with milliseconds
Use Duration#toPeriodFrom(ReadableInstant) to create a Period starting at a given instant in time (e.g. new DateTime()). According to the docs, this should work:
This conversion will determine the fields of a period accurately. The
results are based on the instant millis, the chronology of the
instant, the standard period type and the length of this duration.
Here's what normalize() should do to your millisecond input:
import org.joda.time.Period;
class Normalize {
public static void main(String[] args) {
Period period = new Period(1325965615539L);
System.out.println(period);
System.out.println(period.normalizedStandard());
}
}
// outputs
PT368323H46M55.539S
P2192W2DT19H46M55.539S
Notice the second line has been normalized, but only up to weeks.