Reputation: 1301
In my code I need to iterate between a range of dates using Joda, and I already tried this:
for(LocalDate currentdate = startDate; currenDate.isBefore(endDate); currenDate= currenDate.plusDays(1)){
System.out.println(currentdate);
}
The above code is working, but the iteration stops when currenDate
reaches the day before endDate
. What I want to achieve is that the iteration stops when currentDate
is exactly the same as endDate
.
for(Date currentdate = startDate; currentdate <= endDate; currentdate++){
System.out.println(currentdate );
}
I know the code above is impossible, but I do it to make clear what I'd want.
Upvotes: 7
Views: 7168
Reputation: 1203
Evevntually went for this solution:
Range.inclusive(3, 0).map(i => LocalDate.now.minusDays(i)).foreach()
Upvotes: 0
Reputation: 338326
The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. See Tutorial by Oracle.
The LocalDate
class represents a date-only value without time-of-day and without time zone.
LocalDate start = LocalDate.of( 2017 , Month.JANUARY , 23 ) ;
LocalDate stop = LocalDate.of( 2017 , Month.FEBRUARY , 2 ) ;
By the way, you might want to add a sanity-check to verify that the ending is not before the beginning.
I believe the logic you are looking for, to include the ending date, is “not after“. The LocalDate
class includes a isAfter
method, to which you can add a logical “NOT” (!
).
Also, a while
loop seems more appropriate and self-explanatory in this situation than a for
loop.
LocalDate ld = start ;
List<LocalDate> dates = new ArrayList<>() ;
while ( ! ld.isAfter( stop ) ) {
dates.add( ld ); // Collect this date.
ld = ld.plusDays( 1 ) ; // Setup the next loop.
}
See this code run live at IdeOne.com.
dates: [2017-01-23, 2017-01-24, 2017-01-25, 2017-01-26, 2017-01-27, 2017-01-28, 2017-01-29, 2017-01-30, 2017-01-31, 2017-02-01, 2017-02-02]
the iteration stops when currentDate reaches the day before endDate
This is actually desirable. Known as Half-Open, the common approach in date-time handling is to consider the beginning as inclusive while the ending is exclusive. So a lunch break starts at 12:00:00 (noon) and runs up to, but does not include, 13:00:00 (1 pm). The month of January starts on January 1 and runs up to, but does not include, February 1. A week starts on a Monday and runs up to, but does not include the following Monday. Most usefully, this approach avoids the problem of determining the last split second of a date-time where some systems use milliseconds (x.999), some (x.999999), same nanoseconds( x.999999999 ), and others use variations such as 5 decimal places (x.99999). Instead we go up to, but not include, the first moment of the next hour or day etc.
I find that using Half-Open approach consistently throughout my code makes the code easier to read, easier to comprehend, and much less likely to result in off-by-one bugs. I have been caught in countless financial mystery problems that turned out to be confusion or misunderstandings about a report covering date for with inclusive vs exclusive dates. So if possible, train your users to think the Half-Open way consistently. If not feasible, then adjust your code so your logic and loops are using Half-Open internally at least.
Here is code similar to above, but using isBefore
rather than NOT isAfter
, to use Half-Open approach. The ending is Feb. 3 instead of Feb. 2.
LocalDate start = LocalDate.of( 2017 , Month.JANUARY , 23 ) ;
LocalDate stop = LocalDate.of( 2017 , Month.FEBRUARY , 3 ) ; // Third instead of the Second of February, to be half-open.
LocalDate ld = start ;
List<LocalDate> dates = new ArrayList<>() ;
while ( ld.isBefore( stop ) ) { // Using "isBefore" for Half-Open approach.
dates.add( ld ); // Collect this date.
ld = ld.plusDays( 1 ) ; // Setup the next loop.
}
See this code run live at IdeOne.com.
start: 2017-01-23 | stop: 2017-02-03
dates: [2017-01-23, 2017-01-24, 2017-01-25, 2017-01-26, 2017-01-27, 2017-01-28, 2017-01-29, 2017-01-30, 2017-01-31, 2017-02-01, 2017-02-02]
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?
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.
Upvotes: 1
Reputation: 301
If you want the loop to be inclusive of the endDate you can use !currentDate.isAfter( endDate )
. This is logically equivalent to currentDate.isBefore(endDate) || currentDate.equals(endDate)
.
The following example will print 6/1/2017 through 6/10/2017.
LocalDate startDate = new LocalDate( 2017, 6, 1 );
LocalDate endDate = new LocalDate( 2017, 6, 10 );
for ( LocalDate currentDate = startDate; !currentDate.isAfter( endDate ); currentDate = currentDate.plusDays( 1 ) )
{
System.out.println( currentDate );
}
Upvotes: 2
Reputation: 316
Actually there's a simple way around to your original code you posted, see my implementation below, just modified your for loop implementation:
//test data
LocalDate startDate = LocalDate.now(); //get current date
LocalDate endDate = startDate.plusDays(5); //add 5 days to current date
System.out.println("startDate : " + startDate);
System.out.println("endDate : " + endDate);
for(LocalDate currentdate = startDate;
currentdate.isBefore(endDate) || currentdate.isEqual(endDate);
currentdate= currentdate.plusDays(1)){
System.out.println(currentdate);
}
Below is the Output (with respect to my localDate):
startDate : 2015-03-26
endDate : 2015-03-31
2015-03-26
2015-03-27
2015-03-28
2015-03-29
2015-03-30
2015-03-31
Hope this helps! Cheers. :)
Upvotes: 10
Reputation: 16428
If you want your loop to stop when the date your iterating is the same as todays date, you can use an equality check for that.
Have a look at .equals() on LocalDate
Here is a quick example:
public class DateIterator {
public static void main(String[] args) {
LocalDate lastMonth = LocalDate.now().minusMonths(1);
LocalDate lastWeek = LocalDate.now().minusWeeks(1);
LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDate today = LocalDate.now();
LocalDate tomorrow = LocalDate.now().plusDays(1);
List<LocalDate> dates = Arrays.asList(lastMonth, lastWeek, yesterday, today, tomorrow);
for (LocalDate date : dates) {
if (date.isEqual(today)) {
System.out.println("Date is equal to todays date! Break out, or do something else here");
} else if (date.isBefore(today)) {
System.out.println("The date " + date.toString() + " is in the past");
} else {
System.out.println("The date " + date.toString() + " is in the future");
}
}
}
}
Output is:
The date 2015-02-25 is in the past
The date 2015-03-18 is in the past
The date 2015-03-24 is in the past
Date is equal to todays date! Break out, or do something else here
The date 2015-03-26 is in the future
Obviously, if that equality check passes, you'll need to break out of the loop etc.
Heres another that uses a specific date and increments 1 day at a time, which I think is a bit more like what you want
public class DateIterator {
public static void main(String[] args) {
LocalDate specificDate = LocalDate.now().minusWeeks(1);
LocalDate today = LocalDate.now();
boolean matchFound = false;
while (!matchFound) {
if (!specificDate.isEqual(today)) {
System.out.println(specificDate.toString() + " is in the past, incrementing day and checking again...");
specificDate = specificDate.plusDays(1);
} else {
System.out.println("Date matches today!");
matchFound = true;
}
}
}
}
Output:
2015-03-18 is in the past, incrementing day and checking again...
2015-03-19 is in the past, incrementing day and checking again...
2015-03-20 is in the past, incrementing day and checking again...
2015-03-21 is in the past, incrementing day and checking again...
2015-03-22 is in the past, incrementing day and checking again...
2015-03-23 is in the past, incrementing day and checking again...
2015-03-24 is in the past, incrementing day and checking again...
Date matches today!
Upvotes: 4
Reputation: 240870
Not sure with joda-type but you could iterate at the interval (second, minute, hour, day, month, year) with Calendar
API, here & here are examples
Upvotes: 0