1 /* 2 * Copyright (C) 2017 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 libcore.java.time; 17 18 import org.junit.Test; 19 import java.time.LocalDate; 20 import java.time.LocalDateTime; 21 import java.time.LocalTime; 22 import java.time.Month; 23 import java.time.ZoneId; 24 import java.time.ZoneOffset; 25 import java.time.ZonedDateTime; 26 27 import static org.junit.Assert.assertEquals; 28 29 30 /** 31 * Additional tets for {@link ZonedDateTime}. 32 * 33 * @see tck.java.time.TCKZonedDateTime 34 * @see test.java.time.TestZonedDateTime 35 */ 36 public class ZonedDateTimeTest { 37 38 // Europe/Vienna is UTC+2 during summer, UTC+1 during winter. 39 private static final ZoneId ZONE_VIENNA = ZoneId.of("Europe/Vienna"); 40 41 // UTC+1, the offset during winter time. 42 private static final ZoneOffset OFFSET_P1 = ZoneOffset.ofHours(1); 43 44 // UTC+2, the offset during summer time. 45 private static final ZoneOffset OFFSET_P2 = ZoneOffset.ofHours(2); 46 47 // LocalDateTime during winter time (OFFSET_P1 in ZONE_VIENNA). 48 private static final LocalDateTime LDT_P1 = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0); 49 50 // LocalDateTime during summer time (OFFSET_P2 in ZONE_VIENNA). 51 private static final LocalDateTime LDT_P2 = LocalDateTime.of(2000, Month.JUNE, 1, 0, 0); 52 53 // LocalDateTime that is in a gap that occurs at the switch from winter time to summer time. 54 // This is not a valid local time in ZONE_VIENNA. 55 private static final LocalDateTime LDT_IN_GAP = LocalDateTime.of(2000, Month.MARCH, 26, 2, 30); 56 57 // LocalDateTime that is in an overlap that occurs at the switch from summer time to winter 58 // time. This LDT actually occurs twice in ZONE_VIENNA. 59 private static final LocalDateTime LDT_IN_OVERLAP = 60 LocalDateTime.of(2000, Month.OCTOBER, 29, 2, 30); 61 62 @Test 63 public void test_ofInstant() { 64 // ofInstant behaves as if it calculated an Instant from the LocalDateTime/ZoneOffset 65 // and then calling ofInstant(Instant, ZoneId). That's why "invalid" zone offsets are 66 // tolerated and basically just change how the LocalDateTime is interpreted. 67 68 // checkOfInstant(localDateTime, offset, zone, expectedDateTime, expectedOffset) 69 70 // Correct offset in summer. 71 checkOfInstant(LDT_P1, OFFSET_P1, ZONE_VIENNA, LDT_P1, OFFSET_P1); 72 // Correct offset in winter. 73 checkOfInstant(LDT_P2, OFFSET_P2, ZONE_VIENNA, LDT_P2, OFFSET_P2); 74 // "Wrong" offset in winter. 75 checkOfInstant(LDT_P1, OFFSET_P2, ZONE_VIENNA, LDT_P1.minusDays(1).withHour(23), OFFSET_P1); 76 // "Wrong" offset in summer. 77 checkOfInstant(LDT_P2, OFFSET_P1, ZONE_VIENNA, LDT_P2.withHour(1), OFFSET_P2); 78 79 // Very wrong offset in winter. 80 checkOfInstant(LDT_P1, ZoneOffset.ofHours(-10), ZONE_VIENNA, LDT_P1.withHour(11), 81 OFFSET_P1); 82 83 // Neither of those combinations exist, so they are interpreted as either before or after 84 // the gap, depending on the offset. 85 checkOfInstant(LDT_IN_GAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_GAP.plusHours(1), OFFSET_P2); 86 checkOfInstant(LDT_IN_GAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_GAP.minusHours(1), OFFSET_P1); 87 88 // Both combinations exist and are valid, so they produce exactly the input. 89 checkOfInstant(LDT_IN_OVERLAP, OFFSET_P1, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P1); 90 checkOfInstant(LDT_IN_OVERLAP, OFFSET_P2, ZONE_VIENNA, LDT_IN_OVERLAP, OFFSET_P2); 91 } 92 93 /** 94 * Assert that calling {@link ZonedDateTime#ofInstant(LocalDateTime, ZoneOffset, ZoneId)} with 95 * the first three parameters produces a sane result with the localDateTime and offset equal to 96 * the last two. 97 */ 98 private static void checkOfInstant(LocalDateTime localDateTime, ZoneOffset offset, 99 ZoneId zone, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) { 100 ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(localDateTime, offset, zone); 101 String message = String.format(" for ofInstant(%s, %s, %s) = %s, ", 102 localDateTime, offset, zone, zonedDateTime); 103 // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(), 104 // specifically when offset is not a valid offset for zone at localDateTime (or ever). 105 assertEquals("zone" + message, zone, zonedDateTime.getZone()); 106 107 assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset()); 108 assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime()); 109 if (offset.equals(expectedOffset)) { 110 // When we get same offset, the localDateTime must be the same as the input. This 111 // assert basically just verifies that the test is written correctly. 112 assertEquals("expected localDateTime" + message, 113 expectedDateTime, zonedDateTime.toLocalDateTime()); 114 } 115 } 116 117 @Test(expected = NullPointerException.class) 118 public void test_ofInstant_localDateTime_null() { 119 ZonedDateTime.ofInstant(null, OFFSET_P1, ZONE_VIENNA); 120 } 121 122 @Test(expected = NullPointerException.class) 123 public void test_ofInstant_offset_null() { 124 ZonedDateTime.ofInstant(LDT_P1, null, ZONE_VIENNA); 125 } 126 127 @Test(expected = NullPointerException.class) 128 public void test_ofInstant_zone_null() { 129 ZonedDateTime.ofInstant(LDT_P1, OFFSET_P1, null); 130 } 131 132 @Test 133 public void test_ofLocal() { 134 // checkOfLocal(localDateTime, zone, preferredOffset, expectedDateTime, expectedOffset) 135 136 // Correct offset in summer. 137 checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P1, LDT_P1, OFFSET_P1); 138 // Correct offset in winter. 139 checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P2, LDT_P2, OFFSET_P2); 140 // "Wrong" offset in winter. 141 checkOfLocal(LDT_P1, ZONE_VIENNA, OFFSET_P2, LDT_P1, OFFSET_P1); 142 // "Wrong" offset in summer. 143 checkOfLocal(LDT_P2, ZONE_VIENNA, OFFSET_P1, LDT_P2, OFFSET_P2); 144 // Very wrong offset in winter. 145 checkOfLocal(LDT_P1, ZONE_VIENNA, ZoneOffset.ofHours(-10), LDT_P1, OFFSET_P1); 146 147 // Neither of those combinations exist, so they are interpreted as after the gap. 148 checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_GAP.plusHours(1), OFFSET_P2); 149 checkOfLocal(LDT_IN_GAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_GAP.plusHours(1), OFFSET_P2); 150 151 // Both combinations exist and are valid, so they produce exactly the input. 152 checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P1, LDT_IN_OVERLAP, OFFSET_P1); 153 checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, OFFSET_P2, LDT_IN_OVERLAP, OFFSET_P2); 154 155 // Passing in null for preferredOffset will be biased to the offset before the transition. 156 checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, /* preferredOffset */ null, 157 LDT_IN_OVERLAP, OFFSET_P2); 158 // Passing in an invalid offset will be biased to the offset before the transition. 159 checkOfLocal(LDT_IN_OVERLAP, ZONE_VIENNA, ZoneOffset.ofHours(10), 160 LDT_IN_OVERLAP, OFFSET_P2); 161 } 162 163 /** 164 * Assert that calling {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} with 165 * the first three parameters produces a sane result with the localDateTime, and offset equal 166 * to the last two. 167 */ 168 private static void checkOfLocal(LocalDateTime localDateTime, ZoneId zone, 169 ZoneOffset preferredOffset, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) { 170 ZonedDateTime zonedDateTime = ZonedDateTime.ofLocal(localDateTime, zone, preferredOffset); 171 String message = String.format(" for ofLocal(%s, %s, %s) = %s, ", 172 localDateTime, zone, preferredOffset, zonedDateTime); 173 // Note that localDateTime doesn't necessarily equal zoneDateTime.toLocalDateTime(), 174 // specifically when offset is not a valid offset for zone at localDateTime (or ever). 175 assertEquals("zone" + message, zone, zonedDateTime.getZone()); 176 assertEquals("offset" + message, expectedOffset, zonedDateTime.getOffset()); 177 assertEquals("localDateTime" + message, expectedDateTime, zonedDateTime.toLocalDateTime()); 178 } 179 180 @Test(expected = NullPointerException.class) 181 public void test_ofLocal_localDateTime_null() { 182 ZonedDateTime.ofLocal(null, ZONE_VIENNA, OFFSET_P1); 183 } 184 185 @Test(expected = NullPointerException.class) 186 public void test_ofLocal_zone_null() { 187 ZonedDateTime.ofLocal(LDT_P1, null, OFFSET_P1); 188 } 189 } 190