Serguey Asael Shinder: Instant.toString changes length, so sorted strings are not sorted instants
I keep finding the same shortcut in code that stores timestamps as text: write instant.toString() into a column or a file name, and sort by the string later, on the theory that ISO-8601 sorts correctly. For Instant it does not, and the reason is one sentence in the javadoc.
Instant.toString() says: "The format used is the same as DateTimeFormatter.ISO_INSTANT." And ISO_INSTANT says, about formatting: "The second-of-minute is always output. The nano-of-second outputs zero, three, six or nine digits as necessary." (https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_INSTANT)
"As necessary" is the problem. An instant on a whole second prints with no fraction at all; one half a second later prints three digits:
Instant a = Instant.parse("2026-11-02T08:00:00Z");
Instant b = a.plusMillis(500);
String sa = a.toString(); // 2026-11-02T08:00:00Z
String sb = b.toString(); // 2026-11-02T08:00:00.500Z
a.compareTo(b); // negative: a is earlier
sa.compareTo(sb); // positive: 'Z' sorts after '.'

The strings agree up to the seconds and then differ at the next character: Z in one, . in the other. Z is code point 90 and . is 46, so String.compareTo puts the earlier instant last. The same happens between three digits and six: 08:00:00.500Z against 08:00:00.500100Z differs at Z against 1, and since digits sort before Z, the earlier value again comes last. Either way the order of the strings stops matching the time line, and it does so only for values that happen to fall on a whole second or a whole millisecond, which is why it survives testing with "random" timestamps.
Instant.compareTo has no such issue; its javadoc says the comparison "is based on the time-line position of the instants". (https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/time/Instant.html#compareTo(java.time.Instant))
Two fixes, depending on where the string goes. If it only needs to sort, store the epoch value as a number (toEpochMilli(), or seconds plus nanos) and sort that. If it has to stay human-readable, format with a fixed pattern such as DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'").withZone(ZoneOffset.UTC), so every value has the same width. What I would not do is keep toString() and sort the text: it looks like ISO-8601, and it is, but ISO-8601 with a variable-length fraction is not a sortable format.