如何找到 Java 日期时间 API 或 Joda 时间的第 n 个星期日?
How to find n'th previous Sunday with Java date time API or Joda-time?
我们如何计算前一个星期日或前一个星期日之前的星期日,或者一般来说如何找出 n 周之前的星期日?要注意的是,如果今天是星期天,那么它应该 return 今天是星期天而不是上周。
正在寻找 Joda-Time 或 Java 8 时间解决方案。
编辑:
我试过了
DateTime sunday = now
.minusWeeks(1)
.withDayOfWeek(DateTimeConstants.SUNDAY)
.withTimeAtStartOfDay();
DateTime previousWeekSunday = now
.minusWeeks(2)
.withDayOfWeek(DateTimeConstants.SATURDAY)
.withTime(23, 59, 59, 999);
但是如果当前是星期天,那么这个逻辑就会失败,因为它没有给出今天的日期。
你基本上需要检查今天是否是星期天,如果不是,然后回头看上一个...(或者如果你需要前一个,则递归地向后移动日期...)
使用 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);
这个问题已经用 Java 8 的方法回答了,但是为了记录,这里有一个 Joda-Time 的解决方案(还有我的 Java 8 的 2 美分)。
如果我没理解错的话,获得第 n 个 前一个星期天的一般算法是:
- 如果当前日期已经是星期天,则返回
n-1
周(因此对于 n=1
,它 returns 是相同的日期)
- 否则,找到那个日期
的第 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
,它 returns 04/06/2017
,对于 n >= 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
(算法是一样的,只是在方法中改变变量的类型)。
Java8进场(我的2美分)
在Java8中你可以使用TemporalAdjuster
作为。但只是为了投入我的 2 美分,你可以创建一个 returns 一个 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()
方法returns一个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
,下面的代码将抛出异常只有 hour/minute/second/nanosecond):
// 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
没有日期字段并且不知道工作日。
我们如何计算前一个星期日或前一个星期日之前的星期日,或者一般来说如何找出 n 周之前的星期日?要注意的是,如果今天是星期天,那么它应该 return 今天是星期天而不是上周。
正在寻找 Joda-Time 或 Java 8 时间解决方案。
编辑: 我试过了
DateTime sunday = now
.minusWeeks(1)
.withDayOfWeek(DateTimeConstants.SUNDAY)
.withTimeAtStartOfDay();
DateTime previousWeekSunday = now
.minusWeeks(2)
.withDayOfWeek(DateTimeConstants.SATURDAY)
.withTime(23, 59, 59, 999);
但是如果当前是星期天,那么这个逻辑就会失败,因为它没有给出今天的日期。
你基本上需要检查今天是否是星期天,如果不是,然后回头看上一个...(或者如果你需要前一个,则递归地向后移动日期...)
使用 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);
这个问题已经用 Java 8 的方法回答了,但是为了记录,这里有一个 Joda-Time 的解决方案(还有我的 Java 8 的 2 美分)。
如果我没理解错的话,获得第 n 个 前一个星期天的一般算法是:
- 如果当前日期已经是星期天,则返回
n-1
周(因此对于n=1
,它 returns 是相同的日期) - 否则,找到那个日期 的第 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
,它 returns 04/06/2017
,对于 n >= 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
(算法是一样的,只是在方法中改变变量的类型)。
Java8进场(我的2美分)
在Java8中你可以使用TemporalAdjuster
作为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()
方法returns一个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
,下面的代码将抛出异常只有 hour/minute/second/nanosecond):
// 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
没有日期字段并且不知道工作日。