• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.i18n.test.timezone;
18 
19 import android.icu.testsharding.MainTestShard;
20 import com.android.i18n.timezone.I18nModuleDebug;
21 import com.android.i18n.timezone.DebugInfo;
22 import com.android.i18n.timezone.TimeZoneDataFiles;
23 import com.android.i18n.timezone.TzDataSetVersion;
24 import com.android.i18n.timezone.ZoneInfoDb;
25 import org.junit.Test;
26 
27 import android.icu.platform.AndroidDataFiles;
28 import android.icu.text.TimeZoneNames;
29 import android.icu.util.VersionInfo;
30 import android.system.Os;
31 
32 import com.android.icu.util.ExtendedTimeZone;
33 import com.android.icu.util.Icu4cMetadata;
34 
35 import java.io.File;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.Date;
40 import java.util.List;
41 import java.util.Locale;
42 import java.util.Set;
43 import java.util.concurrent.BrokenBarrierException;
44 import java.util.concurrent.CyclicBarrier;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.atomic.AtomicInteger;
47 import java.util.function.Function;
48 import java.util.stream.Collectors;
49 
50 import com.android.i18n.timezone.TimeZoneFinder;
51 
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertFalse;
54 import static org.junit.Assert.assertTrue;
55 import static org.junit.Assert.fail;
56 
57 
58 /**
59  * Tests that compare ICU and libcore time zone behavior and similar cross-cutting concerns.
60  */
61 @MainTestShard
62 public class TimeZoneIntegrationTest {
63 
64     // http://b/28949992
65     @Test
testJavaSetDefaultAppliesToIcuTimezone()66     public void testJavaSetDefaultAppliesToIcuTimezone() {
67         java.util.TimeZone origTz = java.util.TimeZone.getDefault();
68         try {
69             android.icu.util.TimeZone origIcuTz = android.icu.util.TimeZone.getDefault();
70             assertEquals(origTz.getID(), origIcuTz.getID());
71 
72             java.util.TimeZone tz = java.util.TimeZone.getTimeZone("GMT-05:00");
73             java.util.TimeZone.setDefault(tz);
74             android.icu.util.TimeZone icuTz = android.icu.util.TimeZone.getDefault();
75             assertEquals(tz.getID(), icuTz.getID());
76         } finally {
77             java.util.TimeZone.setDefault(origTz);
78         }
79     }
80 
81     // http://b/30937209
82     @Test
testSetDefaultDeadlock()83     public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException {
84         // Since this tests a deadlock, the test has two fundamental problems:
85         // - it is probabilistic: it's not guaranteed to fail if the problem exists
86         // - if it fails, it will effectively hang the current runtime, as no other thread will
87         //   be able to call TimeZone.getDefault()/setDefault() successfully any more.
88 
89         // 10 was too low to be reliable, 100 failed more than half the time (on a bullhead).
90         final int iterations = 100;
91         java.util.TimeZone otherTimeZone = java.util.TimeZone.getTimeZone("Europe/London");
92         AtomicInteger setterCount = new AtomicInteger();
93         CyclicBarrier startBarrier = new CyclicBarrier(2);
94         Thread setter = new Thread(() -> {
95             waitFor(startBarrier);
96             for (int i = 0; i < iterations; i++) {
97                 java.util.TimeZone.setDefault(otherTimeZone);
98                 java.util.TimeZone.setDefault(null);
99                 setterCount.set(i+1);
100             }
101         });
102         setter.setName("testSetDefaultDeadlock setter");
103 
104         AtomicInteger getterCount = new AtomicInteger();
105         Thread getter = new Thread(() -> {
106             waitFor(startBarrier);
107             for (int i = 0; i < iterations; i++) {
108                 android.icu.util.TimeZone.getDefault();
109                 getterCount.set(i+1);
110             }
111         });
112         getter.setName("testSetDefaultDeadlock getter");
113 
114         setter.start();
115         getter.start();
116 
117         // 2 seconds is plenty: If successful, we usually complete much faster.
118         setter.join(1000);
119         getter.join(1000);
120         if (setter.isAlive() || getter.isAlive()) {
121             fail("Threads are still alive. Getter iteration count: " + getterCount.get()
122                     + ", setter iteration count: " + setterCount.get());
123         }
124         // Guard against unexpected uncaught exceptions.
125         assertEquals("Setter iterations", iterations, setterCount.get());
126         assertEquals("Getter iterations", iterations, getterCount.get());
127     }
128 
129     // http://b/30979219
130     @Test
testSetDefaultRace()131     public void testSetDefaultRace() throws InterruptedException {
132         // Since this tests a race condition, the test is probabilistic: it's not guaranteed to
133         // fail if the problem exists
134 
135         // These iterations are significantly faster than the ones in #testSetDefaultDeadlock
136         final int iterations = 10000;
137         List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
138         Thread.UncaughtExceptionHandler handler = (t, e) -> exceptions.add(e);
139 
140         CyclicBarrier startBarrier = new CyclicBarrier(2);
141         Thread clearer = new Thread(() -> {
142             waitFor(startBarrier);
143             for (int i = 0; i < iterations; i++) {
144                 // This is not public API but can effectively be invoked via
145                 // java.util.TimeZone.setDefault. Call it directly to reduce the amount of code
146                 // involved in this test.
147                 ExtendedTimeZone.clearDefaultTimeZone();
148             }
149         });
150         clearer.setName("testSetDefaultRace clearer");
151         clearer.setUncaughtExceptionHandler(handler);
152 
153         Thread getter = new Thread(() -> {
154             waitFor(startBarrier);
155             for (int i = 0; i < iterations; i++) {
156                 android.icu.util.TimeZone.getDefault();
157             }
158         });
159         getter.setName("testSetDefaultRace getter");
160         getter.setUncaughtExceptionHandler(handler);
161 
162         clearer.start();
163         getter.start();
164 
165         // 20 seconds is plenty: If successful, we usually complete much faster.
166         clearer.join(10000);
167         getter.join(10000);
168 
169         if (!exceptions.isEmpty()) {
170             Throwable firstException = exceptions.get(0);
171             firstException.printStackTrace();
172             fail("Threads did not succeed successfully: " + firstException);
173         }
174         assertFalse("clearer thread is still alive", clearer.isAlive());
175         assertFalse("getter thread is still alive", getter.isAlive());
176     }
177 
waitFor(CyclicBarrier barrier)178     private static void waitFor(CyclicBarrier barrier) {
179         try {
180             barrier.await();
181         } catch (InterruptedException | BrokenBarrierException e) {
182             throw new RuntimeException(e);
183         }
184     }
185 
186     /**
187      * Confirms that ICU agrees with the rest of libcore about the version of the TZ data in use.
188      */
189     @Test
testTimeZoneDataVersion()190     public void testTimeZoneDataVersion() {
191         String icu4cTzVersion = Icu4cMetadata.getTzdbVersion();
192 
193         String zoneInfoTzVersion = ZoneInfoDb.getInstance().getVersion();
194         assertEquals(icu4cTzVersion, zoneInfoTzVersion);
195 
196         String icu4jTzVersion = android.icu.util.TimeZone.getTZDataVersion();
197         assertEquals(icu4jTzVersion, zoneInfoTzVersion);
198 
199         String tzLookupTzVersion = TimeZoneFinder.getInstance().getIanaVersion();
200         assertEquals(icu4jTzVersion, tzLookupTzVersion);
201     }
202 
203     /**
204      * Asserts that the time zone format major / minor versions meets expectations.
205      *
206      * <p>If a set of time zone files is to be compatible with a device then the format of the files
207      * must meet the Android team's expectations. This is a confidence check to ensure that devices
208      * running the test (e.g. under CTS) have not modified the TzDataSetVersion major / minor
209      * versions for some reason: if they have it would render updated time zone files sent to the
210      * device incompatible.
211      */
212     @Test
testTimeZoneFormatVersion()213     public void testTimeZoneFormatVersion() {
214         // The code below compares the final static int constant values (inlined at test compile
215         // time) with the version reported at runtime. This saves us hardcoding the numbers in two
216         // places.
217         assertEquals(TzDataSetVersion.CURRENT_FORMAT_MAJOR_VERSION,
218                 TzDataSetVersion.currentFormatMajorVersion());
219         assertEquals(TzDataSetVersion.CURRENT_FORMAT_MINOR_VERSION,
220                 TzDataSetVersion.currentFormatMinorVersion());
221     }
222 
223     /**
224      * Asserts that all expected sets of time zone files meet format expectations.
225      *
226      * <p>This uses the device's knowledge of the format version it expects and the
227      * {@link TzDataSetVersion} files that accompany the known time zone data files.
228      *
229      * <p>This is a confidence check to ensure that there's no way of installing incompatible data
230      * on a device. It assumes that {@link TzDataSetVersion} is updated as it should be when changes
231      * are made that might affect time zone code / time zone data compatibility.
232      */
233     @Test
testTzDataSetVersions()234     public void testTzDataSetVersions() throws Exception {
235         // The time zone data module is required.
236         String timeZoneModuleVersionFile =
237                 TimeZoneDataFiles.getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
238         assertTzDataSetVersionIsCompatible(timeZoneModuleVersionFile);
239 
240         // Check getTimeZoneModuleTzVersionFile() is doing the right thing.
241         // getTimeZoneModuleTzVersionFile() should go away when its one user, RulesManagerService,
242         // is removed from the platform code. http://b/123398797
243         assertEquals(TimeZoneDataFiles.getTimeZoneModuleTzVersionFile(), timeZoneModuleVersionFile);
244 
245         // TODO: Remove this once the /system copy of time zone files have gone away. See also
246         // testTimeZoneDebugInfo().
247         assertTzDataSetVersionIsCompatible(
248                 TimeZoneDataFiles.getSystemTzFile(TzDataSetVersion.DEFAULT_FILE_NAME));
249     }
250 
assertTzDataSetVersionIsCompatible(String versionFile)251     private static void assertTzDataSetVersionIsCompatible(String versionFile) throws Exception {
252         TzDataSetVersion actualVersion = TzDataSetVersion.readFromFile(new File(versionFile));
253         assertEquals(
254                 TzDataSetVersion.currentFormatMajorVersion(),
255                 actualVersion.getFormatMajorVersion());
256         int minDeviceMinorVersion = TzDataSetVersion.currentFormatMinorVersion();
257         assertTrue(actualVersion.getFormatMinorVersion() >= minDeviceMinorVersion);
258     }
259 
260     /**
261      * A test for confirming debug information matches file system state on device.
262      * It can also be used to confirm that device and host environments satisfy file system
263      * expectations.
264      */
265     @Test
testTimeZoneDebugInfo()266     public void testTimeZoneDebugInfo() throws Exception {
267         DebugInfo debugInfo = I18nModuleDebug.getDebugInfo();
268 
269         // Devices are expected to have a time zone module which overrides or extends the data in
270         // the runtime module depending on the file. It's not actually mandatory for all Android
271         // devices right now although it may be required for some subset of Android devices. It
272         // isn't present on host ART.
273         String tzModuleStatus = getDebugStringValue(debugInfo,
274                 "core_library.timezone.source.tzdata_module_status");
275         String apexRootDir = TimeZoneDataFiles.getTimeZoneModuleFile("");
276         List<String> dataModuleFiles =
277                 createModuleTzFiles(TimeZoneDataFiles::getTimeZoneModuleTzFile);
278         String icuOverlayFile = AndroidDataFiles.getTimeZoneModuleIcuFile("icu_tzdata.dat");
279         if (fileExists(apexRootDir)) {
280             assertEquals("OK", tzModuleStatus);
281             dataModuleFiles.forEach(TimeZoneIntegrationTest::assertFileExists);
282             assertFileExists(icuOverlayFile);
283         } else {
284             assertEquals("NOT_FOUND", tzModuleStatus);
285             dataModuleFiles.forEach(TimeZoneIntegrationTest::assertFileDoesNotExist);
286             assertFileDoesNotExist(icuOverlayFile);
287         }
288 
289         String icuDatFileName = "icudt" + VersionInfo.ICU_VERSION.getMajor() + "l.dat";
290         String i18nModuleIcuData = AndroidDataFiles.getI18nModuleIcuFile(icuDatFileName);
291         assertFileExists(i18nModuleIcuData);
292 
293         // Devices currently have a subset of the time zone files in /system. These are going away
294         // but we test them while they exist. Host ART should match device.
295         assertEquals("OK", getDebugStringValue(debugInfo,
296                 "core_library.timezone.source.system_status"));
297         assertFileExists(
298                 TimeZoneDataFiles.getSystemTzFile(TzDataSetVersion.DEFAULT_FILE_NAME));
299         assertFileExists(TimeZoneDataFiles.getSystemTzFile(ZoneInfoDb.TZDATA_FILE_NAME));
300         // The following files once existed in /system but have been removed as part of APEX work.
301         assertFileDoesNotExist(
302                 TimeZoneDataFiles.getSystemTzFile(TimeZoneFinder.TZLOOKUP_FILE_NAME));
303 
304         // It's hard to assert much about this file as there is a symlink in /system on device for
305         // app compatibility (b/122985829) but it doesn't exist in host environments. If the file
306         // exists we can say it should resolve (realpath) to the same file as the runtime module.
307         String systemIcuData = TimeZoneDataFiles.getSystemIcuFile(icuDatFileName);
308         if (new File(systemIcuData).exists()) {
309             assertEquals(Os.realpath(i18nModuleIcuData), Os.realpath(systemIcuData));
310         }
311     }
312 
createModuleTzFiles( Function<String, String> pathCreationFunction)313     private static List<String> createModuleTzFiles(
314             Function<String, String> pathCreationFunction) {
315         List<String> relativePaths = Arrays.asList(
316                 TzDataSetVersion.DEFAULT_FILE_NAME,
317                 ZoneInfoDb.TZDATA_FILE_NAME,
318                 TimeZoneFinder.TZLOOKUP_FILE_NAME);
319         return relativePaths.stream().map(pathCreationFunction).collect(Collectors.toList());
320     }
321 
fileExists(String fileName)322     private static boolean fileExists(String fileName) {
323         return new File(fileName).exists();
324     }
325 
assertFileDoesNotExist(String fileName)326     private static void assertFileDoesNotExist(String fileName) {
327         assertFalse(fileName + " must not exist", fileExists(fileName));
328     }
329 
assertFileExists(String fileName)330     private static void assertFileExists(String fileName) {
331         assertTrue(fileName + " must exist", fileExists(fileName));
332     }
333 
getDebugStringValue(DebugInfo debugInfo, String key)334     private String getDebugStringValue(DebugInfo debugInfo, String key) {
335         return debugInfo.getDebugEntry(key).getStringValue();
336     }
337 
338     /**
339      * Confirms that ICU can recognize all the time zone IDs used by the ZoneInfoDB data.
340      * ICU's IDs may be a superset.
341      */
342     @Test
testTimeZoneIdLookup()343     public void testTimeZoneIdLookup() {
344         String[] zoneInfoDbAvailableIds = ZoneInfoDb.getInstance().getAvailableIDs();
345 
346         // ICU has a known set of IDs. We want ANY because we don't want to filter to ICU's
347         // canonical IDs only.
348         Set<String> icuAvailableIds = android.icu.util.TimeZone.getAvailableIDs(
349                 android.icu.util.TimeZone.SystemTimeZoneType.ANY, null /* region */,
350                 null /* rawOffset */);
351 
352         List<String> nonIcuAvailableIds = new ArrayList<>();
353         List<String> creationFailureIds = new ArrayList<>();
354         List<String> noCanonicalLookupIds = new ArrayList<>();
355         List<String> nonSystemIds = new ArrayList<>();
356         for (String zoneInfoDbId : zoneInfoDbAvailableIds) {
357             if (!icuAvailableIds.contains(zoneInfoDbId)) {
358                 nonIcuAvailableIds.add(zoneInfoDbId);
359             }
360 
361             boolean[] isSystemId = new boolean[1];
362             String canonicalId = android.icu.util.TimeZone.getCanonicalID(zoneInfoDbId, isSystemId);
363             if (canonicalId == null) {
364                 noCanonicalLookupIds.add(zoneInfoDbId);
365             }
366             if (!isSystemId[0]) {
367                 nonSystemIds.add(zoneInfoDbId);
368             }
369 
370             android.icu.util.TimeZone icuTimeZone =
371                     android.icu.util.TimeZone.getTimeZone(zoneInfoDbId);
372             if (icuTimeZone.getID().equals(android.icu.util.TimeZone.UNKNOWN_ZONE_ID)) {
373                 creationFailureIds.add(zoneInfoDbId);
374             }
375         }
376         assertTrue("Non-ICU available IDs: " + nonIcuAvailableIds
377                         + ", creation failed IDs: " + creationFailureIds
378                         + ", non-system IDs: " + nonSystemIds
379                         + ", ids without canonical IDs: " + noCanonicalLookupIds,
380                 nonIcuAvailableIds.isEmpty()
381                         && creationFailureIds.isEmpty()
382                         && nonSystemIds.isEmpty()
383                         && noCanonicalLookupIds.isEmpty());
384     }
385 
386     // http://b/30527513
387     @Test
testDisplayNamesWithScript()388     public void testDisplayNamesWithScript() throws Exception {
389         Locale latinLocale = Locale.forLanguageTag("sr-Latn-RS");
390         Locale cyrillicLocale = Locale.forLanguageTag("sr-Cyrl-RS");
391         Locale noScriptLocale = Locale.forLanguageTag("sr-RS");
392         java.util.TimeZone tz = java.util.TimeZone.getTimeZone("Europe/London");
393 
394         final String latinName = "Srednje vreme po Griniču";
395         final String cyrillicName = "Средње време по Гриничу";
396 
397         // Check java.util.TimeZone
398         assertEquals(latinName, tz.getDisplayName(latinLocale));
399         assertEquals(cyrillicName, tz.getDisplayName(cyrillicLocale));
400         assertEquals(cyrillicName, tz.getDisplayName(noScriptLocale));
401 
402         // Check ICU TimeZoneNames
403         // The one-argument getDisplayName() override uses LONG_GENERIC style which is different
404         // from what java.util.TimeZone uses. Force the LONG style to get equivalent results.
405         final int style = android.icu.util.TimeZone.LONG;
406         android.icu.util.TimeZone utz = android.icu.util.TimeZone.getTimeZone(tz.getID());
407         assertEquals(latinName, utz.getDisplayName(false, style, latinLocale));
408         assertEquals(cyrillicName, utz.getDisplayName(false, style, cyrillicLocale));
409         assertEquals(cyrillicName, utz.getDisplayName(false, style, noScriptLocale));
410     }
411 
412     /**
413      * This test is to catch issues with the rules update process that could let the
414      * "negative DST" scheme enter the Android data set for either java.util.TimeZone or
415      * android.icu.util.TimeZone.
416      */
417     @Test
testDstMeansSummer()418     public void testDstMeansSummer() {
419         // Ireland was the original example that caused the default IANA upstream tzdata to contain
420         // a zone where DST is in the Winter (since tzdata 2018e, though it was tried in 2018a
421         // first). This change was made to historical and future transitions.
422         //
423         // The upstream reasoning went like this: "Irish *Standard* Time" is summer, so the other
424         // time must be the DST. So, DST is considered to be in the winter and the associated DST
425         // adjustment is negative from the standard time. In the old scheme "Irish Standard Time" /
426         // summer was just modeled as the DST in common with all other global time zones.
427         //
428         // Unfortunately, various users of formatting APIs assume standard and DST times are
429         // consistent and (effectively) that "DST" means "summer". We likely cannot adopt the
430         // concept of a winter DST without risking app compat issues.
431         //
432         // For example, getDisplayName(boolean daylight) has always returned the winter time for
433         // false, and the summer time for true. If we change this then it should be changed on a
434         // major release boundary, with improved APIs (e.g. a version of getDisplayName() that takes
435         // a millis), existing API behavior made dependent on target API version, and after fixing
436         // any platform code that makes incorrect assumptions about DST meaning "1 hour forward".
437 
438         final String timeZoneId = "Europe/Dublin";
439         final Locale locale = Locale.UK;
440         // 26 Oct 2015 01:00:00 GMT - one day after the start of "Greenwich Mean Time" in
441         // Europe/Dublin in 2015. An arbitrary historical example of winter in Ireland.
442         final long winterTimeMillis = 1445821200000L;
443         final String winterTimeName = "Greenwich Mean Time";
444         final int winterOffsetRawMillis = 0;
445         final int winterOffsetDstMillis = 0;
446 
447         // 30 Mar 2015 01:00:00 GMT - one day after the start of "Irish Standard Time" in
448         // Europe/Dublin in 2015. An arbitrary historical example of summer in Ireland.
449         final long summerTimeMillis = 1427677200000L;
450         final String summerTimeName = "Irish Standard Time";
451         final int summerOffsetRawMillis = 0;
452         final int summerOffsetDstMillis = (int) TimeUnit.HOURS.toMillis(1);
453 
454         // There is no common interface between java.util.TimeZone and android.icu.util.TimeZone
455         // so the tests are for each are effectively duplicated.
456 
457         // java.util.TimeZone
458         {
459             java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timeZoneId);
460             assertTrue(timeZone.useDaylightTime());
461 
462             assertFalse(timeZone.inDaylightTime(new Date(winterTimeMillis)));
463             assertTrue(timeZone.inDaylightTime(new Date(summerTimeMillis)));
464 
465             assertEquals(winterOffsetRawMillis + winterOffsetDstMillis,
466                     timeZone.getOffset(winterTimeMillis));
467             assertEquals(summerOffsetRawMillis + summerOffsetDstMillis,
468                     timeZone.getOffset(summerTimeMillis));
469             assertEquals(winterTimeName,
470                     timeZone.getDisplayName(false /* daylight */, java.util.TimeZone.LONG,
471                             locale));
472             assertEquals(summerTimeName,
473                     timeZone.getDisplayName(true /* daylight */, java.util.TimeZone.LONG,
474                             locale));
475         }
476 
477         // android.icu.util.TimeZone
478         {
479             android.icu.util.TimeZone timeZone = android.icu.util.TimeZone.getTimeZone(timeZoneId);
480             assertTrue(timeZone.useDaylightTime());
481 
482             assertFalse(timeZone.inDaylightTime(new Date(winterTimeMillis)));
483             assertTrue(timeZone.inDaylightTime(new Date(summerTimeMillis)));
484 
485             assertEquals(winterOffsetRawMillis + winterOffsetDstMillis,
486                     timeZone.getOffset(winterTimeMillis));
487             assertEquals(summerOffsetRawMillis + summerOffsetDstMillis,
488                     timeZone.getOffset(summerTimeMillis));
489 
490             // These methods show the trouble we'd have if callers were to take the output from
491             // inDaylightTime() and pass it to getDisplayName().
492             assertEquals(winterTimeName,
493                     timeZone.getDisplayName(false /* daylight */, android.icu.util.TimeZone.LONG,
494                             locale));
495             assertEquals(summerTimeName,
496                     timeZone.getDisplayName(true /* daylight */, android.icu.util.TimeZone.LONG,
497                             locale));
498 
499             // APIs not identical to java.util.TimeZone tested below.
500             int[] offsets = new int[2];
501             timeZone.getOffset(winterTimeMillis, false /* local */, offsets);
502             assertEquals(winterOffsetRawMillis, offsets[0]);
503             assertEquals(winterOffsetDstMillis, offsets[1]);
504 
505             timeZone.getOffset(summerTimeMillis, false /* local */, offsets);
506             assertEquals(summerOffsetRawMillis, offsets[0]);
507             assertEquals(summerOffsetDstMillis, offsets[1]);
508         }
509 
510         // icu TimeZoneNames
511         TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
512         // getDisplayName: date = winterTimeMillis
513         assertEquals(winterTimeName, timeZoneNames.getDisplayName(
514                 timeZoneId, TimeZoneNames.NameType.LONG_STANDARD, winterTimeMillis));
515         assertEquals(summerTimeName, timeZoneNames.getDisplayName(
516                 timeZoneId, TimeZoneNames.NameType.LONG_DAYLIGHT, winterTimeMillis));
517         // getDisplayName: date = summerTimeMillis
518         assertEquals(winterTimeName, timeZoneNames.getDisplayName(
519                 timeZoneId, TimeZoneNames.NameType.LONG_STANDARD, summerTimeMillis));
520         assertEquals(summerTimeName, timeZoneNames.getDisplayName(
521                 timeZoneId, TimeZoneNames.NameType.LONG_DAYLIGHT, summerTimeMillis));
522     }
523 }
524