Read the time of a JarEntryData using MSDOS Date Time format

The format is rather unusual.

The time is 16 bits: 5 bits for the hour, 6 bits for the minutes, and 5
bits for the seconds. 5 bits only allows 32 values (0-31) so the number
must be doubled, meaning that the time is only accurate to the nearest
two seconds. Also, the JDK rounds this down by subtracting one. The
doubling and rounding is performed by shifting one place to the left
and masking off the right-most bit respectively.

The date is 16 bits: 7 bits for the year, 4 bits for the month, and 5
bits for the day. The year is from 1980, i.e. the earliest date that
can be represented is 1980-01-01.

See http://mindprod.com/jgloss/zip.html for more details of the format.

Fixes gh-2826
This commit is contained in:
Andy Wilkinson 2015-04-15 17:03:06 +01:00
parent 764e34b9db
commit ebb8d0c55f
2 changed files with 23 additions and 1 deletions

View File

@ -19,6 +19,7 @@ package org.springframework.boot.loader.jar;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.GregorianCalendar;
import java.util.zip.ZipEntry;
import org.springframework.boot.loader.data.RandomAccessData;
@ -145,7 +146,20 @@ public final class JarEntryData {
}
public long getTime() {
return Bytes.littleEndianValue(this.header, 12, 4);
long time = Bytes.littleEndianValue(this.header, 12, 2);
int seconds = (int) ((time << 1) & 0x3E);
int minutes = (int) ((time >> 5) & 0x3F);
int hours = (int) ((time >> 11) & 0x1F);
long date = Bytes.littleEndianValue(this.header, 14, 2);
int day = (int) (date & 0x1F);
int month = (int) ((date >> 5) & 0xF) - 1;
int year = (int) ((date >> 9) & 0x7F) + 1980;
return new GregorianCalendar(year, month, day, hours, minutes, seconds)
.getTimeInMillis();
}
public long getCrc() {

View File

@ -170,6 +170,14 @@ public class JarFileTests {
assertThat(this.jarFile.size(), equalTo((int) this.rootJarFile.length()));
}
@Test
public void getEntryTime() throws Exception {
java.util.jar.JarFile jdkJarFile = new java.util.jar.JarFile(this.rootJarFile);
assertThat(this.jarFile.getEntry("META-INF/MANIFEST.MF").getTime(),
equalTo(jdkJarFile.getEntry("META-INF/MANIFEST.MF").getTime()));
jdkJarFile.close();
}
@Test
public void close() throws Exception {
RandomAccessDataFile randomAccessDataFile = spy(new RandomAccessDataFile(