【问题标题】:How to find n'th previous Sunday with Java date time API or Joda-time?如何使用 Java 日期时间 API 或 Joda-time 查找前第 n 个星期日?
【发布时间】:2017-06-02 05:36:43
【问题描述】:

我们如何计算上周日或上周日之前的,或者一般来说让我们说如何在 n 周之前找出周日?问题是如果今天是星期天,那么它应该在今天返回星期天,而不是上周。

寻找 Joda-Time 或 Java 8 时间解决方案。

编辑: 我试过了

DateTime sunday = now
    .minusWeeks(1)
    .withDayOfWeek(DateTimeConstants.SUNDAY)
    .wi‌​thTimeAtStartOfDay()‌​;
DateTime previousWeekSunday = now
    .minusWeeks(2)
    .withDayOfWeek(DateTimeConstants.SATURDAY)
    .‌​withTime(23, 59, 59, 999); 

但是如果当前是星期天,那么这个逻辑就会失败,因为它没有给出今天的日期。

【问题讨论】:

  • 我建议先阅读docs.oracle.com/javase/tutorial/datetime 的教程,然后展示你的尝试。
  • 看来你应该更喜欢 Java 8 而不是 Joda-Time。 The Joda-Time homepage 说:“现在要求用户迁移到 java.time (JSR-310)。”
  • 您的搜索和研究发现了什么?您已经尝试过什么(以及以什么方式失败)?
  • 试过 DateTime sunday = now.minusWeeks(1).withDayOfWeek(DateTimeConstants.SUNDAY).withTimeAtStartOfDay(); DateTime previousWeekSunday = now.minusWeeks(2).withDayOfWeek(DateTimeConstants.SATURDAY).withTime(23, 59, 59, 999);但是如果当前是星期天,那么这个逻辑就会失败,因为它没有给出今天的日期
  • 非常感谢您代表我这样做

标签: java date jodatime java-time dayofweek


【解决方案1】:

你基本上需要检查今天是否是星期天,如果不是,然后回顾上一个......(或者如果你需要上一个,则递归地将日期移回那个......)

使用 java 8 你需要:


LocalDate date = LocalDate.now();
DayOfWeek todayAsDayOfWeek = date.getDayOfWeek();
LocalDate prevSun = todayAsDayOfWeek == DayOfWeek.SUNDAY ? date : date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
System.out.println(prevSun);

编辑:previousOrSame 方法将跳过对星期几的检查

    LocalDate date = LocalDate.now();
    LocalDate prevSun = date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));

    prevSun = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
    System.out.println(prevSun);

【讨论】:

  • TemporalAdjuster 中的 previousOrSame 只需一步即可完成相同的操作。
  • 在找到上一个星期日(如果是星期日,则为今天)后,您可以使用prevSun.minusWeeks(n) 倒数 n 个星期日。
  • 一个可能会有所作为的细节:我更喜欢将参数传递给LocalDate.now() 以明确时区。即使在您通过ZoneId.systemDefault() 的情况下。通过这种方式,您可以告诉读者以及您自己,您已经考虑过时区并决定了您想要的时区。
  • 您可能可以从答案中删除第一个代码 sn-p。
【解决方案2】:

已经用 Java 8 方法回答了这个问题,但为了记录,这里是 Joda-Time 的解决方案(也是我为 Java 8 支付的 2 美分)。

如果我理解正确,获取上周日第 nth 个的一般算法是:

  • 如果当前日期已经是星期日,则返回n-1 周(因此对于n=1,它返回相同的日期)
  • 否则,查找从该日期开始的第 nth 个周日

我创建了一个接收n(周数)和DateTime(开始日期)的方法。代码是:

// get the n'th previous Sunday, from the given DateTime
public DateTime nPreviousSunday(int n, DateTime dateTime) {
    // avoid zero or negative numbers (optional, see if it fits your use cases)
    if (n <= 0) {
        return dateTime; // return the same date
    }

    DateTime d = dateTime;

    // get first previous (or same) Sunday
    int dow = d.getDayOfWeek();
    if (dow != DateTimeConstants.SUNDAY) { // not a Sunday, adjust the day to the previous one
        int diff = DateTimeConstants.SUNDAY - dow;
        // DateTimeConstants.SUNDAY is 7, so diff is always positive
        // d is (7 - diff) days ahead of Sunday, adjusting
        d = d.minusDays(7 - diff);
    }

    // find the n'th previous (considering that the first was already found above)
    d = d.minusWeeks(n - 1);

    return d;
}

以下是04/06/2017(星期日)的测试。对于n=1,它返回04/06/2017,对于n &gt;= 2,它找到从该日期起的第nth个星期日(考虑到04/06/2017本身是第一个):

System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 4, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(2, new DateTime(2017, 6, 4, 10, 0))); // 2017-05-28
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 4, 10, 0))); // 2017-05-21

测试05/06/2017(不是星期日),得到相同的结果(因为前一个星期日是04/06/2017):

System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 5, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(2, new DateTime(2017, 6, 5, 10, 0))); // 2017-05-28
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 5, 10, 0))); // 2017-05-21

测试整个星期直到星期六 (10/06/2017):

System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 6, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 7, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 8, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 9, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 10, 10, 0))); // 2017-06-04

System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 6, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 7, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 8, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 9, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 10, 10, 0))); // 2017-05-21

PS:我用的是DateTime,不过你也可以用这段代码来换成org.joda.time.LocalDate或者org.joda.time.LocalDateTime(算法是一样的,只是改变变量的类型在方法中)。


Java 8 方法(我的 2 美分)

在 Java 8 中,您可以将TemporalAdjuster 用作already answered。但就我的 2 美分而言,您可以创建一个返回 TemporalAdjuster 的方法,然后您可以将它与任何 java-time 类型一起使用:

// get the n'th previous dayOfWeek, from the given temporal
public TemporalAdjuster previous(int n, DayOfWeek dayOfWeek) {
    return (temporal) -> {
        // avoid zero or negative numbers (optional, see if it fits your use cases)
        if (n <= 0) {
            return temporal; // return the same temporal
        }

        // get first previous (or same) dayOfWeek
        Temporal t = temporal.with(TemporalAdjusters.previousOrSame(dayOfWeek));

        // find the n'th previous (considering that the first was already found above)
        t = t.minus(n - 1, ChronoUnit.WEEKS);

        return t;
    };
}

所以你可以这样使用它:

System.out.println(LocalDate.of(2017, 6, 4).with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04
System.out.println(LocalDate.of(2017, 6, 4).with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28
System.out.println(LocalDate.of(2017, 6, 4).with(previous(3, DayOfWeek.SUNDAY))); // 2017-05-21

System.out.println(LocalDate.of(2017, 6, 5).with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04
System.out.println(LocalDate.of(2017, 6, 5).with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28
System.out.println(LocalDate.of(2017, 6, 5).with(previous(3, DayOfWeek.SUNDAY))); // 2017-05-21

好消息是它也适用于其他类型:

LocalDateTime dt = LocalDateTime.of(2017, 6, 4, 10, 0);
System.out.println(dt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00
System.out.println(dt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00

ZonedDateTime zdt = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo"));
System.out.println(zdt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00-03:00[America/Sao_Paulo]
System.out.println(zdt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00-03:00[America/Sao_Paulo]

OffsetDateTime odt = OffsetDateTime.of(dt, ZoneOffset.ofHours(2));
System.out.println(odt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00+02:00
System.out.println(odt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00+02:00

由于previous()方法返回一个TemporalAdjuster,你不需要每次都调用它,只需将调整器存储在一个变量中并重复使用:

TemporalAdjuster thirdPreviousSunday = previous(3, DayOfWeek.SUNDAY);
System.out.println(LocalDate.of(2017, 6, 4).with(thirdPreviousSunday)); // 2017-05-21
System.out.println(LocalDate.of(2017, 6, 5).with(thirdPreviousSunday)); // 2017-05-21

这种方法的另一个优点是:代码变得更加清晰 (IMO),并且适用于一周中的任何一天。


PS:如果类型没有DayOfWeek 字段(如LocalTime,只有小时/分钟/秒/纳秒),下面的代码将引发异常:

// throws UnsupportedTemporalTypeException (because LocalTime doesn't have the DayOfWeek field)
LocalTime.now().with(previous(1, DayOfWeek.SUNDAY));

只是提醒一下现有的调整器已经发生了这种情况:

// also throws exception (Unsupported field: DayOfWeek)
LocalTime.now().with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));

这是有道理的,因为LocalTime 没有日期字段并且不知道工作日。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-08
    • 2014-03-24
    • 1970-01-01
    • 2017-11-28
    • 2014-01-04
    • 1970-01-01
    • 2015-06-27
    • 2021-05-24
    相关资源
    最近更新 更多