Dates, text and numbers
java.time types, formatting and parsing, BigDecimal for money, and text blocks for readable strings.
java.time
Instant now = Instant.now(); // UTC timeline point
LocalDate today = LocalDate.now(ZoneId.of("Europe/London"));
LocalDateTime meeting = LocalDateTime.of(2026, 9, 18, 9, 30);
Duration elapsed = Duration.between(meeting, meeting.plusHours(2)); // PT2H
Period span = Period.between(LocalDate.of(2026, 1, 1), today); // P8M17D
ZonedDateTime tokyo = meeting.atZone(ZoneId.of("Asia/Tokyo"));
Instant asInstant = tokyo.toInstant();| Type | Represents | Use it for |
|---|---|---|
Instant | A point on the timeline, in UTC | Timestamps, logs, storage |
LocalDate | A date with no time or zone | Deadlines and birthdays |
LocalTime | A time of day | Opening hours |
LocalDateTime | Date and time with no zone | Local scheduling input |
ZonedDateTime | Date and time in a zone, with rules | Displaying to a user |
Duration | A time-based amount | Elapsed time, timeouts |
Period | A calendar-based amount | "Three months later" |
- Store
Instantin UTC and convert to a zone only for display or for calendar arithmetic. - Every
java.timetype is immutable, soplusDaysreturns a new object rather than mutating the receiver. DurationandPeriodlook similar and are not interchangeable: one counts seconds, the other counts calendar units and can produce different results across a daylight-saving change.LocalDateTimecannot represent an ambiguous or skipped wall-clock time, which is exactly what happens at a DST transition — useZonedDateTimethere.- The older
Date,CalendarandSimpleDateFormatclasses are mutable and not thread-safe; do not use them in new code.
Formatting and parsing
DateTimeFormatter friendly =
DateTimeFormatter.ofPattern("d MMM uuuu HH:mm", Locale.UK);
String text = meeting.format(friendly); // "18 Sep 2026 09:30"
LocalDateTime parsed = LocalDateTime.parse(text, friendly);
String machine = DateTimeFormatter.ISO_INSTANT.format(Instant.now());DateTimeFormatteris immutable and thread-safe, so build it once as a constant and reuse it — unlike the legacy formatter.- Use
uuuufor the year in a pattern.yyyymeans year-of-era and behaves oddly around dates before year 1; only use it when you specifically want the era-based year. - Parsing throws
DateTimeParseException, so validate the string at the input boundary rather than deep inside the domain model. - Use
Locale.ROOTfor machine-facing output and the user's locale for anything a person reads. - ISO-8601 is the right wire format for storage and APIs; anything else invites a parsing argument.
Money, numbers and text blocks
BigDecimal price = new BigDecimal("19.99"); // the String constructor
BigDecimal total = price.multiply(BigDecimal.valueOf(3))
.setScale(2, RoundingMode.HALF_UP);
NumberFormat money = NumberFormat.getCurrencyInstance(Locale.UK);
String shown = money.format(total);
double surprise = 0.1 + 0.2; // 0.30000000000000004
String query = """
SELECT id, sku
FROM orders
WHERE status = ?
ORDER BY created_at DESC
""";- Never construct a
BigDecimalfrom adouble:new BigDecimal(0.1)carries the binary rounding error into the value. Use the String constructor orBigDecimal.valueOf. - Always set a scale and a rounding mode for money, and decide the mode deliberately — half-up is common for display, not always correct for accounting.
dividewithout a rounding mode throwsArithmeticExceptionfor a non-terminating decimal such as one third.- A text block strips incidental indentation and keeps the line breaks you typed; add a trailing
\when you need to suppress the final newline. - Use
NumberFormatorString.formatfor presentation instead of hard-coding separators and currency symbols.
⚠️
Binary floating point cannot represent most decimal fractions exactly, so
double is the wrong type for money, quantities that must sum correctly, or anything that is reconciled. Use BigDecimal at the boundary and keep double for science and graphics.FAQ
Should timestamps be stored as strings or instants?
As instants, either a native timestamp column or an ISO-8601 UTC string. Storing local wall-clock time without a zone makes daylight-saving bugs and multi-region bugs inevitable.
When is double acceptable?
For measurement, graphics, physics and statistics, where the input is approximate anyway. Not for money, not for counters that must reconcile, and not for values you compare for equality.
Related
Generics, lambdas and the streams API Files, I/O and JSON
Last refreshed 2026-09-18.