Skip to content

ZIP entries last modification time is rounded down on Java 8 or later #95

Closed
@plamentotev

Description

@plamentotev

Short Description

According to the Plexus Archiver documentation:

    /**
     * Whether the file modification times will be rounded up to the
     * next even number of seconds.
     * <p/>
     * <p>
     * Zip archives store file modification times with a
     * granularity of two seconds, so the times will either be rounded
     * up or down. If you round down, the archive will always seem
     * out-of-date when you rerun the task, so the default is to round
     * up. Rounding up may lead to a different type of problems like
     * JSPs inside a web archive that seem to be slightly more recent
     * than precompiled pages, rendering precompilation useless.</p>
     *
     * <p/>
     * plexus-archiver chooses to round up.
     * <p/>
     * Java versions up to java7 round timestamp down, which means we add a heuristic value (which is slightly
     * questionable)
     * Java versions from 8 and up round timestamp up.
     * s
     */
    private static final boolean isJava7OrLower = getJavaVersion() <= 7;

but that is not true. A simple test shows that when run on Java 8 the entries last modified time is rounded down. If there are no objections I'll remove the check so the last modification times are rounded up (resulting in entries being with the same or newer last modification than the original file and not older) even on Java 8 and later.

Long Description

The ZIP file format stores the last modification time of an entry in DOS date time format - the date and time are stored in 16 bit integer value. This have some limitations but the important for the discussion is that 5 bits are used to store the seconds while to store value from 0 to 59 a 6 bits are required. Seconds are shifted by one bit (effectively dividing them by 2). In practice that means that odd seconds are stored with the same value as the lower even second. For example both 3 (0b11) and 2 (0b10) are stored as 1(0b1). What that means for Maven? Let's assume that a file last modification timestamp(in seconds) is 1534189011 then it will be stored as 1534189010 making the entry appear older than the original file.

What Have Changed in Java 8

Java 7 follows the description above. For example:

zipEntry.setTime(1534189011000L);
zipEntry.getTime(); // returns 1534189010000

effectively rounding the value down. In Java 8 on other hand:

zipEntry.setTime(1534189011000L);
zipEntry.getTime(); // returns 1534189011000

It does not do any rounding - it just returns the original value. That is because it internally stores if the value was rounded down so it can return the original value (you can check the source here).

Why it Does Not Matter

It does not matter if zipEntry.getTime() returns the original time or not because Commons Compress shifts the seconds value again before writing it to the ZIP file headers. From org.apache.commons.compress.archivers.zip,ZipUtil#toDosTime

        final long value =  ((year - 1980) << 25)
                |         (month << 21)
                |         (c.get(Calendar.DAY_OF_MONTH) << 16)
                |         (c.get(Calendar.HOUR_OF_DAY) << 11)
                |         (c.get(Calendar.MINUTE) << 5)
                |         (c.get(Calendar.SECOND) >> 1);
        ZipLong.putLong(value, buf, offset);

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions