我对Java日历的以下意外行为感到非常困惑
Calendar calendar = Calendar.getInstance();
calendar.clear();
//System.out(calendar.getTimeInMillis());
calendar.setLenient(false);
calendar.set(Calendar.YEAR, year);
如果我在“ clear()”之后调用getTimeInMillis(),则所有在clear()之后为false的isSet()字段将变为true。我究竟做错了什么?如果这是正确的行为(看起来很奇怪),是否有办法阻止它?
答案 0 :(得分:3)
切勿使用糟糕的Calendar
类。
使用现代的 java.time 类。这些immutable objects完全避免了您发现的问题。
ZonedDateTime // Represent a moment using the wall-clock time used by the people of a particular region (a time zone).
.now( // Capture the current moment.
ZoneId.systemDefault() // Get the JVM’s current default time zone. Beware: This can change at any moment during runtime. If crucial, confirm with user.
) // Returns a `ZonedDateTime` object.
.withYear( // Per the immutable objects pattern, produce another object based on the original’s values but with a change.
2001 // Change the year, but copy the month, day-of-month, hour, minute, second, fractional second, and time zone all the same. If the time-of-day is not valid on that date in the other year, auto-adjust per algorithm documented in the JavaDoc.
) // Returns another `ZonedDateTime` object.
可怕的Calendar
类已经过时,由现代的 java.time 类取代。
要使用UTC捕获当前时刻,请使用Instant
。
Instant instant = Instant.now();
从该Instant
中可以得到毫秒数,因为与Calendar
相同的纪元参考是:UTC 1970年的第一时刻,1970-01-01T00:00:00Z。谨防数据丢失,因为Instant
的分辨率为纳秒,比诸如Calendar
之类的传统日期时间类中使用的毫秒要精细得多。
long millisecondsSinceEpoch = instant.toEpochMilli() ;
您可以根据该号码创建一个Instant
。
Instant instant = Instant.ofEpochMilli( millisecondsSinceEpoch) ;
要查看某个特定地区的人们所使用的挂钟时间,请应用时区(ZoneId
)以获取ZonedDateTime
对象。
ZoneId z = ZoneId.of( "America/Montreal" ) // Or your JVM’s current default, ZoneId.systemDefault().
ZonedDateTime zdt = instant.atZone( z ) ;
要像在“问题”中一样更改年份,请致电withYear
。
ZonedDateTime zdtOtherYear = zdt.withYear( 2001 ) ;
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
获取和设置日历字段值
可以通过调用set方法来设置日历字段的值。在需要计算其时间值(距纪元为毫秒)或日历字段的值之前,不会解释日历中设置的任何字段值。调用get,getTimeInMillis,getTime,添加和滚动涉及这种计算。
通过调用calendar.getTimeInMillis()
来设置值。这是正确的行为,您不能停止它。如Joe所述,如果可能,您应该切换到java.time
软件包。
答案 2 :(得分:2)
通常,不应使用Calendar
或Date
,因为它们是可变的,并且某些API方法是不可预测的(例如,默认情况下宽松)。在现代代码库中,java.time
是更好的选择。
根据Calendar.isSet()
javadoc
确定给定日历字段是否设置了值, 包括值由内部字段设置的情况 由
get
方法调用触发的计算。
Calendar.getTimeInMillis()
可以将日历位置设置为当前时间,但这可能取决于Java版本。在以下OpenJDK 11.0.1中,代码仅打印false
,因此getTmeInMillis()
不会设置任何字段:
Calendar cal = Calendar.getInstance();
cal.clear();
IntStream.range(0, 17).mapToObj(cal::isSet).forEach(System.out::println);
cal.getTimeInMillis();
IntStream.range(0, 17).mapToObj(cal::isSet).forEach(System.out::println);
将cal.getTimeInMillis()
替换为cal.get(Calendar.YEAR)
会在第二个true
中打印所有IntStream
。
答案 3 :(得分:0)
由于我无法使用java.time程序包(我需要支持的Android版本不支持该程序包)和第三方程序包,因此我必须根据Calendar制作自己的类,而我必须独立跟踪设置了什么,没有设置什么。我需要保留此信息,以便以正确的时间精度在HL7 FHIR资源中表达时间戳。烦恼和痛苦,但它满足了我的需要。