Normalize timestamps to DOS epoch+epsilon to avoid local timezone leaking into ZIPs.

References:
* http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8246129
* https://github.com/bazelbuild/bazel/pull/10976
PiperOrigin-RevId: 313848837
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java b/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
index 360d900..fb27cb1 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
@@ -129,7 +129,7 @@
     SplitZip result = instance.useDefaultEntryDate();
     assertThat(result).isSameInstanceAs(instance);
     Date date = instance.getEntryDate();
-    assertThat(date).isEqualTo(DosTime.DOS_EPOCH);
+    assertThat(date).isEqualTo(DosTime.DOS_EPOCHISH);
   }
 
   @Test
@@ -192,15 +192,15 @@
           .create("input.zip");
 
       new ZipFileBuilder()
-          .add(new ZipFileBuilder.FileInfo("pkg/test.txt", DosTime.EPOCH.time, "hello world"))
+          .add(new ZipFileBuilder.FileInfo("pkg/test.txt", DosTime.EPOCHISH.time, "hello world"))
           .create("expect.zip");
       byte[] expectBytes = fileSystem.toByteArray("expect.zip");
 
       new SplitZip()
-          .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false),
-              "out/shard1.jar"))
+          .addOutput(
+              new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), "out/shard1.jar"))
           .setVerbose(true)
-          .setEntryDate(DosTime.DOS_EPOCH)
+          .setEntryDate(DosTime.DOS_EPOCHISH)
           .addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip"))
           .run()
           .close();
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java b/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
index 441eb71..db704cd 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
@@ -100,16 +100,22 @@
     static final short DEFLATED = 8;
 
     public FileInfo(String filename, String content) {
-      this(filename, DosTime.EPOCH.time, STORED, 0,
-          (content == null ? EMPTY : content.getBytes(CHARSET)), null, null);
+      this(
+          filename,
+          DosTime.EPOCHISH.time,
+          STORED,
+          0,
+          (content == null ? EMPTY : content.getBytes(CHARSET)),
+          null,
+          null);
     }
 
     public FileInfo(String filename, byte[] data) {
-      this(filename, DosTime.EPOCH.time, STORED, 0, data, null, null);
+      this(filename, DosTime.EPOCHISH.time, STORED, 0, data, null, null);
     }
 
     public FileInfo(String filename, byte[] data, int uncompressed) {
-      this(filename, DosTime.EPOCH.time, DEFLATED, uncompressed, data, null, null);
+      this(filename, DosTime.EPOCHISH.time, DEFLATED, uncompressed, data, null, null);
     }
 
     public FileInfo(String filename, int dosTime, String content) {
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java b/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
index 33caeb3..c4e8b9b 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
@@ -15,13 +15,12 @@
 
 import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENTIM;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
 import java.io.IOException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Unit tests for {@link ZipOut}.
@@ -37,11 +36,13 @@
       String filename = "out.zip";
       ZipOut instance = new ZipOut(fileSystem.getOutputChannel(filename, false), filename);
 
-      instance.nextEntry(DirectoryEntry.allocate("pgk/a.class", null, null))
-          .set(CENTIM, DosTime.EPOCH.time);
+      instance
+          .nextEntry(DirectoryEntry.allocate("pgk/a.class", null, null))
+          .set(CENTIM, DosTime.EPOCHISH.time);
 
-      instance.nextEntry(DirectoryEntry.allocate("pgk/b.class", null, null))
-          .set(CENTIM, DosTime.EPOCH.time);
+      instance
+          .nextEntry(DirectoryEntry.allocate("pgk/b.class", null, null))
+          .set(CENTIM, DosTime.EPOCHISH.time);
 
       instance.close();
     } catch (IOException ex) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
index ea80ee4..91209a7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
@@ -459,8 +459,8 @@
               String comment = dirEntry.getComment();
               byte[] extra = dirEntry.getExtraData();
               zipOut.nextEntry(
-                  dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCH.time));
-              zipOut.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCH.time));
+                  dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCHISH.time));
+              zipOut.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCHISH.time));
               zipOut.write(data);
               if ((header.get(LOCFLG) & LocalFileHeader.SIZE_MASKED_FLAG) != 0) {
                 DataDescriptor desc =
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
index db24451..e97e1a5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
@@ -12,6 +12,7 @@
     ],
     deps = [
         "//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
+        "//src/tools/android/java/com/google/devtools/build/android/ziputils:ziputils_lib",
         "//third_party:asm",
         "//third_party:asm-commons",
         "//third_party:asm-tree",
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
index 45a7749..2d3b1b3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
@@ -14,6 +14,7 @@
 package com.google.devtools.build.android.desugar.io;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.devtools.build.android.ziputils.DosTime.EPOCHISH;
 
 import com.google.common.io.ByteStreams;
 import java.io.BufferedOutputStream;
@@ -83,7 +84,7 @@
     checksum.update(content);
 
     ZipEntry result = new ZipEntry(filename);
-    result.setTime(0L); // Use stable timestamp Jan 1 1980
+    result.setTime(EPOCHISH.time); // Use stable timestamp Jan 1 1980
     result.setCrc(checksum.getValue());
     result.setSize(content.length);
     result.setCompressedSize(content.length);
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD b/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
index f22792e..d47af74 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
@@ -5,7 +5,7 @@
 package(
     default_visibility = [
         "//src/test/java/com/google/devtools/build/android/ziputils:__pkg__",
-        "//src/tools/android/java/com/google/devtools/build/android:__pkg__",
+        "//src/tools/android/java/com/google/devtools/build/android:__subpackages__",
     ],
 )
 
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
index 91f1a56..e4feead 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
@@ -100,8 +100,8 @@
     String filename = BASENAME + (count == 1 ? "" : Integer.toString(count)) + SUFFIX;
     String comment = dirEntry.getComment();
     byte[] extra = dirEntry.getExtraData();
-    out.nextEntry(dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCH.time));
-    out.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCH.time));
+    out.nextEntry(dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCHISH.time));
+    out.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCHISH.time));
     out.write(data);
     if ((header.get(LOCFLG) & LocalFileHeader.SIZE_MASKED_FLAG) != 0) {
       DataDescriptor desc = DataDescriptor.allocate()
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
index e2e9dc6..c10e7a5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
@@ -23,10 +23,17 @@
  */
 public final class DosTime {
 
-  /** DOS representation of DOS epoch (midnight, jan 1, 1980) */
-  public static final DosTime EPOCH;
+  /**
+   * DOS representation of DOS epoch (1980-01-01) + epsilon.
+   *
+   * <p>NB: we avoid exact midnight since
+   * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8246129 causes the local timezone to be
+   * leaked into the ZIP file.
+   */
+  public static final DosTime EPOCHISH;
   /** {@code java.util.Date} for DOS epoch */
-  public static final Date DOS_EPOCH;
+  public static final Date DOS_EPOCHISH;
+
   private static final Calendar calendar;
 
   /**
@@ -44,9 +51,9 @@
   }
 
   static {
-    calendar = new GregorianCalendar(1980, 0, 1, 0, 0, 0);
-    DOS_EPOCH = calendar.getTime();
-    EPOCH = new DosTime(DOS_EPOCH);
+    calendar = new GregorianCalendar(1980, 0, 1, /*hrs=*/ 0, /*min=*/ 1);
+    DOS_EPOCHISH = calendar.getTime();
+    EPOCHISH = new DosTime(DOS_EPOCHISH);
   }
 
   private static synchronized int dateToDosTime(Date date) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
index a19cb58..0528175 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
@@ -174,12 +174,13 @@
   }
 
   /**
-   * Sets date to {@link DosTime#DOS_EPOCH}.
+   * Sets date to {@link DosTime#DOS_EPOCHISH}.
+   *
    * @return this object.
    */
   public SplitZip useDefaultEntryDate() {
-    this.date = DosTime.DOS_EPOCH;
-    this.dosTime = DosTime.EPOCH;
+    this.date = DosTime.DOS_EPOCHISH;
+    this.dosTime = DosTime.EPOCHISH;
     return this;
   }