【问题标题】:LocalDateTime Java - Get same day of week for next monthLocalDateTime Java - 获取下个月的同一天
【发布时间】:2019-08-26 22:20:34
【问题描述】:

我有一个月的日期和时间,例如31/01/2020 at 14:00:00,这是一月的最后一个星期五。如何获取 2 月、3 月等的最后一个星期五的日期?它应该是动态的,因为可以输入任何日期,例如任何月份的第二个星期二等等。

我正在尝试以下方法,但没有成功:

LocalDateTime startTime = LocalDateTime.of(2020, 1, 31, 14, 0, 0);

final Calendar calendar = Calendar.getInstance();
calendar.set(startTime.getYear(), startTime.getMonthValue() - 1, startTime.getDayOfMonth(), startTime.getHour(), startTime.getMinute(), startTime.getSecond());

int ordinal = calendar.get(Calendar.WEEK_OF_MONTH);
startTime = startTime.plusMonths(1).with(TemporalAdjusters.dayOfWeekInMonth(ordinal, startTime.getDayOfWeek();
System.out.println(startTime);

它正在打印06/03/2020 (six of march) at 14:00:00,这是错误的,应该是28/02/2020

我错过了什么?

谢谢!

【问题讨论】:

  • 添加一个月,然后调整日期以获得该月的最后一个星期五:docs.oracle.com/javase/8/docs/api/java/time/temporal/…。停止使用旧的、丑陋的 Calendar 类。
  • @JBNizet 它可以是一个月中的任何一天,而不仅仅是上周五。可以是第一个星期一、第三个星期六等
  • 这个要求是不现实的。假设我是本月的第四个星期一。这也是那个月的最后一个星期一。现在假设我想要下个月的下一个“相似的日子”。我应该选择第四个星期一还是最后一个星期一?如果这个月恰好有 5 个星期一,那就不是同一天。
  • 如果你知道你想要的是最后一个星期一,那么使用一个可以为你提供最后一个星期一的调节器。如果您知道您想要第 4 个星期一,那么请使用可以为您提供第 4 个星期一的调节器。那将是两个不同的调节器。
  • 您的问题不清楚,可能需要重写。特别是,“last Friday”与序数“4th Friday”或“5th Friday”不是一回事。

标签: java spring-boot date datetime


【解决方案1】:

如前所述,您指的是一周中的哪一天存在一些歧义,也就是说,您是指一周中的第 n 天还是最后一个 n每月的第几天。

一个这样的例子是 2020 年 2 月 24 日星期一。它是 2020 年 2 月的第四个也是最后一个星期一。如果您要尝试确定 2020 年 3 月的时间,您会选择哪个星期一?第四个星期一是 3 月 23 日,但最后一个星期一是 3 月 30 日。

显然,您需要区分是向前计数还是向后计数。

例如,您可以创建一个类来表示一个月中的某一天。这包含三个字段:星期几、位置以及位置是否向后。例如

  • “每月的第二个星期一”会有

    dayOfWeek = DayOfWeek.MONDAY
    position = 2
    backwards = false
    

  • “本月的最后一个星期四”会有

    dayOfWeek = DayOfWeek.THURSDAY
    position = 1
    backwards = true
    
public class WeekdayInMonth {

    private final boolean backwards;

    private final DayOfWeek dayOfWeek;

    private final int position;

    private WeekdayInMonth(DayOfWeek dayOfWeek, int position, boolean backwards) {
        if (position < 1 || position > 5) {
            throw new DateTimeException("Position in month must be between 1 and 5 inclusive");
        }
        this.dayOfWeek = dayOfWeek;
        this.position = position;
        this.backwards = backwards;
    }
}

我们可以添加工厂方法来从LocalDates 创建WeekdayInMonths:

public static WeekdayInMonth of(LocalDate date) {
    int positionInMonth = (date.getDayOfMonth() - 1) / 7 + 1;
    return new WeekdayInMonth(date.getDayOfWeek(), positionInMonth, false);
}

private static WeekdayInMonth ofReversing(LocalDate date) {
    int lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
    int positionInMonth = (lastDayOfMonth - date.getDayOfMonth()) / 7 + 1;
    return new WeekdayInMonth(date.getDayOfWeek(), positionInMonth, true);
}

最后,我们添加了一个方法来从调整为 WeekdayInMonth 的 YearMonth 中获取 LocalDate

public LocalDate toLocalDate(YearMonth yearMonth) {
    // Get a temporal adjuster to adjust a LocalDate to match a day-of-the-week
    TemporalAdjuster adjuster = this.backwards ? TemporalAdjusters.lastInMonth(this.dayOfWeek) : TemporalAdjusters.firstInMonth(this.dayOfWeek);

    int weeks = this.position - 1;
    LocalDate date = yearMonth.atDay(1)
            .with(adjuster)
            .plusWeeks(this.backwards ? 0 - weeks : weeks);
    if (!Objects.equals(yearMonth, YearMonth.from(date))) {
        throw new DateTimeException(String.format("%s #%s in %s does not exist", this.dayOfWeek, this.position, yearMonth));
    }
    return date;
}

工作示例

Here a working example at Ideone.


附录

如果初始日期是 2020 年 1 月 1 日,我会收到这样的错误:java.time.DateTimeException: FRIDAY #5 in 2020-02 不存在。如果发生这种情况,我怎么能得到前一个工作日?在这种情况下,我将如何获得上一个星期五?

那么,您需要调整您的 LocalDate 以使其在指定的年月内。由于每个月至少有 4 天且不超过 5 天,因此差异永远不会超过一周。我们可以在删除throw new DateTimeException 行之后,使用plusWeeks 简单地调整返回的LocalDate。

我已经 forked the abovementioned example 并添加了 toAdjustingLocalDate 方法。

【讨论】:

  • 谢谢,这是我在前进版本中所需要的,但它也很酷!
  • 如果初始日期是 2020 年 1 月 1 日,我会收到这样的错误:java.time.DateTimeException: FRIDAY #5 in 2020-02 不存在
  • @Wrong 这完全合乎逻辑,因为二月份的星期五从不超过四个。您需要处理该异常。
  • 如果发生这种情况,我怎样才能得到前一个工作日?在这种情况下,我将如何获得上一个星期五?
【解决方案2】:

这个解决方案有点复杂,但这是因为“最后一个”或“第三个”等并不总是很好定义,在某些情况下甚至可能不存在。因此,这里有一个解决方案,它着眼于初始日期,并根据月份中的哪一天执行计算,从月初开始向前计算,或者在月底向后计算。

从我的测试来看,它似乎产生了正确的结果,我确信也可以进行一些代码重构以改进代码,但我将其留给读者。

public static LocalDateTime nextWithSameDayOfMonth(LocalDateTime indate) {
    if (indate.getDayOfMonth() < 15) {
        return getForStartOfMonth(indate);
    } 

    return getForEndOfMonth(indate);
}

private static LocalDateTime getForEndOfMonth(LocalDateTime indate) {
    DayOfWeek dayOfWeek = indate.getDayOfWeek();
    LocalDateTime workDate = indate.with(TemporalAdjusters.lastDayOfMonth());
    int count = 0;
    while (workDate.isAfter(indate)) {
        count++;
        workDate = workDate.minusWeeks(1);
    }

    LocalDateTime nextDate = indate.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
    while (nextDate.getDayOfWeek() != dayOfWeek) {
        nextDate = nextDate.minusDays(1);
    }

    return count == 0 ? nextDate : nextDate.minusWeeks(count - 1);
}

private static LocalDateTime getForStartOfMonth(LocalDateTime indate) {
    DayOfWeek dayOfWeek = indate.getDayOfWeek();
    LocalDateTime workDate = indate.with(TemporalAdjusters.firstDayOfMonth());
    int count = 0;
    while (workDate.isBefore(indate)) {
        count++;
        workDate = workDate.plusWeeks(1);
    }

    LocalDateTime nextDate = indate.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
    while (nextDate.getDayOfWeek() != dayOfWeek) {
        nextDate = nextDate.plusDays(1);
    }

    return count == 0 ? nextDate : nextDate.plusWeeks(count - 1);
}

【讨论】:

    【解决方案3】:

    你能检查一下这个功能是否适合你吗?

    public class FindSameDayNextMonth {
        public static void main(String[] args) {
            System.out.println("Next month of 'today' is " + FindSameDayNextMonth.getSameDayNextMonth());
        }
    
        public static Date getSameDayNextMonth() {
            LocalDateTime dt = LocalDateTime.now();
    
            Calendar c = Calendar.getInstance();
            c.set(Calendar.MONTH, dt.getMonthValue()-1);
            c.set(Calendar.DAY_OF_MONTH, dt.getDayOfMonth());
            c.add(Calendar.MONTH, 1);
    
            return c.getTime();
        }
    }
    

    输出是 今天的下个月是 Mon Sep 23 07:18:09 CDT 2019

    【讨论】:

    • 无需将可怕的遗留Calendar 类与现代的java.time 类混合。仅使用 java.time.
    • 这个答案肯定会误入歧途。当您可以使用现代的LocalDateTime 时,没有任何理由引入设计不佳且早已过时的Calendar 类。 java.time 是现代 Java 日期和时间 API,提供您需要的所有功能。
    猜你喜欢
    • 1970-01-01
    • 2013-12-24
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多