【问题标题】:How to get the next n fridays which are the first, second or third friday in a month?如何获得下一个星期五,即一个月的第一个、第二个或第三个星期五?
【发布时间】:2021-12-04 21:13:06
【问题描述】:

我需要找到接下来的 n 个星期五,但想排除第 4 个和可能的第 5 个星期五,即我想得到接下来的 n 个星期五,即每个月的第 1、2 或 3 个星期五。我找到了一种在接下来的 n 个星期五(例如接下来的 10 个星期五)进行直播的方法:

LocalDate now = LocalDate.now();
Stream.iterate(now , localDate -> localDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)))
     .skip(1) // skip today 
     .limit(10)
     .forEach(System.out::println);

输出:

2021-10-22
2021-10-29
2021-11-05
2021-11-12
2021-11-19
2021-11-26
2021-12-03
2021-12-10
2021-12-17
2021-12-24

但我需要排除例如 2021-10-222021-10-29 因为它们是 10 月的第 4 和第 5 个星期五。同样2021-11-26 & 2021-12-24 因为它们不是第一个、第二个或第三个星期五。

我搜索了 SO 并找到了一些方法来计算一个月的第 n 个星期五,例如这个

public static LocalDate nthFridayOfMonth(LocalDate localDate, int week) {
    return localDate.with(TemporalAdjusters.dayOfWeekInMonth(week, DayOfWeek.FRIDAY));
}

返回给定日期的第 n 个星期五。

如果给定的 LocalDate 是一个月的第一个、第二个或第三个星期五,如何修改此方法以使其返回布尔值?或者使用这个方法来实现另一个我需要的方法?

理想情况下,我想使用上面的流并使用一个方法,该方法返回一个布尔值作为过滤器:

LocalDate now = LocalDate.now();
Stream.iterate(now , localDate -> localDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)))
     .skip(1) // skip today 
     .filter(d -> is1st2ndOr3rdFriday(d))   
     .limit(10)
     .forEach(System.out::println);

得到

2021-11-05
2021-11-12
2021-11-19
2021-12-03
2021-12-10
2021-12-17
2022-01-07
2022-01-14
2022-01-21
2022-02-04

感谢任何有关如何实现过滤器方法boolean is1st2ndOr3rdFriday(LocalDate d) {}的帮助

【问题讨论】:

  • 3×7=21,因此如果一个月中的某天大于或等于 22,则它是第 4 或第 5 个星期五。否则,它是第 1、第 2 或第 3 个。只需在限制之前添加一个过滤器,就可以了。

标签: java localdate date-manipulation


【解决方案1】:

另一种选择:迭代几个月

为了变化,这里有一个不同的想法。我正在创建一个月份流,并明确地从每个月的前 3 个星期五开始。

    LocalDate today = LocalDate.now(ZoneId.systemDefault());
    YearMonth thisMonth = YearMonth.from(today);
    List<LocalDate> fridays = Stream.iterate(thisMonth, ym -> ym.plusMonths(1))
            .flatMap(ym -> Stream.of(fridayOfMonth(ym, 1), fridayOfMonth(ym, 2), fridayOfMonth(ym, 3)))
            .filter(date -> date.isAfter(today))
            .limit(10)
            .collect(Collectors.toList());
    System.out.println(fridays);

今天运行时的输出:

[2021-11-05、2021-11-12、2021-11-19、2021-12-03、2021-12-10、 2021-12-17、2022-01-07、2022-01-14、2022-01-21、2022-02-04]

我使用了以下辅助方法来查找一个月的第 n 个星期五:

private static LocalDate fridayOfMonth(YearMonth ym, int no) {
    return ym.atDay(15).with(DayOfWeek.FRIDAY).with(ChronoField.ALIGNED_WEEK_OF_MONTH, no);
}

变体:仍然为了变体,下面是创建一个月前三个星期五的流的不同方式:

    List<LocalDate> fridays = Stream.iterate(thisMonth, ym -> ym.plusMonths(1))
            .flatMap(MyClass::firstThreeFridays)
            .filter(date -> date.isAfter(today))
            .limit(10)
            .collect(Collectors.toList());

这次我的辅助方法是这样的:

private static Stream<LocalDate> firstThreeFridays(YearMonth ym) {
    LocalDate firstFriday = ym.atDay(1).with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY));
    return Stream.of(firstFriday, firstFriday.plusWeeks(1), firstFriday.plusWeeks(2));
}

结果和之前一样。

(迭代几个月的想法也在 Basil Bourque 现在已删除的答案中。)

【讨论】:

    【解决方案2】:

    另一种解决方案:您可以 GroupBy month 你的日期(到 Map> ),然后如果有超过 3 个 date 的每个月键,则将其删除并返回结果:

    public static void main(String[] args) {
        // initialize your date here:
        LocalDate now = LocalDate.now();
        // Grouping Dates By Month 
        Map<Month, List<LocalDate>> map = Stream
                .iterate(LocalDate.of(now.getYear(), now.getMonth(), 1),
                        localDate -> localDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)))
                .limit(20).collect(Collectors.groupingBy(element -> element.getMonth()));
        // delete the 4 or 5 fridays
        map.forEach((k, v) -> {
            if (v.size() > 3)
                v.removeIf(e -> e.isBefore(now) || v.indexOf(e) > 2);
        });
        // print results sorted
        Stream.of(map.values()).flatMap(e -> e.stream()).flatMap(List::stream).sorted().limit(10)
                .forEach(System.out::println);
    }
    

    输出

    2021-11-05

    2021-11-12

    2021-11-19

    2021-12-03

    2021-12-10

    2021-12-17

    2022-01-07

    2022-01-14

    2022-01-21

    2022-02-04

    【讨论】:

    • 感谢您的帮助。但我需要排除一个月的第 4 个和第 5 个星期五。例如,在 10 月,2021-10-222021-10-29 不应包含在我的结果中。那就是我需要前三个星期五,而不是任何三个星期五。
    • @wannaBeDev 对不起,忘记处理当月,代码更新了
    【解决方案3】:

    获取本地日期的ALIGNED_WEEK_OF_MONTH时间域​​,判断是否≤3。

    Stream.iterate(now , localDate -> localDate.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)))
        .skip(1) // skip today
        .filter(x -> x.get(ChronoField.ALIGNED_WEEK_OF_MONTH) <= 3)
        .limit(10)
        .forEach(System.out::println);
    

    【讨论】:

    • 非常感谢。我已经测试了您的解决方案并且有一个问题。它包括2021-10-22。我想我需要配置我的一周从哪一天开始以及一周至少有多少天。根据文档,WeekFields.ISO.weekOfMonth() 的返回值取决于getFirstDayOfWeek()getMinimalDaysInFirstWeek()。我的一周从星期一开始,不确定一周中的最少天数。第一个星期五应该在第一周。
    • @wannaBeDev 啊,如果你是这样计算星期五的,那么使用ChronoField.ALIGNED_WEEK_OF_MONTH 可能会更好。而不是WeekFields.ISO.weekOfMonth()
    • 完美。再次感谢您。
    猜你喜欢
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多