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 
30 import java.io.*;
31 import java.nio.file.*;
32 import java.time.*;
33 import java.util.*;
34 import java.util.zip.*;
35 
36 public class TestLocalTime {
37     private static TimeZone tz0 = TimeZone.getDefault();
38 
main(String[] args)39     public static void main(String[] args) throws Throwable{
40         try {
41             LocalDateTime ldt = LocalDateTime.now();
42             test(ldt);    // now
43             test(ldt.withYear(1968));
44             test(ldt.withYear(1970));
45             test(ldt.withYear(1982));
46             test(ldt.withYear(2037));
47             test(ldt.withYear(2100));
48             test(ldt.withYear(2106));
49 
50             TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
51             // dos time does not support < 1980, have to use
52             // utc in mtime.
53             testWithTZ(tz, ldt.withYear(1982));
54             testWithTZ(tz, ldt.withYear(2037));
55             testWithTZ(tz, ldt.withYear(2100));
56             testWithTZ(tz, ldt.withYear(2106));
57 
58             // for #8135108
59             test(LocalDateTime.of(2100, 12, 06, 12, 34, 34, 973));
60             test(LocalDateTime.of(2106, 12, 06, 12, 34, 34, 973));
61 
62             // for #8155616
63             test(LocalDateTime.of(2016, 03, 13, 2, 50, 00));  // gap
64             test(LocalDateTime.of(2015, 11, 1,  1, 30, 00));  // overlap
65             test(LocalDateTime.of(1968, 04, 28, 2, 51, 25));
66             test(LocalDateTime.of(1970, 04, 26, 2, 31, 52));
67 
68             // for #8161942
69             test(LocalDateTime.of(2200, 04, 26, 2, 31, 52));  // unix 2038
70 
71         } finally {
72             TimeZone.setDefault(tz0);
73         }
74     }
75 
getBytes(LocalDateTime mtime)76     static byte[] getBytes(LocalDateTime mtime) throws Throwable {
77         ByteArrayOutputStream baos = new ByteArrayOutputStream();
78         ZipOutputStream zos = new ZipOutputStream(baos);
79         ZipEntry ze = new ZipEntry("TestLocalTime.java");
80         ze.setTimeLocal(mtime);
81         check(ze, mtime);
82         zos.putNextEntry(ze);
83         zos.write(new byte[] { 1, 2, 3, 4});
84         zos.close();
85         return baos.toByteArray();
86     }
87 
testWithTZ(TimeZone tz, LocalDateTime ldt)88     static void testWithTZ(TimeZone tz, LocalDateTime ldt) throws Throwable {
89        TimeZone.setDefault(tz);
90        byte[] zbytes = getBytes(ldt);
91        TimeZone.setDefault(tz0);
92        test(zbytes, ldt);
93     }
94 
test(LocalDateTime ldt)95     static void test(LocalDateTime ldt) throws Throwable {
96         test(getBytes(ldt), ldt);
97     }
98 
test(byte[] zbytes, LocalDateTime expected)99     static void test(byte[] zbytes, LocalDateTime expected) throws Throwable {
100         System.out.printf("--------------------%nTesting: [%s]%n", expected);
101         // ZipInputStream
102         ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zbytes));
103         ZipEntry ze = zis.getNextEntry();
104         zis.close();
105         check(ze, expected);
106 
107         // ZipFile
108         Path zpath = Paths.get(System.getProperty("test.dir", "."),
109                                "TestLocalTime.zip");
110         try {
111             Files.copy(new ByteArrayInputStream(zbytes), zpath);
112             ZipFile zf = new ZipFile(zpath.toFile());
113             ze = zf.getEntry("TestLocalTime.java");
114             check(ze, expected);
115             zf.close();
116         } finally {
117             Files.deleteIfExists(zpath);
118         }
119     }
120 
check(ZipEntry ze, LocalDateTime expected)121     static void check(ZipEntry ze, LocalDateTime expected) {
122         LocalDateTime ldt = ze.getTimeLocal();
123         if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
124             != expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
125             // if the LDT is out of the range of the standard ms-dos date-time
126             // format ( < 1980 ) AND the date-time is within the daylight saving
127             // time gap (which means the LDT is actually "invalid"), the LDT will
128             // be adjusted accordingly when ZipEntry.setTimeLocal() converts the
129             // date-time via ldt -> zdt -> Instant -> FileTime.
130             // See ZonedDateTime.of(LocalDateTime, ZoneId) for more details.
131             if (ldt.getYear() < 1980 || ldt.getYear() > (1980 + 0x7f)) {
132                 System.out.println(" Non-MSDOS    ldt : " + ldt);
133                 System.out.println("         expected : " + expected);
134                 // try to adjust the "expected", assume daylight saving gap
135                 expected = ZonedDateTime.of(expected, ZoneId.systemDefault())
136                                         .toLocalDateTime();
137                 if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
138                     == expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
139                     return;
140                 }
141             }
142             throw new RuntimeException("Timestamp: storing mtime failed!");
143         }
144     }
145 }
146