Javascript is case-sensitive
Everything in JavaScript including variables , function names, class names, and operators are case-sensitive. It means that counter and Counter variables are different.
Comments
JavaScript supports both single-line (//) and block(/**/) comments.
// This is a single line comment /* * This is a multiple line * comment block */
Semicolon
Although JavaScript does not require to end a statement with a semicolon (;), it is recommended to always use the semicolon to end a statement which makes your code more readable.
var number1 = 1; var number2 = 2;
Javascript variables
JavaScript variables are loosely typed which means that variables can hold values with any type of data. Variables are just placeholders for values.
To declare a variable, you use the var or let keyword followed by the variable name.
var pointer = "Welcome!"; point = 100;
Undefined and undeclared variables
An undefined variable is a variable that has been declared. Because we have not assigned it a value, the variable used the undefined as its initial value.
var undefinedMessage; console.log(undefinedMessage); // undefined
An undeclared variable is the variable that has not been declared.
console.log(undeclaredVariable); // ReferenceError: undeclaredVariable is not defined
Global and local variables
In JavaScript, all variables exist within a scope that determines the lifetime of the variables and which part of the code can access them.
Local variables
Variables declared within a JavaScript function, become locally accessible to the function and only that function.
function sayHi(){
//helloMsg is a local variable to the sayHi() function
var helloMsg = "Welcome!";
console.log("helloMsg: "+helloMsg);
}
sayHi();
console.log("helloMsg: "+helloMsg);//Uncaught ReferenceError: helloMsg is not defined
Global variables
A variable declared outside a function, becomes global which all functions on a web page can access it.
/*
* Global variables
*/
var message = "Hello";
function sayHello() {
// local variable
message = 'Hi';
console.log(message); // which message?
}
sayHello();// Hi
console.log(message); // Hi
If a local variable is not declared, Javascript will create it as a global variable.
But to avoid creating a global variable accidentally inside a function because of omitting the var keyword, you use the strict mode by adding the “use strict”; at the beginning of the JavaScript file (or the function). Note that by using “use strict” modern javascript will be enforced.
<script>
"use strict";
window.onload = function() {
}
</script>
Javascript Data Types
JavaScript has six primitive data types:
JavaScript has dynamic types. This means that the same variable can be used to hold different data types.
var x; // Now x is undefined x = 10; // Now x is a Number x = "Folau"; // Now x is a String
To get the current type of the value of a variable, you use the typeof operator.
let count = 120; // count is a number console.log(typeof(count)); // "number" count = false; // count is now a boolean console.log(typeof(count)); // "boolean" count = "Hi"; // count is now a string console.log(typeof(count)); // "string"
The null type
Javascript defines that null is an empty object pointer. It is a good practice to assign a variable that later holds an object to null so that you can check whether the object is null or not by using the if statement.
let obj = null;
console.log(typeof obj); // object
if(obj != null) {
// call method of the object
}
The undefined type
The undefined type is a primitive type that has one value undefined. By default, when a variable is declared but not initialized, it is assigned the value undefined.
The number type
Variables that hold whole number values
var num = 100; var dec = 2.5;
The boolean type
Variables that hold true or false
var isSafe = true;
The NAN type
JavaScript has a special numeric value called NAN, which stands for Not a Number. In fact, it means an invalid number.
console.log('john'/2);//NaN;
console.log(NAN/2);//NAN
console.log(NAN==NAN);//false
In JavaScript, a string is a sequence of zero or more characters. A literal string begins and ends with either a single quote(‘) or double quote (“). A string that starts with a double quote must end with a double quote and a string that begins with a single quote must end with a single quote.
let greeting = 'Hi';
let s = "It's a valid string";
let str = 'I\'m also a string'; // use \ to escape the single quote (')
The object type
In JavaScript, an object is a collection of properties, where each property is defined as a key-value pair.
let user = {
firstName: 'Folau',
lastName: 'Kaveinga'
};
Null check with ??
The nullish coalescing operator is written as two question marks ??.
The result of a ?? b is:
a is defined, then a,a isn’t defined, then b.let collegeName;
console.log("collegeName: "+ (collegeName ?? "no collegeName"));//collegeName: no collegeName
collegeName = "BYU";
console.log("collegeName: ", collegeName ?? "no collegeName");// BYU
Spring framework has a retry project that is very useful when you have a business logic failure that you want to be able to retry to complete.
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Configuration
@EnableRetry
public class GlobalConfig {
}
@Service
@Slf4j
public class UserServiceImp implements UserService {
@Retryable(value = {RuntimeException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000))
@Override
public boolean sendEmail(User user) throws RuntimeException {
log.info("send email");
if (true) {
throw new RuntimeException("error");
}
return false;
}
@Recover
@Override
public boolean recover(RuntimeException e, User user) {
log.info("recover");
return true;
}
}
If you have ever dealt with dates in Java using java.util.Date or java.util.Calendar, you know the pain: mutable objects that change unexpectedly, months starting from 0, thread-safety issues, and a confusing API that mixes date concepts with time concepts. For over a decade, Java developers relied on third-party libraries like Joda-Time just to do basic date arithmetic correctly.
Java 8 fixed this by introducing the java.time package (JSR 310), designed by the creator of Joda-Time himself. This package is now the standard for all date and time work in Java.
| Problem with Legacy API | How java.time Fixes It |
|---|---|
Date is mutable — anyone can call setTime() and change it |
All java.time classes are immutable — methods return new instances |
| Months are 0-indexed (January = 0) | Months are 1-indexed (January = 1) or use the Month enum |
Date represents both date and time, but poorly |
Separate classes: LocalDate, LocalTime, LocalDateTime |
No built-in time zone handling beyond TimeZone |
ZonedDateTime and ZoneId with IANA time zone database |
Not thread-safe — SimpleDateFormat causes race conditions |
DateTimeFormatter is thread-safe and immutable |
| No concept of periods or durations | Period (date-based) and Duration (time-based) |
Confusing API (getYear() returns year minus 1900) |
Clear, consistent method naming: getYear() returns the actual year |
| Class | What It Represents | Example |
|---|---|---|
LocalDate |
Date only (no time, no zone) | 2026-02-28 |
LocalTime |
Time only (no date, no zone) | 14:30:00 |
LocalDateTime |
Date and time (no zone) | 2026-02-28T14:30:00 |
ZonedDateTime |
Date, time, and time zone | 2026-02-28T14:30:00-05:00[America/New_York] |
Instant |
Machine timestamp (epoch seconds) | 2026-02-28T19:30:00Z |
Period |
Date-based amount (years, months, days) | 2 years, 3 months, 5 days |
Duration |
Time-based amount (hours, minutes, seconds) | 2 hours, 30 minutes |
DateTimeFormatter |
Parsing and formatting | “dd/MM/yyyy” pattern |
The rule of thumb is simple: use the most specific type that fits your need. If you only care about a date (birthdays, holidays), use LocalDate. If you need to coordinate across time zones (meeting schedulers, flight departures), use ZonedDateTime. If you need a machine-readable timestamp for logging or databases, use Instant.
LocalDate represents a date without a time component and without a time zone. It stores a year, month, and day. This is the class you reach for when you care about what day something happens but not what time: birthdays, holidays, due dates, hire dates.
There are three primary ways to create a LocalDate:
import java.time.LocalDate;
import java.time.Month;
public class LocalDateCreation {
public static void main(String[] args) {
// 1. Current date from the system clock
LocalDate today = LocalDate.now();
System.out.println("Today: " + today); // e.g., 2026-02-28
// 2. Specific date using of()
LocalDate christmas = LocalDate.of(2026, 12, 25);
System.out.println("Christmas: " + christmas); // 2026-12-25
// Using Month enum (more readable)
LocalDate newYear = LocalDate.of(2027, Month.JANUARY, 1);
System.out.println("New Year: " + newYear); // 2027-01-01
// 3. Parsing a string (ISO 8601 format: yyyy-MM-dd)
LocalDate parsed = LocalDate.parse("2026-07-04");
System.out.println("Parsed: " + parsed); // 2026-07-04
}
}
Once you have a LocalDate, you can extract every piece of information from it:
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
public class LocalDateComponents {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, Month.FEBRUARY, 28);
System.out.println("Year: " + date.getYear()); // 2026
System.out.println("Month (enum): " + date.getMonth()); // FEBRUARY
System.out.println("Month (int): " + date.getMonthValue()); // 2
System.out.println("Day of month: " + date.getDayOfMonth()); // 28
System.out.println("Day of week: " + date.getDayOfWeek()); // SATURDAY
System.out.println("Day of year: " + date.getDayOfYear()); // 59
System.out.println("Length of month: " + date.lengthOfMonth()); // 28
System.out.println("Length of year: " + date.lengthOfYear()); // 365
System.out.println("Is leap year: " + date.isLeapYear()); // false
}
}
Because LocalDate is immutable, every manipulation method returns a new LocalDate. The original is never modified.
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class LocalDateManipulation {
public static void main(String[] args) {
LocalDate today = LocalDate.of(2026, 2, 28);
// Adding
System.out.println("Plus 1 day: " + today.plusDays(1)); // 2026-03-01
System.out.println("Plus 2 weeks: " + today.plusWeeks(2)); // 2026-03-14
System.out.println("Plus 1 month: " + today.plusMonths(1)); // 2026-03-28
System.out.println("Plus 1 year: " + today.plusYears(1)); // 2027-02-28
// Subtracting
System.out.println("Minus 10 days: " + today.minusDays(10)); // 2026-02-18
System.out.println("Minus 3 months:" + today.minusMonths(3)); // 2025-11-28
// Using ChronoUnit (flexible alternative)
System.out.println("Plus 100 days: " + today.plus(100, ChronoUnit.DAYS)); // 2026-06-09
// Replacing components with "with" methods
System.out.println("With year 2030: " + today.withYear(2030)); // 2030-02-28
System.out.println("With month 6: " + today.withMonth(6)); // 2026-06-28
System.out.println("With day 15: " + today.withDayOfMonth(15)); // 2026-02-15
// Important: the original date is unchanged (immutability)
System.out.println("Original: " + today); // 2026-02-28
}
}
import java.time.LocalDate;
public class LocalDateComparison {
public static void main(String[] args) {
LocalDate today = LocalDate.of(2026, 2, 28);
LocalDate tomorrow = LocalDate.of(2026, 3, 1);
LocalDate alsoToday = LocalDate.of(2026, 2, 28);
// isBefore, isAfter, isEqual
System.out.println(today.isBefore(tomorrow)); // true
System.out.println(today.isAfter(tomorrow)); // false
System.out.println(today.isEqual(alsoToday)); // true
// equals() works the same as isEqual() for LocalDate
System.out.println(today.equals(alsoToday)); // true
// compareTo() -- returns negative, zero, or positive
System.out.println(today.compareTo(tomorrow)); // negative (today < tomorrow)
// NEVER use == to compare dates (compares object references, not values)
// today == alsoToday --> false (different objects!)
}
}
A common real-world use case: calculating someone’s age from their birth date.
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
public class AgeCalculator {
public static int calculateAge(LocalDate birthDate) {
LocalDate today = LocalDate.now();
if (birthDate.isAfter(today)) {
throw new IllegalArgumentException("Birth date cannot be in the future");
}
return Period.between(birthDate, today).getYears();
}
public static LocalDate getNextBirthday(LocalDate birthDate) {
LocalDate today = LocalDate.now();
LocalDate nextBirthday = birthDate.withYear(today.getYear());
// If birthday already passed this year, use next year
if (nextBirthday.isBefore(today) || nextBirthday.isEqual(today)) {
nextBirthday = nextBirthday.plusYears(1);
}
return nextBirthday;
}
public static void main(String[] args) {
LocalDate birthDate = LocalDate.of(1990, Month.JULY, 15);
int age = calculateAge(birthDate);
System.out.println("Birth date: " + birthDate); // 1990-07-15
System.out.println("Age: " + age + " years"); // e.g., 35 years
LocalDate nextBirthday = getNextBirthday(birthDate);
long daysUntil = LocalDate.now().until(nextBirthday, java.time.temporal.ChronoUnit.DAYS);
System.out.println("Next birthday: " + nextBirthday); // e.g., 2026-07-15
System.out.println("Days until: " + daysUntil); // e.g., 137
}
}
LocalTime represents a time without a date and without a time zone. It stores hours, minutes, seconds, and nanoseconds. Use it when you care about what time something happens but not what day: store opening hours, alarm times, daily schedules.
import java.time.LocalTime;
public class LocalTimeCreation {
public static void main(String[] args) {
// Current time
LocalTime now = LocalTime.now();
System.out.println("Now: " + now); // e.g., 14:30:22.123456789
// Specific time (hour, minute)
LocalTime nineAM = LocalTime.of(9, 0);
System.out.println("9 AM: " + nineAM); // 09:00
// Hour, minute, second
LocalTime precise = LocalTime.of(14, 30, 45);
System.out.println("Precise: " + precise); // 14:30:45
// Hour, minute, second, nanosecond
LocalTime nano = LocalTime.of(14, 30, 45, 123456789);
System.out.println("Nano: " + nano); // 14:30:45.123456789
// Parsing (ISO format: HH:mm:ss)
LocalTime parsed = LocalTime.parse("08:15:30");
System.out.println("Parsed: " + parsed); // 08:15:30
// Special constants
System.out.println("Midnight: " + LocalTime.MIDNIGHT); // 00:00
System.out.println("Noon: " + LocalTime.NOON); // 12:00
System.out.println("Min: " + LocalTime.MIN); // 00:00
System.out.println("Max: " + LocalTime.MAX); // 23:59:59.999999999
}
}
import java.time.LocalTime;
public class LocalTimeComponents {
public static void main(String[] args) {
LocalTime time = LocalTime.of(14, 30, 45, 123456789);
System.out.println("Hour: " + time.getHour()); // 14
System.out.println("Minute: " + time.getMinute()); // 30
System.out.println("Second: " + time.getSecond()); // 45
System.out.println("Nano: " + time.getNano()); // 123456789
// Convert to seconds since midnight
System.out.println("Seconds of day: " + time.toSecondOfDay()); // 52245
// Convert to nanoseconds since midnight
System.out.println("Nanos of day: " + time.toNanoOfDay());
}
}
import java.time.LocalTime;
public class LocalTimeManipulation {
public static void main(String[] args) {
LocalTime time = LocalTime.of(10, 30);
// Adding
System.out.println("Plus 2 hours: " + time.plusHours(2)); // 12:30
System.out.println("Plus 45 minutes:" + time.plusMinutes(45)); // 11:15
System.out.println("Plus 30 seconds:" + time.plusSeconds(30)); // 10:30:30
// Subtracting
System.out.println("Minus 3 hours: " + time.minusHours(3)); // 07:30
// Time wraps around midnight
LocalTime lateNight = LocalTime.of(23, 30);
System.out.println("23:30 + 2 hours: " + lateNight.plusHours(2)); // 01:30
// Comparing
LocalTime morning = LocalTime.of(8, 0);
LocalTime evening = LocalTime.of(20, 0);
System.out.println(morning.isBefore(evening)); // true
System.out.println(morning.isAfter(evening)); // false
}
}
A utility to check if a given time falls within business hours and calculate time until closing.
import java.time.Duration;
import java.time.LocalTime;
public class BusinessHoursChecker {
private final LocalTime openTime;
private final LocalTime closeTime;
private final LocalTime lunchStart;
private final LocalTime lunchEnd;
public BusinessHoursChecker() {
this.openTime = LocalTime.of(9, 0); // 9:00 AM
this.closeTime = LocalTime.of(17, 0); // 5:00 PM
this.lunchStart = LocalTime.of(12, 0); // 12:00 PM
this.lunchEnd = LocalTime.of(13, 0); // 1:00 PM
}
public boolean isOpen(LocalTime time) {
boolean withinHours = !time.isBefore(openTime) && time.isBefore(closeTime);
boolean isLunchBreak = !time.isBefore(lunchStart) && time.isBefore(lunchEnd);
return withinHours && !isLunchBreak;
}
public String getStatus(LocalTime time) {
if (time.isBefore(openTime)) {
Duration untilOpen = Duration.between(time, openTime);
return "Closed. Opens in " + untilOpen.toMinutes() + " minutes.";
}
if (!time.isBefore(lunchStart) && time.isBefore(lunchEnd)) {
Duration untilReopen = Duration.between(time, lunchEnd);
return "Lunch break. Reopens in " + untilReopen.toMinutes() + " minutes.";
}
if (!time.isBefore(openTime) && time.isBefore(closeTime)) {
Duration untilClose = Duration.between(time, closeTime);
return "Open. Closes in " + untilClose.toHours() + "h " +
(untilClose.toMinutes() % 60) + "m.";
}
return "Closed for the day. Opens tomorrow at " + openTime;
}
public static void main(String[] args) {
BusinessHoursChecker checker = new BusinessHoursChecker();
LocalTime[] testTimes = {
LocalTime.of(7, 30), // Before opening
LocalTime.of(10, 15), // Morning hours
LocalTime.of(12, 30), // Lunch break
LocalTime.of(15, 45), // Afternoon hours
LocalTime.of(18, 0) // After closing
};
for (LocalTime time : testTimes) {
System.out.printf("%s -> Open: %-5s | %s%n",
time, checker.isOpen(time), checker.getStatus(time));
}
// Output:
// 07:30 -> Open: false | Closed. Opens in 90 minutes.
// 10:15 -> Open: true | Open. Closes in 6h 45m.
// 12:30 -> Open: false | Lunch break. Reopens in 30 minutes.
// 15:45 -> Open: true | Open. Closes in 1h 15m.
// 18:00 -> Open: false | Closed for the day. Opens tomorrow at 09:00
}
}
LocalDateTime combines LocalDate and LocalTime into one object. It represents a date-time without a time zone. Use it when you need both the date and the time but the time zone is either irrelevant or implied: appointment timestamps within a single-timezone application, database DATETIME columns, event start times.
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class LocalDateTimeCreation {
public static void main(String[] args) {
// Current date and time
LocalDateTime now = LocalDateTime.now();
System.out.println("Now: " + now); // e.g., 2026-02-28T14:30:22.123
// Specific date and time
LocalDateTime meeting = LocalDateTime.of(2026, Month.MARCH, 15, 10, 30);
System.out.println("Meeting: " + meeting); // 2026-03-15T10:30
// With seconds
LocalDateTime precise = LocalDateTime.of(2026, 3, 15, 10, 30, 45);
System.out.println("Precise: " + precise); // 2026-03-15T10:30:45
// From existing LocalDate and LocalTime
LocalDate date = LocalDate.of(2026, 6, 15);
LocalTime time = LocalTime.of(14, 0);
LocalDateTime combined = LocalDateTime.of(date, time);
System.out.println("Combined: " + combined); // 2026-06-15T14:00
// Attach time to a date
LocalDateTime startOfDay = date.atStartOfDay();
System.out.println("Start of day: " + startOfDay); // 2026-06-15T00:00
LocalDateTime atTime = date.atTime(9, 30);
System.out.println("At 9:30: " + atTime); // 2026-06-15T09:30
// Parse from string
LocalDateTime parsed = LocalDateTime.parse("2026-03-15T10:30:00");
System.out.println("Parsed: " + parsed); // 2026-03-15T10:30
}
}
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class LocalDateTimeConversion {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 15, 10, 30, 45);
// Extract date and time components
LocalDate date = dateTime.toLocalDate();
LocalTime time = dateTime.toLocalTime();
System.out.println("Date part: " + date); // 2026-03-15
System.out.println("Time part: " + time); // 10:30:45
// Manipulation works the same way
LocalDateTime nextMonth = dateTime.plusMonths(1);
System.out.println("Next month: " + nextMonth); // 2026-04-15T10:30:45
LocalDateTime twoHoursLater = dateTime.plusHours(2);
System.out.println("+2 hours: " + twoHoursLater); // 2026-03-15T12:30:45
// Replace components
LocalDateTime newTime = dateTime.withHour(8).withMinute(0);
System.out.println("Changed to 8 AM: " + newTime); // 2026-03-15T08:00:45
}
}
A simple appointment system that checks for conflicts and validates time slots.
import java.time.LocalDateTime;
import java.time.Duration;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class AppointmentScheduler {
static class Appointment {
String title;
LocalDateTime start;
LocalDateTime end;
Appointment(String title, LocalDateTime start, Duration duration) {
this.title = title;
this.start = start;
this.end = start.plus(duration);
}
boolean conflictsWith(Appointment other) {
return this.start.isBefore(other.end) && this.end.isAfter(other.start);
}
@Override
public String toString() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MMM dd, HH:mm");
return String.format("%s [%s - %s]", title, start.format(fmt), end.format(fmt));
}
}
private final List appointments = new ArrayList<>();
public boolean schedule(String title, LocalDateTime start, Duration duration) {
Appointment newAppt = new Appointment(title, start, duration);
// Check for conflicts with existing appointments
for (Appointment existing : appointments) {
if (existing.conflictsWith(newAppt)) {
System.out.println("CONFLICT: '" + title + "' overlaps with '" + existing.title + "'");
return false;
}
}
// Validate business hours (9 AM - 5 PM)
if (newAppt.start.getHour() < 9 || newAppt.end.getHour() > 17 ||
(newAppt.end.getHour() == 17 && newAppt.end.getMinute() > 0)) {
System.out.println("REJECTED: '" + title + "' falls outside business hours (9-5)");
return false;
}
appointments.add(newAppt);
System.out.println("BOOKED: " + newAppt);
return true;
}
public static void main(String[] args) {
AppointmentScheduler scheduler = new AppointmentScheduler();
LocalDateTime monday = LocalDateTime.of(2026, 3, 16, 9, 0);
scheduler.schedule("Team Standup", monday, Duration.ofMinutes(30));
// BOOKED: Team Standup [Mar 16, 09:00 - Mar 16, 09:30]
scheduler.schedule("Code Review", monday.plusHours(1), Duration.ofHours(1));
// BOOKED: Code Review [Mar 16, 10:00 - Mar 16, 11:00]
scheduler.schedule("Quick Chat", monday.plusMinutes(15), Duration.ofMinutes(20));
// CONFLICT: 'Quick Chat' overlaps with 'Team Standup'
scheduler.schedule("Late Meeting", monday.withHour(16), Duration.ofHours(2));
// REJECTED: 'Late Meeting' falls outside business hours (9-5)
scheduler.schedule("Lunch 1:1", monday.withHour(12), Duration.ofHours(1));
// BOOKED: Lunch 1:1 [Mar 16, 12:00 - Mar 16, 13:00]
}
}
ZonedDateTime is a date-time with a full time zone, such as America/New_York or Europe/London. This is the class you need whenever time zone matters: scheduling meetings across countries, flight departure/arrival times, coordinating deployments across data centers.
Java uses the IANA Time Zone Database (also known as the Olson database). Always use region-based zone IDs like America/New_York rather than abbreviations like EST, because abbreviations are ambiguous (CST could mean Central Standard Time or China Standard Time).
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
public class ZonedDateTimeCreation {
public static void main(String[] args) {
// Current date-time in the system's default zone
ZonedDateTime now = ZonedDateTime.now();
System.out.println("Now: " + now);
// e.g., 2026-02-28T14:30:00-05:00[America/New_York]
// Current date-time in a specific zone
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("Tokyo: " + tokyo);
// From LocalDateTime + ZoneId
LocalDateTime ldt = LocalDateTime.of(2026, 3, 15, 10, 30);
ZonedDateTime newYork = ldt.atZone(ZoneId.of("America/New_York"));
System.out.println("New York: " + newYork);
// 2026-03-15T10:30-04:00[America/New_York] (EDT in March)
// Using of()
ZonedDateTime london = ZonedDateTime.of(2026, 3, 15, 10, 30, 0, 0,
ZoneId.of("Europe/London"));
System.out.println("London: " + london);
// Parse from string
ZonedDateTime parsed = ZonedDateTime.parse(
"2026-03-15T10:30:00-04:00[America/New_York]");
System.out.println("Parsed: " + parsed);
// Common zone IDs
System.out.println("\nDefault zone: " + ZoneId.systemDefault());
System.out.println("UTC: " + ZoneId.of("UTC"));
}
}
This is one of the most powerful features of ZonedDateTime: converting the same instant in time to different zones. The withZoneSameInstant() method changes the zone while keeping the same moment in time. The withZoneSameLocal() method keeps the local date-time and changes the zone (rarely what you want).
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class TimeZoneConversion {
public static void main(String[] args) {
// A meeting is at 10:00 AM New York time
ZonedDateTime newYork = ZonedDateTime.of(
LocalDateTime.of(2026, 6, 15, 10, 0),
ZoneId.of("America/New_York")
);
// What time is that in other cities?
ZonedDateTime london = newYork.withZoneSameInstant(ZoneId.of("Europe/London"));
ZonedDateTime tokyo = newYork.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
ZonedDateTime sydney = newYork.withZoneSameInstant(ZoneId.of("Australia/Sydney"));
ZonedDateTime losAngeles = newYork.withZoneSameInstant(ZoneId.of("America/Los_Angeles"));
System.out.println("Meeting time across zones:");
System.out.println("New York: " + newYork); // 10:00 EDT (-04:00)
System.out.println("London: " + london); // 15:00 BST (+01:00)
System.out.println("Tokyo: " + tokyo); // 23:00 JST (+09:00)
System.out.println("Sydney: " + sydney); // 00:00+1 AEST (+10:00)
System.out.println("Los Angeles: " + losAngeles); // 07:00 PDT (-07:00)
}
}
ZonedDateTime automatically handles daylight saving time (DST) transitions. This is critical for correctness — naive date-time calculations that ignore DST can be off by an hour.
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Duration;
public class DaylightSavingDemo {
public static void main(String[] args) {
ZoneId eastern = ZoneId.of("America/New_York");
// Spring Forward: March 8, 2026 at 2:00 AM -> clocks jump to 3:00 AM
ZonedDateTime beforeSpringForward = ZonedDateTime.of(
LocalDateTime.of(2026, 3, 8, 1, 30), eastern);
ZonedDateTime afterSpringForward = beforeSpringForward.plusHours(1);
System.out.println("Before spring forward: " + beforeSpringForward);
// 2026-03-08T01:30-05:00[America/New_York]
System.out.println("After +1 hour: " + afterSpringForward);
// 2026-03-08T03:30-04:00[America/New_York] (skipped 2:30!)
// Duration correctly accounts for DST
Duration gap = Duration.between(beforeSpringForward, afterSpringForward);
System.out.println("Actual duration: " + gap); // PT1H (still 1 hour of real time)
// Fall Back: November 1, 2026 at 2:00 AM -> clocks fall back to 1:00 AM
ZonedDateTime beforeFallBack = ZonedDateTime.of(
LocalDateTime.of(2026, 11, 1, 0, 30), eastern);
ZonedDateTime afterFallBack = beforeFallBack.plusHours(2);
System.out.println("\nBefore fall back: " + beforeFallBack);
System.out.println("After +2 hours: " + afterFallBack);
}
}
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
public class MeetingScheduler {
static class Participant {
String name;
ZoneId zone;
LocalTime workStart;
LocalTime workEnd;
Participant(String name, String zoneId) {
this.name = name;
this.zone = ZoneId.of(zoneId);
this.workStart = LocalTime.of(9, 0);
this.workEnd = LocalTime.of(17, 0);
}
boolean isAvailable(ZonedDateTime meetingTime, Duration duration) {
ZonedDateTime localMeeting = meetingTime.withZoneSameInstant(zone);
LocalTime meetStart = localMeeting.toLocalTime();
LocalTime meetEnd = meetStart.plus(duration);
return !meetStart.isBefore(workStart) && !meetEnd.isAfter(workEnd);
}
}
public static void findMeetingTime(List participants,
LocalDate date, Duration duration) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm z");
System.out.println("Finding " + duration.toMinutes() + "-minute slot on " + date);
System.out.println("---");
// Try every hour from 0:00 to 23:00 UTC
for (int hour = 0; hour < 24; hour++) {
ZonedDateTime candidate = ZonedDateTime.of(date, LocalTime.of(hour, 0),
ZoneId.of("UTC"));
boolean allAvailable = participants.stream()
.allMatch(p -> p.isAvailable(candidate, duration));
if (allAvailable) {
System.out.println("AVAILABLE SLOT:");
for (Participant p : participants) {
ZonedDateTime local = candidate.withZoneSameInstant(p.zone);
System.out.printf(" %-12s -> %s%n", p.name, local.format(fmt));
}
System.out.println();
}
}
}
public static void main(String[] args) {
List team = Arrays.asList(
new Participant("Alice", "America/New_York"),
new Participant("Bob", "Europe/London"),
new Participant("Chika", "Asia/Tokyo")
);
findMeetingTime(team, LocalDate.of(2026, 6, 15), Duration.ofHours(1));
// Output: Shows overlapping work hours across all three zones
// The only common slot is typically around 21:00-23:00 UTC
// (morning in Tokyo, afternoon in London, morning in New York)
}
}
Instant represents a single point on the timeline, measured as nanoseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC). It has no concept of human time zones, months, or days — it is purely a machine timestamp. Use it for logging, measuring elapsed time, recording when events occurred, and storing timestamps in databases.
import java.time.Instant;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class InstantDemo {
public static void main(String[] args) {
// Current instant (always in UTC)
Instant now = Instant.now();
System.out.println("Now: " + now);
// e.g., 2026-02-28T19:30:00.123456789Z (the Z means UTC)
// From epoch seconds
Instant epoch = Instant.EPOCH;
System.out.println("Epoch: " + epoch); // 1970-01-01T00:00:00Z
Instant fromSeconds = Instant.ofEpochSecond(1_000_000_000);
System.out.println("1 billion seconds: " + fromSeconds); // 2001-09-09T01:46:40Z
// From epoch milliseconds (common in legacy APIs and JavaScript)
Instant fromMillis = Instant.ofEpochMilli(System.currentTimeMillis());
System.out.println("From millis: " + fromMillis);
// Getting epoch values
System.out.println("Epoch seconds: " + now.getEpochSecond());
System.out.println("Nano adjustment: " + now.getNano());
// Arithmetic
Instant oneHourLater = now.plus(Duration.ofHours(1));
Instant yesterday = now.minus(Duration.ofDays(1));
// Duration between two instants
Duration between = Duration.between(yesterday, now);
System.out.println("Between: " + between); // PT24H
// Convert to ZonedDateTime for human-readable display
ZonedDateTime zdt = now.atZone(ZoneId.of("America/New_York"));
System.out.println("As New York time: " + zdt);
}
}
Instant is the ideal tool for benchmarking and measuring how long operations take.
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ExecutionTimer {
@FunctionalInterface
interface Task {
void run();
}
public static Duration measure(Task task) {
Instant start = Instant.now();
task.run();
Instant end = Instant.now();
return Duration.between(start, end);
}
public static void main(String[] args) {
int size = 100_000;
// Measure ArrayList add performance
Duration arrayListTime = measure(() -> {
List list = new ArrayList<>();
for (int i = 0; i < size; i++) {
list.add(0, i); // Insert at beginning (worst case)
}
});
// Measure LinkedList add performance
Duration linkedListTime = measure(() -> {
List list = new LinkedList<>();
for (int i = 0; i < size; i++) {
list.add(0, i); // Insert at beginning (best case for linked list)
}
});
System.out.println("Inserting " + size + " elements at index 0:");
System.out.println("ArrayList: " + arrayListTime.toMillis() + " ms");
System.out.println("LinkedList: " + linkedListTime.toMillis() + " ms");
// Example output:
// ArrayList: 1245 ms
// LinkedList: 12 ms
}
}
Java provides two classes for representing amounts of time:
Period -- a date-based amount of time (years, months, days). Think: "2 years, 3 months, and 5 days"Duration -- a time-based amount of time (hours, minutes, seconds, nanoseconds). Think: "2 hours and 30 minutes"The distinction matters because date-based arithmetic and time-based arithmetic behave differently. Adding "1 month" to January 31 gives February 28 (or 29), but adding "30 days" gives March 2. These are different operations.
import java.time.LocalDate;
import java.time.Period;
public class PeriodDemo {
public static void main(String[] args) {
// Creating Periods
Period twoYears = Period.ofYears(2);
Period threeMonths = Period.ofMonths(3);
Period tenDays = Period.ofDays(10);
Period custom = Period.of(1, 6, 15); // 1 year, 6 months, 15 days
System.out.println("Custom: " + custom); // P1Y6M15D (ISO 8601)
// Period between two dates
LocalDate startDate = LocalDate.of(2024, 1, 1);
LocalDate endDate = LocalDate.of(2026, 3, 15);
Period between = Period.between(startDate, endDate);
System.out.println("Between: " + between); // P2Y2M14D
System.out.println("Years: " + between.getYears()); // 2
System.out.println("Months: " + between.getMonths()); // 2
System.out.println("Days: " + between.getDays()); // 14
System.out.println("Total months: " + between.toTotalMonths()); // 26
// Applying a Period to a date
LocalDate today = LocalDate.of(2026, 2, 28);
LocalDate future = today.plus(custom);
System.out.println("Today + " + custom + " = " + future);
// 2026-02-28 + P1Y6M15D = 2027-09-12
// Period arithmetic
Period doubled = custom.multipliedBy(2);
System.out.println("Doubled: " + doubled); // P2Y12M30D
Period negated = custom.negated();
System.out.println("Negated: " + negated); // P-1Y-6M-15D
// Checking if zero
System.out.println("Is zero: " + Period.ZERO.isZero()); // true
System.out.println("Is negative: " + negated.isNegative()); // true
}
}
import java.time.Duration;
import java.time.LocalTime;
import java.time.Instant;
public class DurationDemo {
public static void main(String[] args) {
// Creating Durations
Duration twoHours = Duration.ofHours(2);
Duration thirtyMinutes = Duration.ofMinutes(30);
Duration fiveSeconds = Duration.ofSeconds(5);
Duration halfSecond = Duration.ofMillis(500);
System.out.println("2 hours: " + twoHours); // PT2H
System.out.println("30 minutes: " + thirtyMinutes); // PT30M
System.out.println("5 seconds: " + fiveSeconds); // PT5S
// Duration between two times
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
Duration workDay = Duration.between(start, end);
System.out.println("Work day: " + workDay); // PT8H30M
System.out.println("Hours: " + workDay.toHours()); // 8
System.out.println("Minutes: " + workDay.toMinutes()); // 510
System.out.println("Seconds: " + workDay.getSeconds()); // 30600
// Java 9+ part extraction
System.out.println("Hours part: " + workDay.toHoursPart()); // 8
System.out.println("Minutes part: " + workDay.toMinutesPart()); // 30
// Duration between two Instants
Instant i1 = Instant.parse("2026-02-28T10:00:00Z");
Instant i2 = Instant.parse("2026-02-28T14:30:00Z");
Duration gap = Duration.between(i1, i2);
System.out.println("Gap: " + gap); // PT4H30M
// Arithmetic
Duration total = twoHours.plus(thirtyMinutes);
System.out.println("Total: " + total); // PT2H30M
Duration doubled = twoHours.multipliedBy(2);
System.out.println("Doubled: " + doubled); // PT4H
// Apply to time
LocalTime lunchEnd = LocalTime.of(13, 0);
LocalTime afternoonEnd = lunchEnd.plus(Duration.ofHours(4));
System.out.println("Afternoon ends: " + afternoonEnd); // 17:00
}
}
| Aspect | Period | Duration |
|---|---|---|
| Measures | Years, months, days | Hours, minutes, seconds, nanos |
| Works with | LocalDate, LocalDateTime |
LocalTime, LocalDateTime, Instant |
| Use case | Human calendar concepts | Precise time measurements |
| Example | "Renew subscription in 1 year" | "Session expires in 30 minutes" |
| ISO format | P1Y2M3D | PT2H30M15S |
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;
public class SubscriptionManager {
enum Plan {
TRIAL(Period.ofDays(14)),
MONTHLY(Period.ofMonths(1)),
YEARLY(Period.ofYears(1));
final Period duration;
Plan(Period duration) { this.duration = duration; }
}
static class Subscription {
String user;
Plan plan;
LocalDate startDate;
LocalDate expiryDate;
Subscription(String user, Plan plan, LocalDate startDate) {
this.user = user;
this.plan = plan;
this.startDate = startDate;
this.expiryDate = startDate.plus(plan.duration);
}
boolean isActive(LocalDate today) {
return !today.isAfter(expiryDate);
}
long daysRemaining(LocalDate today) {
if (!isActive(today)) return 0;
return ChronoUnit.DAYS.between(today, expiryDate);
}
Subscription renew() {
return new Subscription(user, plan, expiryDate);
}
}
public static void main(String[] args) {
LocalDate today = LocalDate.of(2026, 2, 28);
Subscription trial = new Subscription("Alice", Plan.TRIAL, today);
Subscription monthly = new Subscription("Bob", Plan.MONTHLY, today);
Subscription yearly = new Subscription("Charlie", Plan.YEARLY, today);
System.out.printf("%-10s | Plan: %-8s | Expires: %s | Days left: %d%n",
trial.user, trial.plan, trial.expiryDate, trial.daysRemaining(today));
System.out.printf("%-10s | Plan: %-8s | Expires: %s | Days left: %d%n",
monthly.user, monthly.plan, monthly.expiryDate, monthly.daysRemaining(today));
System.out.printf("%-10s | Plan: %-8s | Expires: %s | Days left: %d%n",
yearly.user, yearly.plan, yearly.expiryDate, yearly.daysRemaining(today));
// Output:
// Alice | Plan: TRIAL | Expires: 2026-03-14 | Days left: 14
// Bob | Plan: MONTHLY | Expires: 2026-03-28 | Days left: 28
// Charlie | Plan: YEARLY | Expires: 2027-02-28 | Days left: 365
// Renew Bob's subscription
Subscription renewed = monthly.renew();
System.out.println("\nBob renewed: expires " + renewed.expiryDate);
// Bob renewed: expires 2026-04-28
}
}
DateTimeFormatter is the java.time replacement for SimpleDateFormat. Unlike its predecessor, it is immutable and thread-safe -- you can safely share a single formatter instance across threads.
Java provides several built-in formatters for common ISO 8601 formats:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class PredefinedFormatters {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 3, 15);
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 15, 14, 30, 0);
ZonedDateTime zdt = dateTime.atZone(ZoneId.of("America/New_York"));
// ISO formatters (most common for APIs and data exchange)
System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
// 2026-03-15
System.out.println(dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// 2026-03-15T14:30:00
System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// 2026-03-15T14:30:00-04:00[America/New_York]
System.out.println(date.format(DateTimeFormatter.ISO_ORDINAL_DATE));
// 2026-074 (day 74 of the year)
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
// 20260315
}
}
For human-readable formats, you define your own pattern using DateTimeFormatter.ofPattern(). Here are the most commonly used pattern symbols:
| Symbol | Meaning | Example |
|---|---|---|
yyyy |
4-digit year | 2026 |
yy |
2-digit year | 26 |
MM |
Month (01-12) | 03 |
MMM |
Abbreviated month | Mar |
MMMM |
Full month name | March |
dd |
Day of month (01-31) | 15 |
EEE |
Abbreviated day | Sun |
EEEE |
Full day name | Sunday |
HH |
Hour (00-23) | 14 |
hh |
Hour (01-12) | 02 |
mm |
Minute (00-59) | 30 |
ss |
Second (00-59) | 45 |
a |
AM/PM | PM |
z |
Time zone name | EDT |
Z |
Zone offset | -0400 |
VV |
Zone ID | America/New_York |
Critical warning: MM is months, mm is minutes. Mixing them up is one of the most common date/time bugs. The pattern "yyyy-mm-dd" gives you "2026-30-15" (using minutes instead of months).
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class CustomFormatting {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 15, 14, 30, 45);
// Common custom patterns
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println(dateTime.format(f1)); // 15/03/2026
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm");
System.out.println(dateTime.format(f2)); // 03-15-2026 14:30
DateTimeFormatter f3 = DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy");
System.out.println(dateTime.format(f3)); // Sunday, March 15, 2026
DateTimeFormatter f4 = DateTimeFormatter.ofPattern("hh:mm a");
System.out.println(dateTime.format(f4)); // 02:30 PM
DateTimeFormatter f5 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dateTime.format(f5)); // 2026/03/15 14:30:45
// Locale-specific formatting
DateTimeFormatter german = DateTimeFormatter.ofPattern(
"EEEE, dd. MMMM yyyy", Locale.GERMAN);
System.out.println(dateTime.format(german)); // Sonntag, 15. März 2026
DateTimeFormatter french = DateTimeFormatter.ofPattern(
"dd MMMM yyyy", Locale.FRENCH);
System.out.println(dateTime.format(french)); // 15 mars 2026
// With time zone
ZonedDateTime zdt = dateTime.atZone(ZoneId.of("America/New_York"));
DateTimeFormatter withZone = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss z (VV)");
System.out.println(zdt.format(withZone));
// 2026-03-15 14:30:45 EDT (America/New_York)
}
}
Parsing is the reverse of formatting: converting a String into a date/time object. If the string does not match the expected format, a DateTimeParseException is thrown.
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class ParsingDates {
public static void main(String[] args) {
// Parse ISO format (no formatter needed)
LocalDate date = LocalDate.parse("2026-03-15");
System.out.println("Parsed ISO: " + date); // 2026-03-15
// Parse custom format
DateTimeFormatter slashFormat = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate custom = LocalDate.parse("15/03/2026", slashFormat);
System.out.println("Parsed custom: " + custom); // 2026-03-15
DateTimeFormatter usFormat = DateTimeFormatter.ofPattern("MM-dd-yyyy");
LocalDate usDate = LocalDate.parse("03-15-2026", usFormat);
System.out.println("Parsed US: " + usDate); // 2026-03-15
DateTimeFormatter fullFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("2026/03/15 14:30:45", fullFormat);
System.out.println("Parsed datetime: " + dateTime); // 2026-03-15T14:30:45
// Always handle parse exceptions in production code
try {
LocalDate bad = LocalDate.parse("not-a-date");
} catch (DateTimeParseException e) {
System.out.println("Parse error: " + e.getMessage());
// Text 'not-a-date' could not be parsed at index 0
}
try {
// Wrong format: trying to parse US format with slash formatter
LocalDate wrong = LocalDate.parse("03-15-2026", slashFormat);
} catch (DateTimeParseException e) {
System.out.println("Format mismatch: " + e.getMessage());
}
}
}
In real applications, you frequently need to convert between the different date/time types. Here is a comprehensive reference for all common conversions.
import java.time.*;
public class TypeConversions {
public static void main(String[] args) {
// ============================================
// LocalDate <-> LocalDateTime
// ============================================
LocalDate date = LocalDate.of(2026, 3, 15);
// LocalDate -> LocalDateTime (add time)
LocalDateTime atStartOfDay = date.atStartOfDay(); // 2026-03-15T00:00
LocalDateTime atSpecificTime = date.atTime(14, 30); // 2026-03-15T14:30
LocalDateTime atLocalTime = date.atTime(LocalTime.NOON); // 2026-03-15T12:00
// LocalDateTime -> LocalDate (drop time)
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 15, 14, 30);
LocalDate dateOnly = dateTime.toLocalDate(); // 2026-03-15
LocalTime timeOnly = dateTime.toLocalTime(); // 14:30
// ============================================
// LocalDateTime <-> ZonedDateTime
// ============================================
// LocalDateTime -> ZonedDateTime (add zone)
ZonedDateTime zoned = dateTime.atZone(ZoneId.of("America/New_York"));
// 2026-03-15T14:30-04:00[America/New_York]
// ZonedDateTime -> LocalDateTime (drop zone)
LocalDateTime local = zoned.toLocalDateTime(); // 2026-03-15T14:30
// ZonedDateTime -> LocalDate / LocalTime
LocalDate zonedDate = zoned.toLocalDate(); // 2026-03-15
LocalTime zonedTime = zoned.toLocalTime(); // 14:30
// ============================================
// ZonedDateTime <-> Instant
// ============================================
// ZonedDateTime -> Instant (to UTC epoch)
Instant instant = zoned.toInstant();
System.out.println("Instant: " + instant);
// 2026-03-15T18:30:00Z (14:30 EDT = 18:30 UTC)
// Instant -> ZonedDateTime (add zone)
ZonedDateTime fromInstant = instant.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println("Tokyo: " + fromInstant);
// 2026-03-16T03:30+09:00[Asia/Tokyo]
// ============================================
// Legacy Date <-> Modern Types
// ============================================
java.util.Date legacyDate = new java.util.Date();
// java.util.Date -> Instant
Instant fromLegacy = legacyDate.toInstant();
// Instant -> java.util.Date
java.util.Date backToLegacy = java.util.Date.from(fromLegacy);
// java.util.Date -> LocalDate (via Instant + zone)
LocalDate fromLegacyDate = legacyDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
// LocalDate -> java.util.Date (via Instant + zone)
java.util.Date toLegacyDate = java.util.Date.from(
date.atStartOfDay(ZoneId.systemDefault()).toInstant());
// java.sql.Date <-> LocalDate (direct conversion)
java.sql.Date sqlDate = java.sql.Date.valueOf(date);
LocalDate fromSql = sqlDate.toLocalDate();
// java.sql.Timestamp <-> LocalDateTime
java.sql.Timestamp timestamp = java.sql.Timestamp.valueOf(dateTime);
LocalDateTime fromTimestamp = timestamp.toLocalDateTime();
System.out.println("All conversions successful!");
}
}
| From | To | Method |
|---|---|---|
LocalDate |
LocalDateTime |
date.atStartOfDay() or date.atTime(14, 30) |
LocalDateTime |
LocalDate |
dateTime.toLocalDate() |
LocalDateTime |
LocalTime |
dateTime.toLocalTime() |
LocalDateTime |
ZonedDateTime |
dateTime.atZone(zoneId) |
ZonedDateTime |
LocalDateTime |
zoned.toLocalDateTime() |
ZonedDateTime |
Instant |
zoned.toInstant() |
Instant |
ZonedDateTime |
instant.atZone(zoneId) |
java.util.Date |
Instant |
legacyDate.toInstant() |
Instant |
java.util.Date |
Date.from(instant) |
java.sql.Date |
LocalDate |
sqlDate.toLocalDate() |
LocalDate |
java.sql.Date |
java.sql.Date.valueOf(date) |
Here are the operations you will use most often in real-world Java applications. Each one comes up regularly in business logic, reporting, and scheduling systems.
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class MonthBoundaries {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 3, 15);
LocalDate firstDay = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("Date: " + date); // 2026-03-15
System.out.println("First day: " + firstDay); // 2026-03-01
System.out.println("Last day: " + lastDay); // 2026-03-31
// First and last day of year
LocalDate firstOfYear = date.with(TemporalAdjusters.firstDayOfYear());
LocalDate lastOfYear = date.with(TemporalAdjusters.lastDayOfYear());
System.out.println("First of year: " + firstOfYear); // 2026-01-01
System.out.println("Last of year: " + lastOfYear); // 2026-12-31
// First day of next month
LocalDate firstOfNext = date.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println("First of next month: " + firstOfNext); // 2026-04-01
}
}
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class DayOfWeekOperations {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 2, 28); // Saturday
// Next Monday (strictly after the given date)
LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("Next Monday: " + nextMonday); // 2026-03-02
// Next or same Monday (returns same date if it's already Monday)
LocalDate nextOrSame = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY));
System.out.println("Next or same Saturday: " + nextOrSame); // 2026-02-28 (already Sat)
// Previous Friday
LocalDate prevFriday = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
System.out.println("Previous Friday: " + prevFriday); // 2026-02-27
// First Monday of the month
LocalDate firstMonday = date.with(
TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println("First Monday: " + firstMonday); // 2026-02-02
// Last Friday of the month
LocalDate lastFriday = date.with(
TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
System.out.println("Last Friday: " + lastFriday); // 2026-02-27
}
}
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class BusinessDayCalculator {
public static boolean isWeekend(LocalDate date) {
DayOfWeek day = date.getDayOfWeek();
return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
}
public static boolean isBusinessDay(LocalDate date) {
return !isWeekend(date);
// In production, also check a holiday calendar
}
public static LocalDate addBusinessDays(LocalDate start, int days) {
LocalDate result = start;
int added = 0;
while (added < days) {
result = result.plusDays(1);
if (isBusinessDay(result)) {
added++;
}
}
return result;
}
public static long countBusinessDays(LocalDate start, LocalDate end) {
long count = 0;
LocalDate current = start;
while (!current.isAfter(end)) {
if (isBusinessDay(current)) {
count++;
}
current = current.plusDays(1);
}
return count;
}
public static void main(String[] args) {
LocalDate today = LocalDate.of(2026, 2, 28); // Saturday
System.out.println(today + " is weekend: " + isWeekend(today)); // true
// Add 5 business days (skip weekends)
LocalDate deadline = addBusinessDays(today, 5);
System.out.println("5 business days from " + today + ": " + deadline);
// 2026-03-06 (Friday)
// Count business days in March 2026
LocalDate marchStart = LocalDate.of(2026, 3, 1);
LocalDate marchEnd = LocalDate.of(2026, 3, 31);
long businessDays = countBusinessDays(marchStart, marchEnd);
System.out.println("Business days in March 2026: " + businessDays); // 22
}
}
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DateRanges {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2026, 3, 1);
LocalDate end = LocalDate.of(2026, 3, 7);
// Java 9+ datesUntil -- stream of dates from start (inclusive) to end (exclusive)
List week = start.datesUntil(end.plusDays(1))
.collect(Collectors.toList());
System.out.println("Week: " + week);
// [2026-03-01, 2026-03-02, ..., 2026-03-07]
// Every Monday in March 2026
LocalDate marchStart = LocalDate.of(2026, 3, 1);
LocalDate marchEnd = LocalDate.of(2026, 3, 31);
List mondays = marchStart.datesUntil(marchEnd.plusDays(1))
.filter(d -> d.getDayOfWeek() == java.time.DayOfWeek.MONDAY)
.collect(Collectors.toList());
System.out.println("Mondays in March: " + mondays);
// [2026-03-02, 2026-03-09, 2026-03-16, 2026-03-23, 2026-03-30]
// Days between two dates
long daysBetween = ChronoUnit.DAYS.between(start, end);
System.out.println("Days between: " + daysBetween); // 6
}
}
Before Java 8, the only date/time classes in the standard library were java.util.Date and java.util.Calendar. You will still encounter them in legacy codebases, older libraries, and frameworks that predate Java 8. Here is what you need to know to work with them and migrate away from them.
Date and Calendar can be modified after creation, leading to bugs when objects are sharedDate.getYear() returns year minus 1900, months are 0-indexed (January = 0)SimpleDateFormat is notorious for causing subtle concurrency bugsDate represents both a date and a time, Calendar mixes concernsimport java.util.Date;
import java.util.Calendar;
import java.text.SimpleDateFormat;
public class LegacyDateProblems {
public static void main(String[] args) throws Exception {
// Problem 1: Mutable objects
Date date = new Date();
Date copy = date; // Not a copy! Same reference.
copy.setTime(0); // This also changes 'date'!
System.out.println("Original date changed: " + date);
// Thu Jan 01 00:00:00 UTC 1970 (modified!)
// Problem 2: Confusing month indexing (0-based)
Calendar cal = Calendar.getInstance();
cal.set(2026, 0, 15); // January is 0, not 1!
System.out.println("Month confusion: " + cal.getTime());
// This is WRONG -- creates February 15, not January!
cal.set(2026, 1, 15); // 1 = February
// Problem 3: getYear() returns year minus 1900
Date now = new Date();
System.out.println("getYear(): " + now.getYear()); // 126, not 2026!
// Problem 4: SimpleDateFormat is not thread-safe
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// DANGER: sharing this across threads causes data corruption
// Use DateTimeFormatter instead (thread-safe)
}
}
When you encounter legacy date code, convert to java.time as early as possible and convert back only at the boundaries (e.g., when calling an old API that requires Date).
import java.time.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class LegacyMigration {
public static void main(String[] args) {
// ============================================
// java.util.Date <-> java.time
// ============================================
// Legacy Date -> Instant -> LocalDate / LocalDateTime / ZonedDateTime
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("America/New_York"));
System.out.println("Legacy Date: " + legacyDate);
System.out.println("As LocalDate: " + localDate);
System.out.println("As LocalDateTime: " + localDateTime);
System.out.println("As ZonedDateTime: " + zonedDateTime);
// Modern -> Legacy Date
Date backToDate = Date.from(
LocalDate.of(2026, 3, 15)
.atStartOfDay(ZoneId.systemDefault())
.toInstant()
);
System.out.println("Back to Date: " + backToDate);
// ============================================
// java.util.Calendar <-> java.time
// ============================================
// Calendar -> ZonedDateTime (GregorianCalendar has a direct method)
Calendar calendar = Calendar.getInstance();
if (calendar instanceof GregorianCalendar) {
ZonedDateTime fromCal = ((GregorianCalendar) calendar).toZonedDateTime();
System.out.println("Calendar -> ZonedDateTime: " + fromCal);
}
// ZonedDateTime -> Calendar
ZonedDateTime zdt = ZonedDateTime.of(2026, 3, 15, 10, 30, 0, 0,
ZoneId.of("America/New_York"));
GregorianCalendar fromZdt = GregorianCalendar.from(zdt);
System.out.println("ZonedDateTime -> Calendar: " + fromZdt.getTime());
}
}
Even with the improved java.time API, there are pitfalls that catch developers at every experience level. Here are the most common ones and how to avoid them.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class MistakeFormatterCase {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2026, 3, 15, 14, 30);
// WRONG: mm is minutes, not months!
DateTimeFormatter wrong = DateTimeFormatter.ofPattern("yyyy-mm-dd");
System.out.println("Wrong: " + dateTime.format(wrong)); // 2026-30-15 (oops!)
// CORRECT: MM is months
DateTimeFormatter correct = DateTimeFormatter.ofPattern("yyyy-MM-dd");
System.out.println("Correct: " + dateTime.format(correct)); // 2026-03-15
}
}
import java.time.LocalDate;
public class MistakeImmutability {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2026, 3, 15);
// WRONG: plusDays() returns a NEW object, it does NOT modify 'date'
date.plusDays(10); // Return value is thrown away!
System.out.println("Still original: " + date); // 2026-03-15
// CORRECT: capture the return value
LocalDate future = date.plusDays(10);
System.out.println("New date: " + future); // 2026-03-25
}
}
import java.time.LocalDate;
public class MistakeEquality {
public static void main(String[] args) {
LocalDate date1 = LocalDate.parse("2026-03-15");
LocalDate date2 = LocalDate.parse("2026-03-15");
// WRONG: == compares object references
System.out.println("== result: " + (date1 == date2)); // May be false!
// CORRECT: use equals() or isEqual()
System.out.println("equals: " + date1.equals(date2)); // true
System.out.println("isEqual: " + date1.isEqual(date2)); // true
}
}
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Optional;
public class MistakeParseException {
// BAD: lets exception propagate unhandled
public static LocalDate parseDateUnsafe(String input) {
return LocalDate.parse(input); // Throws DateTimeParseException if invalid
}
// GOOD: handles the exception gracefully
public static Optional parseDateSafe(String input, DateTimeFormatter formatter) {
try {
return Optional.of(LocalDate.parse(input, formatter));
} catch (DateTimeParseException e) {
System.out.println("Invalid date: '" + input + "' - " + e.getMessage());
return Optional.empty();
}
}
public static void main(String[] args) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
parseDateSafe("2026-03-15", fmt).ifPresent(d ->
System.out.println("Parsed: " + d)); // Parsed: 2026-03-15
parseDateSafe("not-a-date", fmt); // Invalid date: 'not-a-date' - ...
parseDateSafe("2026-13-01", fmt); // Invalid date: '2026-13-01' - ... (no month 13)
parseDateSafe("2026-02-30", fmt); // Invalid date: '2026-02-30' - ... (Feb has no 30th)
}
}
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MistakeWrongType {
public static void main(String[] args) {
// BAD: Using LocalDateTime for a meeting across time zones
LocalDateTime meeting = LocalDateTime.of(2026, 6, 15, 10, 0);
// Is this 10 AM in New York? London? Tokyo? Nobody knows!
// GOOD: Using ZonedDateTime makes the time zone explicit
ZonedDateTime properMeeting = ZonedDateTime.of(2026, 6, 15, 10, 0, 0, 0,
ZoneId.of("America/New_York"));
// Unambiguous: 10 AM Eastern time
System.out.println("Ambiguous: " + meeting);
System.out.println("Clear: " + properMeeting);
}
}
import java.time.LocalDate;
import java.util.Date;
public class MistakeMutableField {
// BAD: legacy Date is mutable -- callers can modify your internal state
static class BadEvent {
private Date eventDate;
public BadEvent(Date date) { this.eventDate = date; }
public Date getEventDate() { return eventDate; } // Exposes mutable reference!
}
// GOOD: use LocalDate (immutable) -- no defensive copying needed
static class GoodEvent {
private final LocalDate eventDate;
public GoodEvent(LocalDate date) { this.eventDate = date; }
public LocalDate getEventDate() { return eventDate; } // Safe! Immutable.
}
public static void main(String[] args) {
// Demonstrating the problem with mutable Date
Date mutableDate = new Date();
BadEvent bad = new BadEvent(mutableDate);
System.out.println("Before: " + bad.getEventDate());
bad.getEventDate().setTime(0); // Caller modifies internal state!
System.out.println("After: " + bad.getEventDate());
// Thu Jan 01 00:00:00 UTC 1970 -- internal state corrupted!
// No such problem with LocalDate
GoodEvent good = new GoodEvent(LocalDate.of(2026, 3, 15));
// good.getEventDate() returns an immutable object -- nothing to corrupt
}
}
These guidelines will help you write correct, maintainable date/time code across any Java project.
| # | Practice | Reason |
|---|---|---|
| 1 | Use java.time for all new code |
Immutable, thread-safe, and well-designed. Never use java.util.Date or Calendar in new code. |
| 2 | Store timestamps as UTC in databases | Avoids ambiguity. Convert to local time zones only at the presentation layer. |
| 3 | Use ISO 8601 format for APIs | yyyy-MM-ddTHH:mm:ssZ is universally understood and sortable. It is the default format for all java.time toString() methods. |
| 4 | Use the most specific type | LocalDate for dates-only, LocalTime for times-only, ZonedDateTime for user-facing date-times, Instant for machine timestamps. |
| 5 | Use IANA zone IDs, not abbreviations | "America/New_York" not "EST". Abbreviations are ambiguous and do not handle DST correctly. |
| 6 | Always handle DateTimeParseException |
User input and external data can always be malformed. Never let parse exceptions crash your application. |
| 7 | Reuse DateTimeFormatter instances |
Formatters are immutable and thread-safe. Create them as static final constants and share them. |
| 8 | Use Clock for testable code |
Instead of LocalDate.now(), inject a Clock so you can test with fixed times: LocalDate.now(clock). |
| 9 | Prefer isBefore()/isAfter() over compareTo() |
More readable and less error-prone than checking if compareTo() returns negative/positive. |
| 10 | Never use == to compare date objects |
Always use equals() or isEqual(). The == operator compares references, not values. |
Hardcoding LocalDate.now() makes your code impossible to test deterministically. Inject a Clock instead:
import java.time.*;
public class TestableService {
private final Clock clock;
// Production constructor uses system clock
public TestableService() {
this(Clock.systemDefaultZone());
}
// Test constructor accepts any clock
public TestableService(Clock clock) {
this.clock = clock;
}
public boolean isWeekend() {
DayOfWeek day = LocalDate.now(clock).getDayOfWeek();
return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
}
public boolean isExpired(LocalDate expiryDate) {
return LocalDate.now(clock).isAfter(expiryDate);
}
public static void main(String[] args) {
// Production usage
TestableService prod = new TestableService();
System.out.println("Is weekend (real): " + prod.isWeekend());
// Test: freeze time to a known Wednesday
Clock fixedClock = Clock.fixed(
LocalDate.of(2026, 3, 11).atStartOfDay(ZoneId.systemDefault()).toInstant(),
ZoneId.systemDefault()
);
TestableService test = new TestableService(fixedClock);
System.out.println("Is weekend (Wed): " + test.isWeekend()); // false
// Test: freeze time to a known Saturday
Clock saturdayClock = Clock.fixed(
LocalDate.of(2026, 3, 14).atStartOfDay(ZoneId.systemDefault()).toInstant(),
ZoneId.systemDefault()
);
TestableService satTest = new TestableService(saturdayClock);
System.out.println("Is weekend (Sat): " + satTest.isWeekend()); // true
}
}
Let us bring everything together with a realistic event scheduling system that demonstrates most of the concepts covered in this tutorial. This system creates events with dates and times, handles time zones, checks for conflicts, formats output, and calculates durations.
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
public class EventSchedulingSystem {
// ============================================
// Event class
// ============================================
static class Event {
private final String name;
private final ZonedDateTime start;
private final Duration duration;
private final String organizer;
public Event(String name, ZonedDateTime start, Duration duration, String organizer) {
if (name == null || name.isBlank()) throw new IllegalArgumentException("Name required");
if (start.isBefore(ZonedDateTime.now())) {
// Allow past events for demo purposes, but warn
System.out.println(" [WARN] Event '" + name + "' is in the past.");
}
this.name = name;
this.start = start;
this.duration = duration;
this.organizer = organizer;
}
public String getName() { return name; }
public ZonedDateTime getStart() { return start; }
public ZonedDateTime getEnd() { return start.plus(duration); }
public Duration getDuration() { return duration; }
public String getOrganizer() { return organizer; }
public boolean conflictsWith(Event other) {
return this.getStart().isBefore(other.getEnd()) &&
this.getEnd().isAfter(other.getStart());
}
public String formatForZone(ZoneId zone) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE, MMM dd yyyy 'at' hh:mm a z");
ZonedDateTime localStart = start.withZoneSameInstant(zone);
ZonedDateTime localEnd = getEnd().withZoneSameInstant(zone);
DateTimeFormatter timeFmt = DateTimeFormatter.ofPattern("hh:mm a z");
return String.format("%s: %s - %s",
name, localStart.format(fmt), localEnd.format(timeFmt));
}
@Override
public String toString() {
return formatForZone(start.getZone());
}
}
// ============================================
// EventScheduler class
// ============================================
static class EventScheduler {
private final List events = new ArrayList<>();
private static final DateTimeFormatter DISPLAY_FORMAT =
DateTimeFormatter.ofPattern("MMM dd, yyyy HH:mm z");
public boolean addEvent(Event event) {
for (Event existing : events) {
if (existing.conflictsWith(event)) {
System.out.println(" CONFLICT: '" + event.getName() +
"' overlaps with '" + existing.getName() + "'");
return false;
}
}
events.add(event);
System.out.println(" ADDED: " + event.getName());
return true;
}
public List getEventsOnDate(LocalDate date, ZoneId zone) {
return events.stream()
.filter(e -> {
LocalDate eventDate = e.getStart()
.withZoneSameInstant(zone).toLocalDate();
return eventDate.equals(date);
})
.sorted(Comparator.comparing(Event::getStart))
.collect(Collectors.toList());
}
public List getUpcomingEvents(ZonedDateTime from, int days) {
ZonedDateTime until = from.plusDays(days);
return events.stream()
.filter(e -> !e.getStart().isBefore(from) && e.getStart().isBefore(until))
.sorted(Comparator.comparing(Event::getStart))
.collect(Collectors.toList());
}
public Duration getTotalScheduledTime() {
return events.stream()
.map(Event::getDuration)
.reduce(Duration.ZERO, Duration::plus);
}
public Map> groupByDate(ZoneId zone) {
return events.stream()
.collect(Collectors.groupingBy(
e -> e.getStart().withZoneSameInstant(zone).toLocalDate(),
TreeMap::new,
Collectors.toList()
));
}
}
// ============================================
// Main -- demonstration
// ============================================
public static void main(String[] args) {
EventScheduler scheduler = new EventScheduler();
ZoneId nyZone = ZoneId.of("America/New_York");
ZoneId londonZone = ZoneId.of("Europe/London");
ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
System.out.println("=== CREATING EVENTS ===");
System.out.println();
// Event 1: Team standup (New York time)
Event standup = new Event("Team Standup",
ZonedDateTime.of(2026, 6, 15, 9, 0, 0, 0, nyZone),
Duration.ofMinutes(30), "Alice");
scheduler.addEvent(standup);
// Event 2: Architecture review
Event archReview = new Event("Architecture Review",
ZonedDateTime.of(2026, 6, 15, 10, 0, 0, 0, nyZone),
Duration.ofHours(2), "Bob");
scheduler.addEvent(archReview);
// Event 3: Try to book overlapping meeting
Event overlap = new Event("Quick Sync",
ZonedDateTime.of(2026, 6, 15, 11, 0, 0, 0, nyZone),
Duration.ofHours(1), "Charlie");
scheduler.addEvent(overlap); // CONFLICT with Architecture Review
// Event 4: Afternoon meeting
Event deployment = new Event("Deployment Planning",
ZonedDateTime.of(2026, 6, 15, 14, 0, 0, 0, nyZone),
Duration.ofMinutes(45), "Alice");
scheduler.addEvent(deployment);
// Event 5: Next day event in London time
Event londonMeeting = new Event("London Client Call",
ZonedDateTime.of(2026, 6, 16, 10, 0, 0, 0, londonZone),
Duration.ofHours(1), "Diana");
scheduler.addEvent(londonMeeting);
// ============================================
// Display events for different time zones
// ============================================
System.out.println("\n=== MONDAY JUNE 15 SCHEDULE (New York) ===");
LocalDate monday = LocalDate.of(2026, 6, 15);
for (Event e : scheduler.getEventsOnDate(monday, nyZone)) {
System.out.println(" " + e.formatForZone(nyZone));
}
System.out.println("\n=== SAME EVENTS VIEWED FROM TOKYO ===");
for (Event e : scheduler.getEventsOnDate(monday, nyZone)) {
System.out.println(" " + e.formatForZone(tokyoZone));
}
// ============================================
// Stats
// ============================================
System.out.println("\n=== SCHEDULE STATS ===");
Duration total = scheduler.getTotalScheduledTime();
System.out.println("Total scheduled: " + total.toHours() + "h " +
total.toMinutesPart() + "m");
// Days until an event
ZonedDateTime now = ZonedDateTime.of(2026, 2, 28, 12, 0, 0, 0, nyZone);
long daysUntilStandup = ChronoUnit.DAYS.between(now, standup.getStart());
System.out.println("Days until standup: " + daysUntilStandup);
// ============================================
// Period calculation
// ============================================
System.out.println("\n=== TIME UNTIL EVENTS ===");
Period period = Period.between(now.toLocalDate(), standup.getStart().toLocalDate());
System.out.println("Until standup: " + period.getMonths() + " months, " +
period.getDays() + " days");
// ============================================
// Grouped by date
// ============================================
System.out.println("\n=== EVENTS GROUPED BY DATE ===");
Map> grouped = scheduler.groupByDate(nyZone);
DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("EEEE, MMMM dd");
for (Map.Entry> entry : grouped.entrySet()) {
System.out.println(entry.getKey().format(dateFmt) + ":");
for (Event e : entry.getValue()) {
System.out.println(" - " + e.getName() + " (" +
e.getDuration().toMinutes() + " min, by " + e.getOrganizer() + ")");
}
}
// Output:
// === CREATING EVENTS ===
//
// ADDED: Team Standup
// ADDED: Architecture Review
// CONFLICT: 'Quick Sync' overlaps with 'Architecture Review'
// ADDED: Deployment Planning
// ADDED: London Client Call
//
// === MONDAY JUNE 15 SCHEDULE (New York) ===
// Team Standup: Mon, Jun 15 2026 at 09:00 AM EDT - 09:30 AM EDT
// Architecture Review: Mon, Jun 15 2026 at 10:00 AM EDT - 12:00 PM EDT
// Deployment Planning: Mon, Jun 15 2026 at 02:00 PM EDT - 02:45 PM EDT
//
// === SAME EVENTS VIEWED FROM TOKYO ===
// Team Standup: Mon, Jun 15 2026 at 10:00 PM JST - 10:30 PM JST
// Architecture Review: Mon, Jun 15 2026 at 11:00 PM JST - 01:00 AM JST
// Deployment Planning: Tue, Jun 16 2026 at 03:00 AM JST - 03:45 AM JST
//
// === SCHEDULE STATS ===
// Total scheduled: 4h 15m
// Days until standup: 107
//
// === TIME UNTIL EVENTS ===
// Until standup: 3 months, 15 days
//
// === EVENTS GROUPED BY DATE ===
// Monday, June 15:
// - Team Standup (30 min, by Alice)
// - Architecture Review (120 min, by Bob)
// - Deployment Planning (45 min, by Alice)
// Tuesday, June 16:
// - London Client Call (60 min, by Diana)
}
}
| # | Concept | Where It Appears |
|---|---|---|
| 1 | ZonedDateTime creation |
All events are created with explicit time zones |
| 2 | Time zone conversion | formatForZone() uses withZoneSameInstant() |
| 3 | Duration |
Event duration, total scheduled time |
| 4 | Period |
Human-readable time until events |
| 5 | ChronoUnit |
Days between now and a future event |
| 6 | DateTimeFormatter |
Custom display formats for dates and times |
| 7 | Conflict detection | conflictsWith() using isBefore()/isAfter() |
| 8 | LocalDate extraction |
Grouping events by date |
| 9 | Immutability | All date/time objects are safely shared |
| 10 | Stream operations on dates | Filtering, sorting, and grouping events |
| Task | Code |
|---|---|
| Current date | LocalDate.now() |
| Current time | LocalTime.now() |
| Current date-time | LocalDateTime.now() |
| Current instant (UTC) | Instant.now() |
| Specific date | LocalDate.of(2026, 3, 15) |
| Parse date string | LocalDate.parse("2026-03-15") |
| Format a date | date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) |
| Add days | date.plusDays(10) |
| Subtract months | date.minusMonths(3) |
| Compare dates | date1.isBefore(date2) |
| Days between dates | ChronoUnit.DAYS.between(date1, date2) |
| Period between dates | Period.between(date1, date2) |
| Duration between times | Duration.between(time1, time2) |
| Convert zone | zdt.withZoneSameInstant(ZoneId.of("Asia/Tokyo")) |
| First day of month | date.with(TemporalAdjusters.firstDayOfMonth()) |
| Last day of month | date.with(TemporalAdjusters.lastDayOfMonth()) |
| Next Monday | date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)) |
| Is leap year | date.isLeapYear() |
| Legacy Date to Instant | legacyDate.toInstant() |
| Instant to legacy Date | Date.from(instant) |
Local Installation
Installing Elasticsearch on your local Mac computer. I have a Mac laptop so this local installation will be for Mac users but you can google Elasticsearch Windows installation.
I use docker and here is the docker command,
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.1.0
Once you have pulled the elasticsearch image, run the following command to start Elasticsearch
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.1.0
If you want to keep your local Elasticsearch server running at all times then run this command. Even when you turn off your computer, it will start back up when you computer comes back on.
docker run -p 9200:9200 -p 9300:9300 --name elasticsearch -e "discovery.type=single-node" -dit --restart unless-stopped -d docker.elastic.co/elasticsearch/elasticsearch:7.1.0
To check if installation well, go to localhost:9200/_cat/nodes?v&pretty on your browser.
You should see something like this which is the status of your Elasticsearch node.
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 172.17.0.3 20 96 0 0.00 0.02 0.00 mdi * f3056ceb45a2
You also need to install kibana which is a UI interface that works with Elasticsearch
Docker command to pull kibana docker image
docker pull docker.elastic.co/kibana/kibana:7.1.0
Run this command to start your kibana server
docker run --link YOUR_ELASTICSEARCH_CONTAINER_NAME_OR_ID:elasticsearch -p 5601:5601 {docker-repo}:{version}
You can search data in Elasticsearch by sending a get request with query string as a parameter or post a query in the message body of post request. A search query, or query, is a request for information about data in Elasticsearch data streams or indices.
GET doctor_ut/_search
{
"query": {
"match_all": {}
}
}
String indexName = Index.DOCTOR_UT.name().toLowerCase();
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("addresses.state.keyword", "UT"));
int from = 1;
int size = 1000;
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(searchSourceBuilder);
// with sorting
// log.info("{\"query\":{}, \"sort\":{}}", searchSourceBuilder.query().toString(),
// searchSourceBuilder.sorts().toString());
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (Exception e) {
log.warn(e.getLocalizedMessage());
}
log.info("search got response from elastic!, totalHits={}, maxScore={}, hitLength={}", searchResponse.getHits().getTotalHits().value, searchResponse.getHits().getMaxScore(),searchResponse.getHits().getHits().length);
Iterator<SearchHit> it = searchResponse.getHits().iterator();
while (it.hasNext()) {
SearchHit searchHit = it.next();
try {
// log.info(searchHit.getSourceAsString());
DoctorIndex doctorIndex = ObjectUtils.getObjectMapper().readValue(searchHit.getSourceAsString(), DoctorIndex.class);
log.info("doctorIndex={}", ObjectUtils.toJson(doctorIndex));
// ObjectUtils.getObjectMapper().writeValue(new FileOutputStream("output-2.json", true),
// doctorIndex);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
By default, the Search API returns the top 10 matching documents.
To paginate through a larger set of results, you can use the search API’s size and from parameters. The size parameter is the number of matching documents to return. The from parameter is a zero-indexed offset from the beginning of the complete result set that indicates the document you want to start with.
By default, you cannot page through more than 10,000 documents using the from and size parameters. This limit is set using the index.max_result_window index setting.
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("addresses.state.keyword", "UT"));
int from = 1;
int size = 1000;
searchSourceBuilder.from(from);
searchSourceBuilder.size(size);
GET doctor_ut/_search
{
"from": 5,
"size": 5,
"query": {
"match_all": {}
}
}
The Scroll API can be used to retrieve a large number of results from a search request.
While a search request returns a single “page” of results, the scroll API can be used to retrieve large numbers of results (or even all results) from a single search request, in much the same way as you would use a cursor on a traditional database.
Scrolling is not intended for real time user requests, but rather for processing large amounts of data, e.g. in order to reindex the contents of one data stream or index into a new data stream or index with a different configuration.
The scroll API requires a scroll ID. To get a scroll ID, submit a search API request that includes an argument for the scroll query parameter . The scroll parameter indicates how long Elasticsearch should retain the search context for the request.
The search response returns a scroll ID in the _scroll_id response body parameter. You can then use the scroll ID with the scroll API to retrieve the next batch of results for the request.
You can also use the scroll API to specify a new scroll parameter that extends or shortens the retention period for the search context.
The scroll API returns the same response body as the search API.
GET doctor_ut/_search/scroll
{
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}
final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
searchRequest.scroll(scroll);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(1000);
searchSourceBuilder.query(QueryBuilders.termQuery("addresses.state.keyword", "UT"));
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("search got response from elastic!, totalHits={}, maxScore={}, hitLength={}", searchResponse.getHits().getTotalHits().value, searchResponse.getHits().getMaxScore(),
searchResponse.getHits().getHits().length);
// process searchResponse
String scrollId = searchResponse.getScrollId();
SearchHit[] searchHits = searchResponse.getHits().getHits();
while (searchHits != null && searchHits.length > 0) {
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
scrollRequest.scroll(scroll);
searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
log.info("search got response from elastic!, totalHits={}, maxScore={}, hitLength={}", searchResponse.getHits().getTotalHits().value, searchResponse.getHits().getMaxScore(),
searchResponse.getHits().getHits().length);
// process searchResponse
scrollId = searchResponse.getScrollId();
searchHits = searchResponse.getHits().getHits();
}
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
boolean succeeded = clearScrollResponse.isSucceeded();
Full Body Search
You should use the request body search API because most parameters are passed in the HTTP request body instead of in the query string with the GET request.
Request body search not only handles the query itself, but also allows you to return highlighted snippets from your results, aggregate analytics across all results or subsets of results, and return did-you-mean suggestions, which will help guide your users to the best results quickly.
POST /_search
{
"from": 30,
"size": 10
}
Multiple Query Clauses
Query clauses are simple building blocks that can be combined with each other to create complex queries.
{
"bool": {
"must": { "match": { "email": "folau@gmail.com" }},
"must_not": { "match": { "name": "folau" }},
"should": { "match": { "lastName": "kaveinga" }}
} }
It is important to note that a compound clause can combine any other query clauses, including other compound clauses. This means that compound clauses can be nested within each other, allowing the expression of very complex logic.
{
"bool": {
"must": { "match": { "email": "folau@gmail.com" }},
"should": [
{ "match": { "starred": true }},
{ "bool": {
"must": { "folder": "inbox" }},
"must_not": { "spam": true }}
}}
],
"minimum_should_match": 1
}
}
A filter asks a yeso r no question of every document and is used for fields that contain exact values. For examples:
The goal of filters is to reduce the number of documents that have to be examined by the query.
A query is similar to a filter, but also asks the question: How well does this document match?
A query calculates how relevant each document is to the query, and assigns it a relevance _score, which is later used to sort matching documents by relevance. This concept of relevance is well suited to full-text search, where there is seldom a completely “correct” answer.
Queries have to not only find matching documents, but also calculate how relevant each document is, which typically makes queries heavier than filters. Also, query results are not cachable.
The match_all query simply matches all documents. It is the default query that is used if no query has been specified. It returns all rows and columns.
{
"match_all": {}
}
The match query should be the standard query that you reach for whenever you want to query for a full-text or exact value in almost any field.
If you run a match query against a full-text field, it will analyze the query string by using the correct analyzer for that field before executing the search
{
"match": {
"email": "folau"
}
}
If you use it on a field containing an exact value, such as a number, a date, a Boolean, or a not_analyzed string field, then it will search for that exact value
Note that for exact-value searches, you probably want to use a filter instead of a query, as a filter will be cached.
The match query does not use a query syntax like +user_id:2 +tweet:search. It just looks for the words that are specified. This means that it is safe to expose to your users via a search field; you control what fields they can query, and it is not prone to throwing syntax errors.
match_phrase query analyzes the text and creates a phrase query out of the analyzed text.
@Test
void searchWithMatchPhrase() {
String description = "His biggest fear";
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* match query is like contain in mysql
*/
searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("description", description));
searchRequest.source(searchSourceBuilder);
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("totalShards={}, totalHits={}", searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
multi_match query builds on the match query to allow multi-field queries. Use * to query against all fields. Note that * will not query against nested fields.
{
"multi_match": {
"query": "full text search",
"fields": ["title","body"]
}
}
@Test
void searchWithMultiMatchAllFields() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* match query is like contain in mysql<br>
* * means all fields<br>
* Isabell - firstName of a diff user <br>
* 3102060312 - phoneNumber of a diff user<br>
* biggest fear - description of a diff user<br>
*/
//searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Isabell 3102060312 biggest fear", "*"));
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Best Buy", "*"));
searchRequest.source(searchSourceBuilder);
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("totalShards={}, totalHits={}", searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You can use the term query to find documents based on a precise value such as a price, a product ID, or a username.
To better search text fields, the match query also analyzes your provided search term before performing a search. This means the match query can search text fields for analyzed tokens rather than an exact term.
The term query does not analyze the search term. The term query only searches for the exact term you provide. This means the term query may return poor or no results when searching text fields.
@Test
void searchWithTerm() {
String firstName = "Isabell";
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* term query looks for exact match. Use keyword
*/
searchSourceBuilder.query(QueryBuilders.termQuery("firstName.keyword", firstName));
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
void searchWithTermAndMultiValues() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* term query looks for exact match. Use keyword
*/
searchSourceBuilder.query(QueryBuilders.termsQuery("firstName.keyword", "Leland","Harmony","Isabell"));
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Return documents that has a value for a field.
GET elasticsearch_learning/_search
{
"query":{
"exists" : {
"field" : "firstName",
"boost" : 1.0
}
}
}
@Test
void searchWithExistQuery() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* term query looks for exact match. Use keyword
*/
searchSourceBuilder.query(QueryBuilders.existsQuery("firstName"));
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Returns documents that have terms matching a wildcard pattern.
GET elasticsearch_learning/_search
{
"query":{
"wildcard" : {
"firstName" : {
"wildcard" : "H*y",
"boost" : 1.0
}
}
}
}
@Test
void searchWithWildcardQuery() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
/**
* Filter<br>
* term query looks for exact match. Use keyword<br>
* These matching terms can include Honey, Henny, or Horsey.<br>
*/
searchSourceBuilder.query(QueryBuilders.wildcardQuery("firstName", "H*y"));
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Returns documents that contain terms matching a regular expression.
The performance of the regexp query can vary based on the regular expression provided. To improve performance, avoid using wildcard patterns, such as .* or .*?+, without a prefix or suffix.
GET elasticsearch_learning/_search
{
"query":{
"regexp" : {
"firstName" : {
"value" : "S.e",
"flags_value" : 255,
"case_insensitive" : true,
"max_determinized_states" : 10000,
"boost" : 1.0
}
}
}
}
/**
* https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html<br>
* https://www.elastic.co/guide/en/elasticsearch/reference/current/regexp-syntax.html
*/
@Test
void searchWithRegexQuery() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName","description"}, new String[]{""});
/**
* Query<br>
* Sydnee<br>
* . means match any character.<br>
* * Repeat the preceding character zero or more times.<br>
*/
searchSourceBuilder.query(QueryBuilders.regexpQuery("firstName", "S.*e")
.flags(RegexpQueryBuilder.DEFAULT_FLAGS_VALUE)
.caseInsensitive(true));
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The bool query, like the bool filter, is used to combine multiple query clauses. However, there are some differences. Remember that while filters give binary yes/no answers, queries calculate a relevance score instead. The bool query combines the _score from each must or should clause that matches. This query accepts the following parameters:
must
Clauses that must match for the document to be included.
must_not
Clauses that must not match for the document to be included.
should
If these clauses match, they increase the _score; otherwise, they have no effect. They are simply used to refine the relevance score for each document.
The bool query takes a more-matches-is-better approach, so the score from each matching must or should clause will be added together to provide the final _score for each document.
The following query finds documents whose title field matches the query string how to make millions and that are not marked as spam. If any documents are starred or are from 2014 onward, they will rank higher than they would have otherwise. Documents that match both conditions will rank even higher.
GET elasticsearch_learning/_search
{
"query":{
"bool" : {
"must" : [
{
"match" : {
"firstName" : {
"query" : "Leland",
"operator" : "OR",
"prefix_length" : 0,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"boost" : 1.0
}
}
}
],
"filter" : [
{
"match" : {
"firstName" : {
"query" : "Leland",
"operator" : "OR",
"prefix_length" : 0,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"boost" : 1.0
}
}
}
],
"must_not" : [
{
"match" : {
"firstName" : {
"query" : "Leilani",
"operator" : "OR",
"prefix_length" : 0,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"boost" : 1.0
}
}
}
],
"should" : [
{
"match" : {
"firstName" : {
"query" : "Lelanddd",
"operator" : "OR",
"prefix_length" : 0,
"max_expansions" : 50,
"fuzzy_transpositions" : true,
"lenient" : false,
"zero_terms_query" : "NONE",
"auto_generate_synonyms_phrase_query" : true,
"boost" : 1.0
}
}
}
],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
}
@Test
void searchWithBooleanQuery() {
int pageNumber = 0;
int pageSize = 10;
SearchRequest searchRequest = new SearchRequest(database);
searchRequest.allowPartialSearchResults(true);
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(pageNumber * pageSize);
searchSourceBuilder.size(pageSize);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
/**
* fetch only a few fields
*/
// searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});
/**
* Query
*/
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
/**
* Filter<br>
* term query looks for exact match. Use keyword
*/
boolQuery.must(QueryBuilders.matchQuery("firstName", "Leland"));
boolQuery.mustNot(QueryBuilders.matchQuery("firstName", "Leilani"));
boolQuery.should(QueryBuilders.matchQuery("firstName", "Lelanddd"));
boolQuery.filter(QueryBuilders.matchQuery("firstName", "Leland"));
searchSourceBuilder.query(boolQuery);
searchRequest.source(searchSourceBuilder);
searchRequest.preference("firstName");
if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
} else {
log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
}
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);
List<User> users = getResponseResult(searchResponse.getHits());
log.info("results={}", ObjectUtils.toJson(users));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Note that if there are no must clauses, at least one should clause has to match. However, if there is at least one must clause, no should clauses are required to match.
Use bool query to combine query and filter.
Let’s assume you have 3 nodes.
#Query Phase

When a search request is sent to a node, that node becomes the coordinating node. It is the job of this node to broadcast the search request to all involved shards, and to gather their responses into a globally sorted result set that it can return to the client.
The first step is to broadcast the request to a shard copy of every node in the index. Just like document GET requests, search requests can be handled by a primary shard or by any of its replicas. This is how more replicas (when combined with more hard‐ ware) can increase search throughput. A coordinating node will round-robin through all shard copies on subsequent requests in order to spread the load.
Each shard executes the query locally and builds a sorted priority queue of length from + size—in other words, enough results to satisfy the global search request all by itself. It returns a lightweight list of results to the coordinating node, which con‐ tains just the doc IDs and any values required for sorting, such as the _score.
The coordinating node merges these shard-level results into its own sorted priority queue, which represents the globally sorted result set. Here the query phase ends.
An index can consist of one or more primary shards, so a search request against a single index needs to be able to combine the results from multiple shards. A search against multiple or all indi‐ ces works in exactly the same way—there are just more shards involved.
#Fetch Phase

The coordinating node first decides which documents actually need to be fetched. For instance, if our query specified { “from”: 90, “size”: 10 }, the first 90 results would be discarded and only the next 10 results would need to be retrieved. These documents may come from one, some, or all of the shards involved in the original search request.
The coordinating node builds a multi-get request for each shard that holds a perti‐ nent document and sends the request to the same shard copy that handled the query phase.
The shard loads the document bodies—the _source field—and, if requested, enriches the results with metadata and search snippet highlighting. Once the coordinating node receives all results, it assembles them into a single response that it returns to the client.
Imagine that you are sorting your results by a timestamp field, and two documents have the same timestamp. Because search requests are round-robined between all available shard copies, these two documents may be returned in one order when the request is served by the primary, and in another order when served by the replica. This is known as the bouncing results problem: every time the user refreshes the page, the results appear in a different order. The problem can be avoided by always using the same shards for the same user, which can be done by setting the preference parameter to an arbitrary string like the user’s session ID.