Merge pull request #19595 from nosan
* pr/19595: Polish "Limit ChronoField values to their range" Limit ChronoField values to their range Closes gh-19595
This commit is contained in:
commit
f375277026
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -17,8 +17,11 @@
|
|||
package org.springframework.boot.loader.jar;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.ValueRange;
|
||||
|
||||
import org.springframework.boot.loader.data.RandomAccessData;
|
||||
|
||||
|
|
@ -27,6 +30,7 @@ import org.springframework.boot.loader.data.RandomAccessData;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Dmytro Nosan
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Zip_%28file_format%29">Zip File Format</a>
|
||||
*/
|
||||
|
||||
|
|
@ -124,10 +128,14 @@ final class CentralDirectoryFileHeader implements FileHeader {
|
|||
* @return the date and time as milliseconds since the epoch
|
||||
*/
|
||||
private long decodeMsDosFormatDateTime(long datetime) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of((int) (((datetime >> 25) & 0x7f) + 1980),
|
||||
(int) ((datetime >> 21) & 0x0f), (int) ((datetime >> 16) & 0x1f), (int) ((datetime >> 11) & 0x1f),
|
||||
(int) ((datetime >> 5) & 0x3f), (int) ((datetime << 1) & 0x3e));
|
||||
return localDateTime.toEpochSecond(ZoneId.systemDefault().getRules().getOffset(localDateTime)) * 1000;
|
||||
int year = getChronoValue(((datetime >> 25) & 0x7f) + 1980, ChronoField.YEAR);
|
||||
int month = getChronoValue((datetime >> 21) & 0x0f, ChronoField.MONTH_OF_YEAR);
|
||||
int day = getChronoValue((datetime >> 16) & 0x1f, ChronoField.DAY_OF_MONTH);
|
||||
int hour = getChronoValue((datetime >> 11) & 0x1f, ChronoField.HOUR_OF_DAY);
|
||||
int minute = getChronoValue((datetime >> 5) & 0x3f, ChronoField.MINUTE_OF_HOUR);
|
||||
int second = getChronoValue((datetime << 1) & 0x3e, ChronoField.SECOND_OF_MINUTE);
|
||||
return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.systemDefault()).toInstant()
|
||||
.truncatedTo(ChronoUnit.SECONDS).toEpochMilli();
|
||||
}
|
||||
|
||||
public long getCrc() {
|
||||
|
|
@ -172,4 +180,9 @@ final class CentralDirectoryFileHeader implements FileHeader {
|
|||
return fileHeader;
|
||||
}
|
||||
|
||||
private static int getChronoValue(long value, ChronoField field) {
|
||||
ValueRange range = field.range();
|
||||
return Math.toIntExact(Math.min(Math.max(value, range.getMinimum()), range.getMaximum()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,12 @@ import java.io.InputStream;
|
|||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
|
@ -39,6 +42,7 @@ import org.junit.rules.TemporaryFolder;
|
|||
|
||||
import org.springframework.boot.loader.TestJarCreator;
|
||||
import org.springframework.boot.loader.data.RandomAccessDataFile;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
|
|
@ -54,6 +58,7 @@ import static org.mockito.Mockito.verify;
|
|||
* @author Phillip Webb
|
||||
* @author Martin Lau
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class JarFileTests {
|
||||
|
||||
|
|
@ -493,6 +498,26 @@ public class JarFileTests {
|
|||
assertThat(inputStream.read()).isEqualTo(getJavaVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jarFileEntryWithEpochTimeOfZeroShouldNotFail() throws Exception {
|
||||
File file = this.temporaryFolder.newFile();
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
try (JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream)) {
|
||||
jarOutputStream.setComment("outer");
|
||||
JarEntry entry = new JarEntry("1.dat");
|
||||
entry.setLastModifiedTime(FileTime.from(Instant.EPOCH));
|
||||
ReflectionTestUtils.setField(entry, "xdostime", 0);
|
||||
jarOutputStream.putNextEntry(entry);
|
||||
jarOutputStream.write(new byte[] { (byte) 1 });
|
||||
jarOutputStream.closeEntry();
|
||||
}
|
||||
JarFile jarFile = new JarFile(file);
|
||||
Enumeration<java.util.jar.JarEntry> entries = jarFile.entries();
|
||||
JarEntry entry = entries.nextElement();
|
||||
assertThat(entry.getLastModifiedTime().toInstant()).isEqualTo(Instant.EPOCH);
|
||||
assertThat(entry.getName()).isEqualTo("1.dat");
|
||||
}
|
||||
|
||||
private int getJavaVersion() {
|
||||
try {
|
||||
Object runtimeVersion = Runtime.class.getMethod("version").invoke(null);
|
||||
|
|
|
|||
Loading…
Reference in New Issue