3

Hi I have something weird happening. I am simply taking a calendar object, converting it to its individual parts, and putting it back into a calendar (with or without any changes. In this case I make no changes). I mean, this should be cut and paste. I've also tried to create a calendar with calendar = Calendar.getInstance() and set everything manually. calendar.set(Calendar.YEAR, mStartYear); so on. Still gives wrong Calendar objects. I've tried also setting Milliseconds, always seem to have some garbage milliseconds.. But the time one way or another just is completely off. Maybe someone sees the stupid oversight, but I'm stumped.

Also this is an android application, but shouldn't matter for a basic Java library object.

Note Weekview is a datawrapper for one of the libraries I'm using. It has a start and an end calendar.

Here is what the debugger lists as in memory..

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);

I end up with

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]"

EXPECT

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]"

Here is where I set it up initially..

   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);
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
StarWind0
  • 1,554
  • 2
  • 17
  • 46
  • 5
    No it doesn't, you never reset the milliseconds when you call `set`. When you call `getInstance`, the `Calendar` is initialised to the CURRENT time in MILLISECONDS, unless you modify the value, it is never changed. If you have a look at the [JavaDocs](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#set-int-int-int-int-int-int-), it says *"Sets the values for the fields YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY, MINUTE, and SECOND. **Previous values of other fields are retained**. If this is not desired, call clear() first."* (emphasis added by me) – MadProgrammer Sep 16 '15 at 00:53
  • 1
    Read it: http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html then you will came to the conclusion that: "Calendar is a specific instant in time" which means it is not random it is that current instant in time. – Jorge Campos Sep 16 '15 at 00:53
  • 1
    So, instead, call `Calendar#clear` FIRST before you call `Calendar#set`. This is actually a known feature of `Calendar` – MadProgrammer Sep 16 '15 at 00:53
  • You are missing seconds and milliseconds in `Calendar` initialization, since the difference. – MaxZoom Sep 16 '15 at 00:58
  • @maxZoom I actually did try the one with millisecond initialization and set it to 0. As well as setting it post facto. But I will try it again. – StarWind0 Sep 18 '15 at 21:48
  • @maxzoom The clear fixed the issue. Make it an answer and I'll accept it. – StarWind0 Sep 19 '15 at 01:22
  • @jorge Campos Sigh yeah I didn't think of that. That was the root of the issue. – StarWind0 Sep 19 '15 at 01:22

4 Answers4

1

In the documentation of Calendar.set, it is said :

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.

The reason is that not all fields are set with this method, in you case, you don't have MILLISECOND set. So it keep the value when the instance was created.

The call of Calendar.clear will

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

A quick example :

    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

Milliseconds being undefined will give 0

AxelH
  • 14,325
  • 2
  • 25
  • 55
0

As per my comments under your question , there is only difference in seconds and milliseconds between startCalendar and calendarStart time, because that values were not reset.

See DEMO

MaxZoom
  • 7,619
  • 5
  • 28
  • 44
0

java.time and ThreeTenABP

I suggest that you use java.time, the modern Java date and time API, for your date and time work. For example:

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

Output when I ran the code in my time zone just now:

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

A ZonedDateTime is a date and time of day in some time zone. It’s the closest we come to a modern equivalent of GregorianCalendar (the subclass of Calendar that your code gave you). Which modern class to use varies with more precise requirements, so sometimes you will prefer to use for example LocalDate, OffsetDateTime or even LocalTime.

To truncate the values to whole minutes (setting seconds and fraction of second to 0):

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

ZonedDateTime and the other classes of java.time offer plenty of ways to modify the values obtained. For example:

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

If you want to create the end time manually using only selected fields from the start time:

    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]

What went wrong in your code?

Part of the answer is already in the other answers: The set methods of Calendar set only the fields they promise to set and leave other fields unchanged where possible. While this is probably expected from the set​(int field, int value) method, it often surprises with the set​(int year, int month, int date) method and even more with set​(int year, int month, int date, int hourOfDay, int minute) and set​(int year, int month, int date, int hourOfDay, int minute, int second). In general while well intended the Calendar class and its subclasses are poorly and confusingly designed and cumbersome to work with. This is the main reason why I recommend java.time above.

The other part of the answer is that Calendar calculates its fields leniently. So when you look at the Calendar object in your debugger after calling set, it will contain a lot of garbage values. Calling getTime() forces the Calendar to compute its fields, so after that call the way it looks in the debugger should make more sense. Again it’s confusing behaviour, and it can also sometimes be observed without using the debugger.

Question: Doesn’t java.time require Android API level 26?

java.time works nicely on both older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

When you initialize the Calendar object, it is getting the current time including the current second and millisecond. The code provided sets the hour and minute, but it does not set the second and millisecond, leaving it as it was when the Calendar object was initialized. In order to set the second and nanosecond to zero, use:

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