diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-08 16:02:17 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-08 16:02:17 +0000 |
commit | 96f0303a37cb1a27e77bcfd22e84dff0644d1a1c (patch) | |
tree | 22fabdf2db0179ed308c4e72ce902bebd5fda3c0 | |
parent | 1777389d688492b11fec84a82ff16139340b0b99 (diff) | |
parent | 6e29ad1dfe9b353af1c41a30101673bf01d7a144 (diff) | |
download | calendar-aml_tz2_305400100.tar.gz |
Snap for 8426163 from 6e29ad1dfe9b353af1c41a30101673bf01d7a144 to mainline-tzdata2-releaseandroid-mainline-12.0.0_r112aml_tz2_305400500aml_tz2_305400300aml_tz2_305400100aml_tz2_304500300aml_tz2_303900110aml_tz2_303900102aml_tz2_303800002aml_tz2_303800001aml_tz2_303200001android12-mainline-tzdata2-releaseaml_tz2_305400100
Change-Id: Ie2b1811eb5f583f2a44fbeeb34cd4d795a87e3d1
-rw-r--r-- | Android.bp | 4 | ||||
-rw-r--r-- | src/com/android/calendarcommon2/EventRecurrence.java | 6 | ||||
-rw-r--r-- | src/com/android/calendarcommon2/RecurrenceProcessor.java | 156 | ||||
-rw-r--r-- | src/com/android/calendarcommon2/RecurrenceSet.java | 44 | ||||
-rw-r--r-- | src/com/android/calendarcommon2/Time.java | 532 | ||||
-rw-r--r-- | tests/Android.bp | 4 | ||||
-rw-r--r-- | tests/src/com/android/calendarcommon2/RRuleTest.java | 4 | ||||
-rw-r--r-- | tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java | 35 | ||||
-rw-r--r-- | tests/src/com/android/calendarcommon2/TimeTest.java | 762 |
9 files changed, 135 insertions, 1412 deletions
@@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - java_library { name: "calendar-common", sdk_version: "15", diff --git a/src/com/android/calendarcommon2/EventRecurrence.java b/src/com/android/calendarcommon2/EventRecurrence.java index d54f2fe..45fd12f 100644 --- a/src/com/android/calendarcommon2/EventRecurrence.java +++ b/src/com/android/calendarcommon2/EventRecurrence.java @@ -17,7 +17,9 @@ package com.android.calendarcommon2; import android.text.TextUtils; +import android.text.format.Time; import android.util.Log; +import android.util.TimeFormatException; import java.util.Calendar; import java.util.HashMap; @@ -474,7 +476,7 @@ public class EventRecurrence { EventRecurrence er = (EventRecurrence) obj; return (startDate == null ? - er.startDate == null : startDate.compareTo(er.startDate) == 0) && + er.startDate == null : Time.compare(startDate, er.startDate) == 0) && freq == er.freq && (until == null ? er.until == null : until.equals(er.until)) && count == er.count && @@ -738,7 +740,7 @@ public class EventRecurrence { // Parse the time to validate it. The result isn't retained. Time until = new Time(); until.parse(value); - } catch (IllegalArgumentException iae) { + } catch (TimeFormatException tfe) { throw new InvalidFormatException("Invalid UNTIL value: " + value); } } diff --git a/src/com/android/calendarcommon2/RecurrenceProcessor.java b/src/com/android/calendarcommon2/RecurrenceProcessor.java index 24decce..d0a647a 100644 --- a/src/com/android/calendarcommon2/RecurrenceProcessor.java +++ b/src/com/android/calendarcommon2/RecurrenceProcessor.java @@ -17,6 +17,7 @@ package com.android.calendarcommon2; +import android.text.format.Time; import android.util.Log; import java.util.TreeSet; @@ -92,7 +93,7 @@ public class RecurrenceProcessor } else if (rrule.until != null) { // according to RFC 2445, until must be in UTC. mIterator.parse(rrule.until); - long untilTime = mIterator.toMillis(); + long untilTime = mIterator.toMillis(false /* use isDst */); if (untilTime > lastTime) { lastTime = untilTime; } @@ -128,8 +129,9 @@ public class RecurrenceProcessor // The expansion might not contain any dates if the exrule or // exdates cancel all the generated dates. long[] dates = expand(dtstart, recur, - dtstart.toMillis() /* range start */, - (maxtime != null) ? maxtime.toMillis() : -1 /* range end */); + dtstart.toMillis(false /* use isDst */) /* range start */, + (maxtime != null) ? + maxtime.toMillis(false /* use isDst */) : -1 /* range end */); // The expansion might not contain any dates if exrule or exdates // cancel all the generated dates. @@ -199,7 +201,7 @@ public class RecurrenceProcessor // BYMONTH if (r.bymonthCount > 0) { found = listContains(r.bymonth, r.bymonthCount, - iterator.getMonth() + 1); + iterator.month + 1); if (!found) { return 1; } @@ -221,7 +223,7 @@ public class RecurrenceProcessor // BYYEARDAY if (r.byyeardayCount > 0) { found = listContains(r.byyearday, r.byyeardayCount, - iterator.getYearDay(), iterator.getActualMaximum(Time.YEAR_DAY)); + iterator.yearDay, iterator.getActualMaximum(Time.YEAR_DAY)); if (!found) { return 3; } @@ -229,7 +231,7 @@ public class RecurrenceProcessor // BYMONTHDAY if (r.bymonthdayCount > 0 ) { found = listContains(r.bymonthday, r.bymonthdayCount, - iterator.getDay(), + iterator.monthDay, iterator.getActualMaximum(Time.MONTH_DAY)); if (!found) { return 4; @@ -241,7 +243,7 @@ byday: if (r.bydayCount > 0) { int a[] = r.byday; int N = r.bydayCount; - int v = EventRecurrence.timeDay2Day(iterator.getWeekDay()); + int v = EventRecurrence.timeDay2Day(iterator.weekDay); for (int i=0; i<N; i++) { if (a[i] == v) { break byday; @@ -253,7 +255,7 @@ byday: if (EventRecurrence.HOURLY >= freq) { // BYHOUR found = listContains(r.byhour, r.byhourCount, - iterator.getHour(), + iterator.hour, iterator.getActualMaximum(Time.HOUR)); if (!found) { return 6; @@ -262,7 +264,7 @@ byday: if (EventRecurrence.MINUTELY >= freq) { // BYMINUTE found = listContains(r.byminute, r.byminuteCount, - iterator.getMinute(), + iterator.minute, iterator.getActualMaximum(Time.MINUTE)); if (!found) { return 7; @@ -271,7 +273,7 @@ byday: if (EventRecurrence.SECONDLY >= freq) { // BYSECOND found = listContains(r.bysecond, r.bysecondCount, - iterator.getSecond(), + iterator.second, iterator.getActualMaximum(Time.SECOND)); if (!found) { return 8; @@ -324,7 +326,7 @@ bysetpos: * (day of the month - 1) mod 7, and then make sure it's positive. We can simplify * that with some algebra. */ - int dotw = (instance.getWeekDay() - instance.getDay() + 36) % 7; + int dotw = (instance.weekDay - instance.monthDay + 36) % 7; /* * The byday[] values are specified as bits, so we can just OR them all @@ -366,14 +368,14 @@ bysetpos: if (index > daySetLength) { continue; // out of range } - if (daySet[index-1] == instance.getDay()) { + if (daySet[index-1] == instance.monthDay) { return true; } } else if (index < 0) { if (daySetLength + index < 0) { continue; // out of range } - if (daySet[daySetLength + index] == instance.getDay()) { + if (daySet[daySetLength + index] == instance.monthDay) { return true; } } else { @@ -427,29 +429,29 @@ bysetpos: boolean get(Time iterator, int day) { - int realYear = iterator.getYear(); - int realMonth = iterator.getMonth(); + int realYear = iterator.year; + int realMonth = iterator.month; Time t = null; if (SPEW) { Log.i(TAG, "get called with iterator=" + iterator - + " " + iterator.getMonth() - + "/" + iterator.getDay() - + "/" + iterator.getYear() + " day=" + day); + + " " + iterator.month + + "/" + iterator.monthDay + + "/" + iterator.year + " day=" + day); } if (day < 1 || day > 28) { // if might be past the end of the month, we need to normalize it t = mTime; t.set(day, realMonth, realYear); unsafeNormalize(t); - realYear = t.getYear(); - realMonth = t.getMonth(); - day = t.getDay(); + realYear = t.year; + realMonth = t.month; + day = t.monthDay; if (SPEW) { - Log.i(TAG, "normalized t=" + t + " " + t.getMonth() - + "/" + t.getDay() - + "/" + t.getYear()); + Log.i(TAG, "normalized t=" + t + " " + t.month + + "/" + t.monthDay + + "/" + t.year); } } @@ -464,9 +466,9 @@ bysetpos: t.set(day, realMonth, realYear); unsafeNormalize(t); if (SPEW) { - Log.i(TAG, "set t=" + t + " " + t.getMonth() - + "/" + t.getDay() - + "/" + t.getYear() + Log.i(TAG, "set t=" + t + " " + t.month + + "/" + t.monthDay + + "/" + t.year + " realMonth=" + realMonth + " mMonth=" + mMonth); } } @@ -505,11 +507,11 @@ bysetpos: count = r.bydayCount; if (count > 0) { // calculate the day of week for the first of this month (first) - j = generated.getDay(); + j = generated.monthDay; while (j >= 8) { j -= 7; } - first = generated.getWeekDay(); + first = generated.weekDay; if (first >= j) { first = first - j + 1; } else { @@ -629,13 +631,13 @@ bysetpos: * UTC milliseconds; use -1 for the entire range. * @return an array of dates, each date is in UTC milliseconds * @throws DateException - * @throws IllegalArgumentException if recur cannot be parsed + * @throws android.util.TimeFormatException if recur cannot be parsed */ public long[] expand(Time dtstart, RecurrenceSet recur, long rangeStartMillis, long rangeEndMillis) throws DateException { - String timezone = dtstart.getTimezone(); + String timezone = dtstart.timezone; mIterator.clear(timezone); mGenerated.clear(timezone); @@ -701,7 +703,7 @@ bysetpos: int i = 0; for (Long val: dtSet) { setTimeFromLongValue(mIterator, val); - dates[i++] = mIterator.toMillis(); + dates[i++] = mIterator.toMillis(true /* ignore isDst */); } return dates; } @@ -726,7 +728,7 @@ bysetpos: * @param add Whether or not we should add to out, or remove from out. * @param out the TreeSet you'd like to fill with the events * @throws DateException - * @throws IllegalArgumentException if r cannot be parsed. + * @throws android.util.TimeFormatException if r cannot be parsed. */ public void expand(Time dtstart, EventRecurrence r, @@ -825,7 +827,7 @@ bysetpos: // we'll skip months if it's greater than 28. // XXX Do we generate days for MONTHLY w/ BYHOUR? If so, // we need to do this then too. - iterator.setDay(1); + iterator.monthDay = 1; } } @@ -845,7 +847,7 @@ bysetpos: // We need the "until" year/month/day values to be in the same // timezone as all the generated dates so that we can compare them // using the values returned by normDateTimeComparisonValue(). - until.switchTimezone(dtstart.getTimezone()); + until.switchTimezone(dtstart.timezone); untilDateValue = normDateTimeComparisonValue(until); } else { untilDateValue = Long.MAX_VALUE; @@ -874,17 +876,17 @@ bysetpos: unsafeNormalize(iterator); - int iteratorYear = iterator.getYear(); - int iteratorMonth = iterator.getMonth() + 1; - int iteratorDay = iterator.getDay(); - int iteratorHour = iterator.getHour(); - int iteratorMinute = iterator.getMinute(); - int iteratorSecond = iterator.getSecond(); + int iteratorYear = iterator.year; + int iteratorMonth = iterator.month + 1; + int iteratorDay = iterator.monthDay; + int iteratorHour = iterator.hour; + int iteratorMinute = iterator.minute; + int iteratorSecond = iterator.second; // year is never expanded -- there is no BYYEAR generated.set(iterator); - if (SPEW) Log.i(TAG, "year=" + generated.getYear()); + if (SPEW) Log.i(TAG, "year=" + generated.year); do { // month int month = usebymonth @@ -921,9 +923,9 @@ bysetpos: * Thursday. If weeks started on Mondays, we would only * need to move back (2 - 1 + 7) % 7 = 1 day. */ - int weekStartAdj = (iterator.getWeekDay() - + int weekStartAdj = (iterator.weekDay - EventRecurrence.day2TimeDay(r.wkst) + 7) % 7; - dayIndex = iterator.getDay() - weekStartAdj; + dayIndex = iterator.monthDay - weekStartAdj; lastDayToExamine = dayIndex + 6; } else { lastDayToExamine = generated @@ -1063,21 +1065,35 @@ bysetpos: // We don't want to "generate" dates with the iterator. // XXX: We do this for days, because there is a varying number of days // per month - int oldDay = iterator.getDay(); + int oldDay = iterator.monthDay; generated.set(iterator); // just using generated as a temporary. int n = 1; while (true) { int value = freqAmount * n; switch (freqField) { case Time.SECOND: + iterator.second += value; + break; case Time.MINUTE: + iterator.minute += value; + break; case Time.HOUR: + iterator.hour += value; + break; case Time.MONTH_DAY: + iterator.monthDay += value; + break; case Time.MONTH: + iterator.month += value; + break; case Time.YEAR: + iterator.year += value; + break; case Time.WEEK_DAY: + iterator.monthDay += value; + break; case Time.YEAR_DAY: - iterator.add(freqField, value); + iterator.monthDay += value; break; default: throw new RuntimeException("bad field=" + freqField); @@ -1087,7 +1103,7 @@ bysetpos: if (freqField != Time.YEAR && freqField != Time.MONTH) { break; } - if (iterator.getDay() == oldDay) { + if (iterator.monthDay == oldDay) { break; } n++; @@ -1120,12 +1136,12 @@ bysetpos: * This method does not modify the fields isDst, or gmtOff. */ static void unsafeNormalize(Time date) { - int second = date.getSecond(); - int minute = date.getMinute(); - int hour = date.getHour(); - int monthDay = date.getDay(); - int month = date.getMonth(); - int year = date.getYear(); + int second = date.second; + int minute = date.minute; + int hour = date.hour; + int monthDay = date.monthDay; + int month = date.month; + int year = date.year; int addMinutes = ((second < 0) ? (second - 59) : second) / 60; second -= addMinutes * 60; @@ -1186,14 +1202,14 @@ bysetpos: // At this point, monthDay <= the length of the current month and is // in the range [1,31]. - date.setSecond(second); - date.setMinute(minute); - date.setHour(hour); - date.setDay(monthDay); - date.setMonth(month); - date.setYear(year); - date.setWeekDay(weekDay(year, month, monthDay)); - date.setYearDay(yearDay(year, month, monthDay)); + date.second = second; + date.minute = minute; + date.hour = hour; + date.monthDay = monthDay; + date.month = month; + date.year = year; + date.weekDay = weekDay(year, month, monthDay); + date.yearDay = yearDay(year, month, monthDay); } /** @@ -1284,17 +1300,17 @@ bysetpos: private static final long normDateTimeComparisonValue(Time normalized) { // 37 bits for the year, 4 bits for the month, 5 bits for the monthDay, // 5 bits for the hour, 6 bits for the minute, 6 bits for the second. - return ((long)normalized.getYear() << 26) + (normalized.getMonth() << 22) - + (normalized.getDay() << 17) + (normalized.getHour() << 12) - + (normalized.getMinute() << 6) + normalized.getSecond(); + return ((long)normalized.year << 26) + (normalized.month << 22) + + (normalized.monthDay << 17) + (normalized.hour << 12) + + (normalized.minute << 6) + normalized.second; } private static final void setTimeFromLongValue(Time date, long val) { - date.setYear((int) (val >> 26)); - date.setMonth((int) (val >> 22) & 0xf); - date.setDay((int) (val >> 17) & 0x1f); - date.setHour((int) (val >> 12) & 0x1f); - date.setMinute((int) (val >> 6) & 0x3f); - date.setSecond((int) (val & 0x3f)); + date.year = (int) (val >> 26); + date.month = (int) (val >> 22) & 0xf; + date.monthDay = (int) (val >> 17) & 0x1f; + date.hour = (int) (val >> 12) & 0x1f; + date.minute = (int) (val >> 6) & 0x3f; + date.second = (int) (val & 0x3f); } } diff --git a/src/com/android/calendarcommon2/RecurrenceSet.java b/src/com/android/calendarcommon2/RecurrenceSet.java index e42c0e9..86e6a2d 100644 --- a/src/com/android/calendarcommon2/RecurrenceSet.java +++ b/src/com/android/calendarcommon2/RecurrenceSet.java @@ -20,7 +20,9 @@ import android.content.ContentValues; import android.database.Cursor; import android.provider.CalendarContract; import android.text.TextUtils; +import android.text.format.Time; import android.util.Log; +import android.util.TimeFormatException; import java.util.ArrayList; import java.util.List; @@ -160,14 +162,14 @@ public class RecurrenceSet { // The timezone is updated to UTC if the time string specified 'Z'. try { time.parse(rawDates[i]); - } catch (IllegalArgumentException e) { + } catch (TimeFormatException e) { throw new EventRecurrence.InvalidFormatException( - "IllegalArgumentException thrown when parsing time " + rawDates[i] + "TimeFormatException thrown when parsing time " + rawDates[i] + " in recurrence " + recurrence); } - dates[i] = time.toMillis(); - time.setTimezone(tz); + dates[i] = time.toMillis(false /* use isDst */); + time.timezone = tz; } return dates; } @@ -194,9 +196,8 @@ public class RecurrenceSet { // NOTE: the timezone may be null, if this is a floating time. String tzid = tzidParam == null ? null : tzidParam.value; Time start = new Time(tzidParam == null ? Time.TIMEZONE_UTC : tzid); - start.parse(dtstart); - boolean inUtc = dtstart.length() == 16 && dtstart.charAt(15) == 'Z'; - boolean allDay = start.isAllDay(); + boolean inUtc = start.parse(dtstart); + boolean allDay = start.allDay; // We force TimeZone to UTC for "all day recurring events" as the server is sending no // TimeZone in DTSTART for them @@ -223,9 +224,9 @@ public class RecurrenceSet { } if (allDay) { - start.setTimezone(Time.TIMEZONE_UTC); + start.timezone = Time.TIMEZONE_UTC; } - long millis = start.toMillis(); + long millis = start.toMillis(false /* use isDst */); values.put(CalendarContract.Events.DTSTART, millis); if (millis == -1) { if (false) { @@ -242,7 +243,7 @@ public class RecurrenceSet { values.put(CalendarContract.Events.DURATION, duration); values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0); return true; - } catch (IllegalArgumentException e) { + } catch (TimeFormatException e) { // Something is wrong with the format of this event Log.i(TAG,"Failed to parse event: " + component.toString()); return false; @@ -300,10 +301,10 @@ public class RecurrenceSet { // TODO: android.pim.Time really should take care of this for us. if (allDay) { dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.setAllDay(true); - dtstartTime.setHour(0); - dtstartTime.setMinute(0); - dtstartTime.setSecond(0); + dtstartTime.allDay = true; + dtstartTime.hour = 0; + dtstartTime.minute = 0; + dtstartTime.second = 0; } dtstartProp.setValue(dtstartTime.format2445()); @@ -359,10 +360,10 @@ public static boolean populateComponent(ContentValues values, // TODO: android.pim.Time really should take care of this for us. if (allDay) { dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE")); - dtstartTime.setAllDay(true); - dtstartTime.setHour(0); - dtstartTime.setMinute(0); - dtstartTime.setSecond(0); + dtstartTime.allDay = true; + dtstartTime.hour = 0; + dtstartTime.minute = 0; + dtstartTime.second = 0; } dtstartProp.setValue(dtstartTime.format2445()); @@ -479,13 +480,14 @@ public static boolean populateComponent(ContentValues values, ICalendar.Parameter endTzidParameter = dtendProperty.getFirstParameter("TZID"); String endTzid = (endTzidParameter == null) - ? start.getTimezone() : endTzidParameter.value; + ? start.timezone : endTzidParameter.value; Time end = new Time(endTzid); end.parse(dtendProperty.getValue()); - long durationMillis = end.toMillis() - start.toMillis(); + long durationMillis = end.toMillis(false /* use isDst */) + - start.toMillis(false /* use isDst */); long durationSeconds = (durationMillis / 1000); - if (start.isAllDay() && (durationSeconds % 86400) == 0) { + if (start.allDay && (durationSeconds % 86400) == 0) { return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S } else { return "P" + durationSeconds + "S"; diff --git a/src/com/android/calendarcommon2/Time.java b/src/com/android/calendarcommon2/Time.java deleted file mode 100644 index f0af248..0000000 --- a/src/com/android/calendarcommon2/Time.java +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.calendarcommon2; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.TimeZone; - -/** - * Helper class to make migration out of android.text.format.Time smoother. - */ -public class Time { - - public static final String TIMEZONE_UTC = "UTC"; - - private static final int EPOCH_JULIAN_DAY = 2440588; - private static final long HOUR_IN_MILLIS = 60 * 60 * 1000; - private static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS; - - private static final String FORMAT_ALL_DAY_PATTERN = "yyyyMMdd"; - private static final String FORMAT_TIME_PATTERN = "yyyyMMdd'T'HHmmss"; - private static final String FORMAT_TIME_UTC_PATTERN = "yyyyMMdd'T'HHmmss'Z'"; - private static final String FORMAT_LOG_TIME_PATTERN = "EEE, MMM dd, yyyy hh:mm a"; - - /* - * Define symbolic constants for accessing the fields in this class. Used in - * getActualMaximum(). - */ - public static final int SECOND = 1; - public static final int MINUTE = 2; - public static final int HOUR = 3; - public static final int MONTH_DAY = 4; - public static final int MONTH = 5; - public static final int YEAR = 6; - public static final int WEEK_DAY = 7; - public static final int YEAR_DAY = 8; - public static final int WEEK_NUM = 9; - - public static final int SUNDAY = 0; - public static final int MONDAY = 1; - public static final int TUESDAY = 2; - public static final int WEDNESDAY = 3; - public static final int THURSDAY = 4; - public static final int FRIDAY = 5; - public static final int SATURDAY = 6; - - private final GregorianCalendar mCalendar; - - private int year; - private int month; - private int monthDay; - private int hour; - private int minute; - private int second; - - private int yearDay; - private int weekDay; - - private String timezone; - private boolean allDay; - - /** - * Enabling this flag will apply appropriate dst transition logic when calling either - * {@code toMillis()} or {@code normalize()} and their respective *ApplyDst() equivalents. <br> - * When this flag is enabled, the following calls would be considered equivalent: - * <ul> - * <li>{@code a.t.f.Time#normalize(true)} and {@code #normalize()}</li> - * <li>{@code a.t.f.Time#toMillis(true)} and {@code #toMillis()}</li> - * <li>{@code a.t.f.Time#normalize(false)} and {@code #normalizeApplyDst()}</li> - * <li>{@code a.t.f.Time#toMillis(false)} and {@code #toMillisApplyDst()}</li> - * </ul> - * When the flag is disabled, both {@code toMillis()} and {@code normalize()} will ignore any - * dst transitions unless minutes or hours were added to the time (the default behavior of the - * a.t.f.Time class). <br> - * - * NOTE: currently, this flag is disabled because there are no direct manipulations of the day, - * hour, or minute fields. All of the accesses are correctly done via setters and they rely on - * a private normalize call in their respective classes to achieve their expected behavior. - * Additionally, using any of the {@code #set()} methods or {@code #parse()} will result in - * normalizing by ignoring DST, which is what the default behavior is for the a.t.f.Time class. - */ - static final boolean APPLY_DST_CHANGE_LOGIC = false; - private int mDstChangedByField = -1; - - public Time() { - this(TimeZone.getDefault().getID()); - } - - public Time(String timezone) { - if (timezone == null) { - throw new NullPointerException("timezone cannot be null."); - } - this.timezone = timezone; - // Although the process's default locale is used here, #clear() will explicitly set the - // first day of the week to MONDAY to match with the expected a.t.f.Time implementation. - mCalendar = new GregorianCalendar(getTimeZone(), Locale.getDefault()); - clear(this.timezone); - } - - private void readFieldsFromCalendar() { - year = mCalendar.get(Calendar.YEAR); - month = mCalendar.get(Calendar.MONTH); - monthDay = mCalendar.get(Calendar.DAY_OF_MONTH); - hour = mCalendar.get(Calendar.HOUR_OF_DAY); - minute = mCalendar.get(Calendar.MINUTE); - second = mCalendar.get(Calendar.SECOND); - } - - private void writeFieldsToCalendar() { - clearCalendar(); - mCalendar.set(year, month, monthDay, hour, minute, second); - mCalendar.set(Calendar.MILLISECOND, 0); - } - - private boolean isInDst() { - return mCalendar.getTimeZone().inDaylightTime(mCalendar.getTime()); - } - - public void add(int field, int amount) { - final boolean wasDstBefore = isInDst(); - mCalendar.add(getCalendarField(field), amount); - if (APPLY_DST_CHANGE_LOGIC && wasDstBefore != isInDst() - && (field == MONTH_DAY || field == HOUR || field == MINUTE)) { - mDstChangedByField = field; - } - } - - public void set(long millis) { - clearCalendar(); - mCalendar.setTimeInMillis(millis); - readFieldsFromCalendar(); - } - - public void set(Time other) { - clearCalendar(); - mCalendar.setTimeZone(other.getTimeZone()); - mCalendar.setTimeInMillis(other.mCalendar.getTimeInMillis()); - readFieldsFromCalendar(); - } - - public void set(int day, int month, int year) { - clearCalendar(); - mCalendar.set(year, month, day); - readFieldsFromCalendar(); - } - - public void set(int second, int minute, int hour, int day, int month, int year) { - clearCalendar(); - mCalendar.set(year, month, day, hour, minute, second); - readFieldsFromCalendar(); - } - - public long setJulianDay(int julianDay) { - long millis = (julianDay - EPOCH_JULIAN_DAY) * DAY_IN_MILLIS; - mCalendar.setTimeInMillis(millis); - readFieldsFromCalendar(); - - // adjust day approximation, set the time to 12am, and re-normalize - monthDay += julianDay - getJulianDay(millis, getGmtOffset()); - hour = 0; - minute = 0; - second = 0; - writeFieldsToCalendar(); - return normalize(); - } - - public static int getJulianDay(long begin, long gmtOff) { - return android.text.format.Time.getJulianDay(begin, gmtOff); - } - - public int getWeekNumber() { - return mCalendar.get(Calendar.WEEK_OF_YEAR); - } - - private int getCalendarField(int field) { - switch (field) { - case SECOND: return Calendar.SECOND; - case MINUTE: return Calendar.MINUTE; - case HOUR: return Calendar.HOUR_OF_DAY; - case MONTH_DAY: return Calendar.DAY_OF_MONTH; - case MONTH: return Calendar.MONTH; - case YEAR: return Calendar.YEAR; - case WEEK_DAY: return Calendar.DAY_OF_WEEK; - case YEAR_DAY: return Calendar.DAY_OF_YEAR; - case WEEK_NUM: return Calendar.WEEK_OF_YEAR; - default: - throw new RuntimeException("bad field=" + field); - } - } - - public int getActualMaximum(int field) { - return mCalendar.getActualMaximum(getCalendarField(field)); - } - - public void switchTimezone(String timezone) { - long msBefore = mCalendar.getTimeInMillis(); - mCalendar.setTimeZone(TimeZone.getTimeZone(timezone)); - mCalendar.setTimeInMillis(msBefore); - mDstChangedByField = -1; - readFieldsFromCalendar(); - } - - /** - * @param apply whether to apply dst logic on the ms or not; if apply is true, it is equivalent - * to calling the normalize or toMillis APIs in a.t.f.Time with ignoreDst=false - */ - private long getDstAdjustedMillis(boolean apply, long ms) { - if (APPLY_DST_CHANGE_LOGIC) { - if (apply && mDstChangedByField == MONTH_DAY) { - return isInDst() ? (ms + HOUR_IN_MILLIS) : (ms - HOUR_IN_MILLIS); - } else if (!apply && (mDstChangedByField == HOUR || mDstChangedByField == MINUTE)) { - return isInDst() ? (ms - HOUR_IN_MILLIS) : (ms + HOUR_IN_MILLIS); - } - } - return ms; - } - - private long normalizeInternal() { - final long ms = mCalendar.getTimeInMillis(); - readFieldsFromCalendar(); - return ms; - } - - public long normalize() { - return getDstAdjustedMillis(false, normalizeInternal()); - } - - long normalizeApplyDst() { - return getDstAdjustedMillis(true, normalizeInternal()); - } - - public void parse(String time) { - if (time == null) { - throw new NullPointerException("time string is null"); - } - parseInternal(time); - writeFieldsToCalendar(); - } - - public String format2445() { - writeFieldsToCalendar(); - final SimpleDateFormat sdf = new SimpleDateFormat( - allDay ? FORMAT_ALL_DAY_PATTERN - : (TIMEZONE_UTC.equals(getTimezone()) ? FORMAT_TIME_UTC_PATTERN - : FORMAT_TIME_PATTERN)); - sdf.setTimeZone(getTimeZone()); - return sdf.format(mCalendar.getTime()); - } - - public long toMillis() { - return getDstAdjustedMillis(false, mCalendar.getTimeInMillis()); - } - - long toMillisApplyDst() { - return getDstAdjustedMillis(true, mCalendar.getTimeInMillis()); - } - - private TimeZone getTimeZone() { - return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault(); - } - - public int compareTo(Time other) { - return mCalendar.compareTo(other.mCalendar); - } - - private void clearCalendar() { - mDstChangedByField = -1; - mCalendar.clear(); - mCalendar.set(Calendar.HOUR_OF_DAY, 0); // HOUR_OF_DAY doesn't get reset with #clear - mCalendar.setTimeZone(getTimeZone()); - // set fields for week number computation according to ISO 8601. - mCalendar.setFirstDayOfWeek(Calendar.MONDAY); - mCalendar.setMinimalDaysInFirstWeek(4); - } - - public void clear(String timezoneId) { - clearCalendar(); - readFieldsFromCalendar(); - setTimezone(timezoneId); - } - - public int getYear() { - return mCalendar.get(Calendar.YEAR); - } - - public void setYear(int year) { - this.year = year; - mCalendar.set(Calendar.YEAR, year); - } - - public int getMonth() { - return mCalendar.get(Calendar.MONTH); - } - - public void setMonth(int month) { - this.month = month; - mCalendar.set(Calendar.MONTH, month); - } - - public int getDay() { - return mCalendar.get(Calendar.DAY_OF_MONTH); - } - - public void setDay(int day) { - this.monthDay = day; - mCalendar.set(Calendar.DAY_OF_MONTH, day); - } - - public int getHour() { - return mCalendar.get(Calendar.HOUR_OF_DAY); - } - - public void setHour(int hour) { - this.hour = hour; - mCalendar.set(Calendar.HOUR_OF_DAY, hour); - } - - public int getMinute() { - return mCalendar.get(Calendar.MINUTE); - } - - public void setMinute(int minute) { - this.minute = minute; - mCalendar.set(Calendar.MINUTE, minute); - } - - public int getSecond() { - return mCalendar.get(Calendar.SECOND); - } - - public void setSecond(int second) { - this.second = second; - mCalendar.set(Calendar.SECOND, second); - } - - public String getTimezone() { - return mCalendar.getTimeZone().getID(); - } - - public void setTimezone(String timezone) { - this.timezone = timezone; - mCalendar.setTimeZone(getTimeZone()); - } - - public int getYearDay() { - // yearDay in a.t.f.Time's implementation starts from 0, whereas Calendar's starts from 1. - return mCalendar.get(Calendar.DAY_OF_YEAR) - 1; - } - - public void setYearDay(int yearDay) { - this.yearDay = yearDay; - // yearDay in a.t.f.Time's implementation starts from 0, whereas Calendar's starts from 1. - mCalendar.set(Calendar.DAY_OF_YEAR, yearDay + 1); - } - - public int getWeekDay() { - // weekDay in a.t.f.Time's implementation starts from 0, whereas Calendar's starts from 1. - return mCalendar.get(Calendar.DAY_OF_WEEK) - 1; - } - - public void setWeekDay(int weekDay) { - this.weekDay = weekDay; - // weekDay in a.t.f.Time's implementation starts from 0, whereas Calendar's starts from 1. - mCalendar.set(Calendar.DAY_OF_WEEK, weekDay + 1); - } - - public boolean isAllDay() { - return allDay; - } - - public void setAllDay(boolean allDay) { - this.allDay = allDay; - } - - public long getGmtOffset() { - return mCalendar.getTimeZone().getOffset(mCalendar.getTimeInMillis()) / 1000; - } - - private void parseInternal(String s) { - int len = s.length(); - if (len < 8) { - throw new IllegalArgumentException("String is too short: \"" + s + - "\" Expected at least 8 characters."); - } else if (len > 8 && len < 15) { - throw new IllegalArgumentException("String is too short: \"" + s - + "\" If there are more than 8 characters there must be at least 15."); - } - - // year - int n = getChar(s, 0, 1000); - n += getChar(s, 1, 100); - n += getChar(s, 2, 10); - n += getChar(s, 3, 1); - year = n; - - // month - n = getChar(s, 4, 10); - n += getChar(s, 5, 1); - n--; - month = n; - - // day of month - n = getChar(s, 6, 10); - n += getChar(s, 7, 1); - monthDay = n; - - if (len > 8) { - checkChar(s, 8, 'T'); - allDay = false; - - // hour - n = getChar(s, 9, 10); - n += getChar(s, 10, 1); - hour = n; - - // min - n = getChar(s, 11, 10); - n += getChar(s, 12, 1); - minute = n; - - // sec - n = getChar(s, 13, 10); - n += getChar(s, 14, 1); - second = n; - - if (len > 15) { - // Z - checkChar(s, 15, 'Z'); - timezone = TIMEZONE_UTC; - } - } else { - allDay = true; - hour = 0; - minute = 0; - second = 0; - } - - weekDay = 0; - yearDay = 0; - } - - private void checkChar(String s, int spos, char expected) { - final char c = s.charAt(spos); - if (c != expected) { - throw new IllegalArgumentException(String.format( - "Unexpected character 0x%02d at pos=%d. Expected 0x%02d (\'%c\').", - (int) c, spos, (int) expected, expected)); - } - } - - private int getChar(String s, int spos, int mul) { - final char c = s.charAt(spos); - if (Character.isDigit(c)) { - return Character.getNumericValue(c) * mul; - } else { - throw new IllegalArgumentException("Parse error at pos=" + spos); - } - } - - // NOTE: only used for outputting time to error logs - public String format() { - final SimpleDateFormat sdf = - new SimpleDateFormat(FORMAT_LOG_TIME_PATTERN, Locale.getDefault()); - return sdf.format(mCalendar.getTime()); - } - - // NOTE: only used in tests - public boolean parse3339(String time) { - android.text.format.Time tmp = generateInstance(); - boolean success = tmp.parse3339(time); - copyAndWriteInstance(tmp); - return success; - } - - // NOTE: only used in tests - public String format3339(boolean allDay) { - return generateInstance().format3339(allDay); - } - - private android.text.format.Time generateInstance() { - android.text.format.Time tmp = new android.text.format.Time(timezone); - tmp.set(second, minute, hour, monthDay, month, year); - - tmp.yearDay = yearDay; - tmp.weekDay = weekDay; - - tmp.timezone = timezone; - tmp.gmtoff = getGmtOffset(); - tmp.allDay = allDay; - tmp.set(mCalendar.getTimeInMillis()); - if (tmp.allDay && (tmp.hour != 0 || tmp.minute != 0 || tmp.second != 0)) { - // Time SDK expects hour, minute, second to be 0 if allDay is true - tmp.hour = 0; - tmp.minute = 0; - tmp.second = 0; - } - - return tmp; - } - - private void copyAndWriteInstance(android.text.format.Time time) { - year = time.year; - month = time.month; - monthDay = time.monthDay; - hour = time.hour; - minute = time.minute; - second = time.second; - - yearDay = time.yearDay; - weekDay = time.weekDay; - - timezone = time.timezone; - allDay = time.allDay; - - writeFieldsToCalendar(); - } -} diff --git a/tests/Android.bp b/tests/Android.bp index 938b51c..cdda049 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - android_test { name: "CalendarCommonTests", sdk_version: "current", diff --git a/tests/src/com/android/calendarcommon2/RRuleTest.java b/tests/src/com/android/calendarcommon2/RRuleTest.java index 18217a3..1d72366 100644 --- a/tests/src/com/android/calendarcommon2/RRuleTest.java +++ b/tests/src/com/android/calendarcommon2/RRuleTest.java @@ -24,6 +24,7 @@ import com.android.calendarcommon2.RecurrenceSet; import android.os.Debug; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.Suppress; +import android.text.format.Time; import junit.framework.TestCase; /** @@ -114,7 +115,8 @@ public class RRuleTest extends TestCase { RecurrenceProcessor rp = new RecurrenceProcessor(); RecurrenceSet recur = new RecurrenceSet(rrule, rdate, exrule, exdate); - long[] out = rp.expand(dtstart, recur, rangeStart.toMillis(), rangeEnd.toMillis()); + long[] out = rp.expand(dtstart, recur, rangeStart.toMillis(false /* use isDst */), + rangeEnd.toMillis(false /* use isDst */)); if (METHOD_TRACE) { Debug.stopMethodTracing(); diff --git a/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java b/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java index 3cd9177..3503aae 100644 --- a/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java +++ b/tests/src/com/android/calendarcommon2/RecurrenceProcessorTest.java @@ -22,7 +22,9 @@ import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; +import android.text.format.Time; import android.util.Log; +import android.util.TimeFormatException; import junit.framework.TestCase; import java.util.TreeSet; @@ -104,7 +106,8 @@ public class RecurrenceProcessorTest extends TestCase { RecurrenceProcessor rp = new RecurrenceProcessor(); RecurrenceSet recur = new RecurrenceSet(rrule, rdate, exrule, exdate); - long[] out = rp.expand(dtstart, recur, rangeStart.toMillis(), rangeEnd.toMillis()); + long[] out = rp.expand(dtstart, recur, rangeStart.toMillis(false /* use isDst */), + rangeEnd.toMillis(false /* use isDst */)); if (METHOD_TRACE) { Debug.stopMethodTracing(); @@ -147,12 +150,12 @@ public class RecurrenceProcessorTest extends TestCase { if (lastOccur != -1) { outCal.set(lastOccur); lastStr = outCal.format2445(); - lastMillis = outCal.toMillis(); + lastMillis = outCal.toMillis(true /* ignore isDst */); } if (last != null && last.length() > 0) { Time expectedLast = new Time(tz); expectedLast.parse(last); - expectedMillis = expectedLast.toMillis(); + expectedMillis = expectedLast.toMillis(true /* ignore isDst */); } if (lastMillis != expectedMillis) { if (SPEW) { @@ -595,7 +598,7 @@ public class RecurrenceProcessorTest extends TestCase { "20060219T100000" }, "20060220T020001"); fail("Bad UNTIL string failed to throw exception"); - } catch (IllegalArgumentException e) { + } catch (TimeFormatException e) { // expected } } @@ -2457,8 +2460,8 @@ public class RecurrenceProcessorTest extends TestCase { dtstart.parse("20010101T000000"); rangeStart.parse("20010101T000000"); rangeEnd.parse("20090101T000000"); - long rangeStartMillis = rangeStart.toMillis(); - long rangeEndMillis = rangeEnd.toMillis(); + long rangeStartMillis = rangeStart.toMillis(false /* use isDst */); + long rangeEndMillis = rangeEnd.toMillis(false /* use isDst */); long startTime = System.currentTimeMillis(); for (int iterations = 0; iterations < 5; iterations++) { @@ -2501,12 +2504,12 @@ public class RecurrenceProcessorTest extends TestCase { long startTime = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - date.add(Time.MONTH, 1); - date.add(Time.MONTH_DAY, 100); - date.normalize(); - date.add(Time.MONTH, -1); - date.add(Time.MONTH_DAY, -100); - date.normalize(); + date.month += 1; + date.monthDay += 100; + date.normalize(true); + date.month -= 1; + date.monthDay -= 100; + date.normalize(true); } long endTime = System.currentTimeMillis(); @@ -2518,11 +2521,11 @@ public class RecurrenceProcessorTest extends TestCase { date.parse("20090404T100000"); startTime = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - date.add(Time.MONTH, 1); - date.add(Time.MONTH_DAY, 100); + date.month += 1; + date.monthDay += 100; RecurrenceProcessor.unsafeNormalize(date); - date.add(Time.MONTH, -1); - date.add(Time.MONTH_DAY, -100); + date.month -= 1; + date.monthDay -= 100; RecurrenceProcessor.unsafeNormalize(date); } diff --git a/tests/src/com/android/calendarcommon2/TimeTest.java b/tests/src/com/android/calendarcommon2/TimeTest.java deleted file mode 100644 index df27c4f..0000000 --- a/tests/src/com/android/calendarcommon2/TimeTest.java +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.calendarcommon2; - -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.TimeFormatException; - -import junit.framework.TestCase; - -/** - * Tests for com.android.calendarcommon2.Time. - * - * Some of these tests are borrowed from android.text.format.TimeTest. - */ -public class TimeTest extends TestCase { - - @SmallTest - public void testNullTimezone() { - try { - Time t = new Time(null); - fail("expected a null timezone to throw an exception."); - } catch (NullPointerException npe) { - // expected. - } - } - - @SmallTest - public void testTimezone() { - Time t = new Time(Time.TIMEZONE_UTC); - assertEquals(Time.TIMEZONE_UTC, t.getTimezone()); - } - - @SmallTest - public void testSwitchTimezone() { - Time t = new Time(Time.TIMEZONE_UTC); - String newTimezone = "America/Los_Angeles"; - t.switchTimezone(newTimezone); - assertEquals(newTimezone, t.getTimezone()); - } - - @SmallTest - public void testGetActualMaximum() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1, 0, 2020); - assertEquals(59, t.getActualMaximum(Time.SECOND)); - assertEquals(59, t.getActualMaximum(Time.MINUTE)); - assertEquals(23, t.getActualMaximum(Time.HOUR)); - assertEquals(31, t.getActualMaximum(Time.MONTH_DAY)); - assertEquals(11, t.getActualMaximum(Time.MONTH)); - assertEquals(7, t.getActualMaximum(Time.WEEK_DAY)); - assertEquals(366, t.getActualMaximum(Time.YEAR_DAY)); // 2020 is a leap year - t.set(1, 0, 2019); - assertEquals(365, t.getActualMaximum(Time.YEAR_DAY)); - } - - @SmallTest - public void testAdd() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(0, 0, 0, 1, 0, 2020); - t.add(Time.SECOND, 1); - assertEquals(1, t.getSecond()); - t.add(Time.MINUTE, 1); - assertEquals(1, t.getMinute()); - t.add(Time.HOUR, 1); - assertEquals(1, t.getHour()); - t.add(Time.MONTH_DAY, 1); - assertEquals(2, t.getDay()); - t.add(Time.MONTH, 1); - assertEquals(1, t.getMonth()); - t.add(Time.YEAR, 1); - assertEquals(2021, t.getYear()); - } - - @SmallTest - public void testClear() { - Time t = new Time(Time.TIMEZONE_UTC); - t.clear(Time.TIMEZONE_UTC); - - assertEquals(Time.TIMEZONE_UTC, t.getTimezone()); - assertFalse(t.isAllDay()); - assertEquals(0, t.getSecond()); - assertEquals(0, t.getMinute()); - assertEquals(0, t.getHour()); - assertEquals(1, t.getDay()); // default for Calendar is 1 - assertEquals(0, t.getMonth()); - assertEquals(1970, t.getYear()); - assertEquals(4, t.getWeekDay()); // 1970 Jan 1 --> Thursday - assertEquals(0, t.getYearDay()); - assertEquals(0, t.getGmtOffset()); - } - - @SmallTest - public void testCompare() { - Time a = new Time(Time.TIMEZONE_UTC); - Time b = new Time("America/Los_Angeles"); - assertTrue(a.compareTo(b) < 0); - - Time c = new Time("Asia/Calcutta"); - assertTrue(a.compareTo(c) > 0); - - Time d = new Time(Time.TIMEZONE_UTC); - assertEquals(0, a.compareTo(d)); - } - - @SmallTest - public void testFormat2445() { - Time t = new Time(); - assertEquals("19700101T000000", t.format2445()); - t.setTimezone(Time.TIMEZONE_UTC); - assertEquals("19700101T000000Z", t.format2445()); - t.setAllDay(true); - assertEquals("19700101", t.format2445()); - } - - @SmallTest - public void testFormat3339() { - Time t = new Time(Time.TIMEZONE_UTC); - assertEquals("1970-01-01", t.format3339(true)); - t.set(29, 1, 2020); - assertEquals("2020-02-29", t.format3339(true)); - } - - @SmallTest - public void testToMillis() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1, 0, 0, 1, 0, 1970); - assertEquals(1000L, t.toMillis()); - - t.set(0, 0, 0, 1, 1, 2020); - assertEquals(1580515200000L, t.toMillis()); - t.set(1, 0, 0, 1, 1, 2020); - assertEquals(1580515201000L, t.toMillis()); - - t.set(1, 0, 2020); - assertEquals(1577836800000L, t.toMillis()); - t.set(1, 1, 2020); - assertEquals(1580515200000L, t.toMillis()); - } - - @SmallTest - public void testToMillis_overflow() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(32, 0, 2020); - assertEquals(1580515200000L, t.toMillis()); - assertEquals(1, t.getDay()); - assertEquals(1, t.getMonth()); - } - - @SmallTest - public void testParse() { - Time t = new Time(Time.TIMEZONE_UTC); - t.parse("20201010T160000Z"); - assertEquals(2020, t.getYear()); - assertEquals(9, t.getMonth()); - assertEquals(10, t.getDay()); - assertEquals(16, t.getHour()); - assertEquals(0, t.getMinute()); - assertEquals(0, t.getSecond()); - - t.parse("20200220"); - assertEquals(2020, t.getYear()); - assertEquals(1, t.getMonth()); - assertEquals(20, t.getDay()); - assertEquals(0, t.getHour()); - assertEquals(0, t.getMinute()); - assertEquals(0, t.getSecond()); - - try { - t.parse("invalid"); - fail(); - } catch (IllegalArgumentException e) { - // expected - } - - try { - t.parse("20201010Z160000"); - fail(); - } catch (IllegalArgumentException e) { - // expected - } - } - - @SmallTest - public void testParse3339() { - Time t = new Time(Time.TIMEZONE_UTC); - - t.parse3339("1980-05-23"); - if (!t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23) { - fail("Did not parse all-day date correctly"); - } - - t.parse3339("1980-05-23T09:50:50"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse timezone-offset-less date correctly"); - } - - t.parse3339("1980-05-23T09:50:50Z"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse UTC date correctly"); - } - - t.parse3339("1980-05-23T09:50:50.0Z"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse UTC date correctly"); - } - - t.parse3339("1980-05-23T09:50:50.12Z"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse UTC date correctly"); - } - - t.parse3339("1980-05-23T09:50:50.123Z"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse UTC date correctly"); - } - - // the time should be normalized to UTC - t.parse3339("1980-05-23T09:50:50-01:05"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 10 || t.getMinute() != 55 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse timezone-offset date correctly"); - } - - // the time should be normalized to UTC - t.parse3339("1980-05-23T09:50:50.123-01:05"); - if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23 - || t.getHour() != 10 || t.getMinute() != 55 || t.getSecond() != 50 - || t.getGmtOffset() != 0) { - fail("Did not parse timezone-offset date correctly"); - } - - try { - t.parse3339("1980"); - fail("Did not throw error on truncated input length"); - } catch (TimeFormatException e) { - // successful - } - - try { - t.parse3339("1980-05-23T09:50:50.123+"); - fail("Did not throw error on truncated timezone offset"); - } catch (TimeFormatException e1) { - // successful - } - - try { - t.parse3339("1980-05-23T09:50:50.123+05:0"); - fail("Did not throw error on truncated timezone offset"); - } catch (TimeFormatException e1) { - // successful - } - } - - @SmallTest - public void testSet_millis() { - Time t = new Time(Time.TIMEZONE_UTC); - - t.set(1000L); - assertEquals(1970, t.getYear()); - assertEquals(1, t.getSecond()); - - t.set(2000L); - assertEquals(2, t.getSecond()); - assertEquals(0, t.getMinute()); - - t.set(1000L * 60); - assertEquals(1, t.getMinute()); - assertEquals(0, t.getHour()); - - t.set(1000L * 60 * 60); - assertEquals(1, t.getHour()); - assertEquals(1, t.getDay()); - - t.set((1000L * 60 * 60 * 24) + 1000L); - assertEquals(2, t.getDay()); - assertEquals(1970, t.getYear()); - } - - @SmallTest - public void testSet_dayMonthYear() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1, 2, 2021); - assertEquals(1, t.getDay()); - assertEquals(2, t.getMonth()); - assertEquals(2021, t.getYear()); - } - - @SmallTest - public void testSet_secondMinuteHour() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1, 2, 3, 4, 5, 2021); - assertEquals(1, t.getSecond()); - assertEquals(2, t.getMinute()); - assertEquals(3, t.getHour()); - assertEquals(4, t.getDay()); - assertEquals(5, t.getMonth()); - assertEquals(2021, t.getYear()); - } - - @SmallTest - public void testSet_overflow() { - // Jan 32nd --> Feb 1st - Time t = new Time(Time.TIMEZONE_UTC); - t.set(32, 0, 2020); - assertEquals(1, t.getDay()); - assertEquals(1, t.getMonth()); - assertEquals(2020, t.getYear()); - - t = new Time(Time.TIMEZONE_UTC); - t.set(5, 10, 15, 32, 0, 2020); - assertEquals(5, t.getSecond()); - assertEquals(10, t.getMinute()); - assertEquals(15, t.getHour()); - assertEquals(1, t.getDay()); - assertEquals(1, t.getMonth()); - assertEquals(2020, t.getYear()); - } - - @SmallTest - public void testSet_other() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1, 2, 3, 4, 5, 2021); - Time t2 = new Time(); - t2.set(t); - assertEquals(Time.TIMEZONE_UTC, t2.getTimezone()); - assertEquals(1, t2.getSecond()); - assertEquals(2, t2.getMinute()); - assertEquals(3, t2.getHour()); - assertEquals(4, t2.getDay()); - assertEquals(5, t2.getMonth()); - assertEquals(2021, t2.getYear()); - } - - @SmallTest - public void testSetToNow() { - long now = System.currentTimeMillis(); - Time t = new Time(Time.TIMEZONE_UTC); - t.set(now); - long ms = t.toMillis(); - // ensure time is within 1 second because of rounding errors - assertTrue("now: " + now + "; actual: " + ms, Math.abs(ms - now) < 1000); - } - - @SmallTest - public void testGetWeekNumber() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(1000L); - assertEquals(1, t.getWeekNumber()); - t.set(1, 1, 2020); - assertEquals(5, t.getWeekNumber()); - - // ensure ISO 8601 standards are met: weeks start on Monday and the first week has at least - // 4 days in it (the year's first Thursday or Jan 4th) - for (int i = 1; i <= 8; i++) { - t.set(i, 0, 2020); - // Jan 6th is the first Monday in 2020 so that would be week 2 - assertEquals(i < 6 ? 1 : 2, t.getWeekNumber()); - } - } - - private static class DateTest { - public int year1; - public int month1; - public int day1; - public int hour1; - public int minute1; - - public int offset; - - public int year2; - public int month2; - public int day2; - public int hour2; - public int minute2; - - public DateTest(int year1, int month1, int day1, int hour1, int minute1, - int offset, int year2, int month2, int day2, int hour2, int minute2) { - this.year1 = year1; - this.month1 = month1; - this.day1 = day1; - this.hour1 = hour1; - this.minute1 = minute1; - this.offset = offset; - this.year2 = year2; - this.month2 = month2; - this.day2 = day2; - this.hour2 = hour2; - this.minute2 = minute2; - } - - public boolean equals(Time time) { - return time.getYear() == year2 && time.getMonth() == month2 && time.getDay() == day2 - && time.getHour() == hour2 && time.getMinute() == minute2; - } - } - - @SmallTest - public void testNormalize() { - Time t = new Time(Time.TIMEZONE_UTC); - t.parse("20060432T010203"); - assertEquals(1146531723000L, t.normalize()); - } - - /* These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am). */ - - // The "offset" field in "dayTests" represents days. - // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 - private DateTest[] dayTests = { - // Nov 4, 12am + 0 day = Nov 4, 12am - new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), - // Nov 5, 12am + 0 day = Nov 5, 12am - new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), - // Nov 3, 12am + 1 day = Nov 4, 12am - new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0), - // Nov 4, 12am + 1 day = Nov 5, 12am - new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0), - // Nov 5, 12am + 1 day = Nov 6, 12am - new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0), - // Nov 3, 1am + 1 day = Nov 4, 1am - new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0), - // Nov 4, 1am + 1 day = Nov 5, 1am - new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0), - // Nov 5, 1am + 1 day = Nov 6, 1am - new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0), - // Nov 3, 2am + 1 day = Nov 4, 2am - new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0), - // Nov 4, 2am + 1 day = Nov 5, 2am - new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0), - // Nov 5, 2am + 1 day = Nov 6, 2am - new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0), - }; - - // The "offset" field in "minuteTests" represents minutes. - // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11 - private DateTest[] minuteTests = { - // Nov 4, 12am + 0 minutes = Nov 4, 12am - new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0), - // Nov 4, 12am + 60 minutes = Nov 4, 1am - new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0), - // Nov 5, 12am + 0 minutes = Nov 5, 12am - new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0), - // Nov 3, 12am + 60 minutes = Nov 3, 1am - new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0), - // Nov 4, 12am + 60 minutes = Nov 4, 1am - new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0), - // Nov 5, 12am + 60 minutes = Nov 5, 1am - new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0), - // Nov 3, 1am + 60 minutes = Nov 3, 2am - new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0), - // Nov 4, 12:59am (PDT) + 2 minutes = Nov 4, 1:01am (PDT) - new DateTest(2007, 10, 4, 0, 59, 2, 2007, 10, 4, 1, 1), - // Nov 4, 12:59am (PDT) + 62 minutes = Nov 4, 1:01am (PST) - new DateTest(2007, 10, 4, 0, 59, 62, 2007, 10, 4, 1, 1), - // Nov 4, 12:30am (PDT) + 120 minutes = Nov 4, 1:30am (PST) - new DateTest(2007, 10, 4, 0, 30, 120, 2007, 10, 4, 1, 30), - // Nov 4, 12:30am (PDT) + 90 minutes = Nov 4, 1:00am (PST) - new DateTest(2007, 10, 4, 0, 30, 90, 2007, 10, 4, 1, 0), - // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT) - new DateTest(2007, 10, 4, 1, 0, 30, 2007, 10, 4, 1, 30), - // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT) - new DateTest(2007, 10, 4, 1, 30, 15, 2007, 10, 4, 1, 45), - // Mar 11, 1:30am (PST) + 30 minutes = Mar 11, 3am (PDT) - new DateTest(2007, 2, 11, 1, 30, 30, 2007, 2, 11, 3, 0), - // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST) - new DateTest(2007, 10, 4, 1, 30, 15, 2007, 10, 4, 1, 45), - // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST) - new DateTest(2007, 10, 4, 1, 30, 30, 2007, 10, 4, 2, 0), - // Nov 5, 1am + 60 minutes = Nov 5, 2am - new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0), - // Nov 3, 2am + 60 minutes = Nov 3, 3am - new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0), - // Nov 4, 2am + 30 minutes = Nov 4, 2:30am - new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30), - // Nov 4, 2am + 60 minutes = Nov 4, 3am - new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0), - // Nov 5, 2am + 60 minutes = Nov 5, 3am - new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0), - // NOTE: Calendar assumes 1am PDT == 1am PST, the two are not distinct, hence why the transition boundary itself has no tests - }; - - @MediumTest - public void testNormalize_dst() { - Time local = new Time("America/Los_Angeles"); - - int len = dayTests.length; - for (int index = 0; index < len; index++) { - DateTest test = dayTests[index]; - local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); - local.add(Time.MONTH_DAY, test.offset); - if (!test.equals(local)) { - String expectedTime = String.format("%d-%02d-%02d %02d:%02d", - test.year2, test.month2, test.day2, test.hour2, test.minute2); - String actualTime = String.format("%d-%02d-%02d %02d:%02d", - local.getYear(), local.getMonth(), local.getDay(), local.getHour(), - local.getMinute()); - fail("Expected: " + expectedTime + "; Actual: " + actualTime); - } - - local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); - local.add(Time.MONTH_DAY, test.offset); - if (!test.equals(local)) { - String expectedTime = String.format("%d-%02d-%02d %02d:%02d", - test.year2, test.month2, test.day2, test.hour2, test.minute2); - String actualTime = String.format("%d-%02d-%02d %02d:%02d", - local.getYear(), local.getMonth(), local.getDay(), local.getHour(), - local.getMinute()); - fail("Expected: " + expectedTime + "; Actual: " + actualTime); - } - } - - len = minuteTests.length; - for (int index = 0; index < len; index++) { - DateTest test = minuteTests[index]; - local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); - local.add(Time.MINUTE, test.offset); - if (!test.equals(local)) { - String expectedTime = String.format("%d-%02d-%02d %02d:%02d", - test.year2, test.month2, test.day2, test.hour2, test.minute2); - String actualTime = String.format("%d-%02d-%02d %02d:%02d", - local.getYear(), local.getMonth(), local.getDay(), local.getHour(), - local.getMinute()); - fail("Expected: " + expectedTime + "; Actual: " + actualTime); - } - - local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1); - local.add(Time.MINUTE, test.offset); - if (!test.equals(local)) { - String expectedTime = String.format("%d-%02d-%02d %02d:%02d", - test.year2, test.month2, test.day2, test.hour2, test.minute2); - String actualTime = String.format("%d-%02d-%02d %02d:%02d", - local.getYear(), local.getMonth(), local.getDay(), local.getHour(), - local.getMinute()); - fail("Expected: " + expectedTime + "; Actual: " + actualTime); - } - } - } - - @SmallTest - public void testNormalize_overflow() { - Time t = new Time(Time.TIMEZONE_UTC); - t.set(32, 0, 2020); - t.normalize(); - assertEquals(1, t.getDay()); - assertEquals(1, t.getMonth()); - } - - @SmallTest - public void testDstBehavior_addDays_ignoreDst() { - Time time = new Time("America/Los_Angeles"); - time.set(4, 10, 2007); // set to Nov 4, 2007, 12am - assertEquals(1194159600000L, time.normalize()); - time.add(Time.MONTH_DAY, 1); // changes to Nov 5, 2007, 12am - assertEquals(1194249600000L, time.toMillis()); - - time = new Time("America/Los_Angeles"); - time.set(11, 2, 2007); // set to Mar 11, 2007, 12am - assertEquals(1173600000000L, time.normalize()); - time.add(Time.MONTH_DAY, 1); // changes to Mar 12, 2007, 12am - assertEquals(1173682800000L, time.toMillis()); - } - - @SmallTest - public void testDstBehavior_addDays_applyDst() { - if (!Time.APPLY_DST_CHANGE_LOGIC) { - return; - } - Time time = new Time("America/Los_Angeles"); - time.set(4, 10, 2007); // set to Nov 4, 2007, 12am - assertEquals(1194159600000L, time.normalizeApplyDst()); - time.add(Time.MONTH_DAY, 1); // changes to Nov 4, 2007, 11pm (fall back) - assertEquals(1194246000000L, time.toMillisApplyDst()); - - time = new Time("America/Los_Angeles"); - time.set(11, 2, 2007); // set to Mar 11, 2007, 12am - assertEquals(1173600000000L, time.normalizeApplyDst()); - time.add(Time.MONTH_DAY, 1); // changes to Mar 12, 2007, 1am (roll forward) - assertEquals(1173686400000L, time.toMillisApplyDst()); - } - - @SmallTest - public void testDstBehavior_addHours_ignoreDst() { - // Note: by default, Calendar applies DST logic if adding hours or minutes but not if adding - // days, hence in this test, only if the APPLY_DST_CHANGE_LOGIC flag is false, then the time - // is adjusted with DST - Time time = new Time("America/Los_Angeles"); - time.set(4, 10, 2007); // set to Nov 4, 2007, 12am - assertEquals(1194159600000L, time.normalize()); - time.add(Time.HOUR, 24); // changes to Nov 5, 2007, 12am - assertEquals(Time.APPLY_DST_CHANGE_LOGIC ? 1194249600000L : 1194246000000L, - time.toMillis()); - - time = new Time("America/Los_Angeles"); - time.set(11, 2, 2007); // set to Mar 11, 2007, 12am - assertEquals(1173600000000L, time.normalize()); - time.add(Time.HOUR, 24); // changes to Mar 12, 2007, 12am - assertEquals(Time.APPLY_DST_CHANGE_LOGIC ? 1173682800000L : 1173686400000L, - time.toMillis()); - } - - @SmallTest - public void testDstBehavior_addHours_applyDst() { - if (!Time.APPLY_DST_CHANGE_LOGIC) { - return; - } - Time time = new Time("America/Los_Angeles"); - time.set(4, 10, 2007); // set to Nov 4, 2007, 12am - assertEquals(1194159600000L, time.normalizeApplyDst()); - time.add(Time.HOUR, 24); // changes to Nov 4, 2007, 11pm (fall back) - assertEquals(1194246000000L, time.toMillisApplyDst()); - - time = new Time("America/Los_Angeles"); - time.set(11, 2, 2007); // set to Mar 11, 2007, 12am - assertEquals(1173600000000L, time.normalizeApplyDst()); - time.add(Time.HOUR, 24); // changes to Mar 12, 2007, 1am (roll forward) - assertEquals(1173686400000L, time.toMillisApplyDst()); - } - - // Timezones that cover the world. - // Some GMT offsets occur more than once in case some cities decide to change their GMT offset. - private static final String[] mTimeZones = { - "Pacific/Kiritimati", - "Pacific/Enderbury", - "Pacific/Fiji", - "Antarctica/South_Pole", - "Pacific/Norfolk", - "Pacific/Ponape", - "Asia/Magadan", - "Australia/Lord_Howe", - "Australia/Sydney", - "Australia/Adelaide", - "Asia/Tokyo", - "Asia/Seoul", - "Asia/Taipei", - "Asia/Singapore", - "Asia/Hong_Kong", - "Asia/Saigon", - "Asia/Bangkok", - "Indian/Cocos", - "Asia/Rangoon", - "Asia/Omsk", - "Antarctica/Mawson", - "Asia/Colombo", - "Asia/Calcutta", - "Asia/Oral", - "Asia/Kabul", - "Asia/Dubai", - "Asia/Tehran", - "Europe/Moscow", - "Asia/Baghdad", - "Africa/Mogadishu", - "Europe/Athens", - "Africa/Cairo", - "Europe/Rome", - "Europe/Berlin", - "Europe/Amsterdam", - "Africa/Tunis", - "Europe/London", - "Europe/Dublin", - "Atlantic/St_Helena", - "Africa/Monrovia", - "Africa/Accra", - "Atlantic/Azores", - "Atlantic/South_Georgia", - "America/Noronha", - "America/Sao_Paulo", - "America/Cayenne", - "America/St_Johns", - "America/Puerto_Rico", - "America/Aruba", - "America/New_York", - "America/Chicago", - "America/Denver", - "America/Los_Angeles", - "America/Anchorage", - "Pacific/Marquesas", - "America/Adak", - "Pacific/Honolulu", - "Pacific/Midway", - }; - - @MediumTest - public void testGetJulianDay() { - Time time = new Time(Time.TIMEZONE_UTC); - - // for 30 random days in the year 2020 and for a random timezone, get the Julian day for - // 12am and then check that if we change the time we get the same Julian day. - for (int i = 0; i < 30; i++) { - int monthDay = (int) (Math.random() * 365) + 1; - int zoneIndex = (int) (Math.random() * mTimeZones.length); - time.setTimezone(mTimeZones[zoneIndex]); - time.set(0, 0, 0, monthDay, 0, 2020); - - int julianDay = Time.getJulianDay(time.normalize(), time.getGmtOffset()); - - // change the time during the day and check that we get the same Julian day. - for (int hour = 0; hour < 24; hour++) { - for (int minute = 0; minute < 60; minute += 15) { - time.set(0, minute, hour, monthDay, 0, 2020); - int day = Time.getJulianDay(time.normalize(), time.getGmtOffset()); - assertEquals(day, julianDay); - time.clear(Time.TIMEZONE_UTC); - } - } - } - } - - @MediumTest - public void testSetJulianDay() { - Time time = new Time(Time.TIMEZONE_UTC); - - // for each day in the year 2020, pick a random timezone, and verify that we can - // set the Julian day correctly. - for (int monthDay = 1; monthDay <= 366; monthDay++) { - int zoneIndex = (int) (Math.random() * mTimeZones.length); - // leave the "month" as zero because we are changing the "monthDay" from 1 to 366. - // the call to normalize() will then change the "month" (but we don't really care). - time.set(0, 0, 0, monthDay, 0, 2020); - time.setTimezone(mTimeZones[zoneIndex]); - long millis = time.normalize(); - int julianDay = Time.getJulianDay(millis, time.getGmtOffset()); - - time.setJulianDay(julianDay); - - // some places change daylight saving time at 12am and so there is no 12am on some days - // in some timezones - in those cases, the time is set to 1am. - // some examples: Africa/Cairo, America/Sao_Paulo, Atlantic/Azores - assertTrue(time.getHour() == 0 || time.getHour() == 1); - assertEquals(0, time.getMinute()); - assertEquals(0, time.getSecond()); - - millis = time.toMillis(); - int day = Time.getJulianDay(millis, time.getGmtOffset()); - assertEquals(day, julianDay); - time.clear(Time.TIMEZONE_UTC); - } - } -} |