Java Date and Time
โก Smart Summary
Java Date and Time handling combines the legacy java.util.Date and SimpleDateFormat classes with the modern java.time API introduced in Java 8, giving developers immutable, thread-safe types for accurate calendar work.

Handling dates and times in Java is a routine part of building scheduling logic, log timestamps, reports, and audit trails. Before writing any code, it helps to know the parameters that make up a date:
- The year (in two or four digits).
- The month (as two digits, as the first three letters of the month, or as the full month name).
- The date (the actual day-of-month number).
- The day (the day name at the given date, such as Sun, Mon, Tue).
Java exposes many other parameters that describe a moment on the timeline, including era, week-in-year, time zone, and offset. The sections below cover each of them with runnable examples.
Display Date in Java
Java provides a Date class in the java.util package. The class stores a specific instant in time to millisecond precision and offers helper methods to print, compare, and format that instant. To get the current date and time, instantiate the class with its default constructor:
import java.util.Date; class Date_Ex1 { public static void main(String args[]) { // Instantiate a Date object by invoking its constructor Date objDate = new Date(); // Display the Date and Time using toString() System.out.println(objDate.toString()); } }
Output:
Wed Nov 29 06:36:22 UTC 2017
The example prints the date in the default toString format. To display the same instant in a different pattern, you first need to understand the formatting letters that Java uses.
SimpleDateFormat: Parse and Format Dates
The SimpleDateFormat class in java.text converts a Date object into a formatted string and parses a formatted string back into a Date. It uses pattern letters that map to a date or time component. The table below lists every supported letter:
| Letter | Date or Time Component | Example |
|---|---|---|
| G | Era designator | AD |
| y | Year | 2026 |
| M | Month in year | July or Jul or 07 |
| w | Week in year | 27 |
| W | Week in month | 2 |
| D | Day in year | 189 |
| d | Day in month | 10 |
| F | Day of week in month | 2 |
| E | Day name in week | Tuesday or Tue |
| u | Day number of week (1 = Monday, 7 = Sunday) | 1 |
| a | AM or PM marker | PM |
| H | Hour in day (0-23) | 0 |
| k | Hour in day (1-24) | 24 |
| K | Hour in AM or PM (0-11) | 0 |
| h | Hour in AM or PM (1-12) | 12 |
| m | Minute in hour | 30 |
| s | Second in minute | 55 |
| S | Millisecond | 978 |
| z | Time zone (name) | Pacific Standard Time; PST; GMT-08:00 |
| Z | Time zone (RFC 822 offset) | -0800 |
| X | Time zone (ISO 8601 offset) | -08 or -0800 or -08:00 |
You do not need to remember the whole table. Refer to it whenever you build a new pattern.
How to Use SimpleDateFormat
SimpleDateFormat is a subclass of DateFormat that lets you format and parse dates using the pattern letters above. Combine the letters to describe the exact string you want to produce or read.
For example:
- Date format required: 2012.10.23 20:20:45 PST. The appropriate format pattern is
yyyy.MM.dd HH:mm:ss zzz. - Date format required: 09:30:00 AM 23-May-2012. The appropriate format pattern is
hh:mm:ss a dd-MMM-yyyy.
Tip: Watch letter case. Uppercase M is month, lowercase m is minute. Uppercase H is a 24-hour clock, lowercase h is a 12-hour clock. Mixing them up produces silently wrong output.
The program below prints the current date twice, once in default format and once through a SimpleDateFormat pattern:
import java.text.SimpleDateFormat; import java.util.Date; class TestDates_Format { public static void main(String args[]) { // Current system date and time is assigned to objDate Date objDate = new Date(); System.out.println(objDate); // Build the target pattern and format the date String strDateFormat = "hh:mm:ss a dd-MMM-yyyy"; SimpleDateFormat objSDF = new SimpleDateFormat(strDateFormat); System.out.println(objSDF.format(objDate)); } }
Output:
Wed Nov 29 06:31:41 UTC 2017 06:31:41 AM 29-Nov-2017
Compare Dates Example
The most reliable way to order two Date objects is the compareTo() method. It returns a negative integer when the caller is earlier, zero when both instants are equal, and a positive integer when the caller is later. The snippet below parses two date strings and prints their relative order:
import java.text.SimpleDateFormat; import java.text.ParseException; import java.util.Date; class TestDates_Compare { public static void main(String args[]) throws ParseException { SimpleDateFormat objSDF = new SimpleDateFormat("dd-MM-yyyy"); Date dt_1 = objSDF.parse("20-08-1981"); Date dt_2 = objSDF.parse("12-10-2012"); System.out.println("Date1 : " + objSDF.format(dt_1)); System.out.println("Date2 : " + objSDF.format(dt_2)); // compareTo returns > 0 when dt_1 is after dt_2 if (dt_1.compareTo(dt_2) > 0) { System.out.println("Date 1 occurs after Date 2"); } // compareTo returns < 0 when dt_1 is before dt_2 else if (dt_1.compareTo(dt_2) < 0) { System.out.println("Date 1 occurs before Date 2"); } // compareTo returns 0 when both dates are equal else { System.out.println("Both are the same date"); } } }
Output:
Date1 : 20-08-1981 Date2 : 12-10-2012 Date 1 occurs before Date 2
Note: The original code used the pattern dd-mm-yyyy. The lowercase mm letter is minute, not month, so the parser silently produced the wrong month value. The fix above uses uppercase MM for month, which is the correct pattern letter.
Java 8 Modern Date and Time API (java.time)
Java 8 introduced the java.time package, a redesigned API inspired by the Joda-Time library. The new classes are immutable, thread-safe, and separate the human calendar from the machine timeline. The most common types are:
LocalDatefor a date without a time or zone (for example, a birthday).LocalTimefor a wall-clock time without a date or zone.LocalDateTimefor a date and time without a zone.ZonedDateTimefor a date and time with a specific time zone.OffsetDateTimefor a date and time with a UTC offset but no named zone.Instantfor a machine timestamp measured from the UNIX epoch.DurationandPeriodfor time-based and date-based amounts.
The example below prints the current date, time, timestamp, and time zone using the modern API:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.time.Instant; import java.time.ZoneId; class ModernDateDemo { public static void main(String args[]) { LocalDate today = LocalDate.now(); LocalDateTime now = LocalDateTime.now(); ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Kolkata")); Instant epoch = Instant.now(); System.out.println("LocalDate : " + today); System.out.println("LocalDateTime : " + now); System.out.println("ZonedDateTime : " + zoned); System.out.println("Instant : " + epoch); } }
Each type has factory methods (now, of, parse) and immutable operations (plusDays, minusHours, withYear) that return a fresh value instead of mutating the original object.
DateTimeFormatter: Format Dates in Modern Java
DateTimeFormatter in java.time.format replaces SimpleDateFormat. It uses the same style of pattern letters, is immutable, and is safe to share across threads without extra synchronization. The class also exposes ready-made constants such as ISO_LOCAL_DATE and ISO_ZONED_DATE_TIME for standard formats.
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; class FormatterDemo { public static void main(String args[]) { LocalDateTime now = LocalDateTime.now(); DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME; DateTimeFormatter human = DateTimeFormatter.ofPattern("hh:mm:ss a dd-MMM-yyyy"); System.out.println(now.format(iso)); System.out.println(now.format(human)); LocalDateTime parsed = LocalDateTime.parse("2026-07-04T09:30:00", iso); System.out.println("Parsed back : " + parsed); } }
To compare two dates in the modern API, call isBefore, isAfter, or isEqual. These methods read more clearly than compareTo and work with every java.time class.
Thread Safety and Best Practices for Java Date and Time
Both java.util.Date and SimpleDateFormat predate modern concurrent Java, and each has known pitfalls. Follow the practices below to keep date handling correct in production code:
- Prefer java.time over Date and Calendar. The new types are immutable and thread-safe, and they model the domain more accurately (dates without times, times without zones, and so on).
- Do not share a SimpleDateFormat across threads. The class stores internal parse state and is not thread-safe. Wrap it in a
ThreadLocal, synchronize on the instance, or switch toDateTimeFormatter. - Always specify a Locale. Month and day names change with the JVM default locale, which makes patterns like
MMMandEEEnon-portable. Pass a locale explicitly (for example,Locale.ENGLISH). - Be explicit about time zones.
LocalDateTimehas no zone, so convert toZonedDateTimeorInstantwhenever the exact moment matters, especially for logs, database columns, and network payloads. - Convert between the two APIs carefully. Use
date.toInstant(),Date.from(instant), andcalendar.toInstant().atZone(zone)at the boundary between legacy code and modern code. - Store timestamps in UTC. Persist and transmit dates as UTC (an
Instantor an ISO 8601 string) and convert to the user zone only when displaying.


