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