Skip to content

Commit f375277

Browse files
committed
Merge pull request #19595 from nosan
* pr/19595: Polish "Limit ChronoField values to their range" Limit ChronoField values to their range Closes gh-19595
2 parents 717439d + 91e459a commit f375277

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,8 +17,11 @@
1717
package org.springframework.boot.loader.jar;
1818

1919
import java.io.IOException;
20-
import java.time.LocalDateTime;
2120
import java.time.ZoneId;
21+
import java.time.ZonedDateTime;
22+
import java.time.temporal.ChronoField;
23+
import java.time.temporal.ChronoUnit;
24+
import java.time.temporal.ValueRange;
2225

2326
import org.springframework.boot.loader.data.RandomAccessData;
2427

@@ -27,6 +30,7 @@
2730
*
2831
* @author Phillip Webb
2932
* @author Andy Wilkinson
33+
* @author Dmytro Nosan
3034
* @see <a href="https://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
3135
*/
3236

@@ -124,10 +128,14 @@ public long getTime() {
124128
* @return the date and time as milliseconds since the epoch
125129
*/
126130
private long decodeMsDosFormatDateTime(long datetime) {
127-
LocalDateTime localDateTime = LocalDateTime.of((int) (((datetime >> 25) & 0x7f) + 1980),
128-
(int) ((datetime >> 21) & 0x0f), (int) ((datetime >> 16) & 0x1f), (int) ((datetime >> 11) & 0x1f),
129-
(int) ((datetime >> 5) & 0x3f), (int) ((datetime << 1) & 0x3e));
130-
return localDateTime.toEpochSecond(ZoneId.systemDefault().getRules().getOffset(localDateTime)) * 1000;
131+
int year = getChronoValue(((datetime >> 25) & 0x7f) + 1980, ChronoField.YEAR);
132+
int month = getChronoValue((datetime >> 21) & 0x0f, ChronoField.MONTH_OF_YEAR);
133+
int day = getChronoValue((datetime >> 16) & 0x1f, ChronoField.DAY_OF_MONTH);
134+
int hour = getChronoValue((datetime >> 11) & 0x1f, ChronoField.HOUR_OF_DAY);
135+
int minute = getChronoValue((datetime >> 5) & 0x3f, ChronoField.MINUTE_OF_HOUR);
136+
int second = getChronoValue((datetime << 1) & 0x3e, ChronoField.SECOND_OF_MINUTE);
137+
return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault()).toInstant()
138+
.truncatedTo(ChronoUnit.SECONDS).toEpochMilli();
131139
}
132140

133141
public long getCrc() {
@@ -172,4 +180,9 @@ public static CentralDirectoryFileHeader fromRandomAccessData(RandomAccessData d
172180
return fileHeader;
173181
}
174182

183+
private static int getChronoValue(long value, ChronoField field) {
184+
ValueRange range = field.range();
185+
return Math.toIntExact(Math.min(Math.max(value, range.getMinimum()), range.getMaximum()));
186+
}
187+
175188
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import java.net.URL;
2626
import java.net.URLClassLoader;
2727
import java.nio.charset.Charset;
28+
import java.nio.file.attribute.FileTime;
29+
import java.time.Instant;
2830
import java.util.Enumeration;
2931
import java.util.jar.JarEntry;
3032
import java.util.jar.JarInputStream;
33+
import java.util.jar.JarOutputStream;
3134
import java.util.jar.Manifest;
3235
import java.util.zip.ZipEntry;
3336
import java.util.zip.ZipFile;
@@ -39,6 +42,7 @@
3942

4043
import org.springframework.boot.loader.TestJarCreator;
4144
import org.springframework.boot.loader.data.RandomAccessDataFile;
45+
import org.springframework.test.util.ReflectionTestUtils;
4246
import org.springframework.util.FileCopyUtils;
4347
import org.springframework.util.StreamUtils;
4448

@@ -54,6 +58,7 @@
5458
* @author Phillip Webb
5559
* @author Martin Lau
5660
* @author Andy Wilkinson
61+
* @author Madhura Bhave
5762
*/
5863
public class JarFileTests {
5964

@@ -493,6 +498,26 @@ public void multiReleaseEntry() throws Exception {
493498
assertThat(inputStream.read()).isEqualTo(getJavaVersion());
494499
}
495500

501+
@Test
502+
public void jarFileEntryWithEpochTimeOfZeroShouldNotFail() throws Exception {
503+
File file = this.temporaryFolder.newFile();
504+
FileOutputStream fileOutputStream = new FileOutputStream(file);
505+
try (JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream)) {
506+
jarOutputStream.setComment("outer");
507+
JarEntry entry = new JarEntry("1.dat");
508+
entry.setLastModifiedTime(FileTime.from(Instant.EPOCH));
509+
ReflectionTestUtils.setField(entry, "xdostime", 0);
510+
jarOutputStream.putNextEntry(entry);
511+
jarOutputStream.write(new byte[] { (byte) 1 });
512+
jarOutputStream.closeEntry();
513+
}
514+
JarFile jarFile = new JarFile(file);
515+
Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();
516+
JarEntry entry = entries.nextElement();
517+
assertThat(entry.getLastModifiedTime().toInstant()).isEqualTo(Instant.EPOCH);
518+
assertThat(entry.getName()).isEqualTo("1.dat");
519+
}
520+
496521
private int getJavaVersion() {
497522
try {
498523
Object runtimeVersion = Runtime.class.getMethod("version").invoke(null);

0 commit comments

Comments
 (0)