简体   繁体   English

Java中两个日期之间的星期数

[英]Week numbers between two dates in Java

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. 日期第24周的第24个秋天,日期第35周的第26个秋天。现在的问题是,如果我将2018-08-22T12:18:06,166作为开始日期,我会得到34,35,36。 I am not expecting 36 here because the end date does not fall into week 36. Can anyone help me. 我不希望在这里看到36,因为结束日期不会在36周内。任何人都可以帮助我。 This question is different from the solution provided here Week numbers from start date to end date Java . 这个问题与此处提供的解决方案不同: 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);
}
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: 使用上面方法从问题中打印出的main方法:

[2018-W34, 2018-W35] [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. 我看到您忽略链接问题中的另一个答案 ,一个使用ThreeTen Extra库中的YearWeek的答案。 So I assumed you didn't want to do that. 所以我以为你不想那样做。 So I am using LocalDate for the weeks. 所以我几个星期都在使用LocalDate

While a couple of users have failed to reproduce your exact issue, I do agree that your code in the question is flawed. 尽管有几个用户无法重现您的确切问题,但我确实同意问题中的代码存在缺陷。

tl;dr tl; dr

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] [34,35]

Streams

Let's take the idea of WeekFields shown in correct Answer by Ole VV but shorten the code using Java Stream technology. 让我们采用Ole VV正确的Answer中显示的WeekFields的想法,但是使用Java Stream技术缩短代码。 While interesting, I do not necessarily recommend this approach. 尽管很有趣,但我不一定推荐这种方法。

First parse your input strings to get LocalDate objects. 首先解析您的输入字符串以获取LocalDate对象。 The LocalDate class represents a date-only value without time-of-day and without time zone. LocalDate类表示没有日期和时区的仅日期值。

Unfortunately, the java.time classes fail to support the comma as the fractional second delimiter, and instead expect a period (FULL STOP). 不幸的是, java.time类无法将逗号作为小数秒定界符来支持,而是期望使用句点(FULL STOP)。 This runs contrary to the ISO 8601 standard which allows both and actually prefers comma. 这与允许两者并实际上更喜欢逗号的ISO 8601标准背道而驰。 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. 这是我在原本出色的java.time类中发现的少数缺陷之一,可能是由于美国程序员的偏见。 To get around this flaw, we substitute a FULL STOP for the comma. 为了解决此缺陷,我们用“ FULL STOP”代替逗号。

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 > . 将一系列日期定义为Stream< LocalDate > We jump a week at a time by passing a Period of one week. 通过一个星期的Period ,我们一次跳一周。

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] [2018-08-19,2018-08-26]

Run through that series of dates in the stream. 在信息流中遍历一系列日期。 On each LocalDate object, get the week number. 在每个LocalDate对象上,获取星期数。 Collect each returned week number as a Integer object, all collected in a List . 将每个返回的星期数收集为Integer对象,所有收集在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] [34,35]

One-liner 一线

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] [34,35]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM