mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-09 13:09:43 +00:00
8075526: Need a way to read and write ZipEntry timestamp using local date/time without tz conversion
To add a pair of set/getTimeLocal() Reviewed-by: ksrini, rriggs
This commit is contained in:
parent
ef917cec15
commit
47dbbc7b72
@ -29,6 +29,9 @@ import static java.util.zip.ZipUtils.*;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import static java.util.zip.ZipConstants64.*;
|
||||
|
||||
@ -194,6 +197,85 @@ class ZipEntry implements ZipConstants, Cloneable {
|
||||
return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last modification time of the entry in local date-time.
|
||||
*
|
||||
* <p> If the entry is output to a ZIP file or ZIP file formatted
|
||||
* output stream the last modification time set by this method will
|
||||
* be stored into the {@code date and time fields} of the zip file
|
||||
* entry and encoded in standard {@code MS-DOS date and time format}.
|
||||
* If the date-time set is out of the range of the standard {@code
|
||||
* MS-DOS date and time format}, the time will also be stored into
|
||||
* zip file entry's extended timestamp fields in {@code optional
|
||||
* extra data} in UTC time. The {@link java.time.ZoneId#systemDefault()
|
||||
* system default TimeZone} is used to convert the local date-time
|
||||
* to UTC time.
|
||||
*
|
||||
* <p> {@code LocalDateTime} uses a precision of nanoseconds, whereas
|
||||
* this class uses a precision of milliseconds. The conversion will
|
||||
* truncate any excess precision information as though the amount in
|
||||
* nanoseconds was subject to integer division by one million.
|
||||
*
|
||||
* @param time
|
||||
* The last modification time of the entry in local date-time
|
||||
*
|
||||
* @see #getTimeLocal()
|
||||
* @since 1.9
|
||||
*/
|
||||
public void setTimeLocal(LocalDateTime time) {
|
||||
int year = time.getYear() - 1980;
|
||||
if (year < 0) {
|
||||
this.xdostime = DOSTIME_BEFORE_1980;
|
||||
} else {
|
||||
this.xdostime = (year << 25 |
|
||||
time.getMonthValue() << 21 |
|
||||
time.getDayOfMonth() << 16 |
|
||||
time.getHour() << 11 |
|
||||
time.getMinute() << 5 |
|
||||
time.getSecond() >> 1)
|
||||
+ ((long)(((time.getSecond() & 0x1) * 1000) +
|
||||
time.getNano() / 1000_000) << 32);
|
||||
}
|
||||
if (xdostime != DOSTIME_BEFORE_1980 && year <= 0x7f) {
|
||||
this.mtime = null;
|
||||
} else {
|
||||
this.mtime = FileTime.from(
|
||||
ZonedDateTime.of(time, ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modification time of the entry in local date-time.
|
||||
*
|
||||
* <p> If the entry is read from a ZIP file or ZIP file formatted
|
||||
* input stream, this is the last modification time from the zip
|
||||
* file entry's {@code optional extra data} if the extended timestamp
|
||||
* fields are present. Otherwise, the last modification time is read
|
||||
* from entry's standard MS-DOS formatted {@code date and time fields}.
|
||||
*
|
||||
* <p> The {@link java.time.ZoneId#systemDefault() system default TimeZone}
|
||||
* is used to convert the UTC time to local date-time.
|
||||
*
|
||||
* @return The last modification time of the entry in local date-time
|
||||
*
|
||||
* @see #setTimeLocal(LocalDateTime)
|
||||
* @since 1.9
|
||||
*/
|
||||
public LocalDateTime getTimeLocal() {
|
||||
if (mtime != null) {
|
||||
return LocalDateTime.ofInstant(mtime.toInstant(), ZoneId.systemDefault());
|
||||
}
|
||||
int ms = (int)(xdostime >> 32);
|
||||
return LocalDateTime.of((int)(((xdostime >> 25) & 0x7f) + 1980),
|
||||
(int)((xdostime >> 21) & 0x0f),
|
||||
(int)((xdostime >> 16) & 0x1f),
|
||||
(int)((xdostime >> 11) & 0x1f),
|
||||
(int)((xdostime >> 5) & 0x3f),
|
||||
(int)((xdostime << 1) & 0x3e) + ms / 1000,
|
||||
(ms % 1000) * 1000_000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the last modification time of the entry.
|
||||
*
|
||||
@ -498,15 +580,15 @@ class ZipEntry implements ZipConstants, Cloneable {
|
||||
// flag its presence or absence. But if mtime is present
|
||||
// in LOC it must be present in CEN as well.
|
||||
if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
|
||||
mtime = unixTimeToFileTime(get32(extra, off + sz0));
|
||||
mtime = unixTimeToFileTime(get32S(extra, off + sz0));
|
||||
sz0 += 4;
|
||||
}
|
||||
if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
|
||||
atime = unixTimeToFileTime(get32(extra, off + sz0));
|
||||
atime = unixTimeToFileTime(get32S(extra, off + sz0));
|
||||
sz0 += 4;
|
||||
}
|
||||
if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
|
||||
ctime = unixTimeToFileTime(get32(extra, off + sz0));
|
||||
ctime = unixTimeToFileTime(get32S(extra, off + sz0));
|
||||
sz0 += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -144,4 +144,13 @@ class ZipUtils {
|
||||
public static final long get64(byte b[], int off) {
|
||||
return get32(b, off) | (get32(b, off+4) << 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches signed 32-bit value from byte array at specified offset.
|
||||
* The bytes are assumed to be in Intel (little-endian) byte order.
|
||||
*
|
||||
*/
|
||||
public static final int get32S(byte b[], int off) {
|
||||
return (get16(b, off) | (get16(b, off+2) << 16));
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641
|
||||
* @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526
|
||||
* @summary Test ZOS and ZIS timestamp in extra field correctly
|
||||
*/
|
||||
|
||||
@ -54,8 +54,12 @@ public class TestExtraTime {
|
||||
|
||||
for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
|
||||
test(mtime, null, null, null, extra);
|
||||
|
||||
// ms-dos 1980 epoch problem
|
||||
test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
|
||||
// negative epoch time
|
||||
test(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
|
||||
|
||||
// non-default tz
|
||||
test(mtime, null, null, tz, extra);
|
||||
|
||||
|
||||
111
jdk/test/java/util/zip/TestLocalTime.java
Normal file
111
jdk/test/java/util/zip/TestLocalTime.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8075526
|
||||
* @summary Test timestamp via ZipEntry.get/setTimeLocal()
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class TestLocalTime {
|
||||
private static TimeZone tz0 = TimeZone.getDefault();
|
||||
|
||||
public static void main(String[] args) throws Throwable{
|
||||
try {
|
||||
LocalDateTime ldt = LocalDateTime.now();
|
||||
test(getBytes(ldt), ldt); // now
|
||||
ldt = ldt.withYear(1968); test(getBytes(ldt), ldt);
|
||||
ldt = ldt.withYear(1970); test(getBytes(ldt), ldt);
|
||||
ldt = ldt.withYear(1982); test(getBytes(ldt), ldt);
|
||||
ldt = ldt.withYear(2037); test(getBytes(ldt), ldt);
|
||||
ldt = ldt.withYear(2100); test(getBytes(ldt), ldt);
|
||||
ldt = ldt.withYear(2106); test(getBytes(ldt), ldt);
|
||||
|
||||
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
|
||||
// dos time does not support < 1980, have to use
|
||||
// utc in mtime.
|
||||
testWithTZ(tz, ldt.withYear(1982));
|
||||
testWithTZ(tz, ldt.withYear(2037));
|
||||
testWithTZ(tz, ldt.withYear(2100));
|
||||
testWithTZ(tz, ldt.withYear(2106));
|
||||
} finally {
|
||||
TimeZone.setDefault(tz0);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] getBytes(LocalDateTime mtime) throws Throwable {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(baos);
|
||||
ZipEntry ze = new ZipEntry("TestLocalTime.java");
|
||||
ze.setTimeLocal(mtime);
|
||||
check(ze, mtime);
|
||||
|
||||
zos.putNextEntry(ze);
|
||||
zos.write(new byte[] { 1, 2, 3, 4});
|
||||
zos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
static void testWithTZ(TimeZone tz, LocalDateTime ldt) throws Throwable {
|
||||
TimeZone.setDefault(tz);
|
||||
byte[] zbytes = getBytes(ldt);
|
||||
TimeZone.setDefault(tz0);
|
||||
test(zbytes, ldt);
|
||||
}
|
||||
|
||||
static void test(byte[] zbytes, LocalDateTime expected) throws Throwable {
|
||||
System.out.printf("--------------------%nTesting: [%s]%n", expected);
|
||||
// ZipInputStream
|
||||
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zbytes));
|
||||
ZipEntry ze = zis.getNextEntry();
|
||||
zis.close();
|
||||
check(ze, expected);
|
||||
|
||||
// ZipFile
|
||||
Path zpath = Paths.get(System.getProperty("test.dir", "."),
|
||||
"TestLocalTime.zip");
|
||||
try {
|
||||
Files.copy(new ByteArrayInputStream(zbytes), zpath);
|
||||
ZipFile zf = new ZipFile(zpath.toFile());
|
||||
ze = zf.getEntry("TestLocalTime.java");
|
||||
check(ze, expected);
|
||||
zf.close();
|
||||
} finally {
|
||||
Files.deleteIfExists(zpath);
|
||||
}
|
||||
}
|
||||
|
||||
static void check(ZipEntry ze, LocalDateTime expected) {
|
||||
LocalDateTime ldt = ze.getTimeLocal();
|
||||
if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
|
||||
!= expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
|
||||
throw new RuntimeException("Timestamp: storing mtime failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user