• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.calendarcommon2;
17 
18 import android.test.suitebuilder.annotation.MediumTest;
19 import android.test.suitebuilder.annotation.SmallTest;
20 import android.util.TimeFormatException;
21 
22 import junit.framework.TestCase;
23 
24 /**
25  * Tests for com.android.calendarcommon2.Time.
26  *
27  * Some of these tests are borrowed from android.text.format.TimeTest.
28  */
29 public class TimeTest extends TestCase {
30 
31     @SmallTest
testNullTimezone()32     public void testNullTimezone() {
33         try {
34             Time t = new Time(null);
35             fail("expected a null timezone to throw an exception.");
36         } catch (NullPointerException npe) {
37             // expected.
38         }
39     }
40 
41     @SmallTest
testTimezone()42     public void testTimezone() {
43         Time t = new Time(Time.TIMEZONE_UTC);
44         assertEquals(Time.TIMEZONE_UTC, t.getTimezone());
45     }
46 
47     @SmallTest
testSwitchTimezone()48     public void testSwitchTimezone() {
49         Time t = new Time(Time.TIMEZONE_UTC);
50         String newTimezone = "America/Los_Angeles";
51         t.switchTimezone(newTimezone);
52         assertEquals(newTimezone, t.getTimezone());
53     }
54 
55     @SmallTest
testGetActualMaximum()56     public void testGetActualMaximum() {
57         Time t = new Time(Time.TIMEZONE_UTC);
58         t.set(1, 0, 2020);
59         assertEquals(59, t.getActualMaximum(Time.SECOND));
60         assertEquals(59, t.getActualMaximum(Time.MINUTE));
61         assertEquals(23, t.getActualMaximum(Time.HOUR));
62         assertEquals(31, t.getActualMaximum(Time.MONTH_DAY));
63         assertEquals(11, t.getActualMaximum(Time.MONTH));
64         assertEquals(7, t.getActualMaximum(Time.WEEK_DAY));
65         assertEquals(366, t.getActualMaximum(Time.YEAR_DAY)); // 2020 is a leap year
66         t.set(1, 0, 2019);
67         assertEquals(365, t.getActualMaximum(Time.YEAR_DAY));
68     }
69 
70     @SmallTest
testAdd()71     public void testAdd() {
72         Time t = new Time(Time.TIMEZONE_UTC);
73         t.set(0, 0, 0, 1, 0, 2020);
74         t.add(Time.SECOND, 1);
75         assertEquals(1, t.getSecond());
76         t.add(Time.MINUTE, 1);
77         assertEquals(1, t.getMinute());
78         t.add(Time.HOUR, 1);
79         assertEquals(1, t.getHour());
80         t.add(Time.MONTH_DAY, 1);
81         assertEquals(2, t.getDay());
82         t.add(Time.MONTH, 1);
83         assertEquals(1, t.getMonth());
84         t.add(Time.YEAR, 1);
85         assertEquals(2021, t.getYear());
86     }
87 
88     @SmallTest
testClear()89     public void testClear() {
90         Time t = new Time(Time.TIMEZONE_UTC);
91         t.clear(Time.TIMEZONE_UTC);
92 
93         assertEquals(Time.TIMEZONE_UTC, t.getTimezone());
94         assertFalse(t.isAllDay());
95         assertEquals(0, t.getSecond());
96         assertEquals(0, t.getMinute());
97         assertEquals(0, t.getHour());
98         assertEquals(1, t.getDay()); // default for Calendar is 1
99         assertEquals(0, t.getMonth());
100         assertEquals(1970, t.getYear());
101         assertEquals(4, t.getWeekDay()); // 1970 Jan 1 --> Thursday
102         assertEquals(0, t.getYearDay());
103         assertEquals(0, t.getGmtOffset());
104     }
105 
106     @SmallTest
testCompare()107     public void testCompare() {
108         Time a = new Time(Time.TIMEZONE_UTC);
109         Time b = new Time("America/Los_Angeles");
110         assertTrue(a.compareTo(b) < 0);
111 
112         Time c = new Time("Asia/Calcutta");
113         assertTrue(a.compareTo(c) > 0);
114 
115         Time d = new Time(Time.TIMEZONE_UTC);
116         assertEquals(0, a.compareTo(d));
117     }
118 
119     @SmallTest
testFormat2445()120     public void testFormat2445() {
121         Time t = new Time();
122         assertEquals("19700101T000000", t.format2445());
123         t.setTimezone(Time.TIMEZONE_UTC);
124         assertEquals("19700101T000000Z", t.format2445());
125         t.setAllDay(true);
126         assertEquals("19700101", t.format2445());
127     }
128 
129     @SmallTest
testFormat3339()130     public void testFormat3339() {
131         Time t = new Time(Time.TIMEZONE_UTC);
132         assertEquals("1970-01-01", t.format3339(true));
133         t.set(29, 1, 2020);
134         assertEquals("2020-02-29", t.format3339(true));
135     }
136 
137     @SmallTest
testToMillis()138     public void testToMillis() {
139         Time t = new Time(Time.TIMEZONE_UTC);
140         t.set(1, 0, 0, 1, 0, 1970);
141         assertEquals(1000L, t.toMillis());
142 
143         t.set(0, 0, 0, 1, 1, 2020);
144         assertEquals(1580515200000L, t.toMillis());
145         t.set(1, 0, 0, 1, 1, 2020);
146         assertEquals(1580515201000L, t.toMillis());
147 
148         t.set(1, 0, 2020);
149         assertEquals(1577836800000L, t.toMillis());
150         t.set(1, 1, 2020);
151         assertEquals(1580515200000L, t.toMillis());
152     }
153 
154     @SmallTest
testToMillis_overflow()155     public void testToMillis_overflow() {
156         Time t = new Time(Time.TIMEZONE_UTC);
157         t.set(32, 0, 2020);
158         assertEquals(1580515200000L, t.toMillis());
159         assertEquals(1, t.getDay());
160         assertEquals(1, t.getMonth());
161     }
162 
163     @SmallTest
testParse()164     public void testParse() {
165         Time t = new Time(Time.TIMEZONE_UTC);
166         t.parse("20201010T160000Z");
167         assertEquals(2020, t.getYear());
168         assertEquals(9, t.getMonth());
169         assertEquals(10, t.getDay());
170         assertEquals(16, t.getHour());
171         assertEquals(0, t.getMinute());
172         assertEquals(0, t.getSecond());
173 
174         t.parse("20200220");
175         assertEquals(2020, t.getYear());
176         assertEquals(1, t.getMonth());
177         assertEquals(20, t.getDay());
178         assertEquals(0, t.getHour());
179         assertEquals(0, t.getMinute());
180         assertEquals(0, t.getSecond());
181 
182         try {
183             t.parse("invalid");
184             fail();
185         } catch (IllegalArgumentException e) {
186             // expected
187         }
188 
189         try {
190             t.parse("20201010Z160000");
191             fail();
192         } catch (IllegalArgumentException e) {
193             // expected
194         }
195     }
196 
197     @SmallTest
testParse3339()198     public void testParse3339() {
199         Time t = new Time(Time.TIMEZONE_UTC);
200 
201         t.parse3339("1980-05-23");
202         if (!t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23) {
203             fail("Did not parse all-day date correctly");
204         }
205 
206         t.parse3339("1980-05-23T09:50:50");
207         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
208                 || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50
209                 || t.getGmtOffset() != 0) {
210             fail("Did not parse timezone-offset-less date correctly");
211         }
212 
213         t.parse3339("1980-05-23T09:50:50Z");
214         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
215                 || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50
216                 || t.getGmtOffset() != 0) {
217             fail("Did not parse UTC date correctly");
218         }
219 
220         t.parse3339("1980-05-23T09:50:50.0Z");
221         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
222                 || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50
223                 || t.getGmtOffset() != 0) {
224             fail("Did not parse UTC date correctly");
225         }
226 
227         t.parse3339("1980-05-23T09:50:50.12Z");
228         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
229                 || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50
230                 || t.getGmtOffset() != 0) {
231             fail("Did not parse UTC date correctly");
232         }
233 
234         t.parse3339("1980-05-23T09:50:50.123Z");
235         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
236                 || t.getHour() != 9 || t.getMinute() != 50 || t.getSecond() != 50
237                 || t.getGmtOffset() != 0) {
238             fail("Did not parse UTC date correctly");
239         }
240 
241         // the time should be normalized to UTC
242         t.parse3339("1980-05-23T09:50:50-01:05");
243         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
244                 || t.getHour() != 10 || t.getMinute() != 55 || t.getSecond() != 50
245                 || t.getGmtOffset() != 0) {
246             fail("Did not parse timezone-offset date correctly");
247         }
248 
249         // the time should be normalized to UTC
250         t.parse3339("1980-05-23T09:50:50.123-01:05");
251         if (t.isAllDay() || t.getYear() != 1980 || t.getMonth() != 4 || t.getDay() != 23
252                 || t.getHour() != 10 || t.getMinute() != 55 || t.getSecond() != 50
253                 || t.getGmtOffset() != 0) {
254             fail("Did not parse timezone-offset date correctly");
255         }
256 
257         try {
258             t.parse3339("1980");
259             fail("Did not throw error on truncated input length");
260         } catch (TimeFormatException e) {
261             // successful
262         }
263 
264         try {
265             t.parse3339("1980-05-23T09:50:50.123+");
266             fail("Did not throw error on truncated timezone offset");
267         } catch (TimeFormatException e1) {
268             // successful
269         }
270 
271         try {
272             t.parse3339("1980-05-23T09:50:50.123+05:0");
273             fail("Did not throw error on truncated timezone offset");
274         } catch (TimeFormatException e1) {
275             // successful
276         }
277     }
278 
279     @SmallTest
testSet_millis()280     public void testSet_millis() {
281         Time t = new Time(Time.TIMEZONE_UTC);
282 
283         t.set(1000L);
284         assertEquals(1970, t.getYear());
285         assertEquals(1, t.getSecond());
286 
287         t.set(2000L);
288         assertEquals(2, t.getSecond());
289         assertEquals(0, t.getMinute());
290 
291         t.set(1000L * 60);
292         assertEquals(1, t.getMinute());
293         assertEquals(0, t.getHour());
294 
295         t.set(1000L * 60 * 60);
296         assertEquals(1, t.getHour());
297         assertEquals(1, t.getDay());
298 
299         t.set((1000L * 60 * 60 * 24) + 1000L);
300         assertEquals(2, t.getDay());
301         assertEquals(1970, t.getYear());
302     }
303 
304     @SmallTest
testSet_dayMonthYear()305     public void testSet_dayMonthYear() {
306         Time t = new Time(Time.TIMEZONE_UTC);
307         t.set(1, 2, 2021);
308         assertEquals(1, t.getDay());
309         assertEquals(2, t.getMonth());
310         assertEquals(2021, t.getYear());
311     }
312 
313     @SmallTest
testSet_secondMinuteHour()314     public void testSet_secondMinuteHour() {
315         Time t = new Time(Time.TIMEZONE_UTC);
316         t.set(1, 2, 3, 4, 5, 2021);
317         assertEquals(1, t.getSecond());
318         assertEquals(2, t.getMinute());
319         assertEquals(3, t.getHour());
320         assertEquals(4, t.getDay());
321         assertEquals(5, t.getMonth());
322         assertEquals(2021, t.getYear());
323     }
324 
325     @SmallTest
testSet_overflow()326     public void testSet_overflow() {
327         // Jan 32nd --> Feb 1st
328         Time t = new Time(Time.TIMEZONE_UTC);
329         t.set(32, 0, 2020);
330         assertEquals(1, t.getDay());
331         assertEquals(1, t.getMonth());
332         assertEquals(2020, t.getYear());
333 
334         t = new Time(Time.TIMEZONE_UTC);
335         t.set(5, 10, 15, 32, 0, 2020);
336         assertEquals(5, t.getSecond());
337         assertEquals(10, t.getMinute());
338         assertEquals(15, t.getHour());
339         assertEquals(1, t.getDay());
340         assertEquals(1, t.getMonth());
341         assertEquals(2020, t.getYear());
342     }
343 
344     @SmallTest
testSet_other()345     public void testSet_other() {
346         Time t = new Time(Time.TIMEZONE_UTC);
347         t.set(1, 2, 3, 4, 5, 2021);
348         Time t2 = new Time();
349         t2.set(t);
350         assertEquals(Time.TIMEZONE_UTC, t2.getTimezone());
351         assertEquals(1, t2.getSecond());
352         assertEquals(2, t2.getMinute());
353         assertEquals(3, t2.getHour());
354         assertEquals(4, t2.getDay());
355         assertEquals(5, t2.getMonth());
356         assertEquals(2021, t2.getYear());
357     }
358 
359     @SmallTest
testSetToNow()360     public void testSetToNow() {
361         long now = System.currentTimeMillis();
362         Time t = new Time(Time.TIMEZONE_UTC);
363         t.set(now);
364         long ms = t.toMillis();
365         // ensure time is within 1 second because of rounding errors
366         assertTrue("now: " + now + "; actual: " + ms, Math.abs(ms - now) < 1000);
367     }
368 
369     @SmallTest
testGetWeekNumber()370     public void testGetWeekNumber() {
371         Time t = new Time(Time.TIMEZONE_UTC);
372         t.set(1000L);
373         assertEquals(1, t.getWeekNumber());
374         t.set(1, 1, 2020);
375         assertEquals(5, t.getWeekNumber());
376 
377         // ensure ISO 8601 standards are met: weeks start on Monday and the first week has at least
378         // 4 days in it (the year's first Thursday or Jan 4th)
379         for (int i = 1; i <= 8; i++) {
380             t.set(i, 0, 2020);
381             // Jan 6th is the first Monday in 2020 so that would be week 2
382             assertEquals(i < 6 ? 1 : 2, t.getWeekNumber());
383         }
384     }
385 
386     private static class DateTest {
387         public int year1;
388         public int month1;
389         public int day1;
390         public int hour1;
391         public int minute1;
392 
393         public int offset;
394 
395         public int year2;
396         public int month2;
397         public int day2;
398         public int hour2;
399         public int minute2;
400 
401         public DateTest(int year1, int month1, int day1, int hour1, int minute1,
402                 int offset, int year2, int month2, int day2, int hour2, int minute2) {
403             this.year1 = year1;
404             this.month1 = month1;
405             this.day1 = day1;
406             this.hour1 = hour1;
407             this.minute1 = minute1;
408             this.offset = offset;
409             this.year2 = year2;
410             this.month2 = month2;
411             this.day2 = day2;
412             this.hour2 = hour2;
413             this.minute2 = minute2;
414         }
415 
416         public boolean equals(Time time) {
417             return time.getYear() == year2 && time.getMonth() == month2 && time.getDay() == day2
418                     && time.getHour() == hour2 && time.getMinute() == minute2;
419         }
420     }
421 
422     @SmallTest
423     public void testNormalize() {
424         Time t = new Time(Time.TIMEZONE_UTC);
425         t.parse("20060432T010203");
426         assertEquals(1146531723000L, t.normalize());
427     }
428 
429     /* These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am). */
430 
431     // The "offset" field in "dayTests" represents days.
432     // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
433     private DateTest[] dayTests = {
434             // Nov 4, 12am + 0 day = Nov 4, 12am
435             new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
436             // Nov 5, 12am + 0 day = Nov 5, 12am
437             new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
438             // Nov 3, 12am + 1 day = Nov 4, 12am
439             new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0),
440             // Nov 4, 12am + 1 day = Nov 5, 12am
441             new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0),
442             // Nov 5, 12am + 1 day = Nov 6, 12am
443             new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0),
444             // Nov 3, 1am + 1 day = Nov 4, 1am
445             new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0),
446             // Nov 4, 1am + 1 day = Nov 5, 1am
447             new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0),
448             // Nov 5, 1am + 1 day = Nov 6, 1am
449             new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0),
450             // Nov 3, 2am + 1 day = Nov 4, 2am
451             new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0),
452             // Nov 4, 2am + 1 day = Nov 5, 2am
453             new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0),
454             // Nov 5, 2am + 1 day = Nov 6, 2am
455             new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0),
456     };
457 
458     // The "offset" field in "minuteTests" represents minutes.
459     // Note: the month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
460     private DateTest[] minuteTests = {
461             // Nov 4, 12am + 0 minutes = Nov 4, 12am
462             new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
463             // Nov 4, 12am + 60 minutes = Nov 4, 1am
464             new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0),
465             // Nov 5, 12am + 0 minutes = Nov 5, 12am
466             new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
467             // Nov 3, 12am + 60 minutes = Nov 3, 1am
468             new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0),
469             // Nov 4, 12am + 60 minutes = Nov 4, 1am
470             new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0),
471             // Nov 5, 12am + 60 minutes = Nov 5, 1am
472             new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0),
473             // Nov 3, 1am + 60 minutes = Nov 3, 2am
474             new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0),
475             // Nov 4, 12:59am (PDT) + 2 minutes = Nov 4, 1:01am (PDT)
476             new DateTest(2007, 10, 4, 0, 59, 2, 2007, 10, 4, 1, 1),
477             // Nov 4, 12:59am (PDT) + 62 minutes = Nov 4, 1:01am (PST)
478             new DateTest(2007, 10, 4, 0, 59, 62, 2007, 10, 4, 1, 1),
479             // Nov 4, 12:30am (PDT) + 120 minutes = Nov 4, 1:30am (PST)
480             new DateTest(2007, 10, 4, 0, 30, 120, 2007, 10, 4, 1, 30),
481             // Nov 4, 12:30am (PDT) + 90 minutes = Nov 4, 1:00am (PST)
482             new DateTest(2007, 10, 4, 0, 30, 90, 2007, 10, 4, 1, 0),
483             // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT)
484             new DateTest(2007, 10, 4, 1, 0, 30, 2007, 10, 4, 1, 30),
485             // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT)
486             new DateTest(2007, 10, 4, 1, 30, 15, 2007, 10, 4, 1, 45),
487             // Mar 11, 1:30am (PST) + 30 minutes = Mar 11, 3am (PDT)
488             new DateTest(2007, 2, 11, 1, 30, 30, 2007, 2, 11, 3, 0),
489             // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST)
490             new DateTest(2007, 10, 4, 1, 30, 15, 2007, 10, 4, 1, 45),
491             // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST)
492             new DateTest(2007, 10, 4, 1, 30, 30, 2007, 10, 4, 2, 0),
493             // Nov 5, 1am + 60 minutes = Nov 5, 2am
494             new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0),
495             // Nov 3, 2am + 60 minutes = Nov 3, 3am
496             new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0),
497             // Nov 4, 2am + 30 minutes = Nov 4, 2:30am
498             new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30),
499             // Nov 4, 2am + 60 minutes = Nov 4, 3am
500             new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0),
501             // Nov 5, 2am + 60 minutes = Nov 5, 3am
502             new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0),
503             // NOTE: Calendar assumes 1am PDT == 1am PST, the two are not distinct, hence why the transition boundary itself has no tests
504     };
505 
506     @MediumTest
507     public void testNormalize_dst() {
508         Time local = new Time("America/Los_Angeles");
509 
510         int len = dayTests.length;
511         for (int index = 0; index < len; index++) {
512             DateTest test = dayTests[index];
513             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
514             local.add(Time.MONTH_DAY, test.offset);
515             if (!test.equals(local)) {
516                 String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
517                         test.year2, test.month2, test.day2, test.hour2, test.minute2);
518                 String actualTime = String.format("%d-%02d-%02d %02d:%02d",
519                         local.getYear(), local.getMonth(), local.getDay(), local.getHour(),
520                         local.getMinute());
521                 fail("Expected: " + expectedTime + "; Actual: " + actualTime);
522             }
523 
524             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
525             local.add(Time.MONTH_DAY, test.offset);
526             if (!test.equals(local)) {
527                 String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
528                         test.year2, test.month2, test.day2, test.hour2, test.minute2);
529                 String actualTime = String.format("%d-%02d-%02d %02d:%02d",
530                         local.getYear(), local.getMonth(), local.getDay(), local.getHour(),
531                         local.getMinute());
532                 fail("Expected: " + expectedTime + "; Actual: " + actualTime);
533             }
534         }
535 
536         len = minuteTests.length;
537         for (int index = 0; index < len; index++) {
538             DateTest test = minuteTests[index];
539             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
540             local.add(Time.MINUTE, test.offset);
541             if (!test.equals(local)) {
542                 String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
543                         test.year2, test.month2, test.day2, test.hour2, test.minute2);
544                 String actualTime = String.format("%d-%02d-%02d %02d:%02d",
545                         local.getYear(), local.getMonth(), local.getDay(), local.getHour(),
546                         local.getMinute());
547                 fail("Expected: " + expectedTime + "; Actual: " + actualTime);
548             }
549 
550             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
551             local.add(Time.MINUTE, test.offset);
552             if (!test.equals(local)) {
553                 String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
554                         test.year2, test.month2, test.day2, test.hour2, test.minute2);
555                 String actualTime = String.format("%d-%02d-%02d %02d:%02d",
556                         local.getYear(), local.getMonth(), local.getDay(), local.getHour(),
557                         local.getMinute());
558                 fail("Expected: " + expectedTime + "; Actual: " + actualTime);
559             }
560         }
561     }
562 
563     @SmallTest
564     public void testNormalize_overflow() {
565         Time t = new Time(Time.TIMEZONE_UTC);
566         t.set(32, 0, 2020);
567         t.normalize();
568         assertEquals(1, t.getDay());
569         assertEquals(1, t.getMonth());
570     }
571 
572     @SmallTest
573     public void testDstBehavior_addDays_ignoreDst() {
574         Time time = new Time("America/Los_Angeles");
575         time.set(4, 10, 2007);  // set to Nov 4, 2007, 12am
576         assertEquals(1194159600000L, time.normalize());
577         time.add(Time.MONTH_DAY, 1); // changes to Nov 5, 2007, 12am
578         assertEquals(1194249600000L, time.toMillis());
579 
580         time = new Time("America/Los_Angeles");
581         time.set(11, 2, 2007);  // set to Mar 11, 2007, 12am
582         assertEquals(1173600000000L, time.normalize());
583         time.add(Time.MONTH_DAY, 1); // changes to Mar 12, 2007, 12am
584         assertEquals(1173682800000L, time.toMillis());
585     }
586 
587     @SmallTest
588     public void testDstBehavior_addDays_applyDst() {
589         if (!Time.APPLY_DST_CHANGE_LOGIC) {
590             return;
591         }
592         Time time = new Time("America/Los_Angeles");
593         time.set(4, 10, 2007);  // set to Nov 4, 2007, 12am
594         assertEquals(1194159600000L, time.normalizeApplyDst());
595         time.add(Time.MONTH_DAY, 1); // changes to Nov 4, 2007, 11pm (fall back)
596         assertEquals(1194246000000L, time.toMillisApplyDst());
597 
598         time = new Time("America/Los_Angeles");
599         time.set(11, 2, 2007);  // set to Mar 11, 2007, 12am
600         assertEquals(1173600000000L, time.normalizeApplyDst());
601         time.add(Time.MONTH_DAY, 1); // changes to Mar 12, 2007, 1am (roll forward)
602         assertEquals(1173686400000L, time.toMillisApplyDst());
603     }
604 
605     @SmallTest
606     public void testDstBehavior_addHours_ignoreDst() {
607         // Note: by default, Calendar applies DST logic if adding hours or minutes but not if adding
608         // days, hence in this test, only if the APPLY_DST_CHANGE_LOGIC flag is false, then the time
609         // is adjusted with DST
610         Time time = new Time("America/Los_Angeles");
611         time.set(4, 10, 2007);  // set to Nov 4, 2007, 12am
612         assertEquals(1194159600000L, time.normalize());
613         time.add(Time.HOUR, 24); // changes to Nov 5, 2007, 12am
614         assertEquals(Time.APPLY_DST_CHANGE_LOGIC ? 1194249600000L : 1194246000000L,
615                         time.toMillis());
616 
617         time = new Time("America/Los_Angeles");
618         time.set(11, 2, 2007);  // set to Mar 11, 2007, 12am
619         assertEquals(1173600000000L, time.normalize());
620         time.add(Time.HOUR, 24); // changes to Mar 12, 2007, 12am
621         assertEquals(Time.APPLY_DST_CHANGE_LOGIC ? 1173682800000L : 1173686400000L,
622                         time.toMillis());
623     }
624 
625     @SmallTest
626     public void testDstBehavior_addHours_applyDst() {
627         if (!Time.APPLY_DST_CHANGE_LOGIC) {
628             return;
629         }
630         Time time = new Time("America/Los_Angeles");
631         time.set(4, 10, 2007);  // set to Nov 4, 2007, 12am
632         assertEquals(1194159600000L, time.normalizeApplyDst());
633         time.add(Time.HOUR, 24); // changes to Nov 4, 2007, 11pm (fall back)
634         assertEquals(1194246000000L, time.toMillisApplyDst());
635 
636         time = new Time("America/Los_Angeles");
637         time.set(11, 2, 2007);  // set to Mar 11, 2007, 12am
638         assertEquals(1173600000000L, time.normalizeApplyDst());
639         time.add(Time.HOUR, 24); // changes to Mar 12, 2007, 1am (roll forward)
640         assertEquals(1173686400000L, time.toMillisApplyDst());
641     }
642 
643     // Timezones that cover the world.
644     // Some GMT offsets occur more than once in case some cities decide to change their GMT offset.
645     private static final String[] mTimeZones = {
646             "Pacific/Kiritimati",
647             "Pacific/Enderbury",
648             "Pacific/Fiji",
649             "Antarctica/South_Pole",
650             "Pacific/Norfolk",
651             "Pacific/Ponape",
652             "Asia/Magadan",
653             "Australia/Lord_Howe",
654             "Australia/Sydney",
655             "Australia/Adelaide",
656             "Asia/Tokyo",
657             "Asia/Seoul",
658             "Asia/Taipei",
659             "Asia/Singapore",
660             "Asia/Hong_Kong",
661             "Asia/Saigon",
662             "Asia/Bangkok",
663             "Indian/Cocos",
664             "Asia/Rangoon",
665             "Asia/Omsk",
666             "Antarctica/Mawson",
667             "Asia/Colombo",
668             "Asia/Calcutta",
669             "Asia/Oral",
670             "Asia/Kabul",
671             "Asia/Dubai",
672             "Asia/Tehran",
673             "Europe/Moscow",
674             "Asia/Baghdad",
675             "Africa/Mogadishu",
676             "Europe/Athens",
677             "Africa/Cairo",
678             "Europe/Rome",
679             "Europe/Berlin",
680             "Europe/Amsterdam",
681             "Africa/Tunis",
682             "Europe/London",
683             "Europe/Dublin",
684             "Atlantic/St_Helena",
685             "Africa/Monrovia",
686             "Africa/Accra",
687             "Atlantic/Azores",
688             "Atlantic/South_Georgia",
689             "America/Noronha",
690             "America/Sao_Paulo",
691             "America/Cayenne",
692             "America/St_Johns",
693             "America/Puerto_Rico",
694             "America/Aruba",
695             "America/New_York",
696             "America/Chicago",
697             "America/Denver",
698             "America/Los_Angeles",
699             "America/Anchorage",
700             "Pacific/Marquesas",
701             "America/Adak",
702             "Pacific/Honolulu",
703             "Pacific/Midway",
704     };
705 
706     @MediumTest
707     public void testGetJulianDay() {
708         Time time = new Time(Time.TIMEZONE_UTC);
709 
710         // for 30 random days in the year 2020 and for a random timezone, get the Julian day for
711         // 12am and then check that if we change the time we get the same Julian day.
712         for (int i = 0; i < 30; i++) {
713             int monthDay = (int) (Math.random() * 365) + 1;
714             int zoneIndex = (int) (Math.random() * mTimeZones.length);
715             time.setTimezone(mTimeZones[zoneIndex]);
716             time.set(0, 0, 0, monthDay, 0, 2020);
717 
718             int julianDay = Time.getJulianDay(time.normalize(), time.getGmtOffset());
719 
720             // change the time during the day and check that we get the same Julian day.
721             for (int hour = 0; hour < 24; hour++) {
722                 for (int minute = 0; minute < 60; minute += 15) {
723                     time.set(0, minute, hour, monthDay, 0, 2020);
724                     int day = Time.getJulianDay(time.normalize(), time.getGmtOffset());
725                     assertEquals(day, julianDay);
726                     time.clear(Time.TIMEZONE_UTC);
727                 }
728             }
729         }
730     }
731 
732     @MediumTest
733     public void testSetJulianDay() {
734         Time time = new Time(Time.TIMEZONE_UTC);
735 
736         // for each day in the year 2020, pick a random timezone, and verify that we can
737         // set the Julian day correctly.
738         for (int monthDay = 1; monthDay <= 366; monthDay++) {
739             int zoneIndex = (int) (Math.random() * mTimeZones.length);
740             // leave the "month" as zero because we are changing the "monthDay" from 1 to 366.
741             // the call to normalize() will then change the "month" (but we don't really care).
742             time.set(0, 0, 0, monthDay, 0, 2020);
743             time.setTimezone(mTimeZones[zoneIndex]);
744             long millis = time.normalize();
745             int julianDay = Time.getJulianDay(millis, time.getGmtOffset());
746 
747             time.setJulianDay(julianDay);
748 
749             // some places change daylight saving time at 12am and so there is no 12am on some days
750             // in some timezones - in those cases, the time is set to 1am.
751             // some examples: Africa/Cairo, America/Sao_Paulo, Atlantic/Azores
752             assertTrue(time.getHour() == 0 || time.getHour() == 1);
753             assertEquals(0, time.getMinute());
754             assertEquals(0, time.getSecond());
755 
756             millis = time.toMillis();
757             int day = Time.getJulianDay(millis, time.getGmtOffset());
758             assertEquals(day, julianDay);
759             time.clear(Time.TIMEZONE_UTC);
760         }
761     }
762 }
763