changeset 7850:a388263a7287

8015666: test/tools/pack200/TimeStamp.java failing Summary: to keep the default behavior of ZOS unchanged, if ze extra time not explicitly set Reviewed-by: alanb, ksrini
author sherman
date Thu, 08 Aug 2013 12:03:04 -0700
parents b7d594716f86
children 67edbf7e6b26
files src/share/classes/java/util/zip/ZipConstants.java src/share/classes/java/util/zip/ZipEntry.java src/share/classes/java/util/zip/ZipFile.java src/share/classes/java/util/zip/ZipInputStream.java src/share/classes/java/util/zip/ZipOutputStream.java src/share/classes/java/util/zip/ZipUtils.java test/ProblemList.txt test/java/util/zip/TestExtraTime.java test/tools/pack200/TimeStamp.java
diffstat 9 files changed, 518 insertions(+), 207 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/zip/ZipConstants.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipConstants.java	Thu Aug 08 12:03:04 2013 -0700
@@ -71,10 +71,17 @@
     /*
      * Extra field header ID
      */
-    static final int  EXTID_ZIP64 = 0x0001;      // Zip64
-    static final int  EXTID_NTFS  = 0x000a;      // NTFS
-    static final int  EXTID_UNIX  = 0x000d;      // UNIX
-    static final int  EXTID_EXTT  = 0x5455;      // Info-ZIP Extended Timestamp
+    static final int  EXTID_ZIP64 = 0x0001;    // Zip64
+    static final int  EXTID_NTFS  = 0x000a;    // NTFS
+    static final int  EXTID_UNIX  = 0x000d;    // UNIX
+    static final int  EXTID_EXTT  = 0x5455;    // Info-ZIP Extended Timestamp
+
+    /*
+     * EXTT timestamp flags
+     */
+    static final int  EXTT_FLAG_LMT = 0x1;       // LastModifiedTime
+    static final int  EXTT_FLAG_LAT = 0x2;       // LastAccessTime
+    static final int  EXTT_FLAT_CT  = 0x4;       // CreationTime
 
     /*
      * Central directory (CEN) header field offsets
--- a/src/share/classes/java/util/zip/ZipEntry.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipEntry.java	Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, 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
@@ -25,6 +25,11 @@
 
 package java.util.zip;
 
+import static java.util.zip.ZipUtils.*;
+import java.nio.file.attribute.FileTime;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
 /**
  * This class is used to represent a ZIP file entry.
  *
@@ -32,8 +37,12 @@
  */
 public
 class ZipEntry implements ZipConstants, Cloneable {
+
     String name;        // entry name
-    long mtime = -1;    // last modification time
+    long time = -1;     // last modification time
+    FileTime mtime;     // last modification time, from extra field data
+    FileTime atime;     // last access time, from extra field data
+    FileTime ctime;     // creation time, from extra field data
     long crc = -1;      // crc-32 of entry data
     long size = -1;     // uncompressed size of entry data
     long csize = -1;    // compressed size of entry data
@@ -55,15 +64,15 @@
     /**
      * Creates a new zip entry with the specified name.
      *
-     * @param name the entry name
-     * @exception NullPointerException if the entry name is null
-     * @exception IllegalArgumentException if the entry name is longer than
-     *            0xFFFF bytes
+     * @param  name
+     *         The entry name
+     *
+     * @throws NullPointerException if the entry name is null
+     * @throws IllegalArgumentException if the entry name is longer than
+     *         0xFFFF bytes
      */
     public ZipEntry(String name) {
-        if (name == null) {
-            throw new NullPointerException();
-        }
+        Objects.requireNonNull(name, "name");
         if (name.length() > 0xFFFF) {
             throw new IllegalArgumentException("entry name too long");
         }
@@ -73,11 +82,19 @@
     /**
      * Creates a new zip entry with fields taken from the specified
      * zip entry.
-     * @param e a zip Entry object
+     *
+     * @param  e
+     *         A zip Entry object
+     *
+     * @throws NullPointerException if the entry object is null
      */
     public ZipEntry(ZipEntry e) {
+        Objects.requireNonNull(e, "entry");
         name = e.name;
+        time = e.time;
         mtime = e.mtime;
+        atime = e.atime;
+        ctime = e.ctime;
         crc = e.crc;
         size = e.size;
         csize = e.csize;
@@ -103,33 +120,178 @@
     /**
      * Sets the last modification time of the entry.
      *
-     * @param time the last modification time of the entry in milliseconds since the epoch
+     * <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}.
+     * The {@link java.util.TimeZone#getDefault() default TimeZone} is
+     * used to convert the epoch time to the MS-DOS data and time.
+     *
+     * @param  time
+     *         The last modification time of the entry in milliseconds
+     *         since the epoch
+     *
      * @see #getTime()
+     * @see #getLastModifiedTime()
      */
     public void setTime(long time) {
-        this.mtime = time;
+        this.time = time;
+        this.mtime = null;
     }
 
     /**
      * Returns the last modification time of the entry.
-     * <p> The last modificatin time may come from zip entry's extensible
-     * data field {@code NTFS} or {@code Info-ZIP Extended Timestamp}, if
-     * the entry is read from {@link ZipInputStream} or {@link ZipFile}.
      *
-     * @return the last modification time of the entry, or -1 if not specified
+     * <p> If the entry is read from a ZIP file or ZIP file formatted
+     * input stream, this is the last modification time from the {@code
+     * date and time fields} of the zip file entry. The
+     * {@link java.util.TimeZone#getDefault() default TimeZone} is used
+     * to convert the standard MS-DOS formatted date and time to the
+     * epoch time.
+     *
+     * @return  The last modification time of the entry in milliseconds
+     *          since the epoch, or -1 if not specified
+     *
      * @see #setTime(long)
+     * @see #setLastModifiedTime(FileTime)
      */
     public long getTime() {
-        return mtime;
+        return time;
+    }
+
+    /**
+     * Sets the last modification time of the entry.
+     *
+     * <p> When output to a ZIP file or ZIP file formatted output stream
+     * the last modification time set by this method will be stored into
+     * zip file entry's {@code date and time fields} in {@code standard
+     * MS-DOS date and time format}), and the extended timestamp fields
+     * in {@code optional extra data} in UTC time.
+     *
+     * @param  time
+     *         The last modification time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getLastModifiedTime()
+     * @since 1.8
+     */
+    public ZipEntry setLastModifiedTime(FileTime time) {
+        Objects.requireNonNull(name, "time");
+        this.mtime = time;
+        this.time = time.to(TimeUnit.MILLISECONDS);
+        return this;
+    }
+
+    /**
+     * Returns the last modification time of the entry.
+     *
+     * <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 the entry's {@code date and time fields}, the {@link
+     * java.util.TimeZone#getDefault() default TimeZone} is used to convert
+     * the standard MS-DOS formatted date and time to the epoch time.
+     *
+     * @return The last modification time of the entry, null if not specified
+     *
+     * @see #setLastModifiedTime(FileTime)
+     * @since 1.8
+     */
+    public FileTime getLastModifiedTime() {
+        if (mtime != null)
+            return mtime;
+        if (time == -1)
+            return null;
+        return FileTime.from(time, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Sets the last access time of the entry.
+     *
+     * <p> If set, the last access time will be stored into the extended
+     * timestamp fields of entry's {@code optional extra data}, when output
+     * to a ZIP file or ZIP file formatted stream.
+     *
+     * @param  time
+     *         The last access time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getLastAccessTime()
+     * @since 1.8
+     */
+    public ZipEntry setLastAccessTime(FileTime time) {
+        Objects.requireNonNull(name, "time");
+        this.atime = time;
+        return this;
+    }
+
+    /**
+     * Returns the last access time of the entry.
+     *
+     * <p> The last access time is from the extended timestamp fields
+     * of entry's {@code optional extra data} when read from a ZIP file
+     * or ZIP file formatted stream.
+     *
+     * @return The last access time of the entry, null if not specified
+
+     * @see #setLastAccessTime(long)
+     * @since 1.8
+     */
+    public FileTime getLastAccessTime() {
+        return atime;
+    }
+
+    /**
+     * Sets the creation time of the entry.
+     *
+     * <p> If set, the creation time will be stored into the extended
+     * timestamp fields of entry's {@code optional extra data}, when
+     * output to a ZIP file or ZIP file formatted stream.
+     *
+     * @param  time
+     *         The creation time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getCreationTime()
+     * @since 1.8
+     */
+    public ZipEntry setCreationTime(FileTime time) {
+        Objects.requireNonNull(name, "time");
+        this.ctime = time;
+        return this;
+    }
+
+    /**
+     * Returns the creation time of the entry.
+     *
+     * <p> The creation time is from the extended timestamp fields of
+     * entry's {@code optional extra data} when read from a ZIP file
+     * or ZIP file formatted stream.
+     *
+     * @return the creation time of the entry, null if not specified
+     * @see #setCreationTime(FileTime)
+     * @since 1.8
+     */
+    public FileTime getCreationTime() {
+        return ctime;
     }
 
     /**
      * Sets the uncompressed size of the entry data.
+     *
      * @param size the uncompressed size in bytes
-     * @exception IllegalArgumentException if the specified size is less
-     *            than 0, is greater than 0xFFFFFFFF when
-     *            <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
-     *            or is less than 0 when ZIP64 is supported
+     *
+     * @throws IllegalArgumentException if the specified size is less
+     *         than 0, is greater than 0xFFFFFFFF when
+     *         <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+     *         or is less than 0 when ZIP64 is supported
      * @see #getSize()
      */
     public void setSize(long size) {
@@ -140,7 +302,8 @@
     }
 
     /**
-     * Returns the uncompressed size of the entry data, or -1 if not known.
+     * Returns the uncompressed size of the entry data.
+     *
      * @return the uncompressed size of the entry data, or -1 if not known
      * @see #setSize(long)
      */
@@ -149,9 +312,11 @@
     }
 
     /**
-     * Returns the size of the compressed entry data, or -1 if not known.
-     * In the case of a stored entry, the compressed size will be the same
+     * Returns the size of the compressed entry data.
+     *
+     * <p> In the case of a stored entry, the compressed size will be the same
      * as the uncompressed size of the entry.
+     *
      * @return the size of the compressed entry data, or -1 if not known
      * @see #setCompressedSize(long)
      */
@@ -161,7 +326,9 @@
 
     /**
      * Sets the size of the compressed entry data.
+     *
      * @param csize the compressed size to set to
+     *
      * @see #getCompressedSize()
      */
     public void setCompressedSize(long csize) {
@@ -170,9 +337,11 @@
 
     /**
      * Sets the CRC-32 checksum of the uncompressed entry data.
+     *
      * @param crc the CRC-32 value
-     * @exception IllegalArgumentException if the specified CRC-32 value is
-     *            less than 0 or greater than 0xFFFFFFFF
+     *
+     * @throws IllegalArgumentException if the specified CRC-32 value is
+     *         less than 0 or greater than 0xFFFFFFFF
      * @see #getCrc()
      */
     public void setCrc(long crc) {
@@ -183,10 +352,11 @@
     }
 
     /**
-     * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
-     * not known.
+     * Returns the CRC-32 checksum of the uncompressed entry data.
+     *
      * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
      * not known
+     *
      * @see #setCrc(long)
      */
     public long getCrc() {
@@ -195,9 +365,11 @@
 
     /**
      * Sets the compression method for the entry.
+     *
      * @param method the compression method, either STORED or DEFLATED
-     * @exception IllegalArgumentException if the specified compression
-     *            method is invalid
+     *
+     * @throws  IllegalArgumentException if the specified compression
+     *          method is invalid
      * @see #getMethod()
      */
     public void setMethod(int method) {
@@ -208,7 +380,8 @@
     }
 
     /**
-     * Returns the compression method of the entry, or -1 if not specified.
+     * Returns the compression method of the entry.
+     *
      * @return the compression method of the entry, or -1 if not specified
      * @see #setMethod(int)
      */
@@ -218,21 +391,104 @@
 
     /**
      * Sets the optional extra field data for the entry.
-     * @param extra the extra field data bytes
-     * @exception IllegalArgumentException if the length of the specified
-     *            extra field data is greater than 0xFFFF bytes
+     *
+     * <p> Invoking this method may change this entry's last modification
+     * time, last access time and creation time, if the {@code extra} field
+     * data includes the extensible timestamp fields, such as {@code NTFS tag
+     * 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
+     * <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
+     * Application Note 970311</a>.
+     *
+     * @param  extra
+     *         The extra field data bytes
+     *
+     * @throws IllegalArgumentException if the length of the specified
+     *         extra field data is greater than 0xFFFF bytes
+     *
      * @see #getExtra()
      */
     public void setExtra(byte[] extra) {
-        if (extra != null && extra.length > 0xFFFF) {
-            throw new IllegalArgumentException("invalid extra field length");
+        setExtra0(extra, false);
+    }
+
+    /**
+     * Sets the optional extra field data for the entry.
+     *
+     * @param extra
+     *        the extra field data bytes
+     * @param doZIP64
+     *        if true, set size and csize from ZIP64 fields if present
+     */
+    void setExtra0(byte[] extra, boolean doZIP64) {
+        if (extra != null) {
+            if (extra.length > 0xFFFF) {
+                throw new IllegalArgumentException("invalid extra field length");
+            }
+            // extra fields are in "HeaderID(2)DataSize(2)Data... format
+            int off = 0;
+            int len = extra.length;
+            while (off + 4 < len) {
+                int tag = get16(extra, off);
+                int sz = get16(extra, off + 2);
+                off += 4;
+                if (off + sz > len)         // invalid data
+                    break;
+                switch (tag) {
+                case EXTID_ZIP64:
+                    if (doZIP64) {
+                        // LOC extra zip64 entry MUST include BOTH original
+                        // and compressed file size fields.
+                        // If invalid zip64 extra fields, simply skip. Even
+                        // it's rare, it's possible the entry size happens to
+                        // be the magic value and it "accidently" has some
+                        // bytes in extra match the id.
+                        if (sz >= 16) {
+                            size = get64(extra, off);
+                            csize = get64(extra, off + 8);
+                        }
+                    }
+                    break;
+                case EXTID_NTFS:
+                    int pos = off + 4;               // reserved 4 bytes
+                    if (get16(extra, pos) !=  0x0001 || get16(extra, pos + 2) != 24)
+                        break;
+                    mtime = winTimeToFileTime(get64(extra, pos + 4));
+                    atime = winTimeToFileTime(get64(extra, pos + 12));
+                    ctime = winTimeToFileTime(get64(extra, pos + 20));
+                    break;
+                case EXTID_EXTT:
+                    int flag = Byte.toUnsignedInt(extra[off]);
+                    int sz0 = 1;
+                    // The CEN-header extra field contains the modification
+                    // time only, or no timestamp at all. 'sz' is used to
+                    // 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));
+                        sz0 += 4;
+                    }
+                    if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
+                        atime = unixTimeToFileTime(get32(extra, off + sz0));
+                        sz0 += 4;
+                    }
+                    if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
+                        ctime = unixTimeToFileTime(get32(extra, off + sz0));
+                        sz0 += 4;
+                    }
+                    break;
+                 default:
+                }
+                off += sz;
+            }
         }
         this.extra = extra;
     }
 
     /**
-     * Returns the extra field data for the entry, or null if none.
+     * Returns the extra field data for the entry.
+     *
      * @return the extra field data for the entry, or null if none
+     *
      * @see #setExtra(byte[])
      */
     public byte[] getExtra() {
@@ -255,8 +511,10 @@
     }
 
     /**
-     * Returns the comment string for the entry, or null if none.
+     * Returns the comment string for the entry.
+     *
      * @return the comment string for the entry, or null if none
+     *
      * @see #setComment(String)
      */
     public String getComment() {
--- a/src/share/classes/java/util/zip/ZipFile.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipFile.java	Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, 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
@@ -567,44 +567,12 @@
                 e.name = zc.toString(bname, bname.length);
             }
         }
+        e.time = dosToJavaTime(getEntryTime(jzentry));
         e.crc = getEntryCrc(jzentry);
         e.size = getEntrySize(jzentry);
-        e. csize = getEntryCSize(jzentry);
+        e.csize = getEntryCSize(jzentry);
         e.method = getEntryMethod(jzentry);
-        e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA);
-        if (e.extra != null) {
-            byte[] extra = e.extra;
-            int len = e.extra.length;
-            int off = 0;
-            while (off + 4 < len) {
-                int pos = off;
-                int tag = get16(extra, pos);
-                int sz = get16(extra, pos + 2);
-                pos += 4;
-                if (pos + sz > len)         // invalid data
-                    break;
-                switch (tag) {
-                case EXTID_NTFS:
-                    pos += 4;    // reserved 4 bytes
-                    if (get16(extra, pos) !=  0x0001 || get16(extra, pos + 2) != 24)
-                        break;
-                    e.mtime  = winToJavaTime(get64(extra, pos + 4));
-                    break;
-                case EXTID_EXTT:
-                    int flag = Byte.toUnsignedInt(extra[pos++]);
-                    if ((flag & 0x1) != 0) {
-                        e.mtime = unixToJavaTime(get32(extra, pos));
-                        pos += 4;
-                    }
-                    break;
-                default:    // unknown tag
-                }
-                off += (sz + 4);
-            }
-        }
-        if (e.mtime == -1) {
-            e.mtime = dosToJavaTime(getEntryTime(jzentry));
-        }
+        e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false);
         byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
         if (bcomm == null) {
             e.comment = null;
--- a/src/share/classes/java/util/zip/ZipInputStream.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipInputStream.java	Thu Aug 08 12:03:04 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2013, 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
@@ -288,9 +288,9 @@
         int len = get16(tmpbuf, LOCNAM);
         int blen = b.length;
         if (len > blen) {
-            do
+            do {
                 blen = blen * 2;
-            while (len > blen);
+            } while (len > blen);
             b = new byte[blen];
         }
         readFully(b, 0, len);
@@ -303,7 +303,7 @@
             throw new ZipException("encrypted ZIP entry not supported");
         }
         e.method = get16(tmpbuf, LOCHOW);
-        e.mtime = dosToJavaTime(get32(tmpbuf, LOCTIM));
+        e.time = dosToJavaTime(get32(tmpbuf, LOCTIM));
         if ((flag & 8) == 8) {
             /* "Data Descriptor" present */
             if (e.method != DEFLATED) {
@@ -319,49 +319,7 @@
         if (len > 0) {
             byte[] extra = new byte[len];
             readFully(extra, 0, len);
-            e.setExtra(extra);
-            // extra fields are in "HeaderID(2)DataSize(2)Data... format
-            int off = 0;
-            while (off + 4 < len) {
-                int pos = off;
-                int tag = get16(extra, pos);
-                int sz = get16(extra, pos + 2);
-                pos += 4;
-                if (pos + sz > len)         // invalid data
-                    break;
-                switch (tag) {
-                case EXTID_ZIP64 :
-                    // LOC extra zip64 entry MUST include BOTH original and
-                    // compressed file size fields.
-                    //
-                    // If invalid zip64 extra fields, simply skip. Even it's
-                    // rare, it's possible the entry size happens to be
-                    // the magic value and it "accidently" has some bytes
-                    // in extra match the id.
-                    if (sz >= 16 && (pos + sz) <= len ) {
-                        e.size = get64(extra, pos);
-                        e.csize = get64(extra, pos + 8);
-                    }
-                    break;
-                case EXTID_NTFS:
-                    pos += 4;    // reserved 4 bytes
-                    if (get16(extra, pos) !=  0x0001 || get16(extra, pos + 2) != 24)
-                        break;
-                    // override the loc field, NTFS time has 'microsecond' granularity
-                    e.mtime  = winToJavaTime(get64(extra, pos + 4));
-                    break;
-                case EXTID_EXTT:
-                    int flag = Byte.toUnsignedInt(extra[pos++]);
-                    if ((flag & 0x1) != 0) {
-                        e.mtime = unixToJavaTime(get32(extra, pos));
-                        pos += 4;
-                    }
-                    break;
-                default:    // unknown tag
-                }
-                off += (sz + 4);
-            }
-
+            e.setExtra0(extra, true);
         }
         return e;
     }
--- a/src/share/classes/java/util/zip/ZipOutputStream.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipOutputStream.java	Thu Aug 08 12:03:04 2013 -0700
@@ -59,8 +59,9 @@
                     "jdk.util.zip.inhibitZip64", "false")));
 
     private static class XEntry {
-        public final ZipEntry entry;
-        public final long offset;
+        final ZipEntry entry;
+        final long offset;
+        long dostime;    // last modification time in msdos format
         public XEntry(ZipEntry entry, long offset) {
             this.entry = entry;
             this.offset = offset;
@@ -191,7 +192,9 @@
         if (current != null) {
             closeEntry();       // close previous entry
         }
-        if (e.mtime == -1) {
+        if (e.time == -1) {
+            // by default, do NOT use extended timestamps in extra
+            // data, for now.
             e.setTime(System.currentTimeMillis());
         }
         if (e.method == -1) {
@@ -384,25 +387,20 @@
         ZipEntry e = xentry.entry;
         int flag = e.flag;
         boolean hasZip64 = false;
-        int elen = (e.extra != null) ? e.extra.length : 0;
-        int eoff = 0;
-        boolean foundEXTT = false;      // if EXTT already present
-                                        // do nothing.
-        while (eoff + 4 < elen) {
-            int tag = get16(e.extra, eoff);
-            int sz = get16(e.extra, eoff + 2);
-            if (tag == EXTID_EXTT) {
-                foundEXTT = true;
-            }
-            eoff += (4 + sz);
-        }
+        int elen = getExtraLen(e.extra);
+
+        // keep a copy of dostime for writeCEN(), otherwise the tz
+        // sensitive local time entries in loc and cen might be
+        // different if the default tz get changed during writeLOC()
+        // and writeCEN()
+        xentry.dostime = javaToDosTime(e.time);
+
         writeInt(LOCSIG);               // LOC header signature
         if ((flag & 8) == 8) {
             writeShort(version(e));     // version needed to extract
             writeShort(flag);           // general purpose bit flag
             writeShort(e.method);       // compression method
-            writeInt(javaToDosTime(e.mtime)); // last modification time
-
+            writeInt(xentry.dostime);   // last modification time
             // store size, uncompressed size, and crc-32 in data descriptor
             // immediately following compressed entry data
             writeInt(0);
@@ -417,7 +415,7 @@
             }
             writeShort(flag);           // general purpose bit flag
             writeShort(e.method);       // compression method
-            writeInt(javaToDosTime(e.mtime)); // last modification time
+            writeInt(xentry.dostime);   // last modification time
             writeInt(e.crc);            // crc-32
             if (hasZip64) {
                 writeInt(ZIP64_MAGICVAL);
@@ -430,8 +428,23 @@
         }
         byte[] nameBytes = zc.getBytes(e.name);
         writeShort(nameBytes.length);
-        if (!foundEXTT)
-            elen += 9;              // use Info-ZIP's ext time in extra
+
+        int elenEXTT = 0;               // info-zip extended timestamp
+        int flagEXTT = 0;
+        if (e.mtime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAG_LMT;
+        }
+        if (e.atime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAG_LAT;
+        }
+        if (e.ctime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAT_CT;
+        }
+        if (flagEXTT != 0)
+            elen += (elenEXTT + 5);    // headid(2) + size(2) + flag(1) + data
         writeShort(elen);
         writeBytes(nameBytes, 0, nameBytes.length);
         if (hasZip64) {
@@ -440,15 +453,18 @@
             writeLong(e.size);
             writeLong(e.csize);
         }
-        if (!foundEXTT) {
+        if (flagEXTT != 0) {
             writeShort(EXTID_EXTT);
-            writeShort(5);          // size for the folowing data block
-            writeByte(0x1);         // flags byte, mtime only
-            writeInt(javaToUnixTime(e.mtime));
+            writeShort(elenEXTT + 1);      // flag + data
+            writeByte(flagEXTT);
+            if (e.mtime != null)
+                writeInt(fileTimeToUnixTime(e.mtime));
+            if (e.atime != null)
+                writeInt(fileTimeToUnixTime(e.atime));
+            if (e.ctime != null)
+                writeInt(fileTimeToUnixTime(e.ctime));
         }
-        if (e.extra != null) {
-            writeBytes(e.extra, 0, e.extra.length);
-        }
+        writeExtra(e.extra);
         locoff = written;
     }
 
@@ -506,31 +522,35 @@
         }
         writeShort(flag);           // general purpose bit flag
         writeShort(e.method);       // compression method
-        writeInt(javaToDosTime(e.mtime)); // last modification time
+        // use the copy in xentry, which has been converted
+        // from e.time in writeLOC()
+        writeInt(xentry.dostime);   // last modification time
         writeInt(e.crc);            // crc-32
         writeInt(csize);            // compressed size
         writeInt(size);             // uncompressed size
         byte[] nameBytes = zc.getBytes(e.name);
         writeShort(nameBytes.length);
 
-        int elen = (e.extra != null) ? e.extra.length : 0;
-        int eoff = 0;
-        boolean foundEXTT = false;  // if EXTT already present
-                                    // do nothing.
-        while (eoff + 4 < elen) {
-            int tag = get16(e.extra, eoff);
-            int sz = get16(e.extra, eoff + 2);
-            if (tag == EXTID_EXTT) {
-                foundEXTT = true;
-            }
-            eoff += (4 + sz);
+        int elen = getExtraLen(e.extra);
+        if (hasZip64) {
+            elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
         }
-        if (hasZip64) {
-            // + headid(2) + datasize(2)
-            elen += (elenZIP64 + 4);
+        // cen info-zip extended timestamp only outputs mtime
+        // but set the flag for a/ctime, if present in loc
+        int flagEXTT = 0;
+        if (e.mtime != null) {
+            elen += 4;              // + mtime(4)
+            flagEXTT |= EXTT_FLAG_LMT;
         }
-        if (!foundEXTT)
-            elen += 9;              // Info-ZIP's Extended Timestamp
+        if (e.atime != null) {
+            flagEXTT |= EXTT_FLAG_LAT;
+        }
+        if (e.ctime != null) {
+            flagEXTT |= EXTT_FLAT_CT;
+        }
+        if (flagEXTT != 0) {
+            elen += 5;             // headid + sz + flag
+        }
         writeShort(elen);
         byte[] commentBytes;
         if (e.comment != null) {
@@ -545,6 +565,8 @@
         writeInt(0);                // external file attributes (unused)
         writeInt(offset);           // relative offset of local header
         writeBytes(nameBytes, 0, nameBytes.length);
+
+        // take care of EXTID_ZIP64 and EXTID_EXTT
         if (hasZip64) {
             writeShort(ZIP64_EXTID);// Zip64 extra
             writeShort(elenZIP64);
@@ -555,15 +577,18 @@
             if (offset == ZIP64_MAGICVAL)
                 writeLong(xentry.offset);
         }
-        if (!foundEXTT) {
+        if (flagEXTT != 0) {
             writeShort(EXTID_EXTT);
-            writeShort(5);
-            writeByte(0x1);            // flags byte
-            writeInt(javaToUnixTime(e.mtime));
+            if (e.mtime != null) {
+                writeShort(5);      // flag + mtime
+                writeByte(flagEXTT);
+                writeInt(fileTimeToUnixTime(e.mtime));
+            } else {
+                writeShort(1);      // flag only
+                writeByte(flagEXTT);
+            }
         }
-        if (e.extra != null) {
-            writeBytes(e.extra, 0, e.extra.length);
-        }
+        writeExtra(e.extra);
         if (commentBytes != null) {
             writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
         }
@@ -627,6 +652,47 @@
     }
 
     /*
+     * Returns the length of extra data without EXTT and ZIP64.
+     */
+    private int getExtraLen(byte[] extra) {
+        if (extra == null)
+            return 0;
+        int skipped = 0;
+        int len = extra.length;
+        int off = 0;
+        while (off + 4 <= len) {
+            int tag = get16(extra, off);
+            int sz = get16(extra, off + 2);
+            if (tag == EXTID_EXTT || tag == EXTID_ZIP64) {
+                skipped += (sz + 4);
+            }
+            off += (sz + 4);
+        }
+        return len - skipped;
+    }
+
+    /*
+     * Writes extra data without EXTT and ZIP64.
+     *
+     * Extra timestamp and ZIP64 data is handled/output separately
+     * in writeLOC and writeCEN.
+     */
+    private void writeExtra(byte[] extra) throws IOException {
+        if (extra != null) {
+            int len = extra.length;
+            int off = 0;
+            while (off + 4 <= len) {
+                int tag = get16(extra, off);
+                int sz = get16(extra, off + 2);
+                if (tag != EXTID_EXTT && tag != EXTID_ZIP64) {
+                    writeBytes(extra, off, sz + 4);
+                }
+                off += (sz + 4);
+            }
+        }
+    }
+
+    /*
      * Writes a 8-bit byte to the output stream.
      */
     private void writeByte(int v) throws IOException {
--- a/src/share/classes/java/util/zip/ZipUtils.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/src/share/classes/java/util/zip/ZipUtils.java	Thu Aug 08 12:03:04 2013 -0700
@@ -25,42 +25,45 @@
 
 package java.util.zip;
 
+import java.nio.file.attribute.FileTime;
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
 
+import static java.util.zip.ZipConstants.*;
+import static java.util.zip.ZipConstants64.*;
+
 class ZipUtils {
 
     // used to adjust values between Windows and java epoch
     private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
 
     /**
-     * Converts Windows time (in microseconds, UTC/GMT) time to Java time.
+     * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
      */
-    public static final long winToJavaTime(long wtime) {
-        return TimeUnit.MILLISECONDS.convert(
-               wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS);
+    public static final FileTime winTimeToFileTime(long wtime) {
+        return FileTime.from(wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS,
+                             TimeUnit.MICROSECONDS);
     }
 
     /**
-     * Converts Java time to Windows time.
+     * Converts FileTime to Windows time.
      */
-    public static final long javaToWinTime(long time) {
-        return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS)
-               - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
+    public static final long fileTimeToWinTime(FileTime ftime) {
+        return (ftime.to(TimeUnit.MICROSECONDS) - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
     }
 
     /**
-     * Converts "standard Unix time"(in seconds, UTC/GMT) to Java time
+     * Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
      */
-    public static final long unixToJavaTime(long utime) {
-        return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS);
+    public static final FileTime unixTimeToFileTime(long utime) {
+        return FileTime.from(utime, TimeUnit.SECONDS);
     }
 
     /**
-     * Converts Java time to "standard Unix time".
+     * Converts FileTime to "standard Unix time".
      */
-    public static final long javaToUnixTime(long time) {
-        return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS);
+    public static final long fileTimeToUnixTime(FileTime ftime) {
+        return ftime.to(TimeUnit.SECONDS);
     }
 
     /**
@@ -92,7 +95,6 @@
                d.getSeconds() >> 1;
     }
 
-
     /**
      * Fetches unsigned 16-bit value from byte array at specified offset.
      * The bytes are assumed to be in Intel (little-endian) byte order.
@@ -116,5 +118,4 @@
     public static final long get64(byte b[], int off) {
         return get32(b, off) | (get32(b, off+4) << 32);
     }
-
 }
--- a/test/ProblemList.txt	Thu Aug 08 21:13:01 2013 +0800
+++ b/test/ProblemList.txt	Thu Aug 08 12:03:04 2013 -0700
@@ -335,8 +335,6 @@
 # Tests take too long, on sparcs see 7143279
 tools/pack200/CommandLineTests.java                             solaris-all, macosx-all
 tools/pack200/Pack200Test.java                                  solaris-all, macosx-all
-# 8015666 
-tools/pack200/TimeStamp.java                                    generic-all
 
 # 8007410
 tools/launcher/FXLauncherTest.java                              linux-all
--- a/test/java/util/zip/TestExtraTime.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/test/java/util/zip/TestExtraTime.java	Thu Aug 08 12:03:04 2013 -0700
@@ -23,14 +23,19 @@
 
 /**
  * @test
- * @bug 4759491 6303183 7012868
+ * @bug 4759491 6303183 7012868 8015666
  * @summary Test ZOS and ZIS timestamp in extra field correctly
  */
 
 import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.FileTime;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
 
@@ -41,14 +46,32 @@
 
         File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java");
         if (src.exists()) {
-            long mtime = src.lastModified();
-            test(mtime, null);
-            test(10, null);  // ms-dos 1980 epoch problem
-            test(mtime, TimeZone.getTimeZone("Asia/Shanghai"));
+            long time = src.lastModified();
+            FileTime mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
+            FileTime atime = FileTime.from(time + 300000, TimeUnit.MILLISECONDS);
+            FileTime ctime = FileTime.from(time - 300000, TimeUnit.MILLISECONDS);
+            TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
+
+            test(mtime, null, null, null);
+            // ms-dos 1980 epoch problem
+            test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null);
+            // non-default tz
+            test(mtime, null, null, tz);
+
+            test(mtime, atime, null, null);
+            test(mtime, null, ctime, null);
+            test(mtime, atime, ctime, null);
+
+            test(mtime, atime, null, tz);
+            test(mtime, null, ctime, tz);
+            test(mtime, atime, ctime, tz);
         }
     }
 
-    private static void test(long mtime, TimeZone tz) throws Throwable {
+    static void test(FileTime mtime, FileTime atime, FileTime ctime,
+                     TimeZone tz) throws Throwable {
+        System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
+                          mtime, atime, ctime);
         TimeZone tz0 = TimeZone.getDefault();
         if (tz != null) {
             TimeZone.setDefault(tz);
@@ -57,23 +80,55 @@
         ZipOutputStream zos = new ZipOutputStream(baos);
         ZipEntry ze = new ZipEntry("TestExtreTime.java");
 
-        ze.setTime(mtime);
+        ze.setLastModifiedTime(mtime);
+        if (atime != null)
+            ze.setLastAccessTime(atime);
+        if (ctime != null)
+            ze.setCreationTime(ctime);
         zos.putNextEntry(ze);
         zos.write(new byte[] { 1,2 ,3, 4});
         zos.close();
         if (tz != null) {
             TimeZone.setDefault(tz0);
         }
+        // ZipInputStream
         ZipInputStream zis = new ZipInputStream(
                                  new ByteArrayInputStream(baos.toByteArray()));
         ze = zis.getNextEntry();
         zis.close();
+        check(mtime, atime, ctime, ze);
 
-        System.out.printf("%tc  => %tc%n", mtime, ze.getTime());
+        // ZipFile
+        Path zpath = Paths.get(System.getProperty("test.dir", "."),
+                               "TestExtraTimp.zip");
+        Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
+        ZipFile zf = new ZipFile(zpath.toFile());
+        ze = zf.getEntry("TestExtreTime.java");
+        // ZipFile read entry from cen, which does not have a/ctime,
+        // for now.
+        check(mtime, null, null, ze);
+        zf.close();
+        Files.delete(zpath);
+    }
 
-        if (TimeUnit.MILLISECONDS.toSeconds(mtime) !=
-            TimeUnit.MILLISECONDS.toSeconds(ze.getTime()))
-            throw new RuntimeException("Timestamp storing failed!");
-
+    static void check(FileTime mtime, FileTime atime, FileTime ctime,
+                      ZipEntry ze) {
+        /*
+        System.out.printf("    mtime [%tc]: [%tc]/[%tc]%n",
+                          mtime.to(TimeUnit.MILLISECONDS),
+                          ze.getTime(),
+                          ze.getLastModifiedTime().to(TimeUnit.MILLISECONDS));
+         */
+        if (mtime.to(TimeUnit.SECONDS) !=
+            ze.getLastModifiedTime().to(TimeUnit.SECONDS))
+            throw new RuntimeException("Timestamp: storing mtime failed!");
+        if (atime != null &&
+            atime.to(TimeUnit.SECONDS) !=
+            ze.getLastAccessTime().to(TimeUnit.SECONDS))
+            throw new RuntimeException("Timestamp: storing atime failed!");
+        if (ctime != null &&
+            ctime.to(TimeUnit.SECONDS) !=
+            ze.getCreationTime().to(TimeUnit.SECONDS))
+            throw new RuntimeException("Timestamp: storing ctime failed!");
     }
 }
--- a/test/tools/pack200/TimeStamp.java	Thu Aug 08 21:13:01 2013 +0800
+++ b/test/tools/pack200/TimeStamp.java	Thu Aug 08 12:03:04 2013 -0700
@@ -88,6 +88,7 @@
         unpackNative(packFile, pstFile);
         verifyJar(goldenFile, pstFile);
         pstFile.delete();
+        Utils.cleanup();
     }
 
     static void unpackNative(File packFile, File outFile) {
@@ -149,7 +150,6 @@
             Utils.close(jf1);
             Utils.close(jf2);
         }
-        Utils.cleanup();
         if (errors > 0) {
             throw new RuntimeException("FAIL:" + errors + " error(s) encounted");
         }