Reputation: 1413
My intention is to get the week numbers between two date ranges. The date 24th fall in the week number 34 and 26th fall in week number 35. Now the problem is if I put 2018-08-22T12:18:06,166
as the start date, i am getting 34,35,36. I am not expecting 36 here because the end date does not fall into week 36. Can anyone help me. This question is different from the solution provided here Week numbers from start date to end date Java . The solution has a problem which I detected recently
The below is the code for getting it :
public static void main(String[] args) {
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss','SSS");
LocalDateTime startDate = LocalDateTime.parse("2018-08-24T12:18:06,166", format);
LocalDateTime endDate = LocalDateTime.parse("2018-08-26T12:19:06,188", format);
numberOfWeeks(startDate, endDate);
}
public static void numberOfWeeks(LocalDateTime startDate, LocalDateTime endDate) {
int addWeek = 0;
TemporalField tf = WeekFields.SUNDAY_START.weekOfYear();
if (startDate.get(tf) < endDate.get(tf)) {
addWeek = 1;
}
long weeks = WEEKS.between(startDate, endDate) + addWeek;
List<String> numberWeeks = new ArrayList<>();
if (weeks >= 0) {
int week = 0;
do {
//Get the number of week
LocalDateTime dt = startDate.plusWeeks(week);
int weekNumber = dt.get(tf);
numberWeeks.add(String.format("%d-W%d", dt.getYear(), weekNumber));
week++;
} while (week <= weeks);
}
System.out.println(numberWeeks);
}
Upvotes: 0
Views: 2844
Reputation: 338266
LocalDateTime.parse( "2018-08-24T12:18:06,166".replace( "," , "." ) ).toLocalDate()
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
.datesUntil(
LocalDateTime.parse( "2018-08-26T12:19:06,188".replace( "," , "." ) ).toLocalDate()
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
.plusWeeks( 1 )
,
Period.ofWeeks( 1 )
)
.map( localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() ) )
.collect( Collectors.toList() )
.toString()
[34, 35]
Let’s take the idea of WeekFields
shown in correct Answer by Ole V.V. but shorten the code using Java Stream technology. While interesting, I do not necessarily recommend this approach.
First parse your input strings to get LocalDate
objects. The LocalDate
class represents a date-only value without time-of-day and without time zone.
Unfortunately, the java.time classes fail to support the comma as the fractional second delimiter, and instead expect a period (FULL STOP). This runs contrary to the ISO 8601 standard which allows both and actually prefers comma. This is one of the few flaws I have found in the otherwise excellent java.time classes, presumably due to the bias of programmers from the United States. To get around this flaw, we substitute a FULL STOP for the comma.
LocalDate inputDateStart =
LocalDateTime.parse(
"2018-08-24T12:18:06,166".replace( "," , "." ) // Unfortunately, the *java.time* classes fail to support the comma and instead only period. This runs contrary to the ISO 8601 standard which allows both and prefers comma.
)
.toLocalDate()
;
LocalDate inputDateStop =
LocalDateTime.parse(
"2018-08-26T12:19:06,188".replace( "," , "." )
)
.toLocalDate()
;
You want to work with weeks defined as starting on Sunday. So adjust from your input dates to the Sunday on or before that date.
Note that here we are adding a week to the stop to accommodate the needs of the Question. More commonly we would not do this addition, to follow the Half-Open approach to defining a span-of-time where beginning is inclusive while the ending is exclusive. In contrast to Half-Open, the Question apparently wants Fully-Closed approach where both the beginning and ending are inclusive (I do not recommend this).
LocalDate start = inputDateStart.with(
TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY )
);
LocalDate stop = inputDateStop.with(
TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY )
)
.plusWeeks( 1 ) // Add one to suit the Question, whereas commonly in date-time work we would have used Half-Open logic.
;
Define a series of dates as a Stream< LocalDate >
. We jump a week at a time by passing a Period
of one week.
Stream< LocalDate > stream =
startDate
.datesUntil(
stopDate ,
Period.ofWeeks( 1 )
)
;
If you want, you can see those dates by collecting them from the stream into a list. But note that this exhausts the stream. You'll need to re-establish the stream to continue our code further down.
List< LocalDate > dates = stream.collect( Collectors.toList() );
System.out.println( dates );
[2018-08-19, 2018-08-26]
Run through that series of dates in the stream. On each LocalDate
object, get the week number. Collect each returned week number as a Integer
object, all collected in a List
.
List< Integer > weekNumbers =
stream
.map(
localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() )
)
.collect( Collectors.toList() )
;
Dump to console.
System.out.println( weekNumbers );
[34, 35]
If you really want to go crazy with brevity, we can do all this in one line of code. I do not recommend this, but it is fun to try.
System.out.println(
LocalDateTime.parse( "2018-08-24T12:18:06,166".replace( "," , "." ) ).toLocalDate()
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
.datesUntil(
LocalDateTime.parse( "2018-08-26T12:19:06,188".replace( "," , "." ) ).toLocalDate()
.with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )
.plusWeeks( 1 )
,
Period.ofWeeks( 1 )
)
.map( localDate -> localDate.get( WeekFields.SUNDAY_START.weekOfWeekBasedYear() ) )
.collect( Collectors.toList() )
);
[34, 35]
Upvotes: 4
Reputation: 86203
public static void numberOfWeeks(LocalDateTime startDateTime, LocalDateTime endDateTime) {
if (startDateTime.isAfter(endDateTime)) {
throw new IllegalArgumentException("End date must not be before start date");
}
LocalDate endDate = endDateTime.toLocalDate();
List<String> numberWeeks = new ArrayList<>();
LocalDate currentDate = startDateTime.toLocalDate();
while (currentDate.isBefore(endDate)) {
numberWeeks.add(formatWeek(currentDate));
currentDate = currentDate.plusWeeks(1);
}
// Now currentDate is on or after endDate, but are they in the same week?
if (currentDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear())
== endDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear())) {
numberWeeks.add(formatWeek(currentDate));
}
System.out.println(numberWeeks);
}
public static String formatWeek(LocalDate currentDate) {
return String.format("%d-W%d",
currentDate.get(WeekFields.SUNDAY_START.weekBasedYear()),
currentDate.get(WeekFields.SUNDAY_START.weekOfWeekBasedYear()));
}
With the methods above your main
method from the question prints:
[2018-W34, 2018-W35]
I see that you have ignored the other answer in the linked question, the one using YearWeek
from the ThreeTen Extra library. So I assumed you didn’t want to do that. So I am using LocalDate
for the weeks.
While a couple of users have failed to reproduce your exact issue, I do agree that your code in the question is flawed.
Upvotes: 4