Java 设置周的第天返回意外结果

Java 设置周的第天返回意外结果,java,calendar,Java,Calendar,我想将给定日历实例的时间戳设置为一周的开始(星期一),而不是返回一个看起来完全不相关的时间戳,除非我在这样做之前访问日历的任何字段。我在下面提供了一个示例,请参见中的这个可运行示例 这是预期的行为吗?这背后的逻辑是什么?是的,我听说过乔达时代 import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; class MyTest

我想将给定日历实例的时间戳设置为一周的开始(星期一),而不是返回一个看起来完全不相关的时间戳,除非我在这样做之前访问日历的任何字段。我在下面提供了一个示例,请参见中的这个可运行示例

这是预期的行为吗?这背后的逻辑是什么?是的,我听说过乔达时代

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;


class MyTest {

private static Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"), Locale.FRANCE);
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

public static void main(String[] args) {

    // Set to any date.
    calendar.set(2013, 10, 3);
    System.out.println(dateFormat.format(calendar.getTime()));

    // Set to another day.
    calendar.set(2014, 0, 15);
    // --- THE WTF STARTS HERE ---
    // Uncommenting the line below returns the correct date in the end.
    // calendar.getTime();

    // Set to monday of current week.
    calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());

    // Expected outdate is 20140113
    System.out.println(dateFormat.format(calendar.getTime()));

}

}

当日历保留值时,能否清除日历

例如:

这将返回:

20140113而不是您之前得到的20131223

从文档中:

作废java.util.Calendar.set(整年、整月、整日)

设置日历字段“年”、“月”和“月的天”的值。 其他日历字段的先前值将保留。如果不是这样的话 如果需要,请首先调用clear()


文档中的字段操作章节对此进行了清楚的解释。不过它的工作原理很奇怪

例如:考虑GregorianCalendar最初设定为1999年8月31日。使命感 set(Calendar.MONTH,Calendar.septer)将日期设置为1999年9月31日。 这是一个临时内部代表,如有必要,决议于1999年10月1日生效 然后调用getTime()。但是,在 调用getTime()将日期设置为1999年9月30日,因为不会重新计算 在set()本身之后

编辑

来自同一文档的日历字段解析部分

If there is any conflict in calendar field values, Calendar gives priorities to 
calendar fields that have been set more recently. The following are the default 
combinations of the calendar fields. The most recent combination, as determined 
by the most recently set single field, will be used.

For the date fields:

 YEAR + MONTH + DAY_OF_MONTH
 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
 YEAR + DAY_OF_YEAR
 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
我认为一周中的一个月和一天的区别是这样的。如果您在最后一条语句中设置了月份,它将与年+月+月的天匹配,并覆盖所有这些。如果设置周中的日,它将与年+周中的日+年中的周匹配,因此不会覆盖月值。或者类似的。老实说,我看起来越是伤心。这毫无意义。最好继续使用JodaTime

tl;博士 java.time
Calendar
类现在是遗留类,被java.time类取代,特别是
ZonedDateTime
。所以这个问题现在还没有定论

遗留类的问题之一是,周开始的定义因区域设置而不同,年、月和星期几的编号方案也不尽相同。相反,根据标准,java.time默认情况下总是将星期一视为一周的开始,一直运行到星期天。和java.time使用sane编号:

  • 2018
    是2018年,1900年算不上什么乱七八糟的数字
  • 1-12个月为1-12月
  • 周一至周日1-7
LocalDate
该类表示一个仅限日期的值,不包含一天中的时间和时区

时区对于确定日期至关重要。在任何一个特定的时刻,世界各地的日期都因地区而异。例如,中午夜后几分钟是新的一天,而中仍然是“昨天”

如果未指定时区,JVM将隐式应用其当前默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/预期时区明确指定为参数

大陆/地区
的格式指定,例如,或
太平洋/奥克兰
。切勿使用3-4个字母的缩写,如
EST
IST
,因为它们不是真正的时区,也不是标准化的,甚至不是唯一的(!)

如果要使用JVM的当前默认时区,请请求它并将其作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好是显式的,因为JVM中任何应用程序的任何线程中的任何代码都可能在运行时的任何时刻更改默认值

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.
或者指定一个日期。你可以用一个数字来设置月份,1-12月的数字为1-12

LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
或者,最好使用预定义的枚举对象,一年中每个月一个。提示:在整个代码库中使用这些
Month
对象,而不仅仅是一个整数,这样可以使代码更加自我记录,确保有效值,并提供更高的可用性

调节器 要从一个日期移动到另一个日期,请使用
TemporalAdjusters
类中的
TemporalAdjusters
实现。使用枚举指定所需的星期几


关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

从哪里获得java.time类

  • ,及以后
    • 内置的
    • 标准JavaAPI的一部分,带有捆绑实现
    • Java9添加了一些次要功能和修复
    • 大部分java.time功能都在中向后移植到Java6和Java7
    • 更高版本的Android捆绑包实现了java.time类
    • 对于早期的Android,该项目采用了ThreeTen Backport(如上所述)。看

该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,如、、和。

您可以这样设置第一天:calendar.setFirstDayOfWeek(0);calendar.set(calendar.DAY\u OF_WEEK,calendar.getFirstDayOfWeek());这将返回:20140111编辑:零是星期天等。是的,但我要的是星期一20140113。看起来像是
getTime
triggers
computeTime
内部。。。可能连续设置两个日期会弄乱日历的内部状态。有趣的是,它似乎适用于除周一以外的任何“一周的第一天”…日历和Joda Time都是过去式,被java.Time类取代。我可以这样做,但为什么呢
ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;
ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
LocalDate previousOrSameMonday = today.with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;