1. Historical context: how dates were handled before Java 8
In the distant past (before Java 8), for working with dates and time, developers used the classes java.util.Date, java.util.Calendar and — for formatting — java.text.SimpleDateFormat. It was one of those cases where developers around the world sighed and, through gritted teeth, wrote something like:
import java.util.Date;
Date now = new Date();
System.out.println(now); // Will print something odd like "Wed Jun 05 14:15:22 MSK 2025"
It sounds simple, but in reality it wasn’t that rosy. Here are just some of the “joys” of the old API:
- Date is a mutable object. You could accidentally change it, and that often led to bugs.
- Months in Date and Calendar started at zero (January is 0, December is 11), while days started at one.
- SimpleDateFormat was not thread‑safe: if two threads formatted a date simultaneously, you could get unexpected results.
- A huge number of methods were annotated @Deprecated, and IDEs constantly peppered you with yellow warnings.
- Working with time zones was a real pain: it was easy to confuse local time and UTC, and daylight saving time transitions were better left unmentioned.
An example of the pain
import java.util.Date;
Date date = new Date(2025, 5, 1); // year 2025, month 5 (June?), day 1
System.out.println(date); // Not what you expect!
2. The arrival of java.time: a new approach
By 2014 it had become clear: the old API was not just inconvenient — it was dangerous. Therefore, Java introduced a new package — java.time, implementing the JSR‑310 specification. This API was inspired by the popular Joda‑Time library and immediately became the de facto standard.
Core packages and classes
- java.time — the core package where all the new date and time classes live.
- java.time.format — for formatting and parsing dates and times.
- java.time.temporal — for more advanced temporal operations.
- java.time.zone — for working with time zones.
Here are the main players of the new API:
| Class | What for? | Usage example |
|---|---|---|
|
Date only (year, month, day) | Birthday, no time |
|
Time only (hours, minutes, seconds) | Meeting time, no date |
|
Date and time, no time zone | Local event |
|
Date and time with time zone | A meeting in Minsk in local time |
|
Absolute point in time (UTC) | Event log timestamp |
|
Duration (hours, minutes, sec.) | Call length |
|
Period (years, months, days) | Work tenure, age |
Example: creating a date the new way
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // For example, "2025-06-05"
3. Advantages of the new API
Immutability (immutable)
All classes from java.time are immutable. This means: if you created a LocalDate object, you cannot change it. Any operation (for example, adding a day) returns a new object.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Explicit time‑zone handling
In the old API it was easy to forget which time zone a date was in. In java.time everything is explicit: if you need a time zone — use ZonedDateTime, if you don’t — use LocalDateTime.
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
System.out.println(MinskTime); // 2025-06-05T14:23:45+03:00[Europe/Minsk]
Convenient methods for calculations and comparison
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Formatting and parsing
Formatting and parsing are done via DateTimeFormatter (details — in the next lecture):
import java.time.format.DateTimeFormatter;
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String formatted = today.format(formatter); // "05.08.2025"
4. Compatibility: how to live with legacy code
In the real world, you very often have to work with libraries or legacy systems that use Date and Calendar. Fortunately, the new API is friendly to legacy: you can convert old types into new ones and back.
Converting Date ↔ Instant
import java.util.Date;
import java.time.Instant;
// Date → Instant
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
// Instant → Date
Date dateBack = Date.from(instant);
Converting Calendar ↔ ZonedDateTime
import java.util.Calendar;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.Date;
// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
// ZonedDateTime → Calendar
Calendar calBack = Calendar.getInstance();
calBack.setTime(Date.from(zdt.toInstant()));
Table: mapping old and new classes
| Old class | New class | Comment |
|---|---|---|
|
|
Absolute time |
|
|
Date and time with time zone |
|
|
Date formatting/parsing |
5. Practice: first steps with java.time
Suppose you have a user with a date of birth. Let’s store and print this date:
import java.time.LocalDate;
public class UserProfile {
private String name;
private LocalDate birthDate;
public UserProfile(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public void printProfile() {
System.out.println("Name: " + name);
System.out.println("Date of birth: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Alisa", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Output:
Name: Alisa
Date of birth: 1998-12-25
6. Comparison: old API vs new API
Example: add a week to a birth date
Old way (Date/Calendar):
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
cal.set(1998, Calendar.DECEMBER, 25);
cal.add(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal.getTime()); // Cumbersome and non-obvious
New way (java.time):
import java.time.LocalDate;
LocalDate birthDate = LocalDate.of(1998, 12, 25);
LocalDate nextWeek = birthDate.plusWeeks(1);
System.out.println(nextWeek); // 1999-01-01
In the new API, the code is shorter, simpler, and safer.
7. Common mistakes when working with java.time
Error No. 1: forgetting that objects are immutable.
If you call date.plusDays(1); and don’t store the result, the original date will remain the same.
Error No. 2: mixing up LocalDate and LocalDateTime.
LocalDate stores only a date (year, month, day), while LocalDateTime also stores time. Don’t confuse them if you need to handle hours and minutes.
Error No. 3: using old classes in new projects.
If possible — always use java.time. Old classes are only needed for compatibility.
Error No. 4: incorrect handling of time zones.
If you need to store an event that matters across regions, use ZonedDateTime or at least Instant. LocalDateTime does not contain time‑zone information!
Error No. 5: trying to compare LocalDate and LocalDateTime directly.
They are different data types; you cannot compare them directly. First convert to a common type.
GO TO FULL VERSION