Pattern YYYY-MM-DD'T'hh:mm'Z' is wrong:
YYYY- week-based-year wrong: useuuuuyearMM- month-of-yearDD- day-of-year wrong: useddday-of-monthhh- clock-hour-of-am-pm (1-12) without AM/PM you probably wantHHhour-of-day (0-23)mm- minute-of-hour
It's weird, because you even referenced a link that had the right pattern characters. Unless of course you thought upper- vs lower-case didn't matter, but if so, how did you think MM (month) vs mm (minute) worked?
You might want to actually read the documentation.
Answer from Andreas on Stack OverflowPattern YYYY-MM-DD'T'hh:mm'Z' is wrong:
YYYY- week-based-year wrong: useuuuuyearMM- month-of-yearDD- day-of-year wrong: useddday-of-monthhh- clock-hour-of-am-pm (1-12) without AM/PM you probably wantHHhour-of-day (0-23)mm- minute-of-hour
It's weird, because you even referenced a link that had the right pattern characters. Unless of course you thought upper- vs lower-case didn't matter, but if so, how did you think MM (month) vs mm (minute) worked?
You might want to actually read the documentation.
Take a look at the documentation of the DateTimeFormatter. So, D stands for the day of the year, while d is the day of the month, which is what you want.
Plus, there are several formats that are already defined. The one you want is almost like DateTimeFormatter.ISO_INSTANT.
java - Why is Instant.now() sometimes formatted differently? - Stack Overflow
Has the precision of Instant.now changed in Java 17?
How to create Java time instant from pattern? - Stack Overflow
java.time.DateTimeFormatter : Need ISO_INSTANT that always renders milliseconds - Stack Overflow
Videos
When the nanoseconds is zero, it's not included in the formatted string. So the output would be formatted as "yyyy-MM-dd'T'HH:mm:ssX"
You can see the difference with:
public static void main(String[] args) throws ParseException {
final Instant now = Instant.now();
System.out.println("Now:\t" + now);
final Instant zeroedNow = now.with(ChronoField.NANO_OF_SECOND, 0);
System.out.println("Zeroed:\t" + zeroedNow);
}
The output looks like
Now: 2018-05-25T20:23:54.208Z
Zeroed: 2018-05-25T20:23:54Z
Your code fails because Instant.toString() is documented as
The format used is the same as DateTimeFormatter.ISO_INSTANT.
And according to DateTimeFormatter.html#ISO_INSTANT
The nano-of-second outputs zero, three, six or nine digits digits as necessary
So you have a one in thousand chance of getting an Instant where the millis are 000 and your code fails.
Anyway, your code is unnecessarly complex and could be replaced with
String formattedDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
.withZone(ZoneId.systemDefault())
.format(Instant.now());
There is no need to transform the Instant into a String only to parse it into a Date and then transform it into a String again.
I'm just upgrading a project from Java 11 to Java 21 and found that Instant.now() has greater precision now since Java 17. Previously, it was microseconds and now it is nanoseconds.
This is reasonable as the Javadoc does say the precision is system dependent. Unfortunately, this makes the upgrade less trivial for us as this extra precision causes issues with some databases, and some clients of our API.
I can't find anything in the release notes and want to confirm that:
-
This is actually a change due to my upgrade and not some other factor I haven't realized
-
There isn't a flag that I can use to activate the previous behaviour
I'm a bit paranoid because I don't see why this wouldn't have occurred with Java 11 also but it seems to me that upgrading past Java 17 reliably reproduces the behaviour, on the same exact system.
Otherwise, I think I will need to wrap this method and truncate it in order to get back the previous behavior as I cannot update all of the clients in a backwards compatible way.
The string "9999-12-31" only contains information about a date. It does not contain any information about the time-of-day or offset. As such, there is insufficient information to create an Instant. (Other date and time libraries are more lenient, but java.time avoids defaulting these values)
Your first choice is to use a LocalDate instead of an `Instant:
LocalDate date = LocalDate.parse("9999-12-31");
Your second choice is to post process the date to convert it to an instant, which requires a time-zone, here chosen to be Paris:
LocalDate date = LocalDate.parse("9999-12-31");
Instant instant = date.atStartOfDay(ZoneId.of("Europe/Paris")).toInstant();
Your third choice is to add the time-zone to the formatter, and default the time-of-day:
static final DateTimeFormatter FMT = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd")
.parseDefaulting(ChronoField.NANO_OF_DAY, 0)
.toFormatter()
.withZone(ZoneId.of("Europe/Paris"));
Instant instant = FMT.parse("9999-31-12", Instant::from);
(If this doesn't work, ensure you have the latest JDK 8 release as a bug was fixed in this area).
It is worth noting that none of these possibilities use TemporalAccessor directly, because that type is a low-level framework interface, not one for most application developers to use.
The problem isn't the fact that you are using the year 9999. The Instant.MAX field evaluates to the timestamp 1000000000-12-31T23:59:59.999999999Z, so 9999 as a year is fine.
Dealing with TemporalAccessors instead of the more semantically rich types like LocalDateTime or ZonedDateTime is like using a Map to model an object and its properties instead of writing a class -- you have to assure that the value has the fields (like seconds, nanoseconds, etc) that are expected by something receiving it, rather than depending on formally declared operations in a higher level class to prevent dependencies from going unmet.
In your case it is likely that the temporal accessor contained the parsed date fields it was given, but didn't have a "seconds" field that the Instant needed. It is best to use the more semantically rich types like LocalDateTime in most instances.
Since you only have date fields, you should parse it as a date, then add the time fields before converting it to an Instant. Here is one way, using LocalDate to parse the date:
LocalDate localDate = LocalDate.parse("2016-04-17");
LocalDateTime localDateTime = localDate.atStartOfDay();
Instant instant = localDateTime.toInstant(ZoneOffset.UTC);
OK, I looked at the the source code and it's pretty straightforward:
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendInstant(3).toFormatter();
I hope it works for all scenarios, and it can help someone else. Don't hesitate to add a better/cleaner answer.
Just to explain where it comes from, in the JDK's code,
ISO_INSTANT is defined like this:
public static final DateTimeFormatter ISO_INSTANT;
static {
ISO_INSTANT = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendInstant()
.toFormatter(ResolverStyle.STRICT, null);
}
And DateTimeFormatterBuilder::appendInstant is declared as:
public DateTimeFormatterBuilder appendInstant() {
appendInternal(new InstantPrinterParser(-2));
return this;
}
And the constructor InstantPrinterParser signature is:
InstantPrinterParser(int fractionalDigits)
The accepted Answer by Florent is correct and good.
I just want to add some clarification.
The mentioned formatter, DateTimeFormatter.ISO_INSTANT, is default only for the Instant class. Other classes such as OffsetDateTime and ZonedDateTime may use other formatters by default.
The java.time classes offer a resolution up to nanosecond, much finer granularity than milliseconds. That means up to 9 digits in the decimal fraction rather than merely 3 digits.
The behavior of DateTimeFormatter.ISO_INSTANT varies by the number of digits in the decimal fraction. As the doc says (emphasis mine):
When formatting, the second-of-minute is always output. The nano-of-second outputs zero, three, six or nine digits as necessary.
So depending on the data value contained within the Instant object, you may see any of these outputs:
2011-12-03T10:15:30Z
2011-12-03T10:15:30.100Z
2011-12-03T10:15:30.120Z
2011-12-03T10:15:30.123Z
2011-12-03T10:15:30.123400Z
2011-12-03T10:15:30.123456Z
2011-12-03T10:15:30.123456780Z
2011-12-03T10:15:30.123456789Z
The Instant class is meant to be the basic building block of java.time. Use it frequently for data passing, data storage, and data exchange. When generating String representations of the data for presentation to users, use OffsetDateTime or ZonedDateTime.