Java 日历添加了一个随机的毫秒数?

Java Calendar adds a random number of milliseconds?

嗨,我遇到了一些奇怪的事情。我只是获取一个日历对象,将其转换为各个部分,然后将其放回日历中(有或没有任何更改。在这种情况下我不做任何更改)。我的意思是,这应该被剪切和粘贴。我还尝试使用 calendar = Calendar.getInstance() 创建日历并手动设置所有内容。 calendar.set(Calendar.YEAR, mStartYear);很快。仍然给出错误的 Calendar 对象。我也试过设置毫秒,似乎总是有一些垃圾毫秒..但是时间以某种方式完全关闭。也许有人看到了愚蠢的疏忽,但我很难过。

这也是一个 android 应用程序,但对于基本 Java 库对象应该无关紧要。

注意 Weekview 是我正在使用的其中一个库的数据包装器。它有一个开始日历和一个结束日历。

这是调试器在内存中列出的内容。

mEndDay = 19
mEndHour = 9

mEndMinute = 30
mEndMonth = 8
mEndYear = 2015
mSeekAmount = 0
mStartDay = 18
mStartHour = 23
mStartMinute = 0
mStartMonth = 8
mStartYear = 2015


            Calendar calendarStart = Calendar.getInstance();
            calendarStart.set(mStartYear,mStartMonth,mStartDay,mStartHour,mStartMinute);

            Calendar calendarEnd = Calendar.getInstance();
            calendarEnd.set(mEndYear,mEndMonth,mEndDay,mEndHour,mEndMinute);

我最终得到

Start 1442363359161
End 1442363359161

calendarStart = {GregorianCalendar@20968} "java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=258,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=6,HOUR_OF_DAY=23,MINUTE=0,SECOND=19,MILLISECOND=161,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
calendarEnd = {GregorianCalendar@20969} "java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=19,DAY_OF_YEAR=258,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=6,HOUR_OF_DAY=9,MINUTE=30,SECOND=19,MILLISECOND=161,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"

期待

Start 1442638800000
End 1442676600000

mEndTime = {GregorianCalendar@20990} "java.util.GregorianCalendar[time=1442676600000,areFieldsSet=true,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=19,DAY_OF_YEAR=262,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=9,HOUR_OF_DAY=9,MINUTE=30,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
mName = {String@20991} "sleep"
mStartTime = {GregorianCalendar@20992} "java.util.GregorianCalendar[time=1442638800000,areFieldsSet=true,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=261,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"

这是我最初设置它的地方..

   WeekViewEvent weekViewEvent = dateWrapperParam.getWeekViewEvent();
    Calendar endCalendar = weekViewEvent.getEndTime();
    Calendar startCalendar = weekViewEvent.getStartTime();

    Date endDate = endCalendar.getTime();
    Date startDate = startCalendar.getTime();

    mStartHour = startCalendar.get(Calendar.HOUR_OF_DAY);
    mStartMinute = startCalendar.get(Calendar.MINUTE);
    mStartDay = startCalendar.get(Calendar.DAY_OF_MONTH);
    mStartMonth = startCalendar.get(Calendar.MONTH);
    mStartYear = startCalendar.get(Calendar.YEAR);

    mEndHour = endCalendar.get(Calendar.HOUR_OF_DAY);
    mEndMinute = endCalendar.get(Calendar.MINUTE);
    mEndDay = endCalendar.get(Calendar.DAY_OF_MONTH);
    mEndMonth = endCalendar.get(Calendar.MONTH);
    mEndYear = endCalendar.get(Calendar.YEAR);

根据我在你的问题下的评论,startCalendarcalendarStart 时间之间只有秒和毫秒的差异,因为这些值没有被重置。

参见 DEMO

Calendar.set的文档中说:

Sets the values for the fields YEAR, MONTH, DAY_OF_MONTH, HOUR, MINUTE, and SECOND. Previous values of other fields are retained. If this is not desired, call clear() first.

原因是并非所有字段都使用此方法设置,在您的情况下,您没有设置 MILLISECOND。所以它保留实例创建时的值。

Calendar.clear的调用将

Sets all the calendar field values and the time value (millisecond offset from the Epoch) of this Calendar undefined.

一个简单的例子:

    Calendar c = GregorianCalendar.getInstance();
    c.clear();
    c.set(2019, Calendar.NOVEMBER, 03, 16, 15, 03);

    System.out.println(c.getTime());
    System.out.println(c.getTimeInMillis());

Sun Nov 03 16:15:03 CET 2019
1572794103000

未定义的毫秒将给出 0

java.time 和 ThreeTenABP

我建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。例如:

    ZonedDateTime start = ZonedDateTime.now(ZoneId.systemDefault());
    ZonedDateTime end = start;
    
    System.out.println("Start: " + start);
    System.out.println("End:   " + end);

我刚才运行我所在时区的代码输出:

Start: 2020-06-24T19:24:04.811+02:00[Europe/Copenhagen]
End:   2020-06-24T19:24:04.811+02:00[Europe/Copenhagen]

A ZonedDateTime 是某个时区的日期和时间。这是我们最接近 GregorianCalendar 的现代等价物(您的代码为您提供的 Calendar 的子 class)。使用哪个现代 class 因更精确的要求而异,因此有时您更喜欢使用 LocalDateOffsetDateTime 甚至 LocalTime.

要将值截断为整分钟(将秒和秒的小数部分设置为 0):

    ZonedDateTime start = ZonedDateTime.now(ZoneId.systemDefault())
            .truncatedTo(ChronoUnit.MINUTES);
Start: 2020-06-24T19:24+02:00[Europe/Copenhagen]

ZonedDateTime 和 java.time 的其他 classes 提供了很多方法来修改获得的值。例如:

    ZonedDateTime end = start.plusDays(2).withHour(13);
End:   2020-06-26T13:24+02:00[Europe/Copenhagen]

如果您想仅使用开始时间的选定字段手动创建结束时间:

    ZonedDateTime end = ZonedDateTime.of(
            2021, start.getMonthValue(), start.getDayOfMonth(),
            start.getHour(), 30, 0, 0, start.getZone());
End:   2021-06-24T19:30+02:00[Europe/Copenhagen]

你的代码出了什么问题?

部分答案已经在其他答案中:Calendarset 方法仅设置他们承诺设置的字段,并尽可能保持其他字段不变。虽然这可能是 set​(int field, int value) 方法所期望的,但 set​(int year, int month, int date) 方法常常令人惊讶,set​(int year, int month, int date, int hourOfDay, int minute)set​(int year, int month, int date, int hourOfDay, int minute, int second) 方法更是如此。总的来说,虽然 Calendar class 及其子 class 的设计很糟糕且令人困惑,并且使用起来很麻烦。这也是我上面推荐java.time的主要原因。

答案的另一部分是 Calendar 宽松地计算其字段。因此,当您在调用 set 后查看调试器中的 Calendar 对象时,它将包含大量垃圾值。调用 getTime() 会强制 Calendar 计算其字段,因此在调用之后它在调试器中的样子应该更有意义。同样,这是令人困惑的行为,有时不使用调试器也可以观察到它。

问题:java.time 不需要 Android API 26 级吗?

java.time 在新旧 Android 设备上都能很好地工作。它只需要至少 Java 6.

  • 在 Java 8 及更高版本和较新的 Android 设备(从 API 级别 26)中内置现代 API。
  • 在非 Android Java 6 和 7 中获取 ThreeTen Backport,现代 classes 的 backport(ThreeTen 用于 JSR 310;请参阅底部的链接) .
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 classes。

链接

当您初始化 Calendar 对象时,它正在获取当前时间,包括当前秒数和毫秒数。提供的代码设置了小时和分钟,但没有设置秒和毫秒,它保持了初始化 Calendar 对象时的状态。 为了将秒和纳秒设置为零,使用:

StartCalendar.set(Year, Month, DayofMonth, Hour, Minute, 0,0);