Do not use DOS epoch for timestamps of zip entries in Android rules **Background** I noticed cache output inconsistencies between my local and CI builds. I traced it down to the zip library in Java includes the timezone info within the extra field section of each zip entry. This appears to be a problem starting with Java 8 (See comment from Gradle's ZipCopyAction source code for more info: https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java#L42-L56) **Changes** * Switch to using `February 1, 1980` instead to avoid this edge case **Test Plan** Ensure unit tests pass Closes #10976. PiperOrigin-RevId: 317075345
diff --git a/src/test/java/com/google/devtools/build/android/RClassGeneratorActionTest.java b/src/test/java/com/google/devtools/build/android/RClassGeneratorActionTest.java index 3dbf763..ca87e29 100644 --- a/src/test/java/com/google/devtools/build/android/RClassGeneratorActionTest.java +++ b/src/test/java/com/google/devtools/build/android/RClassGeneratorActionTest.java
@@ -501,26 +501,23 @@ } private static final class ZipMtimeAsserter { - private static final long ZIP_EPOCH = Instant.parse("1980-01-01T00:00:00Z").getEpochSecond(); - private static final long ZIP_EPOCH_PLUS_ONE_DAY = - Instant.parse("1980-01-02T00:00:00Z").getEpochSecond(); + private static final long DEFAULT_TIMESTAMP = + Instant.parse("1980-02-01T00:00:00Z").getEpochSecond(); + private static final long DEFAULT_TIMESTAMP_PLUS_ONE_DAY = + Instant.parse("1980-02-02T00:00:00Z").getEpochSecond(); public static void assertEntry(ZipEntry e) { // getLastModifiedTime().toMillis() returns milliseconds, Instant.getEpochSecond() returns // seconds. long mtime = e.getLastModifiedTime().toMillis() / 1000; - // The ZIP epoch is the same as the MS-DOS epoch, 1980-01-01T00:00:00Z. - // AndroidResourceOutputs.ZipBuilder sets this to most of its entries, except for .class files - // for which the ZipBuilder increments the timestamp by 2 seconds. + // AndroidResourceOutputs.ZipBuilder sets the timestamp to one month after ZIP epoch + // which is the same as the MS-DOS epoch, 1980-01-01T00:00:00Z. + // The one exception being .class files for which the ZipBuilder + // increments the timestamp by 2 seconds. // We don't care about the details of this logic and asserting exact timestamps would couple // the test to the code too tightly, so here we only assert that the timestamp is on - // 1980-01-01, ignoring the exact time. - // AndroidResourceOutputs.ZipBuilde sets the ZIP epoch (same as the MS-DOS epoch, - // 1980-01-01T00:00:00Z) as the timestamp for all of its entries (except .class files, for - // which it sets a timestamp 2 seconds later than the DOS epoch). - // We don't care about the exact timestamps though, only that they are stable, so let's just - // assert that they are all on the day of 1980-01-01. - if (mtime < ZIP_EPOCH || mtime > ZIP_EPOCH_PLUS_ONE_DAY) { + // 1980-02-01, ignoring the exact time. + if (mtime < DEFAULT_TIMESTAMP || mtime > DEFAULT_TIMESTAMP_PLUS_ONE_DAY) { Assert.fail(String.format("e=(%s) mtime=(%s)", e.getName(), e.getLastModifiedTime())); } }
diff --git a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java index f6a82cb..575c82b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java +++ b/src/tools/android/java/com/google/devtools/build/android/AndroidResourceOutputs.java
@@ -58,9 +58,13 @@ // see http://www.info-zip.org/FAQ.html#limits private static final long MINIMUM_TIMESTAMP_INCREMENT = 2000L; - // The earliest date representable in a zip file, 1-1-1980 (the DOS epoch). - private static final long ZIP_EPOCH = - new GregorianCalendar(1980, Calendar.JANUARY, 01, 0, 0).getTimeInMillis(); + /** + * Normalized timestamp for zip entries We use the system's default timezone and locale and + * additionally avoid using the DOS epoch to ensure Java's zip implementation does not add the + * System's timezone into the extra field of the zip entry + */ + private static final long DEFAULT_TIMESTAMP = + new GregorianCalendar(1980, Calendar.FEBRUARY, 01, 0, 0).getTimeInMillis(); private final ZipOutputStream zip; @@ -84,9 +88,9 @@ */ protected long normalizeTime(String filename) { if (filename.endsWith(".class")) { - return ZIP_EPOCH + MINIMUM_TIMESTAMP_INCREMENT; + return DEFAULT_TIMESTAMP + MINIMUM_TIMESTAMP_INCREMENT; } else { - return ZIP_EPOCH; + return DEFAULT_TIMESTAMP; } }