I using JodaTime and I need to check if 2 dates are in in range of 3 months difference. So I wrote simple method to check this
private boolean inRangeOf3Months(Pair<BusinessData, BusinessData> pair) {
return pair.getKey().getDateTimeValue() != null && pair.getValue().getDateTimeValue() != null ?
new Period(pair.getKey().getDateTimeValue(), pair.getValue().getDateTimeValue()).getMonths() <= 3 : false;
}
Now I'm writing tests and this one is fine
#Test
public void shouldReturnTrueWhenInRangeOf3Months() {
BusinessData closingDateFrom = businessData("closingDateFrom");
closingDateFrom.setDateTimeValue(DateTime.now());
BusinessData closingDateTo = businessData("closingDateTo");
closingDateTo.setDateTimeValue(DateTime.now().plusMonths(3));
ReportingSearchCriteria criteria = criteriaOf(closingDateFrom, closingDateTo);
Assert.assertTrue(validator.isSufficient(criteria));
}
But that one is not, I'm setting first date to now() and second one to now().plusMonths(3).plusDays(1). So its over my range, it shouldn't be allowed.
#Test
public void shouldReturnFalseWhenOverRangeOf3Months() {
BusinessData closingDateFrom = businessData("closingDateFrom");
closingDateFrom.setDateTimeValue(DateTime.now());
BusinessData closingDateTo = businessData("closingDateTo");
closingDateTo.setDateTimeValue(DateTime.now().plusMonths(3).plusDays(1));
ReportingSearchCriteria criteria = criteriaOf(closingDateFrom, closingDateTo);
Assert.assertFalse(validator.isSufficient(criteria));
}
Period of 3 months and a few days still have a difference of 3 months.
So if you want to make it 3 months inclusive you can add one day to last date and use just less than (instead of less than or equal):
new Period(pair.getKey().getDateTimeValue(), pair.getValue().getDateTimeValue().plusDays(1)).getMonths() < 3
Also, pay attention that the second date should be later the first one (otherwise it won't work).
If i understand your comparison must be on total days not on months because for example let's consider today's date as first date and second date with addition of 3 months plus 25 days
DateTime date1 = DateTime.now();
System.out.println(date1);
DateTime date2 = DateTime.now().plusMonths(3).plusDays(25);
System.out.println(date2);
Period p = new Period(date1,date2);
System.out.println(p.getMonths());
Output
2019-08-09T04:31:07.400-05:00
2019-12-04T04:31:07.449-06:00
3
Still it returns difference of months is 3 because the difference is 3 months and 25 days were 4 months not completed yet. Now if you add 3 months plus 30 days to second date it will return difference as 4 months (some times you might need to add 31 days because it depends on month that has 31 days)
DateTime date1 = DateTime.now();
System.out.println(date1);
DateTime date2 = DateTime.now().plusMonths(3).plusDays(30);
System.out.println(date2);
Period p = new Period(date1,date2);
System.out.println(p.getMonths());
Output
2019-08-09T04:34:29.530-05:00
2019-12-09T04:34:29.579-06:00
4
From the above approach you can't even take the months of two days and check whether difference is 3 months or not, because second date may return third month in some cases (like above if date is middle of month and if you add couple of days to it).
I will suggest compare the total days, for example total no of days between two days > 90 or > 91 for this you can use Duration
DateTime date1 = DateTime.now();
System.out.println(date1);
DateTime date2 = DateTime.now().plusMonths(3).plusDays(1);
System.out.println(date2);
Duration d = new Duration(date1, date2);
System.out.println(d.getStandardDays());
Output
2019-08-09T04:40:47.334-05:00
2019-11-10T04:40:47.381-06:00
93
Seems like the easiest is to just compare the 3 month away marks (buth past and future) to the other date.
public boolean areWithin3Months(DateTime dateTime1, DateTime dateTime2) {
return !dateTime1.minusMonths(3).isAfter(dateTime2)
&& !dateTime1.plusMonths(3).isBefore(dateTime2);
}
Related
For a span of time running from one date to another date, how to get the number of calendar months containing one or more days of my span?
So for example:
2016-01-23/2016-01-23 = 1 calendar month (January)
2016-01-31/2016-02-01 = 2 calendar months (January, February)
2016-01-23/2016-02-28 = 2 calendar months (January, February)
2016-01-15/2016-03-15 = 3 calendar months (January, February, March)
2016-01-15/2017-03-15 = 15 calendar months (Jan-Dec of 2016 plus January, February, March of 2017)
I do not define a month as “30 days”. I am asking about calendar months, January-December.
Similar to this Question but that asks about PHP/MySQL.
Calculate the "epoch" month of both dates, then subtract them and add 1.
Using LocalDate like in the other answer, an epochMonth() helper method makes it easy:
private static int monthsTouched(LocalDate fromDate, LocalDate toDate) {
return epochMonth(toDate) - epochMonth(fromDate) + 1;
}
private static int epochMonth(LocalDate date) {
return date.getYear() * 12 + date.getMonthValue();
}
Like the results in the question, both dates are inclusive.
Note: Validation skipped for brevity, e.g. what is result if fromDate > toDate?
Test
public static void main(String[] args) {
test("2016-01-23", "2016-01-23");
test("2016-01-31", "2016-02-01");
test("2016-01-23", "2016-02-28");
test("2016-01-15", "2016-03-15");
test("2016-01-15", "2017-03-15");
}
private static void test(String fromDate, String toDate) {
System.out.println(monthsTouched(LocalDate.parse(fromDate), LocalDate.parse(toDate)));
}
Output (matches results from question)
1
2
2
3
15
Use ChronoField.PROLEPTIC_MONTH, which returns a count of months from year zero:
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
long monthsTouched = date2.getLong(PROLEPTIC_MONTH) - date1.getLong(PROLEPTIC_MONTH) + 1;
Adjust the start and end dates
The key is to adjust your dates.
Move the starting date to the first of the month
Move the ending date to the first of the following month
We move the ending to the next month after because the Half-Open approach is commonly used when considering spans of time. Half-Open means the beginning is inclusive while the ending is exclusive. So lunch hour runs from 12:00 to 13:00 but does not include the 61st minute of 1 PM. A week runs from Monday to Monday, for seven days not including that second Monday.
So a span running from the first of January to the first of March is two months rather than three because we run up to, but do not include, that last date, the first of March.
java.time
The java.time classes built into Java 8 and later make easier work of this.
For date-only values, without a time-of-day and without a time zone, use the LocalDate class.
LocalDate start = LocalDate.parse( "2016-01-31" );
LocalDate stop = LocalDate.parse( "2016-02-01" );
To adjust, use a TemporalAdjuster. Implementations can be found in the TemporalAdjusters class (note the plural 's'). We need firstDayOfMonth and firstDayOfNextMonth.
LocalDate startAdjusted = start.with( TemporalAdjusters.firstDayOfMonth() );
LocalDate stopAdjusted = stop.with( TemporalAdjusters.firstDayOfNextMonth() );
Now use the ChronoUnit class to calculate elapsed whole months.
long calendarMonthsTouched = ChronoUnit.MONTHS.between( startAdjusted , stopAdjusted );
span: 2016-01-31/2016-02-01
calendarMonthsTouched: 2
See this code live in IdeOne.com.
I am trying to obtaining remaining years, months, and days between two dates:
So I have used Joda Time to do so:
DateTime endDate = new DateTime(2018,12,25,0,0);
DateTime startDate = new DateTime();
Period period = new Period(startDate,endDate,PeriodType.yearMonthDay());
PeriodFormatter formatter = new PeriodFormatterBuilder().appendYears().appendSuffix(" Year ").
appendMonths().appendSuffix(" Month ").appendDays().appendSuffix(" Day ").appendHours()..toFormatter();
String time = formatter.print(period);
This gives me string time: 2 Year 4 Month 22 Day
However, I want integer values of each number of remaining years, months, days.
So, Instead of "2 Year 4 Month 22 Day", I want to set my variables:
int year = 2
int month = 4
int day = 22
Is there any way to obtain these values separately instead of obtaining one string? Thank you so much! :)
i had the same requirement once ,here is the code snippet
LocalDate d=LocalDate.of(yy,mm,dd);
LocalDate d2=LocalDate.of(yy, mm, dd);
Period p=Period.between(d, d2);
long day,month,year;
day=p.getDays();
month=p.getMonths();
year=p.getYears();
System.out.println(day+" : "+month+" : "+year);
Invoke the methods provided by the DateTime class and just subtract them. An example for years is below:
int year = (int) dateTime#year#getField() - (int) dateTime2#year#getField()
UNTESTED code!! I'll be looking into it later but the general idea is the same, get the field information then just subtract it to get a value
I am trying to display number of days in every month of the year
LocalDate start = LocalDate.of(2016, 01, 01);
LocalDate end = start.plusYears(1);
Period everyMonth = Period.ofMonths(1);
for (;start.isBefore(end); start = start.plus(everyMonth)) {
System.out.println(Period.between(start, start.plus(everyMonth)).getDays());
}
Why do I get 12 0s?
You are not using correctly the Period class here. start represents the date 01/01/2016 (in dd/MM/yyyy format). When you are adding a period of 1 month, the result is the date 01/02/2016.
The period between those two dates, as defined by the Period class is "1 month". If you print the period, you will have "P1M", which is the pattern to say that:
A date-based amount of time in the ISO-8601 calendar system, such as '2 years, 3 months and 4 days'.
As such, getDays(), which return the amount of days in the period, will return 0. The result is different than the number of days between the two dates. You can convince yourself of that by printing the result of getMonths, it would return 1:
public static void main(String[] args) {
LocalDate start = LocalDate.of(2016, 01, 01);
Period period = Period.between(start, start.plus(Period.ofMonths(1)));
System.out.println(period.getDays()); // prints 0
System.out.println(period.getMonths()); // prints 1
}
Now, in your question, you want to print the number of days in every month. You can simply have the following:
for (Month month : Month.values()) {
System.out.println(month.length(Year.now().isLeap()));
}
In Java Time, there is an enum Month for all the months, and the method length(leapYear) return the length of this month, i.e. the number of days in the month. Since this depends on whether the current year is a leap year or not, there is a boolean argument for that.
To check for the current year, we can call Year.now() and return if it's a leap year or not with isLeap().
As a side-note, if you truly wanted to print the number of days between two dates, you would need to use ChronoUnit.DAYS.between(start, end).
You are doing everything correctly except one thing. You try to print days in the period, but since you always add 1 month to the date the period is 0 years, 1 month, 0 days. When you call getDays() it returns number of days in period which is 0.
final Period period = Period.between(start, start.plus(everyMonth);
System.out.println(period.getDays()); // 0
System.out.println(period.getMonths()); // 1
I think what you are looking for is:
System.out.println(ChronoUnit.DAYS.between(start, start.plus(everyMonth)));
I am trying to check if a given interval is greater than 12 months using the following code:
public void testMonths() throws Exception {
DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-mm");
DateTime from = format.parseDateTime("2010-01");
DateTime until = format.parseDateTime("2011-06");
int months = Months.monthsBetween(from, until).getMonths();
assertTrue(months > 12); // months = 12 for some reason.
}
Whenever the given interval is more then a year it will truncate the remaining months in the last year.
Input I've tried running:
15 months will return 12 months.
25 months will return 24 months.
36 months will return 36 months.
37 months will return 36 months.
The problem is your text pattern. "mm" means "minutes" not "months". If you change your pattern to "yyyy-MM" you'll find it works fine.
As a guiding principle the first thing I would do in a situation like this is try to isolate this to the smallest possible problem. So take text parsing and the complexity of DateTime (time zones etc) out of the equation:
LocalDate from = new LocalDate(2010, 1, 1);
LocalDate to = new LocalDate(2011, 6, 1);
int months = Months.monthsBetween(from, to).getMonths();
Now you'll see it's 17. So then the next step would be to go back to your original code, and print out the values of from and to... and you'll see something like:
2010-01-01T00:01:00.000Z
2011-01-01T00:06:00.000Z
Basically, avoid text parsing unless that's fundamentally part of the problem you're trying to diagnose.
This question already has answers here:
How can I determine if a date is between two dates in Java? [duplicate]
(11 answers)
Closed 9 years ago.
I'm trying to write a schedule program in Java and I need to figure out what time it is, and whether the current time is in between two set times. Figuring out the current time is pretty simple, but do you have any suggestions for figuring out whether it is between two times of day. For example, it is 9:33 AM on a Thursday. So I would need to figure out which scheduled section of the week that time corresponds to. How would I go about comparing the time to set periods during the week, for example an Array of sectioned times during a week such as {Monday from 9-10 AM, Tuesday from 3-4 PM, Thursday from 8-11 AM}, and seeing which section of time the current time falls between?
An efficient way to find which period any date lies within would be to have a class;
public class TimePeriod implements Comparable<TimePeriod>{
Date start;
Date end;
//Constructor, getters, setters
boolean isIn(Date date) {
return date.after(start) && date.before(end);
}
public int compareTo(TimePeriod other) {
return start.compareTo(other.start);
}
}
..and then create a sorted list of TimePeriod where you can perform a binary search.
edit:
This might make the binary search easier;
int check(Date date) {
if (isIn(date)) {
return 0;
} else if (start.after(date)) {
return -1;
} else if (end.before(date)) {
return 1;
} else {
throw new IllegalStateException("Time has gone badly wrong");
}
}
If you're using Date Class, you could do it like this
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
Date before = sdf.parse("07/05/2012 08:00");
Date after = sdf.parse("07/05/2012 08:30");
Date toCheck = sdf.parse("07/05/2012 08:15");
//is toCheck between the two?
boolean isAvailable = (before.getTime() < toCheck.getTime()) && after.getTime() > toCheck.getTime();
EDITED
As suggested by Jonathan Drapeau you could also use compareTo.
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm");
Date before = sdf.parse("07/05/2012 08:00");
Date after = sdf.parse("07/05/2012 08:30");
Date toCheck = sdf.parse("07/05/2012 08:15");
//is toCheck between the two?
if you want to include the "initial" and "final" date range
boolean isAvailable = before.compareTo(toCheck) >= 0 && after.compareTo(toCheck) <= 0
if you want to exclude the "initial" and "final" date range
boolean isAvailable = before.compareTo(toCheck) > 0 && after.compareTo(toCheck) < 0
You could use it too on Calendar class.
Anyway, i highly recommend you to use Calendar. It's a way precise class
you could check it like this:
Calendar cal1 = Calendar.getInstance(); // for example 12:00:00
Calendar cal2 = Calendar.getInstance(); // for exmaple 12:30:00
Calendar userTime = Calendar.getInstance(); // time to test: 12:15:00
if(user.after(cal1)&& user.before(cal2)){
//...
}
And to initialize and set times to Calendar, check this:
http://www.tutorialspoint.com/java/util/calendar_settime.htm
I would suggest using the Epoch time.
For a definition of Epoch time: http://en.wikipedia.org/wiki/Epoch_time
Basically, its a number of seconds after a specific date, i believe in 1989. If you translate the 3 times (the current time and the 2 times to compare to) in epoch time you can just use > < = etc.
For information on getting epoch time, Try here (has many languages): http://shafiqissani.wordpress.com/2010/09/30/how-to-get-the-current-epoch-time-unix-timestamp/
Unfortunately, my java is lacking or I'd give you some code :)
Edit:
Java epoch time code:
long epoch = System.currentTimeMillis()/1000;
Because my Java is bad and I don't have an interpreter where I am, I can only suggest using this site to help convert the other dates to epoch time: http://www.epochconverter.com/
There is before(Date) and after(Date) method in Date Class.
secondDate.before(firstDate)
If you use Calendar class, it has explicit before() and after() methods:
Calendar startDate = ...
Calendar endData = ...
isBetween = currentDate.after(startDate) && currentDate.before(endDate);