Modern Java

The Modern Date and Time API

ReadingPreview

You are viewing a free preview lesson.

Lesson Overview

Date এবং time নিয়ে কাজ দেখতে simple মনে হলেও software engineering-এ এটি surprisingly difficult একটি area।

Consider:

2026-08-08 10:00

এই value দেখে আমরা এখনো জানি না:

10:00 কোন time zone-এর?

Tallinn?
Dhaka?
London?
UTC?

আরেকটি example:

A subscription expires after 30 days.

এখানে প্রশ্ন আসতে পারে:

30 calendar days?
নাকি exactly 720 hours?

আর:

A meeting is at 10:00 Europe/Tallinn.

Daylight Saving Time change হলে UTC time কী হবে?

এই সমস্যাগুলো handle করার জন্য modern Java provides:

java.time

package।

এই lesson-এ আমরা শিখব:

  • LocalDate
  • LocalTime
  • LocalDateTime
  • Instant
  • ZoneId
  • ZonedDateTime
  • Duration
  • Period
  • DateTimeFormatter
  • Date/time creation
  • Date/time arithmetic
  • Date/time comparison
  • Parsing
  • Formatting
  • Time-zone conversion
  • LocalDateTime এবং Instant-এর difference
  • User-facing time বনাম system timestamp
  • Daylight Saving Time intuition
  • Common date/time mistakes
  • Backend systems-এ practical date/time strategy

Why Date and Time Is Difficult

একটি timestamp শুধু:

year
month
day
hour
minute
second

নয়।

Real-world time-এর সাথে জড়িত:

Time zone
UTC offset
Daylight Saving Time
Calendar rules
Leap years
Different month lengths
Human calendar semantics
Machine timestamp semantics

তাই date/time code design করার সময় type selection খুব important।


The java.time Package

Modern Java date/time API-এর classes থাকে:

java.time

package-এ।

Common imports:

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Duration;
import java.time.Period;
import java.time.format.DateTimeFormatter;

The Most Important Rule

সব date/time value এক ধরনের নয়।

Different problems-এর জন্য different types আছে।

Mental model:

Date only
→ LocalDate

Time only
→ LocalTime

Date + time, but no time zone
→ LocalDateTime

Exact point on global timeline
→ Instant

Date + time + time-zone rules
→ ZonedDateTime

Machine-style elapsed time
→ Duration

Calendar-style elapsed time
→ Period

এই mapping clear থাকলে date/time design অনেক সহজ হয়।


LocalDate

LocalDate represent করে:

year
month
day

কিন্তু এতে নেই:

time
time zone
UTC offset

Example:

LocalDate date =
        LocalDate.of(
                2026,
                8,
                8
        );

Represents:

2026-08-08

When LocalDate Is Appropriate

Examples:

Birthday
Course start date
Invoice due date
Holiday date
Subscription renewal calendar date

যেখানে exact time-of-day important নয়।


Current Date

LocalDate today =
        LocalDate.now();

এটি current system/default time zone অনুযায়ী date দেয়।

Backend code-এ default zone blindly use করার আগে application requirement বুঝতে হবে।


Specific Zone-এর Current Date

LocalDate todayInTallinn =
        LocalDate.now(
                ZoneId.of(
                        "Europe/Tallinn"
                )
        );

এখন date explicitly Tallinn time-zone rules অনুযায়ী calculate হবে।


LocalDate Is Immutable

LocalDate date =
        LocalDate.of(
                2026,
                8,
                8
        );

Then:

date.plusDays(
        5
);

original date modify করে না।

Need:

LocalDate later =
        date.plusDays(
                5
        );

Important Immutability Pattern

Wrong expectation:

date.plusDays(
        5
);

System.out.println(
        date
);

date unchanged থাকবে।

Correct:

date =
        date.plusDays(
                5
        );

যদি variable reassign করতে চান।


LocalDate Arithmetic

LocalDate date =
        LocalDate.of(
                2026,
                8,
                8
        );

Add days:

LocalDate tomorrow =
        date.plusDays(
                1
        );

Add weeks:

LocalDate nextWeek =
        date.plusWeeks(
                1
        );

Add months:

LocalDate nextMonth =
        date.plusMonths(
                1
        );

Add years:

LocalDate nextYear =
        date.plusYears(
                1
        );

Subtracting

LocalDate yesterday =
        date.minusDays(
                1
        );

Also:

minusWeeks(...)
minusMonths(...)
minusYears(...)

Calendar Arithmetic Handles Month Rules

Suppose:

LocalDate date =
        LocalDate.of(
                2026,
                1,
                31
        );

Then:

date.plusMonths(
        1
);

Java applies calendar rules rather than pretending every month has 31 days।

এ ধরনের logic manually milliseconds দিয়ে করা উচিত নয়।


Accessing Date Parts

int year =
        date.getYear();

Month:

Month month =
        date.getMonth();

Month number:

int month =
        date.getMonthValue();

Day:

int day =
        date.getDayOfMonth();

Day of week:

DayOfWeek dayOfWeek =
        date.getDayOfWeek();

Comparing LocalDate

date.isBefore(
        anotherDate
);
date.isAfter(
        anotherDate
);
date.isEqual(
        anotherDate
);

Example

if (
        dueDate.isBefore(
                today
        )
) {
    System.out.println(
            "Overdue"
    );
}

LocalTime

LocalTime represent করে:

hour
minute
second
nanosecond

কিন্তু এতে date বা time zone নেই।

Example:

LocalTime time =
        LocalTime.of(
                14,
                30
        );

Represents:

14:30

When LocalTime Is Useful

Examples:

Store opens at 09:00
Daily class begins at 18:30
Reminder should run at 08:00 local time

যেখানে time-of-day important কিন্তু specific date নয়।


Current Time

LocalTime now =
        LocalTime.now();

Specific zone:

LocalTime tallinnTime =
        LocalTime.now(
                ZoneId.of(
                        "Europe/Tallinn"
                )
        );

Time Arithmetic

LocalTime start =
        LocalTime.of(
                10,
                0
        );

Add 90 minutes:

LocalTime end =
        start.plusMinutes(
                90
        );

Result:

11:30

LocalDateTime

LocalDateTime combines:

LocalDate
+
LocalTime

Example:

LocalDateTime value =
        LocalDateTime.of(
                2026,
                8,
                8,
                14,
                30
        );

Represents:

2026-08-08T14:30

Critical: LocalDateTime Has No Time Zone

This is one of the most important concepts in this lesson।

LocalDateTime

contains:

date
time

but does not contain:

time zone
UTC offset

So:

2026-08-08 14:30

by itself is not an exact global moment।


Why Not?

Because:

14:30 in Tallinn

and:

14:30 in Dhaka

occur at different points on the global timeline।

Same wall-clock time।

Different real moment।


When LocalDateTime Is Appropriate

Use it when business meaning genuinely does not yet include a time zone।

Example:

A user entered:
2026-09-01 at 10:00

but zone is stored separately.

Then:

LocalDateTime localDateTime
ZoneId zone

together define the intended moment।


Dangerous Use of LocalDateTime

Suppose database stores:

created_at = 2026-08-08 10:30

without any agreement on time zone।

Later one service assumes UTC।

Another assumes Tallinn।

Now same stored value means two different moments।

For system timestamps:

createdAt
updatedAt
processedAt

an Instant is often more appropriate।


Instant

Instant represent করে global timeline-এর একটি exact point।

Conceptually:

one unambiguous moment

Example:

Instant now =
        Instant.now();

Why Instant Is Important

Suppose event happened:

Tallinn:
2026-08-08 20:00

Dhaka:
2026-08-08 23:00

These different local clock representations can refer to the same global instant।

Instant stores the global moment rather than local wall-clock representation।


UTC Mental Model

Instant is naturally represented relative to UTC timeline।

Example textual representation may look like:

2026-08-08T17:00:00Z

Z indicates:

UTC

Common Backend Use Cases for Instant

Excellent for:

createdAt
updatedAt
eventReceivedAt
paymentProcessedAt
requestTimestamp
auditTimestamp
tokenIssuedAt
tokenExpiresAt

where requirement is:

Exactly when did this happen?

Comparing Instants

instant.isBefore(
        anotherInstant
);
instant.isAfter(
        anotherInstant
);

Add Time to Instant

Instant expiresAt =
        issuedAt.plus(
                Duration.ofHours(
                        24
                )
        );

Now semantics are:

exactly 24 hours later

ZoneId

ZoneId represents a geographic time-zone ruleset।

Example:

ZoneId tallinn =
        ZoneId.of(
                "Europe/Tallinn"
        );

Another:

ZoneId dhaka =
        ZoneId.of(
                "Asia/Dhaka"
        );

Why Use Region-Based Zone IDs?

Prefer:

Europe/Tallinn
Asia/Dhaka
Europe/London
America/New_York

over manually assuming:

UTC+2
UTC+3

for human locations।

Why?

Because a location's offset can change according to time-zone rules, including Daylight Saving Time।


Offset Is Not the Same as Time Zone

An offset looks like:

+03:00

A zone is:

Europe/Tallinn

Europe/Tallinn includes rules that determine which offset applies at a specific date।

So:

ZoneId

carries richer semantics than a fixed offset।


ZonedDateTime

ZonedDateTime combines:

date
time
zone

Example:

ZonedDateTime meeting =
        ZonedDateTime.of(
                2026,
                8,
                8,
                10,
                0,
                0,
                0,
                ZoneId.of(
                        "Europe/Tallinn"
                )
        );

This means:

2026-08-08
10:00
in Europe/Tallinn

Zoned Time Defines an Actual Moment

Because zone rules are known, Java can determine the corresponding:

Instant

Example:

Instant instant =
        meeting.toInstant();

Convert an Instant to Local Zoned Time

Suppose system stores:

Instant timestamp

To display in Tallinn:

ZonedDateTime tallinnTime =
        timestamp.atZone(
                ZoneId.of(
                        "Europe/Tallinn"
                )
        );

To display in Dhaka:

ZonedDateTime dhakaTime =
        timestamp.atZone(
                ZoneId.of(
                        "Asia/Dhaka"
                )
        );

Same instant।

Different local representations।


This Is a Strong Backend Pattern

Store:

Instant

for actual events।

Display:

ZonedDateTime

using user's relevant time zone।

Conceptually:

Storage:
exact moment

Presentation:
local human time

Example

Suppose:

Instant createdAt =
        Instant.now();

Then:

ZonedDateTime userTime =
        createdAt.atZone(
                ZoneId.of(
                        "Asia/Dhaka"
                )
        );

You have not changed when the event happened।

You changed only:

how the same moment is represented locally.

Converting LocalDateTime to Zoned Time

Suppose:

LocalDateTime local =
        LocalDateTime.of(
                2026,
                8,
                8,
                10,
                0
        );

and we know it means Tallinn time:

ZonedDateTime zoned =
        local.atZone(
                ZoneId.of(
                        "Europe/Tallinn"
                )
        );

Now the previously ambiguous local time has a zone context।


Then Convert to Instant

Instant instant =
        zoned.toInstant();

Flow:

LocalDateTime
+
ZoneId
↓
ZonedDateTime
↓
Instant

Do Not Invent a Zone After the Fact

If a stored LocalDateTime was created without knowing its true zone, later writing:

local.atZone(
        ZoneId.of(
                "UTC"
        )
);

does not magically make it correct।

You are asserting:

This local time was intended as UTC.

That assertion must come from the original data contract।


Duration

Duration represents time-based amount।

Examples:

30 seconds
15 minutes
24 hours

Example:

Duration timeout =
        Duration.ofSeconds(
                30
        );

Duration Between Instants

Duration elapsed =
        Duration.between(
                start,
                end
        );

Get seconds:

long seconds =
        elapsed.toSeconds();

Example

Instant startedAt =
        Instant.now();

// work

Instant finishedAt =
        Instant.now();

Duration duration =
        Duration.between(
                startedAt,
                finishedAt
        );

This is appropriate for measuring elapsed timeline duration।


Duration Is Time-Based

Think:

seconds
minutes
hours

rather than human calendar months/years।


Period

Period represents date-based calendar amount।

Examples:

3 days
2 months
1 year

Example:

Period trialPeriod =
        Period.ofDays(
                30
        );

Another:

Period membership =
        Period.ofMonths(
                6
        );

Period Between Dates

Period age =
        Period.between(
                birthDate,
                today
        );

Then:

int years =
        age.getYears();

Duration vs Period

Very important distinction:

Duration
→ timeline/time-based amount

Period
→ calendar/date-based amount

24 Hours Is Not Always the Same as One Calendar Day

This becomes important around Daylight Saving Time।

Suppose a local time zone changes its clock。

Then:

plus 1 calendar day

and:

plus exactly 24 hours

can lead to different local times।


Calendar Requirement

If requirement is:

Same local time tomorrow

calendar date arithmetic may be appropriate।

Example:

zonedDateTime.plusDays(
        1
);

Exact Elapsed-Time Requirement

If requirement is:

Exactly 24 hours later

think in timeline terms:

instant.plus(
        Duration.ofHours(
                24
        )
);

These business requirements are not always equivalent।


Period Example — Subscription Calendar Date

Suppose subscription renews:

one month later

Use:

LocalDate nextRenewal =
        startDate.plus(
                Period.ofMonths(
                        1
                )
        );

Not:

30 × 24 hours

because months have different lengths।


Duration Example — Timeout

Suppose request timeout:

30 seconds

Use:

Duration timeout =
        Duration.ofSeconds(
                30
        );

A calendar Period makes no sense here।


DateTimeFormatter

Humans rarely want default ISO representation everywhere।

For formatting and parsing:

DateTimeFormatter

Formatting a Date

LocalDate date =
        LocalDate.of(
                2026,
                8,
                8
        );

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "dd/MM/yyyy"
        );

String formatted =
        date.format(
                formatter
        );

Result:

08/08/2026

Formatting Date and Time

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "dd MMM yyyy HH:mm"
        );

Then:

String value =
        dateTime.format(
                formatter
        );

Pattern Case Matters

Date/time formatter pattern symbols are case-sensitive।

Examples:

MM
→ month

mm
→ minute

These are not the same।

A very common bug হলো month-এর জায়গায় minute pattern ব্যবহার করা।


Parsing

String:

08/08/2026

to LocalDate:

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "dd/MM/yyyy"
        );

LocalDate date =
        LocalDate.parse(
                "08/08/2026",
                formatter
        );

Prefer Standard Formats for Machine Interfaces

For APIs and persistence, standardized machine-readable formats are usually better than custom human presentation strings।

Example:

2026-08-08

for a date।

Or an unambiguous timestamp including zone/offset/UTC semantics।

Human formatting:

08 Aug 2026

belongs more naturally at presentation boundaries।


Parsing Invalid Input

This:

LocalDate.parse(
        "not-a-date"
);

will fail।

User-provided date parsing is an input-validation boundary।

Do not assume all strings are valid।


Date Validation Through Types

Instead of storing:

String birthDate

throughout application code, parse once:

LocalDate birthDate

Now downstream code works with a proper date type।

This reduces invalid-state possibilities।


Use Types to Represent Meaning

Bad:

String createdAt;
String birthday;
String timeout;

All are strings but represent completely different concepts।

Better:

Instant createdAt;
LocalDate birthday;
Duration timeout;

Now type itself communicates semantics।


LocalDateTime.now() vs Instant.now()

A common question:

Which one should I use for createdAt?

For an event timestamp:

Instant.now()

is usually the stronger default।

Why?

Because:

createdAt

asks:

Exactly when did this happen?

not:

What did the local wall clock show?

Bad Timestamp Design

LocalDateTime createdAt =
        LocalDateTime.now();

Then send it between services without zone information।

Now another system does not know which actual moment it represents।


Better

Instant createdAt =
        Instant.now();

Convert to local user time only when necessary।


User-Scheduled Events Are Different

Suppose user schedules:

Live class at 10:00 Europe/Tallinn
on 2026-09-10

Here the business input includes:

local date
local time
zone

So model may involve:

ZonedDateTime

or:

LocalDateTime
+
ZoneId

depending on domain design।


Why Store Zone for Recurring Events?

Suppose requirement:

Every Monday at 09:00 Tallinn time

If you store only current UTC equivalent, Daylight Saving Time changes may shift local wall-clock behavior।

For recurring human schedules, the intended:

local time
+
zone rules

may matter more than one fixed UTC instant।


One-Time Event vs Recurring Schedule

A useful distinction:

One-time event

Occurs exactly once at a known moment

Store/represent exact:

Instant

plus zone for display if needed।

Recurring local schedule

Every day at 09:00 Europe/Tallinn

Needs local scheduling semantics and zone rules।


Daylight Saving Time Intuition

Some time zones change UTC offset during the year।

For example, a zone may use one offset in winter and another in summer।

Therefore:

Europe/Tallinn

is not equivalent to permanently hardcoding one offset।

Use ZoneId so Java's time-zone rules can resolve the correct offset for the date।


Do Not Hardcode User Time-Zone Offsets

Avoid business logic like:

utc.plusHours(
        3
);

to mean:

Tallinn time

That may fail when the zone's applicable offset changes।

Use:

instant.atZone(
        ZoneId.of(
                "Europe/Tallinn"
        )
);

Converting Between Zones

Suppose meeting:

ZonedDateTime tallinnMeeting

Convert the same instant to Dhaka:

ZonedDateTime dhakaMeeting =
        tallinnMeeting.withZoneSameInstant(
                ZoneId.of(
                        "Asia/Dhaka"
                )
        );

withZoneSameInstant()

Meaning:

Keep the same global moment,
show it in another time zone.

This is usually what you want for timezone conversion।


Do Not Confuse Same Instant with Same Local Time

These are different questions:

Same event in another zone?

versus:

Keep 10:00 but reinterpret it in another zone?

For normal user-facing conversion, preserve the instant।


Comparing Zoned Times

Two ZonedDateTime values may display different local times but refer to the same Instant

When exact global ordering matters, converting/comparing timeline values is often clearer।

Example:

first.toInstant()
        .isBefore(
                second.toInstant()
        );

Example — Course Publication Timestamp

record Course(
        String code,
        Instant publishedAt
) {
}

When Course is published:

Course course =
        new Course(
                "JAVA",
                Instant.now()
        );

For user display:

ZonedDateTime localPublishedAt =
        course.publishedAt()
                .atZone(
                        ZoneId.of(
                                "Asia/Dhaka"
                        )
                );

Example — Course Start Date

Suppose Course starts on a calendar date and time-of-day is irrelevant:

record Course(
        String code,
        LocalDate startDate
) {
}

This is better than storing midnight Instant just to represent a date।


Do Not Invent Precision

If business concept is:

Birthday

do not model:

Instant birthday

because birthday usually has no meaningful exact global second।

Use:

LocalDate

Do Not Lose Precision Either

If business concept is:

Payment processed at exact moment

do not store only:

LocalDate paymentDate

because hour/minute/second information matters।


Choosing the Right Type

Ask:

Do I care only about date?
→ LocalDate

Only local clock time?
→ LocalTime

Date + clock time, no zone yet?
→ LocalDateTime

Exact global moment?
→ Instant

Human local moment with zone rules?
→ ZonedDateTime

Duration.between()

Suppose:

Instant startedAt;
Instant finishedAt;

Elapsed:

Duration elapsed =
        Duration.between(
                startedAt,
                finishedAt
        );

Check:

if (
        elapsed.toSeconds()
        > 5
) {
    System.out.println(
            "Slow operation"
    );
}

Be Careful Measuring Program Performance

Instant.now() can be useful for business timestamps।

For precise benchmarking or elapsed execution measurement, specialized monotonic timing APIs may be more appropriate than wall-clock timestamps।

Do not turn business date/time APIs into a microbenchmarking strategy।


Period.between()

Suppose:

LocalDate start =
        LocalDate.of(
                2020,
                1,
                1
        );

LocalDate end =
        LocalDate.of(
                2026,
                8,
                8
        );

Then:

Period period =
        Period.between(
                start,
                end
        );

Can inspect:

period.getYears();
period.getMonths();
period.getDays();

Period Components Are Calendar Components

Period result:

6 years
7 months
7 days

style calendar decomposition হতে পারে।

Do not interpret getDays() alone as total number of days between distant dates।

It is the day component of the Period।


Need Total Number of Days?

For two LocalDate values, use a date-based unit operation rather than summing Period components manually।

Example:

long days =
        ChronoUnit.DAYS.between(
                startDate,
                endDate
        );

Import:

import java.time.temporal.ChronoUnit;

ChronoUnit

ChronoUnit useful for differences such as:

ChronoUnit.DAYS
ChronoUnit.HOURS
ChronoUnit.MINUTES

But choose units compatible with the temporal types and business semantics।


Comparing Dates Is Better Than Comparing Strings

Avoid:

if (
        dateString.compareTo(
                otherDateString
        ) > 0
) {
    ...
}

unless format semantics are intentionally designed for lexical ordering।

Better:

LocalDate date =
        LocalDate.parse(
                input
        );

if (
        date.isAfter(
                otherDate
        )
) {
    ...
}

Use date/time types for date/time logic।


DateTimeFormatter Should Be Reused

A formatter can be stored as a constant when reused։

Example:

private static final DateTimeFormatter DISPLAY_FORMAT =
        DateTimeFormatter.ofPattern(
                "dd MMM yyyy"
        );

Then:

String formatted =
        date.format(
                DISPLAY_FORMAT
        );

Avoid Repeated Magic Patterns

Instead of scattering:

DateTimeFormatter.ofPattern(
        "dd/MM/yyyy"
)

everywhere, define formatting policy near presentation boundaries।


Storage Format vs Display Format

These are different concerns।

Storage/API:

stable
machine-readable
unambiguous

Display:

localized
human-friendly

Do not mix them unnecessarily।


Time Zone Should Be Explicit at Boundaries

If API accepts a scheduled local class time, define clearly whether input is:

UTC Instant

or:

LocalDateTime + ZoneId

Do not accept:

2026-09-10T10:00

and leave every service guessing which zone it means।


Example Request Model

Conceptually:

record ScheduleRequest(
        LocalDateTime startsAt,
        String zoneId
) {
}

Then:

ZoneId zone =
        ZoneId.of(
                request.zoneId()
        );

ZonedDateTime scheduled =
        request.startsAt()
                .atZone(
                        zone
                );

Better Value Type

Could also model:

record ScheduleRequest(
        ZonedDateTime startsAt
) {
}

depending on serialization/API conventions।

The key principle:

time-zone semantics must not be ambiguous.

Common Mistake 1 — Using LocalDateTime for Every Timestamp

LocalDateTime does not identify a global moment।

For system event timestamps:

Instant

is often more appropriate।


Common Mistake 2 — Storing Dates as Strings

Example:

String createdAt;

This loses type safety and may introduce parsing ambiguity।

Prefer proper Java time types internally।


Common Mistake 3 — Hardcoding UTC Offsets

Avoid:

plusHours(
        3
);

to represent a geographic zone।

Use:

ZoneId

Common Mistake 4 — Confusing MM and mm

Formatter:

MM
→ month

mm
→ minute

Case matters।


Common Mistake 5 — Forgetting Immutability

Wrong:

date.plusDays(
        1
);

and expecting date to change।

Correct:

LocalDate tomorrow =
        date.plusDays(
                1
        );

Common Mistake 6 — Using Duration for Calendar Months

Do not represent:

one month

as:

30 days

unless business definition explicitly says exactly 30 days।

For calendar month semantics:

Period.ofMonths(
        1
)

or:

plusMonths(
        1
)

is more appropriate।


Common Mistake 7 — Using Period for Exact Timeouts

A request timeout:

30 seconds

should use:

Duration

not Period


Common Mistake 8 — Assuming Local Time Is an Instant

2026-08-08 10:00

without zone is not enough to identify exactly when something happened।


Common Mistake 9 — Converting Zones by Changing Hour Manually

Do not write timezone conversion as arithmetic:

time.plusHours(
        difference
);

Use zone-aware conversion।


Common Mistake 10 — Mixing Presentation and Domain Logic

Do not make business decisions based on formatted strings।

Keep:

Instant
LocalDate
ZonedDateTime

for logic।

Format to String only near UI/API presentation boundaries when needed।


Common Mistake 11 — Default Time Zone Everywhere

Code such as:

LocalDate.now()

or:

ZonedDateTime.now()

uses a default zone context।

For business logic where zone matters, pass or define the intended zone explicitly।


Common Mistake 12 — Hiding Time-Zone Assumptions

If business says:

Subscription expires at midnight

ask:

Midnight in which zone?

Time-zone assumptions are part of the requirement।


Practical Example — Enrollment Expiry

Suppose an enrollment expires exactly:

72 hours

after activation।

Use Instant + Duration:

Instant activatedAt =
        Instant.now();

Instant expiresAt =
        activatedAt.plus(
                Duration.ofHours(
                        72
                )
        );

Why Not LocalDateTime.plusDays(3)?

Because requirement is:

exactly 72 elapsed hours

not necessarily:

same local clock time three calendar dates later

Timeline semantics fit better।


Practical Example — Monthly Renewal

Suppose billing renewal date is:

one calendar month after 2026-01-31

Use:

LocalDate renewalDate =
        LocalDate.of(
                2026,
                1,
                31
        ).plusMonths(
                1
        );

Calendar API handles valid resulting date।


Practical Example — User Display

Store:

Instant createdAt;

Display in user's zone:

ZonedDateTime displayed =
        createdAt.atZone(
                userZone
        );

Then format:

String output =
        displayed.format(
                DateTimeFormatter.ofPattern(
                        "dd MMM yyyy HH:mm"
                )
        );

Complete Example

import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class Main {

    private static final DateTimeFormatter FORMATTER =
            DateTimeFormatter.ofPattern(
                    "dd MMM yyyy HH:mm"
            );

    public static void main(String[] args) {
        Instant publishedAt =
                Instant.now();

        Instant expiresAt =
                publishedAt.plus(
                        Duration.ofHours(
                                48
                        )
                );

        ZoneId tallinn =
                ZoneId.of(
                        "Europe/Tallinn"
                );

        ZoneId dhaka =
                ZoneId.of(
                        "Asia/Dhaka"
                );

        ZonedDateTime tallinnPublished =
                publishedAt.atZone(
                        tallinn
                );

        ZonedDateTime dhakaPublished =
                publishedAt.atZone(
                        dhaka
                );

        System.out.println(
                "Tallinn: "
                + tallinnPublished.format(
                        FORMATTER
                )
        );

        System.out.println(
                "Dhaka: "
                + dhakaPublished.format(
                        FORMATTER
                )
        );

        System.out.println(
                "Expires at: "
                + expiresAt
        );
    }
}

Same Instant:

publishedAt

different local representations:

Tallinn
Dhaka

Practice 1 — Birthday

Which type?

A person's birthday:
1995-04-12

Answer

LocalDate

Practice 2 — Daily Opening Time

Shop opens every day at 09:00.

Answer

LocalTime

Practice 3 — Database createdAt

Need exact time an entity was created।

Answer

Usually:

Instant

Practice 4 — Meeting

2026-10-10 at 15:00 Europe/Tallinn

Answer

ZonedDateTime

is a natural representation।


Practice 5 — Ambiguous Value

LocalDateTime.of(
        2026,
        10,
        10,
        15,
        0
)

Does it identify an exact global moment?

Answer

No।

There is no zone or offset।


Practice 6 — Timeout

30 seconds

Answer

Duration

Practice 7 — Membership Length

6 calendar months

Answer

Period

or calendar plusMonths() arithmetic।


Practice 8 — Add One Day

Does:

date.plusDays(
        1
);

mutate date?

Answer

No।

Java time types are immutable।


Practice 9 — Formatting

What is wrong with using:

dd/mm/yyyy

when mm is intended to mean month?

Answer

Lowercase:

mm

means minute।

Month is:

MM

Practice 10 — Time Zone

Why prefer:

ZoneId.of(
        "Europe/Tallinn"
)

over manually adding hours?

Answer

Because ZoneId contains time-zone rules and can account for date-dependent offset changes such as Daylight Saving Time।


Practice 11 — Same Event in Another Zone

Given:

Instant eventTime

show it in Dhaka।

Solution

ZonedDateTime dhakaTime =
        eventTime.atZone(
                ZoneId.of(
                        "Asia/Dhaka"
                )
        );

Practice 12 — Parse Date

Input:

08/08/2026

Solution

DateTimeFormatter formatter =
        DateTimeFormatter.ofPattern(
                "dd/MM/yyyy"
        );

LocalDate date =
        LocalDate.parse(
                "08/08/2026",
                formatter
        );

Practice 13 — One Calendar Month

What should you avoid?

Assuming one month = 30 days

Answer

Use calendar-based operations:

plusMonths(
        1
)

or:

Period.ofMonths(
        1
)

when business requirement means a calendar month।


Practice 14 — 24-Hour Expiry

Token expires exactly 24 hours after creation।

Solution

Instant expiresAt =
        createdAt.plus(
                Duration.ofHours(
                        24
                )
        );

Practice 15 — Recurring Local Time

Requirement:

Every day at 09:00 Europe/Tallinn.

Would storing only one current UTC hour fully capture the rule?

Answer

No।

The rule is based on:

local time
+
time zone

and future zone offsets may vary।


True or False

  1. LocalDate contains a time zone.
  2. LocalTime contains a date.
  3. LocalDateTime contains date and time but no zone.
  4. Instant represents an exact global timeline point.
  5. ZoneId can represent geographic time-zone rules.
  6. ZonedDateTime combines local date/time with a zone.
  7. Java time objects such as LocalDate are mutable.
  8. Duration is suitable for exact elapsed hours.
  9. Period is suitable for calendar months.
  10. One calendar day is always identical to exactly 24 elapsed hours in every time zone.
  11. MM and mm mean the same thing in formatter patterns.
  12. Instant is usually a strong choice for backend event timestamps.
  13. LocalDateTime alone is enough to identify any event globally.
  14. Geographic time-zone conversion should usually use ZoneId.
  15. Date/time values should always be stored as Strings internally.

Answers

1. False
2. False
3. True
4. True
5. True
6. True
7. False
8. True
9. True
10. False
11. False
12. True
13. False
14. True
15. False

Knowledge Check

Question 1

LocalDate কী represent করে?

Question 2

LocalDateTime এবং Instant-এর মূল difference কী?

Question 3

কেন LocalDateTime global timestamp হিসেবে ambiguous হতে পারে?

Question 4

ZoneId কেন fixed hour offset-এর চেয়ে richer?

Question 5

ZonedDateTime কী represent করে?

Question 6

Duration এবং Period-এর difference কী?

Question 7

কেন one month-কে blindly 30 days হিসেবে represent করা উচিত নয়?

Question 8

Java time types-এর immutability practical code-এ কীভাবে affect করে?

Question 9

DateTimeFormatter কেন প্রয়োজন?

Question 10

Backend createdAt field-এর জন্য Instant কেন useful?

Question 11

একই Instant কীভাবে Tallinn এবং Dhaka-তে different local clock time হিসেবে দেখা যেতে পারে?

Question 12

Recurring local schedule-এর জন্য time zone কেন preserve করা গুরুত্বপূর্ণ?


Knowledge Check Answers

Answer 1

LocalDate শুধু calendar date represent করে:

year
month
day

এতে time বা time zone নেই।

Answer 2

LocalDateTime একটি local wall-clock date এবং time represent করে কিন্তু time zone জানে না।

Instant global timeline-এর একটি exact moment represent করে।

Answer 3

কারণ:

2026-08-08 10:00

Tallinn, Dhaka, London বা অন্য যেকোনো zone-এর local time হতে পারে।

Zone ছাড়া কোন actual global moment বোঝানো হয়েছে তা জানা যায় না।

Answer 4

ZoneId একটি region-এর historical/current time-zone rules represent করে।

Fixed offset:

+03:00

শুধু একটি offset value।

ZoneId date অনুযায়ী applicable offset determine করতে পারে।

Answer 5

এটি:

local date
+
local time
+
time zone

একসঙ্গে represent করে।

Answer 6

Duration time-based elapsed amount represent করে:

seconds
minutes
hours

Period calendar-based amount represent করে:

days
months
years

Answer 7

কারণ calendar months-এর length একই নয়।

এক মাস হতে পারে:

28
29
30
31 days

Business requirement যদি calendar month হয়, calendar arithmetic ব্যবহার করতে হবে।

Answer 8

Operations original object modify করে না।

Example:

date.plusDays(
        1
);

new LocalDate return করে।

Result ব্যবহার করতে variable assign করতে হবে।

Answer 9

Human-readable String এবং Java date/time type-এর মধ্যে formatting/parsing করতে।

Example:

LocalDate
↔
08/08/2026

Answer 10

কারণ createdAt সাধারণত প্রশ্নের উত্তর দেয়:

Exactly when was this created?

Instant time-zone ambiguity ছাড়াই exact global moment represent করে।

Answer 11

Instant same global moment ধরে রাখে।

Different ZoneId apply করলে সেই moment-এর local wall-clock representation zone অনুযায়ী পরিবর্তিত হয়।

Answer 12

কারণ recurring schedule-এর business rule প্রায়ই:

09:00 local time

কে preserve করতে চায়।

Time-zone offset future dates-এ change হলেও ZoneId local scheduling rule correctly interpret করতে সাহায্য করে।


Practical Type Selection Guide

Date only:

LocalDate

Time only:

LocalTime

Date + time, zone not yet known:

LocalDateTime

Exact global moment:

Instant

Local date/time with time-zone rules:

ZonedDateTime

Exact elapsed time:

Duration

Calendar amount:

Period

Human formatting/parsing:

DateTimeFormatter

Backend Date/Time Strategy

একটি useful starting strategy:

System event timestamp
→ Instant

User birthday / due date
→ LocalDate

Daily local clock time
→ LocalTime

User-scheduled zoned event
→ ZonedDateTime
  or LocalDateTime + ZoneId

Exact timeout / expiry duration
→ Duration

Calendar subscription period
→ Period

Core Mental Model

Date/time type choose করার আগে প্রশ্ন করুন:

এই value আসলে কী বোঝাচ্ছে?

If:

কোন দিন?
→ LocalDate

If:

দিনের কোন সময়?
→ LocalTime

If:

local date and time কী?
→ LocalDateTime

If:

exactly কখন ঘটেছে?
→ Instant

If:

কোন local date/time এবং কোন zone?
→ ZonedDateTime

এই distinctionগুলো type system-এর মধ্যে রাখলে অনেক subtle bug design stage-এই avoid করা যায়।


Lesson Summary

এই lesson-এ আমরা Modern Java Date and Time API-এর foundation শিখেছি।

আমরা শিখেছি:

  • java.time modern date/time types provide করে
  • LocalDate date-only value
  • LocalTime time-only value
  • LocalDateTime date + time কিন্তু zone ছাড়া
  • LocalDateTime global timestamp নয়
  • Instant exact global moment represent করে
  • ZoneId geographic time-zone rules represent করে
  • ZonedDateTime local date/time এবং zone combine করে
  • একই Instant different zones-এ different local times হিসেবে display হতে পারে
  • Duration exact time-based amount-এর জন্য
  • Period calendar-based amount-এর জন্য
  • 24 hours এবং one calendar day সবসময় semantically same নয়
  • One month-কে blindly 30 days ধরা উচিত নয়
  • Java time types immutable
  • Date/time arithmetic new object return করে
  • DateTimeFormatter parsing এবং formatting handle করে
  • Formatter pattern case-sensitive
  • System timestamps-এর জন্য Instant strong default হতে পারে
  • User-facing local times-এর জন্য appropriate ZoneId apply করতে হয়
  • Geographic zones manually hour addition দিয়ে model করা উচিত নয়
  • Recurring local schedules-এর জন্য zone semantics preserve করা important
  • Proper date/time types String-based date handling-এর চেয়ে safer
  • Storage semantics এবং display formatting আলাদা concern

সবচেয়ে গুরুত্বপূর্ণ principle:

Time নিয়ে কাজ করার আগে
type choose করবেন না।

আগে decide করুন
time value-এর meaning কী।

কারণ:

2026-08-08 10:00

একটি date/time value হতে পারে।

কিন্তু:

2026-08-08 10:00 Europe/Tallinn

এবং:

an exact Instant

একই concept নয়।


Next Lesson

পরবর্তী lesson:

Modern Java Language Features

আমরা শিখব:

  • var
  • Local variable type inference
  • কখন var useful
  • কখন explicit type clearer
  • Switch expressions
  • yield
  • Arrow-style switch cases
  • Text blocks
  • Pattern matching with instanceof
  • Pattern matching with switch
  • Sealed classes overview যেখানে appropriate
  • Modern syntax ব্যবহার করে code concise করা
  • Modern syntax overuse না করা
  • Readability এবং type clarity preserve করা