Need To Query Calendar Instance For Calendar Setting To Work
I have a Calendar instance that starts with the first day in December. I am trying to set that Calendar instance to the last Tuesday of the year. Somewhere between setting the last day of the year and the day of week, the Calendar instance reverts back to the original time:
Calendar cal = ....;
Log.d(TAG, String.format("Starting here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR));
cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
Log.d(TAG, String.format("Ending here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
/* Other stuff */
Gives me this output for 2017 and 2018:
2017:
Starting here 2017-12-1
Ending here 2017-11-28
2018:
Starting here 2018-12-1
Ending here 2018-11-27
An Unsatisfying Fix
However, if I retrieve data from the Calendar instance, it works fine:
Calendar cal = ....;
Log.d(TAG, String.format("Starting here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR));
// Ask the Calendar instance for information
long dummyValue = cal.getTimeInMillis();
cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
Log.d(TAG, String.format("Ending here %d-%d-%d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH)));
/* Other stuff */
Gives me this output for 2017 and 2018:
2017:
Starting here 2017-12-1
Ending here 2018-1-2
2018:
Starting here 2018-12-1
Ending here 2019-1-1
And yes, I know the dates for the second set of values are not the last Tuesday. That's what /* Other stuff */
is for.
Answer
java.time and ThreeTenABP
LocalDate myLocalDate = LocalDate.of(2017, Month.DECEMBER, 1);
System.out.format("Starting here: %s%n", myLocalDate);
LocalDate lastTuesdayOfYear = myLocalDate
.with(TemporalAdjusters.lastDayOfYear())
.with(TemporalAdjusters.previousOrSame(DayOfWeek.TUESDAY));
System.out.format("Ending here: %s%n", lastTuesdayOfYear);
Output for 2017:
Starting here: 2017-12-01 Ending here: 2017-12-26
For 2018:
Starting here: 2018-12-01 Ending here: 2018-12-25
This is giving you the last Tuesday of the year. Nothing further is needed for that.
Question: Can I use java.time on Android?
Yes, java.time works nicely on 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 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.
What went wrong in your code?
The Calendar
class is just confusing. In your case it is behaving according to the documentation. Allow me to quote:
Calendar Fields Resolution
When computing a date and time from the calendar fields, … there may be inconsistent information (such as Tuesday, July 15, 1996 (Gregorian) -- July 15, 1996 is actually a Monday).
Calendar
will resolve calendar field values to determine the date and time in the following way.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
…
In your case DAY_OF_WEEK
was set most recently. DAY_OF_WEEK
is in three of the five combinations above. Which one it picks I don’t know. None of them also include DAY_OF_YEAR
, the other field you set, so DAY_OF_YEAR
is not being used. Which explains the behaviour you saw. If you insist on using Calendar
, you should be able to get somewhere with cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, -1);
since negative values count backward from the end of the month.
However, Calendar
is not only poorly and confusingly designed, it is also long outdated. So consider using java.time instead.
Links
- Oracle tutorial: Date Time explaining how to use java.time.
- Java Specification Request (JSR) 310, where
java.time
was first described. - ThreeTen Backport project, the backport of
java.time
to Java 6 and 7 (ThreeTen for JSR-310). - ThreeTenABP, Android edition of ThreeTen Backport
- Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Related Questions
- → should I choose reactjs+f7 or f7+vue.js?
- → Phonegap Android write to sd card
- → Local reference jquery script in nanohttpd (Android)
- → Click to navigate on mobile devices
- → How to allow api access to android or ios app only(laravel)?
- → Access the Camera and CameraRoll on Android using React Native?
- → React native change listening port
- → What is the default unit of style in React Native?
- → Google play market autocomplete icon
- → Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`
- → Using Laravel with Genymotion
- → react native using like web-based ajax function
- → react native pdf View