Fix JdkObsolete issues with Date (part 2)

This is a step towards enabling the error level for the JdkObsolete
error prone check.

Use Instant instead of Date/Timestamp.

For usages of Date/Timestamp in the extension API suppress the warnings,
since fixing this would break the API. Later we may consider breaking
the Java extension API to get this fixed and use Gson adapters to
maintain compatibility in the REST API.

Since there are many usages of Date, and fixing all issues requires
touching a lot of code, fixing the JdkObsolete issues with Date is done
in steps over multiple changes.

In this step we change the type for:

* PatchsetApproval#granted
* ChangeNotesParser#createdOn, ChangeNotesParser#lastUpdated and
  ChangeNotesParser#mergedOn
* the date column in ReviewerSet and ReviewerByEmailSet
* the date in ReviewerStatusUpdate and AssigneeStatusUpdate

This includes modifying serializeable classes but this is fine since
dates/timestamps are serialized as epoch millis and we can simply
convert Instants to epoch millis too, and vice versa.

Bug: Issue 15070
Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: I9be945f297254bb6f80a3247c9b902682b74200e
diff --git a/java/com/google/gerrit/entities/PatchSetApproval.java b/java/com/google/gerrit/entities/PatchSetApproval.java
index 607f5c8..608cf0d 100644
--- a/java/com/google/gerrit/entities/PatchSetApproval.java
+++ b/java/com/google/gerrit/entities/PatchSetApproval.java
@@ -16,8 +16,7 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.primitives.Shorts;
-import java.sql.Timestamp;
-import java.util.Date;
+import java.time.Instant;
 import java.util.Optional;
 
 /** An approval (or negative approval) on a patch set. */
@@ -97,11 +96,7 @@
       return value(Shorts.checkedCast(value));
     }
 
-    public abstract Builder granted(Timestamp granted);
-
-    public Builder granted(Date granted) {
-      return granted(new Timestamp(granted.getTime()));
-    }
+    public abstract Builder granted(Instant granted);
 
     public abstract Builder tag(String tag);
 
@@ -147,7 +142,7 @@
    */
   public abstract short value();
 
-  public abstract Timestamp granted();
+  public abstract Instant granted();
 
   public abstract Optional<String> tag();
 
diff --git a/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java b/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java
index 134e25f..e8ef346 100644
--- a/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java
+++ b/java/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverter.java
@@ -19,7 +19,7 @@
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.proto.Entities;
 import com.google.protobuf.Parser;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Objects;
 
 @Immutable
@@ -38,7 +38,7 @@
         Entities.PatchSetApproval.newBuilder()
             .setKey(patchSetApprovalKeyProtoConverter.toProto(patchSetApproval.key()))
             .setValue(patchSetApproval.value())
-            .setGranted(patchSetApproval.granted().getTime())
+            .setGranted(patchSetApproval.granted().toEpochMilli())
             .setPostSubmit(patchSetApproval.postSubmit())
             .setCopied(patchSetApproval.copied());
 
@@ -62,7 +62,7 @@
         PatchSetApproval.builder()
             .key(patchSetApprovalKeyProtoConverter.fromProto(proto.getKey()))
             .value(proto.getValue())
-            .granted(new Timestamp(proto.getGranted()))
+            .granted(Instant.ofEpochMilli(proto.getGranted()))
             .postSubmit(proto.getPostSubmit())
             .copied(proto.getCopied());
     if (proto.hasUuid()) {
diff --git a/java/com/google/gerrit/extensions/common/ApprovalInfo.java b/java/com/google/gerrit/extensions/common/ApprovalInfo.java
index a4e0baa..3ca3c00 100644
--- a/java/com/google/gerrit/extensions/common/ApprovalInfo.java
+++ b/java/com/google/gerrit/extensions/common/ApprovalInfo.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.common.Nullable;
 import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Objects;
 
 /**
@@ -62,10 +63,10 @@
 
   public ApprovalInfo(
       Integer id,
-      Integer value,
+      @Nullable Integer value,
       @Nullable VotingRangeInfo permittedVotingRange,
       @Nullable String tag,
-      Timestamp date) {
+      @Nullable Timestamp date) {
     super(id);
     this.value = value;
     this.permittedVotingRange = permittedVotingRange;
@@ -73,6 +74,26 @@
     this.tag = tag;
   }
 
+  public ApprovalInfo(
+      Integer id,
+      @Nullable Integer value,
+      @Nullable VotingRangeInfo permittedVotingRange,
+      @Nullable String tag,
+      @Nullable Instant date) {
+    super(id);
+    this.value = value;
+    this.permittedVotingRange = permittedVotingRange;
+    this.tag = tag;
+    if (date != null) {
+      setDate(date);
+    }
+  }
+
+  @SuppressWarnings("JdkObsolete")
+  public void setDate(Instant date) {
+    this.date = Timestamp.from(date);
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o instanceof ApprovalInfo) {
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 55e4670..165ad13 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.client.SubmitType;
 import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -126,4 +127,10 @@
   public ChangeInfo(Map<String, RevisionInfo> revisions) {
     this.revisions = ImmutableMap.copyOf(revisions);
   }
+
+  @SuppressWarnings("JdkObsolete")
+  public void setSubmitted(Instant when, AccountInfo who) {
+    submitted = Timestamp.from(when);
+    submitter = who;
+  }
 }
diff --git a/java/com/google/gerrit/extensions/common/ReviewerUpdateInfo.java b/java/com/google/gerrit/extensions/common/ReviewerUpdateInfo.java
index 37e1ceb..6c4eb2c 100644
--- a/java/com/google/gerrit/extensions/common/ReviewerUpdateInfo.java
+++ b/java/com/google/gerrit/extensions/common/ReviewerUpdateInfo.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.client.ReviewerState;
 import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Objects;
 
 public class ReviewerUpdateInfo {
@@ -24,6 +25,17 @@
   public AccountInfo reviewer;
   public ReviewerState state;
 
+  public ReviewerUpdateInfo() {}
+
+  @SuppressWarnings("JdkObsolete")
+  public ReviewerUpdateInfo(
+      Instant updated, AccountInfo updatedBy, AccountInfo reviewer, ReviewerState state) {
+    this.updated = Timestamp.from(updated);
+    this.updatedBy = updatedBy;
+    this.reviewer = reviewer;
+    this.state = state;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o instanceof ReviewerUpdateInfo) {
diff --git a/java/com/google/gerrit/server/AssigneeStatusUpdate.java b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
index 3d6242b..812aad1 100644
--- a/java/com/google/gerrit/server/AssigneeStatusUpdate.java
+++ b/java/com/google/gerrit/server/AssigneeStatusUpdate.java
@@ -16,18 +16,18 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.gerrit.entities.Account;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Optional;
 
 /** Change to an assignee's status. */
 @AutoValue
 public abstract class AssigneeStatusUpdate {
   public static AssigneeStatusUpdate create(
-      Timestamp ts, Account.Id updatedBy, Optional<Account.Id> currentAssignee) {
+      Instant ts, Account.Id updatedBy, Optional<Account.Id> currentAssignee) {
     return new AutoValue_AssigneeStatusUpdate(ts, updatedBy, currentAssignee);
   }
 
-  public abstract Timestamp date();
+  public abstract Instant date();
 
   public abstract Account.Id updatedBy();
 
diff --git a/java/com/google/gerrit/server/ReviewerByEmailSet.java b/java/com/google/gerrit/server/ReviewerByEmailSet.java
index 4a317c3..c6ba7b5 100644
--- a/java/com/google/gerrit/server/ReviewerByEmailSet.java
+++ b/java/com/google/gerrit/server/ReviewerByEmailSet.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.Table;
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import java.sql.Timestamp;
+import java.time.Instant;
 
 /**
  * Set of reviewers on a change that do not have a Gerrit account and were added by email instead.
@@ -30,8 +30,7 @@
 public class ReviewerByEmailSet {
   private static final ReviewerByEmailSet EMPTY = new ReviewerByEmailSet(ImmutableTable.of());
 
-  public static ReviewerByEmailSet fromTable(
-      Table<ReviewerStateInternal, Address, Timestamp> table) {
+  public static ReviewerByEmailSet fromTable(Table<ReviewerStateInternal, Address, Instant> table) {
     return new ReviewerByEmailSet(table);
   }
 
@@ -39,10 +38,10 @@
     return EMPTY;
   }
 
-  private final ImmutableTable<ReviewerStateInternal, Address, Timestamp> table;
+  private final ImmutableTable<ReviewerStateInternal, Address, Instant> table;
   private ImmutableSet<Address> users;
 
-  private ReviewerByEmailSet(Table<ReviewerStateInternal, Address, Timestamp> table) {
+  private ReviewerByEmailSet(Table<ReviewerStateInternal, Address, Instant> table) {
     this.table = ImmutableTable.copyOf(table);
   }
 
@@ -58,7 +57,7 @@
     return table.row(state).keySet();
   }
 
-  public ImmutableTable<ReviewerStateInternal, Address, Timestamp> asTable() {
+  public ImmutableTable<ReviewerStateInternal, Address, Instant> asTable() {
     return table;
   }
 
diff --git a/java/com/google/gerrit/server/ReviewerSet.java b/java/com/google/gerrit/server/ReviewerSet.java
index 0f6bf29..0ff68e0 100644
--- a/java/com/google/gerrit/server/ReviewerSet.java
+++ b/java/com/google/gerrit/server/ReviewerSet.java
@@ -25,7 +25,7 @@
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import java.sql.Timestamp;
+import java.time.Instant;
 
 /**
  * Set of reviewers on a change.
@@ -38,7 +38,7 @@
 
   public static ReviewerSet fromApprovals(Iterable<PatchSetApproval> approvals) {
     PatchSetApproval first = null;
-    Table<ReviewerStateInternal, Account.Id, Timestamp> reviewers = HashBasedTable.create();
+    Table<ReviewerStateInternal, Account.Id, Instant> reviewers = HashBasedTable.create();
     for (PatchSetApproval psa : approvals) {
       if (first == null) {
         first = psa;
@@ -58,7 +58,7 @@
     return new ReviewerSet(reviewers);
   }
 
-  public static ReviewerSet fromTable(Table<ReviewerStateInternal, Account.Id, Timestamp> table) {
+  public static ReviewerSet fromTable(Table<ReviewerStateInternal, Account.Id, Instant> table) {
     return new ReviewerSet(table);
   }
 
@@ -66,10 +66,10 @@
     return EMPTY;
   }
 
-  private final ImmutableTable<ReviewerStateInternal, Account.Id, Timestamp> table;
+  private final ImmutableTable<ReviewerStateInternal, Account.Id, Instant> table;
   private ImmutableSet<Account.Id> accounts;
 
-  private ReviewerSet(Table<ReviewerStateInternal, Account.Id, Timestamp> table) {
+  private ReviewerSet(Table<ReviewerStateInternal, Account.Id, Instant> table) {
     this.table = ImmutableTable.copyOf(table);
   }
 
@@ -85,7 +85,7 @@
     return table.row(state).keySet();
   }
 
-  public ImmutableTable<ReviewerStateInternal, Account.Id, Timestamp> asTable() {
+  public ImmutableTable<ReviewerStateInternal, Account.Id, Instant> asTable() {
     return table;
   }
 
diff --git a/java/com/google/gerrit/server/ReviewerStatusUpdate.java b/java/com/google/gerrit/server/ReviewerStatusUpdate.java
index 938d985..1e0aa43 100644
--- a/java/com/google/gerrit/server/ReviewerStatusUpdate.java
+++ b/java/com/google/gerrit/server/ReviewerStatusUpdate.java
@@ -17,17 +17,17 @@
 import com.google.auto.value.AutoValue;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import java.sql.Timestamp;
+import java.time.Instant;
 
 /** Change to a reviewer's status. */
 @AutoValue
 public abstract class ReviewerStatusUpdate {
   public static ReviewerStatusUpdate create(
-      Timestamp ts, Account.Id updatedBy, Account.Id reviewer, ReviewerStateInternal state) {
+      Instant ts, Account.Id updatedBy, Account.Id reviewer, ReviewerStateInternal state) {
     return new AutoValue_ReviewerStatusUpdate(ts, updatedBy, reviewer, state);
   }
 
-  public abstract Timestamp date();
+  public abstract Instant date();
 
   public abstract Account.Id updatedBy();
 
diff --git a/java/com/google/gerrit/server/approval/ApprovalsUtil.java b/java/com/google/gerrit/server/approval/ApprovalsUtil.java
index c3921f8..0c32072 100644
--- a/java/com/google/gerrit/server/approval/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/approval/ApprovalsUtil.java
@@ -85,7 +85,7 @@
         PatchSetApproval.builder()
             .key(PatchSetApproval.key(psId, user.getAccountId(), labelId))
             .value(value)
-            .granted(when);
+            .granted(when.toInstant());
     user.updateRealAccountId(b::realAccountId);
     return b;
   }
@@ -208,7 +208,7 @@
           PatchSetApproval.builder()
               .key(PatchSetApproval.key(psId, account, labelId))
               .value(0)
-              .granted(update.getWhen())
+              .granted(update.getWhen().toInstant())
               .build());
       update.putReviewer(account, REVIEWER);
     }
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 41723c8..dd83eb6 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -775,11 +775,12 @@
     List<ReviewerStatusUpdate> reviewerUpdates = cd.reviewerUpdates();
     List<ReviewerUpdateInfo> result = new ArrayList<>(reviewerUpdates.size());
     for (ReviewerStatusUpdate c : reviewerUpdates) {
-      ReviewerUpdateInfo change = new ReviewerUpdateInfo();
-      change.updated = c.date();
-      change.state = c.state().asReviewerState();
-      change.updatedBy = accountLoader.get(c.updatedBy());
-      change.reviewer = accountLoader.get(c.reviewer());
+      ReviewerUpdateInfo change =
+          new ReviewerUpdateInfo(
+              c.date(),
+              accountLoader.get(c.updatedBy()),
+              accountLoader.get(c.reviewer()),
+              c.state().asReviewerState());
       result.add(change);
     }
     return result;
@@ -801,8 +802,7 @@
     if (!s.isPresent()) {
       return;
     }
-    out.submitted = s.get().granted();
-    out.submitter = accountLoader.get(s.get().accountId());
+    out.setSubmitted(s.get().granted(), accountLoader.get(s.get().accountId()));
   }
 
   private ImmutableList<ChangeMessageInfo> messages(ChangeData cd) {
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index 0bc0f64..325b20a 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -48,7 +48,7 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -199,10 +199,10 @@
   private ApprovalInfo approvalInfo(
       AccountLoader accountLoader,
       Account.Id id,
-      Integer value,
-      VotingRangeInfo permittedVotingRange,
-      String tag,
-      Timestamp date) {
+      @Nullable Integer value,
+      @Nullable VotingRangeInfo permittedVotingRange,
+      @Nullable String tag,
+      @Nullable Instant date) {
     ApprovalInfo ai = new ApprovalInfo(id.get(), value, permittedVotingRange, tag, date);
     accountLoader.put(ai);
     return ai;
@@ -309,7 +309,7 @@
         if (info != null) {
           info.value = Integer.valueOf(val);
           info.permittedVotingRange = pvr.getOrDefault(type.get().getName(), null);
-          info.date = psa.granted();
+          info.setDate(psa.granted());
           info.tag = psa.tag().orElse(null);
           if (psa.postSubmit()) {
             info.postSubmit = true;
@@ -442,7 +442,7 @@
         Integer value;
         VotingRangeInfo permittedVotingRange = pvr.getOrDefault(lt.get().getName(), null);
         String tag = null;
-        Timestamp date = null;
+        Instant date = null;
         PatchSetApproval psa = current.get(accountId, lt.get().getName());
         if (psa != null) {
           value = Integer.valueOf(psa.value());
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 2a2bf73..7727e5a 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -534,7 +534,7 @@
     a.type = approval.labelId().get();
     a.value = Short.toString(approval.value());
     a.by = asAccountAttribute(approval.accountId());
-    a.grantedOn = approval.granted().getTime() / 1000L;
+    a.grantedOn = approval.granted().getEpochSecond();
     a.oldValue = null;
 
     Optional<LabelType> lt = labelTypes.byLabel(approval.labelId());
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 6c8e3fb..bc5a889 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -435,11 +435,10 @@
   @VisibleForTesting
   static List<String> getReviewerFieldValues(ReviewerSet reviewers) {
     List<String> r = new ArrayList<>(reviewers.asTable().size() * 2);
-    for (Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> c :
-        reviewers.asTable().cellSet()) {
+    for (Table.Cell<ReviewerStateInternal, Account.Id, Instant> c : reviewers.asTable().cellSet()) {
       String v = getReviewerFieldValue(c.getRowKey(), c.getColumnKey());
       r.add(v);
-      r.add(v + ',' + c.getValue().getTime());
+      r.add(v + ',' + c.getValue().toEpochMilli());
     }
     return r;
   }
@@ -451,7 +450,7 @@
   @VisibleForTesting
   static List<String> getReviewerByEmailFieldValues(ReviewerByEmailSet reviewersByEmail) {
     List<String> r = new ArrayList<>(reviewersByEmail.asTable().size() * 2);
-    for (Table.Cell<ReviewerStateInternal, Address, Timestamp> c :
+    for (Table.Cell<ReviewerStateInternal, Address, Instant> c :
         reviewersByEmail.asTable().cellSet()) {
       String v = getReviewerByEmailFieldValue(c.getRowKey(), c.getColumnKey());
       r.add(v);
@@ -460,7 +459,7 @@
         Address emailOnly = Address.create(c.getColumnKey().email());
         r.add(getReviewerByEmailFieldValue(c.getRowKey(), emailOnly));
       }
-      r.add(v + ',' + c.getValue().getTime());
+      r.add(v + ',' + c.getValue().toEpochMilli());
     }
     return r;
   }
@@ -470,8 +469,7 @@
   }
 
   public static ReviewerSet parseReviewerFieldValues(Change.Id changeId, Iterable<String> values) {
-    ImmutableTable.Builder<ReviewerStateInternal, Account.Id, Timestamp> b =
-        ImmutableTable.builder();
+    ImmutableTable.Builder<ReviewerStateInternal, Account.Id, Instant> b = ImmutableTable.builder();
     for (String v : values) {
 
       int i = v.indexOf(',');
@@ -513,7 +511,7 @@
             "Failed to parse timestamp of reviewer field from change %s: %s", changeId.get(), v);
         continue;
       }
-      Timestamp timestamp = new Timestamp(l);
+      Instant timestamp = Instant.ofEpochMilli(l);
 
       b.put(reviewerState.get(), accountId.get(), timestamp);
     }
@@ -522,7 +520,7 @@
 
   public static ReviewerByEmailSet parseReviewerByEmailFieldValues(
       Change.Id changeId, Iterable<String> values) {
-    ImmutableTable.Builder<ReviewerStateInternal, Address, Timestamp> b = ImmutableTable.builder();
+    ImmutableTable.Builder<ReviewerStateInternal, Address, Instant> b = ImmutableTable.builder();
     for (String v : values) {
       int i = v.indexOf(',');
       if (i < 0) {
@@ -566,7 +564,7 @@
             changeId.get(), v);
         continue;
       }
-      Timestamp timestamp = new Timestamp(l);
+      Instant timestamp = Instant.ofEpochMilli(l);
 
       b.put(reviewerState.get(), address, timestamp);
     }
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index ec98f6ea..063566e 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -125,8 +125,8 @@
 
   // Private final but mutable members initialized in the constructor and filled
   // in during the parsing process.
-  private final Table<Account.Id, ReviewerStateInternal, Timestamp> reviewers;
-  private final Table<Address, ReviewerStateInternal, Timestamp> reviewersByEmail;
+  private final Table<Account.Id, ReviewerStateInternal, Instant> reviewers;
+  private final Table<Address, ReviewerStateInternal, Instant> reviewersByEmail;
   private final List<Account.Id> allPastReviewers;
   private final List<ReviewerStatusUpdate> reviewerUpdates;
   /** Holds only the most recent update per user. Older updates are discarded. */
@@ -151,8 +151,8 @@
   private Change.Status status;
   private String topic;
   private Set<String> hashtags;
-  private Timestamp createdOn;
-  private Timestamp lastUpdatedOn;
+  private Instant createdOn;
+  private Instant lastUpdatedOn;
   private Account.Id ownerId;
   private String serverId;
   private String changeId;
@@ -173,7 +173,7 @@
   // We only set the value once, based on the latest update (the actual value or Optional.empty() if
   // the latest record unsets the field).
   private Optional<PatchSet.Id> cherryPickOf;
-  private Timestamp mergedOn;
+  private Instant mergedOn;
 
   ChangeNotesParser(
       Change.Id changeId,
@@ -413,7 +413,7 @@
   }
 
   private void parse(ChangeNotesCommit commit) throws ConfigInvalidException {
-    Timestamp commitTimestamp = getCommitTimestamp(commit);
+    Instant commitTimestamp = getCommitTimestamp(commit);
 
     createdOn = commitTimestamp;
     parseTag(commit);
@@ -467,7 +467,7 @@
 
     parseSubmission(commit, commitTimestamp);
 
-    if (lastUpdatedOn == null || commitTimestamp.after(lastUpdatedOn)) {
+    if (lastUpdatedOn == null || commitTimestamp.isAfter(lastUpdatedOn)) {
       lastUpdatedOn = commitTimestamp;
     }
 
@@ -529,7 +529,7 @@
     }
   }
 
-  private void parseSubmission(ChangeNotesCommit commit, Timestamp commitTimestamp)
+  private void parseSubmission(ChangeNotesCommit commit, Instant commitTimestamp)
       throws ConfigInvalidException {
     // Only parse the most recent sumbit commit (there should be exactly one).
     if (submissionId == null) {
@@ -612,7 +612,7 @@
     }
   }
 
-  private void parsePatchSet(PatchSet.Id psId, ObjectId rev, Account.Id accountId, Timestamp ts)
+  private void parsePatchSet(PatchSet.Id psId, ObjectId rev, Account.Id accountId, Instant ts)
       throws ConfigInvalidException {
     if (accountId == null) {
       throw parseException("patch set %s requires an identified user as uploader", psId.get());
@@ -629,7 +629,7 @@
         .id(psId)
         .commitId(rev)
         .uploader(accountId)
-        .createdOn(ts);
+        .createdOn(Timestamp.from(ts));
     // Fields not set here:
     // * Groups, parsed earlier in parseGroups.
     // * Description, parsed earlier in parseDescription.
@@ -707,7 +707,7 @@
     }
   }
 
-  private void parseAssigneeUpdates(Timestamp ts, ChangeNotesCommit commit)
+  private void parseAssigneeUpdates(Instant ts, ChangeNotesCommit commit)
       throws ConfigInvalidException {
     String assigneeValue = parseOneFooter(commit, FOOTER_ASSIGNEE);
     if (assigneeValue != null) {
@@ -817,7 +817,7 @@
       Account.Id accountId,
       Account.Id realAccountId,
       ChangeNotesCommit commit,
-      Timestamp ts) {
+      Instant ts) {
     Optional<String> changeMsgString = getChangeMessageString(commit);
     if (!changeMsgString.isPresent()) {
       return false;
@@ -827,7 +827,7 @@
         ChangeMessage.create(
             ChangeMessage.key(psId.changeId(), commit.name()),
             accountId,
-            ts,
+            Timestamp.from(ts),
             psId,
             changeMsgString.get(),
             realAccountId,
@@ -882,7 +882,7 @@
   }
 
   /** Parses copied {@link PatchSetApproval}. */
-  private void parseCopiedApproval(PatchSet.Id psId, Timestamp ts, String line)
+  private void parseCopiedApproval(PatchSet.Id psId, Instant ts, String line)
       throws ConfigInvalidException {
     ParsedPatchSetApproval parsedPatchSetApproval = ChangeNoteUtil.parseCopiedApproval(line);
     checkFooter(
@@ -930,7 +930,7 @@
   }
 
   private void parseApproval(
-      PatchSet.Id psId, Account.Id accountId, Account.Id realAccountId, Timestamp ts, String line)
+      PatchSet.Id psId, Account.Id accountId, Account.Id realAccountId, Instant ts, String line)
       throws ConfigInvalidException {
     if (accountId == null) {
       throw parseException("patch set %s requires an identified user as uploader", psId.get());
@@ -950,7 +950,7 @@
       PatchSet.Id psId,
       Account.Id committerId,
       Account.Id realAccountId,
-      Timestamp ts,
+      Instant ts,
       ParsedPatchSetApproval parsedPatchSetApproval)
       throws ConfigInvalidException {
 
@@ -984,7 +984,7 @@
       PatchSet.Id psId,
       Account.Id committerId,
       Account.Id realAccountId,
-      Timestamp ts,
+      Instant ts,
       ParsedPatchSetApproval parsedPatchSetApproval)
       throws ConfigInvalidException {
 
@@ -1098,7 +1098,7 @@
     return parseIdent(a);
   }
 
-  private void parseReviewer(Timestamp ts, ReviewerStateInternal state, String line)
+  private void parseReviewer(Instant ts, ReviewerStateInternal state, String line)
       throws ConfigInvalidException {
     PersonIdent ident = RawParseUtils.parsePersonIdent(line);
     if (ident == null) {
@@ -1111,7 +1111,7 @@
     }
   }
 
-  private void parseReviewerByEmail(Timestamp ts, ReviewerStateInternal state, String line)
+  private void parseReviewerByEmail(Instant ts, ReviewerStateInternal state, String line)
       throws ConfigInvalidException {
     Address adr;
     try {
@@ -1225,15 +1225,15 @@
    * @param commit the commit to return commit time.
    * @return the timestamp when the commit was applied.
    */
-  private Timestamp getCommitTimestamp(ChangeNotesCommit commit) {
-    return new Timestamp(commit.getCommitterIdent().getWhen().getTime());
+  private Instant getCommitTimestamp(ChangeNotesCommit commit) {
+    return commit.getCommitterIdent().getWhen().toInstant();
   }
 
   private void pruneReviewers() {
-    Iterator<Table.Cell<Account.Id, ReviewerStateInternal, Timestamp>> rit =
+    Iterator<Table.Cell<Account.Id, ReviewerStateInternal, Instant>> rit =
         reviewers.cellSet().iterator();
     while (rit.hasNext()) {
-      Table.Cell<Account.Id, ReviewerStateInternal, Timestamp> e = rit.next();
+      Table.Cell<Account.Id, ReviewerStateInternal, Instant> e = rit.next();
       if (e.getColumnKey() == ReviewerStateInternal.REMOVED) {
         rit.remove();
       }
@@ -1241,10 +1241,10 @@
   }
 
   private void pruneReviewersByEmail() {
-    Iterator<Table.Cell<Address, ReviewerStateInternal, Timestamp>> rit =
+    Iterator<Table.Cell<Address, ReviewerStateInternal, Instant>> rit =
         reviewersByEmail.cellSet().iterator();
     while (rit.hasNext()) {
-      Table.Cell<Address, ReviewerStateInternal, Timestamp> e = rit.next();
+      Table.Cell<Address, ReviewerStateInternal, Instant> e = rit.next();
       if (e.getColumnKey() == ReviewerStateInternal.REMOVED) {
         rit.remove();
       }
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 4d6b9cf..5e40125 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -103,8 +103,8 @@
       ObjectId metaId,
       Change.Id changeId,
       Change.Key changeKey,
-      Timestamp createdOn,
-      Timestamp lastUpdatedOn,
+      Instant createdOn,
+      Instant lastUpdatedOn,
       Account.Id owner,
       String serverId,
       String branch,
@@ -136,7 +136,7 @@
       @Nullable Change.Id revertOf,
       @Nullable PatchSet.Id cherryPickOf,
       int updateCount,
-      @Nullable Timestamp mergedOn) {
+      @Nullable Instant mergedOn) {
     requireNonNull(
         metaId,
         () ->
@@ -151,8 +151,8 @@
         .columns(
             ChangeColumns.builder()
                 .changeKey(changeKey)
-                .createdOn(createdOn)
-                .lastUpdatedOn(lastUpdatedOn)
+                .createdOn(Timestamp.from(createdOn))
+                .lastUpdatedOn(Timestamp.from(lastUpdatedOn))
                 .owner(owner)
                 .branch(branch)
                 .status(status)
@@ -185,7 +185,7 @@
         .publishedComments(publishedComments)
         .submitRequirementsResult(submitRequirementResults)
         .updateCount(updateCount)
-        .mergedOn(mergedOn)
+        .mergedOn(mergedOn != null ? Timestamp.from(mergedOn) : null)
         .build();
   }
 
@@ -581,26 +581,26 @@
     }
 
     private static ReviewerSetEntryProto toReviewerSetEntry(
-        Table.Cell<ReviewerStateInternal, Account.Id, Timestamp> c) {
+        Table.Cell<ReviewerStateInternal, Account.Id, Instant> c) {
       return ReviewerSetEntryProto.newBuilder()
           .setState(REVIEWER_STATE_CONVERTER.reverse().convert(c.getRowKey()))
           .setAccountId(c.getColumnKey().get())
-          .setTimestampMillis(c.getValue().getTime())
+          .setTimestampMillis(c.getValue().toEpochMilli())
           .build();
     }
 
     private static ReviewerByEmailSetEntryProto toReviewerByEmailSetEntry(
-        Table.Cell<ReviewerStateInternal, Address, Timestamp> c) {
+        Table.Cell<ReviewerStateInternal, Address, Instant> c) {
       return ReviewerByEmailSetEntryProto.newBuilder()
           .setState(REVIEWER_STATE_CONVERTER.reverse().convert(c.getRowKey()))
           .setAddress(c.getColumnKey().toHeaderString())
-          .setTimestampMillis(c.getValue().getTime())
+          .setTimestampMillis(c.getValue().toEpochMilli())
           .build();
     }
 
     private static ReviewerStatusUpdateProto toReviewerStatusUpdateProto(ReviewerStatusUpdate u) {
       return ReviewerStatusUpdateProto.newBuilder()
-          .setTimestampMillis(u.date().getTime())
+          .setTimestampMillis(u.date().toEpochMilli())
           .setUpdatedBy(u.updatedBy().get())
           .setReviewer(u.reviewer().get())
           .setState(REVIEWER_STATE_CONVERTER.reverse().convert(u.state()))
@@ -620,7 +620,7 @@
     private static AssigneeStatusUpdateProto toAssigneeStatusUpdateProto(AssigneeStatusUpdate u) {
       AssigneeStatusUpdateProto.Builder builder =
           AssigneeStatusUpdateProto.newBuilder()
-              .setTimestampMillis(u.date().getTime())
+              .setTimestampMillis(u.date().toEpochMilli())
               .setUpdatedBy(u.updatedBy().get())
               .setHasCurrentAssignee(u.currentAssignee().isPresent());
 
@@ -719,26 +719,25 @@
     }
 
     private static ReviewerSet toReviewerSet(List<ReviewerSetEntryProto> protos) {
-      ImmutableTable.Builder<ReviewerStateInternal, Account.Id, Timestamp> b =
+      ImmutableTable.Builder<ReviewerStateInternal, Account.Id, Instant> b =
           ImmutableTable.builder();
       for (ReviewerSetEntryProto e : protos) {
         b.put(
             REVIEWER_STATE_CONVERTER.convert(e.getState()),
             Account.id(e.getAccountId()),
-            new Timestamp(e.getTimestampMillis()));
+            Instant.ofEpochMilli(e.getTimestampMillis()));
       }
       return ReviewerSet.fromTable(b.build());
     }
 
     private static ReviewerByEmailSet toReviewerByEmailSet(
         List<ReviewerByEmailSetEntryProto> protos) {
-      ImmutableTable.Builder<ReviewerStateInternal, Address, Timestamp> b =
-          ImmutableTable.builder();
+      ImmutableTable.Builder<ReviewerStateInternal, Address, Instant> b = ImmutableTable.builder();
       for (ReviewerByEmailSetEntryProto e : protos) {
         b.put(
             REVIEWER_STATE_CONVERTER.convert(e.getState()),
             Address.parse(e.getAddress()),
-            new Timestamp(e.getTimestampMillis()));
+            Instant.ofEpochMilli(e.getTimestampMillis()));
       }
       return ReviewerByEmailSet.fromTable(b.build());
     }
@@ -749,7 +748,7 @@
       for (ReviewerStatusUpdateProto proto : protos) {
         b.add(
             ReviewerStatusUpdate.create(
-                new Timestamp(proto.getTimestampMillis()),
+                Instant.ofEpochMilli(proto.getTimestampMillis()),
                 Account.id(proto.getUpdatedBy()),
                 Account.id(proto.getReviewer()),
                 REVIEWER_STATE_CONVERTER.convert(proto.getState())));
@@ -791,7 +790,7 @@
       for (AssigneeStatusUpdateProto proto : protos) {
         b.add(
             AssigneeStatusUpdate.create(
-                new Timestamp(proto.getTimestampMillis()),
+                Instant.ofEpochMilli(proto.getTimestampMillis()),
                 Account.id(proto.getUpdatedBy()),
                 proto.getHasCurrentAssignee()
                     ? Optional.of(Account.id(proto.getCurrentAssignee()))
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 0c64402..2e70e90 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -1456,7 +1456,7 @@
           PatchSetApproval.Builder b =
               c.toBuilder()
                   .value(ent.getValue())
-                  .granted(ctx.getWhen())
+                  .granted(ctx.getWhen().toInstant())
                   .tag(Optional.ofNullable(in.tag));
           ctx.getUser().updateRealAccountId(b::realAccountId);
           c = b.build();
@@ -1473,7 +1473,7 @@
           c =
               ApprovalsUtil.newApproval(psId, user, lt.getLabelId(), ent.getValue(), ctx.getWhen())
                   .tag(Optional.ofNullable(in.tag))
-                  .granted(ctx.getWhen())
+                  .granted(ctx.getWhen().toInstant())
                   .build();
           ups.add(c);
           addLabelDelta(normName, c.value());
diff --git a/javatests/com/google/gerrit/acceptance/server/query/ApprovalQueryIT.java b/javatests/com/google/gerrit/acceptance/server/query/ApprovalQueryIT.java
index 97f072e..925c855 100644
--- a/javatests/com/google/gerrit/acceptance/server/query/ApprovalQueryIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/query/ApprovalQueryIT.java
@@ -35,7 +35,7 @@
 import com.google.gerrit.server.query.approval.ApprovalContext;
 import com.google.gerrit.server.query.approval.ApprovalQueryBuilder;
 import com.google.inject.Inject;
-import java.util.Date;
+import java.time.Instant;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.junit.Test;
@@ -261,7 +261,7 @@
     PatchSetApproval approval =
         PatchSetApproval.builder()
             .postSubmit(false)
-            .granted(new Date())
+            .granted(Instant.now())
             .key(PatchSetApproval.key(psId, approver, LabelId.create("Code-Review")))
             .value(value)
             .build();
diff --git a/javatests/com/google/gerrit/entities/LabelFunctionTest.java b/javatests/com/google/gerrit/entities/LabelFunctionTest.java
index 0132697..80d97db 100644
--- a/javatests/com/google/gerrit/entities/LabelFunctionTest.java
+++ b/javatests/com/google/gerrit/entities/LabelFunctionTest.java
@@ -19,7 +19,6 @@
 import com.google.common.collect.ImmutableList;
 import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 import org.junit.Test;
 
@@ -94,7 +93,7 @@
     return PatchSetApproval.builder()
         .key(PatchSetApproval.key(PS_ID, Account.id(10000 + value), LABEL_ID))
         .value(value)
-        .granted(Date.from(Instant.now()))
+        .granted(Instant.now())
         .build();
   }
 
diff --git a/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java
index 0338959..f9e07b4 100644
--- a/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java
+++ b/javatests/com/google/gerrit/entities/converter/PatchSetApprovalProtoConverterTest.java
@@ -28,8 +28,7 @@
 import com.google.gerrit.proto.testing.SerializedClassSubject;
 import com.google.inject.TypeLiteral;
 import java.lang.reflect.Type;
-import java.sql.Timestamp;
-import java.util.Date;
+import java.time.Instant;
 import java.util.Optional;
 import org.junit.Test;
 
@@ -46,7 +45,7 @@
                     PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
             .uuid(Optional.of(PatchSetApproval.uuid("577fb248e474018276351785930358ec0450e9f7")))
             .value(456)
-            .granted(new Date(987654L))
+            .granted(Instant.ofEpochMilli(987654L))
             .tag("tag-21")
             .realAccountId(Account.id(612))
             .postSubmit(true)
@@ -84,7 +83,7 @@
                 PatchSetApproval.key(
                     PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
             .value(456)
-            .granted(new Date(987654L))
+            .granted(Instant.ofEpochMilli(987654L))
             .build();
 
     Entities.PatchSetApproval proto = protoConverter.toProto(patchSetApproval);
@@ -117,7 +116,7 @@
                     PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
             .uuid(Optional.of(PatchSetApproval.uuid("577fb248e474018276351785930358ec0450e9f7")))
             .value(456)
-            .granted(new Date(987654L))
+            .granted(Instant.ofEpochMilli(987654L))
             .tag("tag-21")
             .realAccountId(Account.id(612))
             .postSubmit(true)
@@ -137,7 +136,7 @@
                 PatchSetApproval.key(
                     PatchSet.id(Change.id(42), 14), Account.id(100013), LabelId.create("label-8")))
             .value(456)
-            .granted(new Date(987654L))
+            .granted(Instant.ofEpochMilli(987654L))
             .build();
 
     PatchSetApproval convertedPatchSetApproval =
@@ -167,7 +166,7 @@
     assertThat(patchSetApproval.labelId()).isEqualTo(LabelId.create("label-8"));
     // Default values for unset protobuf fields which can't be unset in the entity object.
     assertThat(patchSetApproval.value()).isEqualTo(0);
-    assertThat(patchSetApproval.granted()).isEqualTo(new Timestamp(0));
+    assertThat(patchSetApproval.granted()).isEqualTo(Instant.ofEpochMilli(0));
     assertThat(patchSetApproval.postSubmit()).isEqualTo(false);
     assertThat(patchSetApproval.copied()).isEqualTo(false);
   }
@@ -181,7 +180,7 @@
                 .put("key", PatchSetApproval.Key.class)
                 .put("uuid", new TypeLiteral<Optional<PatchSetApproval.UUID>>() {}.getType())
                 .put("value", short.class)
-                .put("granted", Timestamp.class)
+                .put("granted", Instant.class)
                 .put("tag", new TypeLiteral<Optional<String>>() {}.getType())
                 .put("realAccountId", Account.Id.class)
                 .put("postSubmit", boolean.class)
diff --git a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
index 8f341aa..38e50b5 100644
--- a/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
+++ b/javatests/com/google/gerrit/server/change/LabelNormalizerTest.java
@@ -213,7 +213,7 @@
     return PatchSetApproval.builder()
         .key(PatchSetApproval.key(change.currentPatchSetId(), accountId, LabelId.create(label)))
         .value(value)
-        .granted(TimeUtil.nowTs())
+        .granted(TimeUtil.now())
         .build();
   }
 
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
index 6ad2060..dc1440b 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeFieldTest.java
@@ -33,7 +33,7 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.TestTimeUtil;
-import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -55,17 +55,22 @@
 
   @Test
   public void reviewerFieldValues() {
-    Table<ReviewerStateInternal, Account.Id, Timestamp> t = HashBasedTable.create();
-    Timestamp t1 = TimeUtil.nowTs();
+    Table<ReviewerStateInternal, Account.Id, Instant> t = HashBasedTable.create();
+
+    // Timestamps are stored as epoch millis in the reviewer field. Epoch millis are less precise
+    // than Instants which have nanosecond precision. Create Instants with millisecond precision
+    // here so that the comparison for the assertions works.
+    Instant t1 = Instant.ofEpochMilli(TimeUtil.nowMs());
+    Instant t2 = Instant.ofEpochMilli(TimeUtil.nowMs());
+
     t.put(ReviewerStateInternal.REVIEWER, Account.id(1), t1);
-    Timestamp t2 = TimeUtil.nowTs();
     t.put(ReviewerStateInternal.CC, Account.id(2), t2);
     ReviewerSet reviewers = ReviewerSet.fromTable(t);
 
     List<String> values = ChangeField.getReviewerFieldValues(reviewers);
     assertThat(values)
         .containsExactly(
-            "REVIEWER,1", "REVIEWER,1," + t1.getTime(), "CC,2", "CC,2," + t2.getTime());
+            "REVIEWER,1", "REVIEWER,1," + t1.toEpochMilli(), "CC,2", "CC,2," + t2.toEpochMilli());
 
     assertThat(ChangeField.parseReviewerFieldValues(Change.id(1), values)).isEqualTo(reviewers);
   }
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index a2a9b7d..0efa843 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -68,6 +68,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.TimeZone;
 import java.util.concurrent.ExecutorService;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -286,11 +287,11 @@
     return c;
   }
 
-  protected static Timestamp truncate(Timestamp ts) {
-    return new Timestamp((ts.getTime() / 1000) * 1000);
+  protected static Instant truncate(Instant ts) {
+    return Instant.ofEpochMilli((ts.toEpochMilli() / 1000) * 1000);
   }
 
-  protected static Timestamp after(Change c, long millis) {
-    return new Timestamp(c.getCreatedOn().getTime() + millis);
+  protected static Instant after(Change c, long millis) {
+    return Instant.ofEpochMilli(c.getCreatedOn().getTime() + millis);
   }
 }
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index d9846ce..60aa5f3 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -376,7 +376,7 @@
                     PatchSet.id(ID, 1), Account.id(2001), LabelId.create(LabelId.CODE_REVIEW)))
             .value(1)
             .tag("tag")
-            .granted(new Timestamp(1212L))
+            .granted(Instant.ofEpochMilli(1212L))
             .build();
     Entities.PatchSetApproval psa1 = PatchSetApprovalProtoConverter.INSTANCE.toProto(a1);
     ByteString a1Bytes = Protos.toByteString(psa1);
@@ -389,7 +389,7 @@
             .value(-1)
             .tag("tag")
             .copied(true)
-            .granted(new Timestamp(3434L))
+            .granted(Instant.ofEpochMilli(3434L))
             .build();
     Entities.PatchSetApproval psa2 = PatchSetApprovalProtoConverter.INSTANCE.toProto(a2);
     ByteString a2Bytes = Protos.toByteString(psa2);
@@ -419,7 +419,7 @@
             .uuid(Optional.of(PatchSetApproval.uuid("577fb248e474018276351785930358ec0450e9f7")))
             .value(1)
             .tag("tag")
-            .granted(new Timestamp(1212L))
+            .granted(Instant.ofEpochMilli(1212L))
             .build();
     Entities.PatchSetApproval psa1 = PatchSetApprovalProtoConverter.INSTANCE.toProto(a1);
     ByteString a1Bytes = Protos.toByteString(psa1);
@@ -433,7 +433,7 @@
             .value(-1)
             .tag("tag")
             .copied(true)
-            .granted(new Timestamp(3434L))
+            .granted(Instant.ofEpochMilli(3434L))
             .build();
     Entities.PatchSetApproval psa2 = PatchSetApprovalProtoConverter.INSTANCE.toProto(a2);
     ByteString a2Bytes = Protos.toByteString(psa2);
@@ -459,9 +459,13 @@
         newBuilder()
             .reviewers(
                 ReviewerSet.fromTable(
-                    ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
-                        .put(ReviewerStateInternal.CC, Account.id(2001), new Timestamp(1212L))
-                        .put(ReviewerStateInternal.REVIEWER, Account.id(2002), new Timestamp(3434L))
+                    ImmutableTable.<ReviewerStateInternal, Account.Id, Instant>builder()
+                        .put(
+                            ReviewerStateInternal.CC, Account.id(2001), Instant.ofEpochMilli(1212L))
+                        .put(
+                            ReviewerStateInternal.REVIEWER,
+                            Account.id(2002),
+                            Instant.ofEpochMilli(3434L))
                         .build()))
             .build(),
         ChangeNotesStateProto.newBuilder()
@@ -487,15 +491,15 @@
         newBuilder()
             .reviewersByEmail(
                 ReviewerByEmailSet.fromTable(
-                    ImmutableTable.<ReviewerStateInternal, Address, Timestamp>builder()
+                    ImmutableTable.<ReviewerStateInternal, Address, Instant>builder()
                         .put(
                             ReviewerStateInternal.CC,
                             Address.create("Name1", "email1@example.com"),
-                            new Timestamp(1212L))
+                            Instant.ofEpochMilli(1212L))
                         .put(
                             ReviewerStateInternal.REVIEWER,
                             Address.create("Name2", "email2@example.com"),
-                            new Timestamp(3434L))
+                            Instant.ofEpochMilli(3434L))
                         .build()))
             .build(),
         ChangeNotesStateProto.newBuilder()
@@ -525,7 +529,7 @@
                         ImmutableTable.of(
                             ReviewerStateInternal.CC,
                             Address.create("emailonly@example.com"),
-                            new Timestamp(1212L))))
+                            Instant.ofEpochMilli(1212L))))
                 .build(),
             ChangeNotesStateProto.newBuilder()
                 .setMetaId(SHA_BYTES)
@@ -553,9 +557,13 @@
         newBuilder()
             .pendingReviewers(
                 ReviewerSet.fromTable(
-                    ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
-                        .put(ReviewerStateInternal.CC, Account.id(2001), new Timestamp(1212L))
-                        .put(ReviewerStateInternal.REVIEWER, Account.id(2002), new Timestamp(3434L))
+                    ImmutableTable.<ReviewerStateInternal, Account.Id, Instant>builder()
+                        .put(
+                            ReviewerStateInternal.CC, Account.id(2001), Instant.ofEpochMilli(1212L))
+                        .put(
+                            ReviewerStateInternal.REVIEWER,
+                            Account.id(2002),
+                            Instant.ofEpochMilli(3434L))
                         .build()))
             .build(),
         ChangeNotesStateProto.newBuilder()
@@ -581,15 +589,15 @@
         newBuilder()
             .pendingReviewersByEmail(
                 ReviewerByEmailSet.fromTable(
-                    ImmutableTable.<ReviewerStateInternal, Address, Timestamp>builder()
+                    ImmutableTable.<ReviewerStateInternal, Address, Instant>builder()
                         .put(
                             ReviewerStateInternal.CC,
                             Address.create("Name1", "email1@example.com"),
-                            new Timestamp(1212L))
+                            Instant.ofEpochMilli(1212L))
                         .put(
                             ReviewerStateInternal.REVIEWER,
                             Address.create("Name2", "email2@example.com"),
-                            new Timestamp(3434L))
+                            Instant.ofEpochMilli(3434L))
                         .build()))
             .build(),
         ChangeNotesStateProto.newBuilder()
@@ -629,12 +637,12 @@
             .reviewerUpdates(
                 ImmutableList.of(
                     ReviewerStatusUpdate.create(
-                        new Timestamp(1212L),
+                        Instant.ofEpochMilli(1212L),
                         Account.id(1000),
                         Account.id(2002),
                         ReviewerStateInternal.CC),
                     ReviewerStatusUpdate.create(
-                        new Timestamp(3434L),
+                        Instant.ofEpochMilli(3434L),
                         Account.id(1000),
                         Account.id(2001),
                         ReviewerStateInternal.REVIEWER)))
@@ -796,9 +804,11 @@
             .assigneeUpdates(
                 ImmutableList.of(
                     AssigneeStatusUpdate.create(
-                        new Timestamp(1212L), Account.id(1000), Optional.of(Account.id(2001))),
+                        Instant.ofEpochMilli(1212L),
+                        Account.id(1000),
+                        Optional.of(Account.id(2001))),
                     AssigneeStatusUpdate.create(
-                        new Timestamp(3434L), Account.id(1000), Optional.empty())))
+                        Instant.ofEpochMilli(3434L), Account.id(1000), Optional.empty())))
             .build(),
         ChangeNotesStateProto.newBuilder()
             .setMetaId(SHA_BYTES)
@@ -1024,7 +1034,7 @@
                 .put("key", PatchSetApproval.Key.class)
                 .put("uuid", new TypeLiteral<Optional<PatchSetApproval.UUID>>() {}.getType())
                 .put("value", short.class)
-                .put("granted", Timestamp.class)
+                .put("granted", Instant.class)
                 .put("tag", new TypeLiteral<Optional<String>>() {}.getType())
                 .put("realAccountId", Account.Id.class)
                 .put("postSubmit", boolean.class)
@@ -1040,7 +1050,7 @@
             ImmutableMap.of(
                 "table",
                 new TypeLiteral<
-                    ImmutableTable<ReviewerStateInternal, Account.Id, Timestamp>>() {}.getType(),
+                    ImmutableTable<ReviewerStateInternal, Account.Id, Instant>>() {}.getType(),
                 "accounts",
                 new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType()));
   }
@@ -1052,7 +1062,7 @@
             ImmutableMap.of(
                 "table",
                 new TypeLiteral<
-                    ImmutableTable<ReviewerStateInternal, Address, Timestamp>>() {}.getType(),
+                    ImmutableTable<ReviewerStateInternal, Address, Instant>>() {}.getType(),
                 "users",
                 new TypeLiteral<ImmutableSet<Address>>() {}.getType()));
   }
@@ -1062,7 +1072,7 @@
     assertThatSerializedClass(ReviewerStatusUpdate.class)
         .hasAutoValueMethods(
             ImmutableMap.of(
-                "date", Timestamp.class,
+                "date", Instant.class,
                 "updatedBy", Account.Id.class,
                 "reviewer", Account.Id.class,
                 "state", ReviewerStateInternal.class));
@@ -1074,7 +1084,7 @@
         .hasAutoValueMethods(
             ImmutableMap.of(
                 "date",
-                Timestamp.class,
+                Instant.class,
                 "updatedBy",
                 Account.Id.class,
                 "currentAssignee",
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 38daae6..1c568aa 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -373,7 +373,7 @@
                 PatchSetApproval.builder()
                     .key(psa.key())
                     .value(0)
-                    .granted(update.getWhen())
+                    .granted(update.getWhen().toInstant())
                     .build()));
   }
 
@@ -923,7 +923,7 @@
                     LabelId.create(LabelId.CODE_REVIEW)))
             .value(1)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .tag("tag")
             .realAccountId(otherUserId)
             .build());
@@ -975,7 +975,7 @@
                     LabelId.create(LabelId.CODE_REVIEW)))
             .value(1)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .build());
     update.commit();
 
@@ -1008,7 +1008,7 @@
                     LabelId.create(LabelId.CODE_REVIEW)))
             .value(1)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .tag(strangeTag)
             .build());
     update.commit();
@@ -1037,7 +1037,7 @@
                     LabelId.create(LabelId.CODE_REVIEW)))
             .value(1)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .build());
     update.putCopiedApproval(
         PatchSetApproval.builder()
@@ -1048,7 +1048,7 @@
                     LabelId.create(LabelId.VERIFIED)))
             .value(1)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .build());
     update.commit();
 
@@ -1073,7 +1073,7 @@
                     LabelId.create(LabelId.CODE_REVIEW)))
             .value(2)
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .build());
     update.commit();
 
@@ -1097,11 +1097,11 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    Timestamp ts = new Timestamp(update.getWhen().getTime());
+    Instant ts = update.getWhen().toInstant();
     assertThat(notes.getReviewers())
         .isEqualTo(
             ReviewerSet.fromTable(
-                ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
+                ImmutableTable.<ReviewerStateInternal, Account.Id, Instant>builder()
                     .put(REVIEWER, Account.id(1), ts)
                     .put(REVIEWER, Account.id(2), ts)
                     .build()));
@@ -1116,11 +1116,11 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    Timestamp ts = new Timestamp(update.getWhen().getTime());
+    Instant ts = update.getWhen().toInstant();
     assertThat(notes.getReviewers())
         .isEqualTo(
             ReviewerSet.fromTable(
-                ImmutableTable.<ReviewerStateInternal, Account.Id, Timestamp>builder()
+                ImmutableTable.<ReviewerStateInternal, Account.Id, Instant>builder()
                     .put(REVIEWER, Account.id(1), ts)
                     .put(CC, Account.id(2), ts)
                     .build()));
@@ -1134,7 +1134,7 @@
     update.commit();
 
     ChangeNotes notes = newNotes(c);
-    Timestamp ts = new Timestamp(update.getWhen().getTime());
+    Instant ts = update.getWhen().toInstant();
     assertThat(notes.getReviewers())
         .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(REVIEWER, Account.id(2), ts)));
 
@@ -1143,7 +1143,7 @@
     update.commit();
 
     notes = newNotes(c);
-    ts = new Timestamp(update.getWhen().getTime());
+    ts = update.getWhen().toInstant();
     assertThat(notes.getReviewers())
         .isEqualTo(ReviewerSet.fromTable(ImmutableTable.of(CC, Account.id(2), ts)));
   }
@@ -4016,7 +4016,7 @@
             .key(originalPsa.key())
             .value(originalPsa.value())
             .copied(true)
-            .granted(TimeUtil.nowTs())
+            .granted(TimeUtil.now())
             .tag(originalPsa.tag())
             .uuid(originalPsa.uuid())
             .realAccountId(originalPsa.realAccountId())
diff --git a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
index b29ce29..1818285 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitRewriterTest.java
@@ -48,6 +48,7 @@
 import com.google.gson.Gson;
 import com.google.inject.Inject;
 import java.sql.Timestamp;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -495,7 +496,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    Timestamp updateTimestamp = new Timestamp(serverIdent.getWhen().getTime());
+    Instant updateTimestamp = serverIdent.getWhen().toInstant();
     ImmutableList<ReviewerStatusUpdate> expectedReviewerUpdates =
         ImmutableList.of(
             ReviewerStatusUpdate.create(
@@ -579,21 +580,18 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    Timestamp updateTimestamp = new Timestamp(serverIdent.getWhen().getTime());
+    Instant updateTimestamp = serverIdent.getWhen().toInstant();
     ImmutableList<ReviewerStatusUpdate> expectedReviewerUpdates =
         ImmutableList.of(
             ReviewerStatusUpdate.create(
-                new Timestamp(addReviewerUpdate.when.getTime()),
+                addReviewerUpdate.when.toInstant(),
                 changeOwner.getAccountId(),
                 otherUserId,
                 REVIEWER),
             ReviewerStatusUpdate.create(
                 updateTimestamp, changeOwner.getAccountId(), otherUserId, REMOVED),
             ReviewerStatusUpdate.create(
-                new Timestamp(addCcUpdate.when.getTime()),
-                changeOwner.getAccountId(),
-                otherUserId,
-                CC),
+                addCcUpdate.when.toInstant(), changeOwner.getAccountId(), otherUserId, CC),
             ReviewerStatusUpdate.create(
                 updateTimestamp, changeOwner.getAccountId(), otherUserId, REMOVED));
     ChangeNotes notesAfterRewrite = newNotes(c);
@@ -715,7 +713,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    Timestamp updateTimestamp = new Timestamp(serverIdent.getWhen().getTime());
+    Instant updateTimestamp = serverIdent.getWhen().toInstant();
     ImmutableList<PatchSetApproval> expectedApprovals =
         ImmutableList.of(
             PatchSetApproval.builder()
@@ -852,7 +850,7 @@
     BackfillResult result = rewriter.backfillProject(project, repo, options);
     assertThat(result.fixedRefDiff.keySet()).containsExactly(RefNames.changeMetaRef(c.getId()));
 
-    Timestamp updateTimestamp = new Timestamp(serverIdent.getWhen().getTime());
+    Instant updateTimestamp = serverIdent.getWhen().toInstant();
     ImmutableList<PatchSetApproval> expectedApprovals =
         ImmutableList.of(
             PatchSetApproval.builder()
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
index e5dd817..509447a 100644
--- a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -27,7 +27,6 @@
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
 import org.junit.Test;
 
@@ -84,7 +83,7 @@
     return PatchSetApproval.builder()
         .key(PatchSetApproval.key(PS_ID, accountId, labelId))
         .value(value)
-        .granted(Date.from(Instant.now()))
+        .granted(Instant.now())
         .build();
   }
 
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index a28ae59..6226d01 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit a28ae590486934690e4e0a95d7eb75f8b60644a6
+Subproject commit 6226d01c563846ae479cb4fdafd698b31472772c