Apply consistent timestamps to files added to a fat archive

Update logic in `BootZipCopyAction` to align with the recent changes
made in the Maven plugin (commit 998d59b7). Timestamps are now
specified in UTC and offset against the default timezone before being
written.

Removing the offset from our UTC time before calling `entry.setTime()`
ensures that we get consistent bytes in the zip file when the output
stream reapplies the offset during write.

Closes gh-21005
This commit is contained in:
Phillip Webb
2023-03-02 12:03:16 -08:00
parent 998d59b7ac
commit 69d34c96bf
5 changed files with 146 additions and 9 deletions

View File

@@ -22,9 +22,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Calendar;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -67,8 +67,9 @@ import org.springframework.util.StringUtils;
*/
class BootZipCopyAction implements CopyAction {
static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = new GregorianCalendar(1980, Calendar.FEBRUARY, 1, 0, 0, 0)
.getTimeInMillis();
static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
private final File output;
@@ -364,7 +365,7 @@ class BootZipCopyAction implements CopyAction {
writeParentDirectoriesIfNecessary(name, time);
entry.setUnixMode(mode);
if (time != null) {
entry.setTime(time);
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(time));
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.gradle.tasks.bundling;
import java.nio.file.attribute.FileTime;
import java.util.TimeZone;
import java.util.zip.ZipEntry;
/**
* Utility class that can be used change a UTC time based on the
* {@link java.util.TimeZone#getDefault() default TimeZone}. This is required because
* {@link ZipEntry#setTime(long)} expects times in the default timezone and not UTC.
*
* @author Phillip Webb
*/
class DefaultTimeZoneOffset {
static final DefaultTimeZoneOffset INSTANCE = new DefaultTimeZoneOffset(TimeZone.getDefault());
private final TimeZone defaultTimeZone;
DefaultTimeZoneOffset(TimeZone defaultTimeZone) {
this.defaultTimeZone = defaultTimeZone;
}
/**
* Remove the default offset from the given time.
* @param time the time to remove the default offset from
* @return the time with the default offset removed
*/
FileTime removeFrom(FileTime time) {
return FileTime.fromMillis(removeFrom(time.toMillis()));
}
/**
* Remove the default offset from the given time.
* @param time the time to remove the default offset from
* @return the time with the default offset removed
*/
long removeFrom(long time) {
return time - this.defaultTimeZone.getOffset(time);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2023 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.
@@ -81,7 +81,7 @@ class LoaderZipEntries {
private void prepareEntry(ZipArchiveEntry entry, int unixMode) {
if (this.entryTime != null) {
entry.setTime(this.entryTime);
entry.setTime(DefaultTimeZoneOffset.INSTANCE.removeFrom(this.entryTime));
}
entry.setUnixMode(unixMode);
}