Java日历“ getTimeInMillis()”使所有“ isSet”字段为true

时间:2019-01-01 22:43:26

标签: java calendar

我对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。我究竟做错了什么?如果这是正确的行为(看起来很奇怪),是否有办法阻止它?

4 个答案:

答案 0 :(得分:3)

tl; dr

切勿使用糟糕的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.

java.time

可怕的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.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance 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中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 1 :(得分:2)

参考Calendar javadoc

  

获取和设置日历字段值

     

可以通过调用set方法来设置日历字段的值。在需要计算其时间值(距纪元为毫秒)或日历字段的值之前,不会解释日历中设置的任何字段值。调用get,getTimeInMillis,getTime,添加和滚动涉及这种计算。

通过调用calendar.getTimeInMillis()来设置值。这是正确的行为,您不能停止它。如Joe所述,如果可能,您应该切换到java.time软件包。

答案 2 :(得分:2)

通常,不应使用CalendarDate,因为它们是可变的,并且某些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资源中表达时间戳。烦恼和痛苦,但它满足了我的需要。

相关问题