1 /*
2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8075526 8135108 8155616 8161942
27  * @summary Test timestamp via ZipEntry.get/setTimeLocal()
28  */
29 package test.java.util.zip;
30 
31 import java.io.*;
32 import java.nio.file.*;
33 import java.time.*;
34 import java.util.*;
35 import java.util.zip.*;
36 
37 public class TestLocalTime {
38     private static TimeZone tz0 = TimeZone.getDefault();
39 
main(String[] args)40     public static void main(String[] args) throws Throwable{
41         try {
42             LocalDateTime ldt = LocalDateTime.now();
43             test(ldt);    // now
44             test(ldt.withYear(1968));
45             test(ldt.withYear(1970));
46             test(ldt.withYear(1982));
47             test(ldt.withYear(2037));
48             test(ldt.withYear(2100));
49             test(ldt.withYear(2106));
50 
51             TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
52             // dos time does not support < 1980, have to use
53             // utc in mtime.
54             testWithTZ(tz, ldt.withYear(1982));
55             testWithTZ(tz, ldt.withYear(2037));
56             testWithTZ(tz, ldt.withYear(2100));
57             testWithTZ(tz, ldt.withYear(2106));
58 
59             // for #8135108
60             test(LocalDateTime.of(2100, 12, 06, 12, 34, 34, 973));
61             test(LocalDateTime.of(2106, 12, 06, 12, 34, 34, 973));
62 
63             // for #8155616
64             test(LocalDateTime.of(2016, 03, 13, 2, 50, 00));  // gap
65             test(LocalDateTime.of(2015, 11, 1,  1, 30, 00));  // overlap
66             test(LocalDateTime.of(1968, 04, 28, 2, 51, 25));
67             test(LocalDateTime.of(1970, 04, 26, 2, 31, 52));
68 
69             // for #8161942
70             test(LocalDateTime.of(2200, 04, 26, 2, 31, 52));  // unix 2038
71 
72         } finally {
73             TimeZone.setDefault(tz0);
74         }
75     }
76 
getBytes(LocalDateTime mtime)77     static byte[] getBytes(LocalDateTime mtime) throws Throwable {
78         ByteArrayOutputStream baos = new ByteArrayOutputStream();
79         ZipOutputStream zos = new ZipOutputStream(baos);
80         ZipEntry ze = new ZipEntry("TestLocalTime.java");
81         ze.setTimeLocal(mtime);
82         check(ze, mtime);
83         zos.putNextEntry(ze);
84         zos.write(new byte[] { 1, 2, 3, 4});
85         zos.close();
86         return baos.toByteArray();
87     }
88 
testWithTZ(TimeZone tz, LocalDateTime ldt)89     static void testWithTZ(TimeZone tz, LocalDateTime ldt) throws Throwable {
90        TimeZone.setDefault(tz);
91        byte[] zbytes = getBytes(ldt);
92        TimeZone.setDefault(tz0);
93        test(zbytes, ldt);
94     }
95 
test(LocalDateTime ldt)96     static void test(LocalDateTime ldt) throws Throwable {
97         test(getBytes(ldt), ldt);
98     }
99 
test(byte[] zbytes, LocalDateTime expected)100     static void test(byte[] zbytes, LocalDateTime expected) throws Throwable {
101         System.out.printf("--------------------%nTesting: [%s]%n", expected);
102         // ZipInputStream
103         ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zbytes));
104         ZipEntry ze = zis.getNextEntry();
105         zis.close();
106         check(ze, expected);
107 
108         // ZipFile
109         // Android-changed: create file in a temp dir, 'test.dir' is not available.
110         // Path zpath = Paths.get(System.getProperty("test.dir", "."),
111         //                       "TestLocalTime.zip");
112         Path zpath = Files.createTempDirectory("TestLocalTime").resolve("TestLocalTime.zip");
113         try {
114             Files.copy(new ByteArrayInputStream(zbytes), zpath);
115             ZipFile zf = new ZipFile(zpath.toFile());
116             ze = zf.getEntry("TestLocalTime.java");
117             check(ze, expected);
118             zf.close();
119         } finally {
120             Files.deleteIfExists(zpath);
121         }
122     }
123 
check(ZipEntry ze, LocalDateTime expected)124     static void check(ZipEntry ze, LocalDateTime expected) {
125         LocalDateTime ldt = ze.getTimeLocal();
126         if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
127             != expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
128             // if the LDT is out of the range of the standard ms-dos date-time
129             // format ( < 1980 ) AND the date-time is within the daylight saving
130             // time gap (which means the LDT is actually "invalid"), the LDT will
131             // be adjusted accordingly when ZipEntry.setTimeLocal() converts the
132             // date-time via ldt -> zdt -> Instant -> FileTime.
133             // See ZonedDateTime.of(LocalDateTime, ZoneId) for more details.
134             if (ldt.getYear() < 1980 || ldt.getYear() > (1980 + 0x7f)) {
135                 System.out.println(" Non-MSDOS    ldt : " + ldt);
136                 System.out.println("         expected : " + expected);
137                 // try to adjust the "expected", assume daylight saving gap
138                 expected = ZonedDateTime.of(expected, ZoneId.systemDefault())
139                                         .toLocalDateTime();
140                 if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
141                     == expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
142                     return;
143                 }
144             }
145             throw new RuntimeException("Timestamp: storing mtime failed!");
146         }
147     }
148 }
149