انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-033-L01

لماذا الوقت صعب

  • الدرس 1 من 5
  • 11 دقيقة قراءة
  • مبتدئ
في هذا الدرس 11 موضوعات

هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.

Lesson Overview

Every backend system deals with time: when an order was placed, when a token expires, when a report is due. It looks easy, because we read clocks all day. It is not easy, because a single moment is written differently in every city, a day is not always 24 hours long, months have different lengths, and Java carries an old date API that is easy to misuse.

This lesson does not teach the new API in detail yet. It shows you the problems, so that the types in the next lessons make sense as answers rather than as things to memorize. Picture a wall of world clocks, one for Riyadh, one for London, one for New York: they all show the same moment, with different numbers.

You already know classes, objects, immutability (BEG-021) and exceptions (BEG-029). That is all you need. The lesson ends by pointing you to the java.time package, the modern tool for the job.

Learning Objectives

By the end of this lesson you will be able to:

  • Explain why one moment shows different clock times in different time zones.
  • Explain why daylight saving time makes some days 23 or 25 hours long.
  • Name two calendar facts that break naive date arithmetic (month lengths, leap years).
  • State why the legacy java.util.Date and Calendar should be avoided in new code.
  • Name the package to use instead (java.time) and describe what immutable date objects mean for your code.
  • Read a short program that prints the same moment in three zones.

Written Explanation

One moment, many clocks

Imagine a video call that starts at one exact moment. In Riyadh the clock on the wall says 10:00. In London it says 08:00. In New York it says 03:00. Nobody is wrong. The clock reading is a local description of a moment, and the description depends on where you stand. The moment itself is the same everywhere.

The world is divided into time zones. Each zone has an offset: how far its clock is from UTC (Coordinated Universal Time), a common reference. Riyadh is three hours ahead of UTC, which is written +03:00. When it is 07:00 UTC, it is 10:00 in Riyadh.

Two ideas to hold on to for the whole module:

  • A moment (a point on the global timeline) exists independently of any zone.
  • A local date-time (such as "10:00 on 4 May") only identifies a moment once you say which zone it is in.

Daylight saving makes the offset change

Many regions move their clocks forward in spring and back in autumn, called daylight saving time (DST). The offset of such a region is not a constant: London is UTC+0 in winter and UTC+1 in summer. Riyadh does not change; its offset stays +03:00.

Consequences for programmers:

  • Two cities can be 5 hours apart for part of the year and 4 hours apart for another part, because they change their clocks on different dates.
  • On the day clocks go forward, the local day is only 23 hours long. On the day they go back, it is 25 hours long.
  • Some local times do not exist (the hour skipped in spring) and some happen twice (the hour repeated in autumn). Lesson 3 shows how Java handles both.

The rules about who changes their clocks, and when, are set by governments and can change. That is why Java ships a copy of the IANA time zone database, which identifies regions by names such as Europe/London and Asia/Riyadh.

The calendar is irregular too

Even without zones, "add one month" is not a simple operation.

  • Months have 28, 29, 30, or 31 days.
  • February has 29 days in a leap year (2028 is one; 2026 is not).
  • So what is one month after 31 January? There is no 31 February. The library must pick a rule, and you must know it.

You also cannot assume a year has 365 days or a month 30 days when you compute with raw numbers. Using a real date library prevents a whole class of "off by one day" bugs.

The old API: Date and Calendar

Java 1.0 shipped java.util.Date. Its documentation says that since JDK 1.1 the date-field methods are deprecated and that Calendar should be used for fields. The old types have real traps:

  • The deprecated Date(int year, int month, int date) constructor takes the year minus 1900 and a month counted from 0, so new Date(2026, 2, 15) does not mean 15 March 2026.
  • Date is actually a point in time, not a calendar date, despite its name, yet it prints in the machine's default zone.
  • A Date is mutable: any code that holds a reference can change it.

You will still meet Date in older code and libraries. For new code, use java.time, added in Java 8. Its documentation describes the classes as immutable and thread-safe, and based on the ISO calendar. java.util.Date has a toInstant() method (since Java 8) for converting old values to the new world.

Immutable date objects

You studied immutable objects in BEG-021: a String never changes, and operations return a new one. All java.time classes work like that. Writing date.plusDays(30) computes a new date and returns it; the original is untouched. If you throw the result away, nothing happened.

java
LocalDate a = LocalDate.of(2026, 3, 15);
LocalDate b = a.plusDays(30);   // a is still 2026-03-15

This makes date values safe to share between methods and threads, and is the reason they make good fields in classes.

The map of the module

Over the next lessons you will meet a small set of types, each answering one question:

QuestionType (lesson)
A calendar date with no time or zoneLocalDate (2)
A time of day with no date or zoneLocalTime (2)
Date and time, but not a specific momentLocalDateTime (2)
An exact point on the global timelineInstant (3)
Date, time and zone togetherZonedDateTime (3)
An amount of timeDuration, Period (4)
Text to and from datesDateTimeFormatter (4)

Examples

Example 1: the same moment on three clocks (simple)

java
import java.time.*;

public class Clocks {
    public static void main(String[] args) {
        Instant moment = Instant.parse("2026-05-04T07:00:00Z");
        for (String id : new String[]{"Asia/Riyadh", "Europe/London", "America/New_York"}) {
            System.out.println(id + " -> " + moment.atZone(ZoneId.of(id)).toLocalTime());
        }
    }
}

Output:

text
Asia/Riyadh -> 10:00
Europe/London -> 08:00
America/New_York -> 03:00

Instant.parse reads a moment written in UTC (the Z means UTC). atZone views that moment on the clock of a region. You do not need to understand the details yet: notice that one moment gave three clock readings. London is at +01:00 on that date because of daylight saving.

Example 2: a day that is not 24 hours (practical)

java
ZoneId ny = ZoneId.of("America/New_York");
for (LocalDate d : new LocalDate[]{LocalDate.of(2026, 3, 7), LocalDate.of(2026, 3, 8), LocalDate.of(2026, 11, 1)}) {
    long hours = Duration.between(d.atStartOfDay(ny), d.plusDays(1).atStartOfDay(ny)).toHours();
    System.out.println(d + " in New York has " + hours + " hours");
}

Output:

text
2026-03-07 in New York has 24 hours
2026-03-08 in New York has 23 hours
2026-11-01 in New York has 25 hours

The code measures from midnight to the next midnight in New York. On 8 March 2026 the clocks there jumped forward, so the day is 23 hours long; on 1 November they went back, so it is 25. A program that assumes "a day is 86,400 seconds" is wrong on these dates. (The date and the 23/25-hour results come from this program run with JDK 21; the rule dates can change if governments change them.)

Example 3: the legacy trap (realistic)

A developer maintaining old code writes this, intending 15 March 2026:

java
Date old = new Date(2026, 2, 15);          // deprecated constructor
System.out.println(old.getYear() + " " + old.getMonth());

Compiling with -Xlint:deprecation warns that getYear() and getMonth() are deprecated, and the program prints:

text
2026 2

getYear() returns the year minus 1900, so 2026 means the real year is 3926, and month 2 means March only because counting starts at 0. The code compiled, ran, and was wrong. Replacement in modern Java:

java
LocalDate right = LocalDate.of(2026, 3, 15);   // year, month, day: no tricks

Backend Insight

Servers are rarely in the same zone as their users, and cloud servers often run in UTC. If your code uses "the machine's current zone" implicitly, behavior changes when you move the server. Date bugs also hide: a report that is correct for 360 days and wrong on the daylight saving weekend is hard to catch in a test.

Good backend habits that start here:

  • Use java.time types and name your zone explicitly where one matters.
  • Decide for each field what it means: a calendar date (a birthday), or an exact moment (a created timestamp).
  • Treat date values as immutable values you pass around, like strings.

Later in your path, you will store timestamps in databases (JNR-028) and reason about clocks on different machines (MID-023). Both start from the vocabulary of this module.

Common Mistakes

Mistake 1: assuming a day has 24 hours

  • Incorrect: "Add 24 hours to get tomorrow at the same time."
  • Correct: Use calendar arithmetic (plusDays) when you mean "the same time tomorrow", and exact arithmetic (hours) when you mean "24 hours later". Lesson 4 shows the difference in output.

Mistake 2: using java.util.Date in new code

Date has misleading names, zero-based months and a 1900 offset in deprecated methods, and it is mutable. Use java.time. Convert old values with date.toInstant() when a library hands you one.

Mistake 3: ignoring the result of plusDays

java
LocalDate d = LocalDate.of(2026, 3, 15);
d.plusDays(30);                 // result discarded: d is unchanged

Immutable objects return new values. Assign the result: d = d.plusDays(30);.

Mistake 4: treating "10:00" as one moment

"10:00 on 4 May" in Riyadh and in New York are different moments. A bare local date-time is not a point on the timeline.

Exercises

Try each exercise yourself before opening the solutions. Where code is involved, you need a JDK, but the steps for running it depend on your IDE or terminal.

Level A: Foundation

A1. A call starts at 12:00 UTC on 15 January. Riyadh is +03:00 all year. What time is it in Riyadh?

أظهر الحل A1

A1. 15:00. Riyadh is three hours ahead of UTC: 12:00 + 3 hours.

A2. Which package should new Java code use for dates and times?

أظهر الحل A2

A2. java.time, introduced in Java 8.

A3. Is LocalDate.of(2026, 2, 1).plusDays(28) the same object as LocalDate.of(2026, 2, 1) afterwards? Explain in one sentence.

أظهر الحل A3

A3. The original is unchanged. plusDays returns a new LocalDate because date objects are immutable; the result was not stored, so nothing else changes.

Level B: Application

B1. Why can the time difference between London and New York be 5 hours in some weeks and 4 hours in others?

أظهر الحل B1

B1. The two regions change their clocks on different dates. For part of the year one has moved and the other has not, so the offset between them temporarily differs by one hour. (Lesson 5 shows it: in 2026 the gap is 4 hours on 16 March and 5 hours on 30 March.)

B2. What does the deprecated new Date(2026, 0, 1) actually represent? Give the year and the month it uses.

أظهر الحل B2

B2. The constructor takes the year minus 1900 and a month starting at 0. So 2026 means the year 3926, and month 0 is January: 1 January 3926 at local midnight (the documented behavior of the deprecated constructor).

B3. Write a short list of three calendar facts that make "add one month" or "add one year" tricky.

أظهر الحل B3

B3. For example: months have different lengths (28 to 31 days); leap years add 29 February; there is no 31 April, so "31 January plus one month" needs a rule; a year is not always 365 days. Any three of these.

Level C: Challenge

C1. A scheduling app stores "every day at 02:30" for users in New York as 02:30 local time. Describe what can go wrong on the spring daylight saving day. (Do not write code; use the idea of a skipped hour.)

أظهر الحل C1

C1. On the spring day the clock jumps from 02:00 to 03:00, so 02:30 does not exist that day. The app must decide what to do (skip the alarm, run it at 03:30, ...). Without a decision, the alarm may be late or missing. Lesson 3 shows what Java does by default.

C2. A teammate says: "I added 30 days but the date did not change." Write the one-line bug and the one-line fix.

أظهر الحل C2

C2. Bug: d.plusDays(30); with the result discarded. Fix: d = d.plusDays(30); (or store it in a new variable).

Mini Challenge

Write a class ClockWall that prints the same moment on three clocks of your choice. Use Instant.parse("2026-05-04T07:00:00Z"), then atZone(ZoneId.of("...")) for three region IDs of the form Area/City. Use Asia/Riyadh as one of them.

Check your work:

  1. The program compiles and prints three lines.
  2. The Riyadh line shows 10:00.
  3. You can say, for each other city, what its offset from UTC is on that date.

Bonus: change the moment to 2026-01-15T07:00:00Z and see which cities change relative to Riyadh. Explain why using daylight saving.

Summary & Next Lesson

Key takeaways

  • One moment has different clock readings in different time zones; a zone is described by an offset from UTC.
  • Daylight saving makes offsets change over the year, and makes some days 23 or 25 hours long.
  • Month lengths and leap years make naive date arithmetic risky.
  • java.util.Date and Calendar are legacy; use java.time.
  • java.time objects are immutable: operations return new objects, so assign the result.

Next lesson

The next lesson, "Local dates and times", introduces LocalDate, LocalTime and LocalDateTime: dates and times without any time zone, ideal for birthdays and deadlines.

Self-check

  1. Same moment, 07:00 UTC: what time is it in Riyadh (+03:00)?
  2. Name the package you should use for dates in new Java code.
  3. How long can a local day be around daylight saving changes?
  4. Why is new Date(2026, 2, 15) a bug?
  5. What happens if you call d.plusDays(1) and ignore the returned value?
أظهر الإجابات
  1. 10:00.
  2. java.time.
  3. 23 hours when clocks go forward and 25 hours when they go back (in regions that use daylight saving).
  4. The deprecated constructor expects the year minus 1900 and a zero-based month, so it creates the year 3926, not 2026.
  5. Nothing visible: d is unchanged, because plusDays returns a new object.

References

  • Oracle, java.time package summary (Java SE 21). Supports the descriptions of the date and time types, that all classes are immutable and thread-safe, and that they are based on the ISO calendar.
  • Oracle, java.util.Date (Java SE 21). Supports the deprecation of the field methods in favor of Calendar, the year-minus-1900 and zero-based-month parameters of the deprecated constructor, and toInstant().
  • Oracle, java.time.ZoneId (Java SE 21). Supports region IDs, fixed offsets, and that the default region data comes from the IANA Time Zone Database.
  • The 23-hour and 25-hour day lengths, and the printed outputs, come from running the example programs on JDK 21 on the author's machine. The statement that governments can change the rules is general background and was not checked on a specific source here.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.