Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8184940

JDK 9 rejects zip files where the modified day or month is 0

XMLWordPrintable

    • b20
    • Verified

        Dates in zip files are encoded in MS-DOS format [1][2] where the year is relative to 1980 and the day and month both start at 1. In JDK 8 and earlier the zip implementations in java.util and zipfs supported reading dates where the year, day, and month were 0, interpreting them as 1979-11-30. Starting in JDK 9 zip archives containing 0 as the modified day or month are rejected.

        The rejected dates are arguably invalid, but they are used by existing zip implementations: for example the go language's archive/zip package uses 0 as the date unless a date is set explicitly, and some build tools [3] zero out the date to make their output deterministic.

        To ensure JDK 9 is able to read all existing and previously supported zip files it should continue to handle 0 dates in the same manner as JDK 8.

        [1] https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT (see 4.4.6 date and time fields)
        [2] https://docs.microsoft.com/en-us/cpp/c-runtime-library/32-bit-windows-time-date-formats
        [3] https://github.com/google/protobuf/blob/50aa4febaffa6a6f168ec3f9afc6fbdd40e4d31f/src/google/protobuf/compiler/zip_writer.cc#L157

        Proposed fix:

        To support these dates the implementation of ZipUtils.dosToJavaTime (in both java.util and zipfs) could be changed to:

        ===
           public static long dosToJavaTime(long dtime) {
                int year;
                int month;
                int day;
                int hour = (int) ((dtime >> 11) & 0x1f);
                int minute = (int) ((dtime >> 5) & 0x3f);
                int second = (int) ((dtime << 1) & 0x3e);
                if ((dtime >> 25) == 0) {
                    // interpret the 0 DOS date as 1979-11-30 for compatibility with other implementations.
                    year = 1979;
                    month = 11;
                    day = 30;
                } else {
                    year = (int) (((dtime >> 25) & 0x7f) + 1980);
                    month = (int) ((dtime >> 21) & 0x0f);
                    day = (int) ((dtime >> 16) & 0x1f);
                }
                LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second);
                return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond(
                        ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS);
            }
        ===

        Steps to reproduce:

        1) Create a zip archive where the modified date is '0'. Note that both unzip and the JDK 8 jar tool accept the archive. (unzip reports the date as 1980-0-0, jar reports 1979-11-30)

        $ go run zipwriter.go
        $ unzip -l go.zip
        Archive: go.zip
          Length Date Time Name
        --------- ---------- ----- ----
                5 1980-00-00 00:00 hello
        --------- -------
                5 1 file
        $ jar tvf go.zip
             5 Fri Nov 30 00:00:00 PST 1979 hello

        2) Dump the zip entry's modified date using the JDK 8 java.util.JarFile and zipfs:

        $ java -fullversion
        java full version "1.8.0_152-ea-b05"
        $ java ZipfsJarTime go.zip/hello / creationTime : null
        ...
            lastModifiedTime: Fri Nov 30 00:00:00 PST 1979
        $ java JarTime go.zip
        hello / 1979-11-30T08:00:00Z

        3) Repeating (2) with JDK 9 fails:

        $ java -fullversion
        java full version "9+178"
        $ java JarTime go.zip
        Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 0
                at java.base/java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
                at java.base/java.time.temporal.ChronoField.checkValidValue(ChronoField.java:714)
                at java.base/java.time.LocalDate.of(LocalDate.java:269)
                at java.base/java.time.LocalDateTime.of(LocalDateTime.java:336)
                at java.base/java.util.zip.ZipUtils.dosToJavaTime(ZipUtils.java:82)
                at java.base/java.util.zip.ZipUtils.extendedDosToJavaTime(ZipUtils.java:101)
                at java.base/java.util.zip.ZipEntry.getTime(ZipEntry.java:198)
                at java.base/java.util.zip.ZipEntry.getLastModifiedTime(ZipEntry.java:325)
                at JarTime.main(JarTime.java:13)
        $ java ZipfsJarTime go.zip
        Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 0
                at java.base/java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
                at java.base/java.time.temporal.ChronoField.checkValidValue(ChronoField.java:714)
                at java.base/java.time.LocalDate.of(LocalDate.java:269)
                at java.base/java.time.LocalDateTime.of(LocalDateTime.java:336)
                at jdk.zipfs/jdk.nio.zipfs.ZipUtils.dosToJavaTime(ZipUtils.java:109)
                at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$Entry.cen(ZipFileSystem.java:1950)
                at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem$Entry.readCEN(ZipFileSystem.java:1937)
                at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.getEntry(ZipFileSystem.java:1324)
                at jdk.zipfs/jdk.nio.zipfs.ZipFileSystem.getFileAttributes(ZipFileSystem.java:312)
                at jdk.zipfs/jdk.nio.zipfs.ZipPath.getAttributes(ZipPath.java:721)
                at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.readAttributes(ZipFileSystemProvider.java:293)
                at java.base/java.nio.file.Files.readAttributes(Files.java:1755)
                at java.base/java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219)
                at java.base/java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276)
                at java.base/java.nio.file.FileTreeWalker.next(FileTreeWalker.java:373)
                at java.base/java.nio.file.Files.walkFileTree(Files.java:2749)
                at java.base/java.nio.file.Files.walkFileTree(Files.java:2785)
                at ZipfsJarTime.main(ZipfsJarTime.java:12)


          1. JarTime.java
            0.5 kB
            Liam Miller-Cushon
          2. ZipfsJarTime.java
            0.9 kB
            Liam Miller-Cushon
          3. zipwriter.go
            0.3 kB
            Liam Miller-Cushon

              cushon Liam Miller-Cushon
              cushon Liam Miller-Cushon
              Votes:
              0 Vote for this issue
              Watchers:
              17 Start watching this issue

                Created:
                Updated:
                Resolved: