Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

Java的localdate如何调整一周起始日为周日

本来为了老外的习惯把周日定为一周的起始日,写着写着发现LocalDate计算时是把周一当作起始日的
没出过国全凭小学讲的周日是起始日
如下是计算2020-10-21上一周的周日
本来以为值应该是2020-10-11
但实际输出的值是2020-10-18

LocalDate date = LocaDate.of(2020,10,21);
date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));

这个Localdate有哪个接口能快速调整吗,哭了


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

外国的文化习惯和外国的代码设计其实还是有很大差别的,毕竟代码是全球的人都要用的,是不能强行说偏袒某一方的(不像现在的漂亮国,扯远了),只能说代码里唯一借鉴了外国的只是用字母而已,其实代码设计是一个逻辑思考过程,题主太焦急了一点,不妨,我们慢慢来看

首先是TemporalAdjusters.previous方法,在该方法的注释中已经提到了,该方法采用的时间字段是ChronoField.DAY_OF_WEEK,也就是按照一周7天来算,也就是满打满算的一周七天,并且返回上一次出现的你传入的DayOfWeek
image.png

注意这里说的是上一次的,而不是上一周的

有区别么?当然有,举个例子,还是用你的LocaDate.of(2020,10,21),虽然

TemporalAdjusters.previous(DayOfWeek.SUNDAY)

最终返回的是2020-10-18,那如果填入的是DayOfWeek.MONDAY,也就是上一个周一,会是多少呢?难道会是2020-10-12日么?也就是上一周周一。不是的,答案其实是2020-10-19,也就是上一个周一,不是上一周的周一

image.png

所以首先在方法选型上,可能就不太对口,更不用谈是不是周一开始一周还是周日开始一周了,用这个方法是完成不了根据一个时间取到上一周的某个星期的问题

(如果不想看下面的思路,可能写的有点绕,容易混乱,可以直接拖到最下面查看最终代码)

当然也不是说TemporalAdjusters.previous方法完全没用,起码在注释中已经说到,这样的处理适用于很多日历系统,算是比较通用的方法吧

不说TemporalAdjusters.previous了,咱们来看看题主要解决的问题如何操作。

我觉得关键就是在于上一个和上一周的区别

  • 上一个:只是说往前遍历,找到第一次出现
  • 上一周:应该首先给予一个周定义,它应该定义在更大范围内,比如月份里的第几周,这个周定义是相对于DayOfWeek的,比DayOfWeek更大范围,在更大范围的定位里,找到上一周,然后再回到上一周中,根据DayOfWeek定义里需要修改的数值进行修改。当然你也可以先修改DayOfWeek定义里需要的数值,然后再找到上一周。顺序肯定是可以颠倒的

所以说从理解上来说是有2步要做的。如果是对Java8时间API设计比较了解的话,应该知道,在API设计中描述飘渺的时间,主要就是靠定义,也就是时间字段的定义,一个时间,用不同的时间字段去描述,是可以获取到不同的值,这也就是时间字段+数值=时间

在时间顶层接口TemporalAccessorget方法就可以见一斑
image.png

那时间字段TemporalField由于它是定义,所以时间定义上肯定会有单位,也就是TemporalUnit,以及范围,比如一个分钟有60秒,一周有7天,一年有365天等等,这都是基于某个单位下的数值范围。

所以我们现在需要的就是构造一个周定义,单位肯定是天,范围也是7天,但是最小一周也有1天,不像之前提到的的ChronoField.DAY_OF_WEEK,它的最小一周也有7天,因为是满打满算嘛

那这个定义我在另一个回答里有提到就是参考WeekFields的静态变量SUNDAY_START
image.png

WeekFields可以简单理解为构建自定义一周的工具类,里面有个静态变量SUNDAY_START就是以周日为起点,最小一周只有1天的WeekFields定义了

此时还不是周定义,因为周定义需要在月份中,而恰好WeekFields提供了方法weekOfMonth

TemporalField weekOfMonthTemporalField = WeekFields.SUNDAY_START.weekOfMonth();

weekOfMonthTemporalField才是我们要找的,以周日为一周起点的更大范围下的周定义

有了它,我们就可以构造一个TemporalAdjuster了,用于把参数日期按照weekOfMonthTemporalField的定义调整到上一周

TemporalAdjuster temporalAdjuster = temporal -> temporal.minus(1, weekOfMonthTemporalField.getBaseUnit());

我们这里采用了Temporalminus方法,减去一个1个weekOfMonthTemporalField定义下的单位,因为之前我们提到了TemporalField本来就是由TemporalUnit组成的,所以这样操作是合理的

当然由于这里是时间单位,不是时间字段,所以不论是周一开始的周,还是周日开始的周,它们的单位都是周,而周在Java8API中是有枚举的,也就是ChronoUnit其中的WEEKS,所以我们也可以这么写

TemporalAdjuster temporalAdjuster = temporal -> temporal.minus(1, ChronoUnit.WEEKS);

其实就是日期减了一周,然后减了一周之后,我们在按照要求把时间调整(with方法,带TemporalField参数的)到传入的DayOfWeek即可,调整需要涉及时间字段,那我们就从刚才的周日开始的WeekFields定义取出DayOfWeek的定义,也就是

TemporalField dayOfWeekTemporalField = WeekFields.SUNDAY_START.dayOfWeek()

那这样,我们的TemporalAdjuster就变为

TemporalAdjuster temporalAdjuster = temporal -> temporal.minus(1, ChronoUnit.WEEKS)
                                                        .with(dayOfWeekTemporalField, 1);

为啥填入的是1,因为dayOfWeekTemporalField是时间字段,是定义,在这个定义下,周日是第一天,所以这个with方法含义就是按照dayOfWeekTemporalField的定义修改该字段值为1

整合一下完整的代码:

LocalDate date = LocalDate.of(2020,10,21);
TemporalField dayOfWeekTemporalField = WeekFields.SUNDAY_START.dayOfWeek();
TemporalAdjuster temporalAdjuster = temporal -> temporal.minus(1, WEEKS)
                                                        .with(dayOfWeekTemporalField, 1);
LocalDate newLocalDate = date.with(temporalAdjuster);
System.out.println(newLocalDate);

你也可以自己模仿TemporalAdjusters创建一个属于自己的TemporalAdjuster工厂CustomTemporalAdjusters

public class CustomTemporalAdjusters {
    public static TemporalAdjuster sundayOfPreviousWeek() {
        return temporal -> temporal.minus(1, WEEKS)
                                   .with(WeekFields.SUNDAY_START.dayOfWeek(), 1);
 }
}

然后调用起来就比较方便了

LocalDate date = LocalDate.of(2020,10,21);
LocalDate newLocalDate = date.with(CustomTemporalAdjusters.sundayOfPreviousWeek());
System.out.println(newLocalDate);

但是这样做CustomTemporalAdjusters中的处理还是有点局限性,万一想看上一周的周三,周四呢?现在只能处理周日,所以我们再稍微改一下就可以了,主要修改最后的调整方法,也就是with(WeekFields.SUNDAY_START.dayOfWeek(), 1);,这里为什么是1,是因为在WeekFields.SUNDAY_START.dayOfWeek()的定义下是1,也就是DayOfWeek.SUNDAYWeekFields.SUNDAY_START.dayOfWeek()定义为1,所以我们只要把需要查看的DayOfWeek放到WeekFields.SUNDAY_START.dayOfWeek()的定义里获取值即可,这个值就是需要修改的。也就是用到我们TemporalAccessorget方法

最终代码应该是这样的

public class CustomTemporalAdjusters {
    public static TemporalAdjuster sundayOfPreviousWeek() {
        return dayOfPreviousWeek(DayOfWeek.SUNDAY);
 }
    public static TemporalAdjuster dayOfPreviousWeek(DayOfWeek dayOfWeek) {
        TemporalField temporalField = WeekFields.SUNDAY_START.dayOfWeek();
        return temporal -> temporal.minus(1, WEEKS)
                                   .with(temporalField, dayOfWeek.get(temporalField));
 }
}

调用的时候,两种方法都可以

LocalDate date = LocalDate.of(2020,10,21);
date.with(CustomTemporalAdjusters.dayOfPreviousWeek(DayOfWeek.SUNDAY));
date.with(CustomTemporalAdjusters.sundayOfPreviousWeek());

以上就是我的思路和答案,希望能对你有所帮助,拜了个拜(′▽`〃)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...