Skip to content

Commit ebb8d0c

Browse files
committed
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
1 parent 764e34b commit ebb8d0c

File tree

2 files changed

+23
-1
lines changed

2 files changed

+23
-1
lines changed

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/JarEntryData.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.lang.ref.SoftReference;
22+
import java.util.GregorianCalendar;
2223
import java.util.zip.ZipEntry;
2324

2425
import org.springframework.boot.loader.data.RandomAccessData;
@@ -145,7 +146,20 @@ public int getMethod() {
145146
}
146147

147148
public long getTime() {
148-
return Bytes.littleEndianValue(this.header, 12, 4);
149+
long time = Bytes.littleEndianValue(this.header, 12, 2);
150+
151+
int seconds = (int) ((time << 1) & 0x3E);
152+
int minutes = (int) ((time >> 5) & 0x3F);
153+
int hours = (int) ((time >> 11) & 0x1F);
154+
155+
long date = Bytes.littleEndianValue(this.header, 14, 2);
156+
157+
int day = (int) (date & 0x1F);
158+
int month = (int) ((date >> 5) & 0xF) - 1;
159+
int year = (int) ((date >> 9) & 0x7F) + 1980;
160+
161+
return new GregorianCalendar(year, month, day, hours, minutes, seconds)
162+
.getTimeInMillis();
149163
}
150164

151165
public long getCrc() {

spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/JarFileTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ public void getSize() throws Exception {
170170
assertThat(this.jarFile.size(), equalTo((int) this.rootJarFile.length()));
171171
}
172172

173+
@Test
174+
public void getEntryTime() throws Exception {
175+
java.util.jar.JarFile jdkJarFile = new java.util.jar.JarFile(this.rootJarFile);
176+
assertThat(this.jarFile.getEntry("META-INF/MANIFEST.MF").getTime(),
177+
equalTo(jdkJarFile.getEntry("META-INF/MANIFEST.MF").getTime()));
178+
jdkJarFile.close();
179+
}
180+
173181
@Test
174182
public void close() throws Exception {
175183
RandomAccessDataFile randomAccessDataFile = spy(new RandomAccessDataFile(

0 commit comments

Comments
 (0)