นากกายต้นฉบับกำลังตรึงเข็มนาฬิกาไม้ไว้เพื่อทดสอบสายพานบัตรหมดอายุบนโต๊ะสว่าง

Java Clock.fixed: หยุดเวลาใน test โดยไม่แตะนาฬิการะบบ

พี่โชว์Engineering

โค้ดที่เรียก Instant.now() ตรง ๆ ทดสอบขอบเขตเวลาได้ยากและอาจ flaky ตอนวินาทีเปลี่ยนครับ การรับ Clock ผ่าน constructor แล้วใช้ Clock.fixed ใน test ทำให้เวลาคงที่โดยไม่แก้นาฬิการะบบหรือใช้ sleep

WHY: ยืมนาฬิกาจำลอง แทนปีนไปหมุนหอนาฬิกา

เหมือนร้านใช้เครื่องจำลองเวลาเช็กคูปอง ไม่ต้องหยุดนาฬิกาทั้งเมือง Production ส่ง Clock.systemUTC() ส่วน test ส่งเวลาที่รู้แน่ ทำให้กรณี “เท่ากับ deadline” อ่านได้ตรงและรันซ้ำเหมือนเดิม

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;

public class Main {
  static final class Coupon {
    private final Clock clock;

    Coupon(Clock clock) {
      this.clock = clock;
    }

    boolean isExpired(Instant deadline) {
      return !Instant.now(clock).isBefore(deadline);
    }
  }

  public static void main(String[] args) {
    Instant now = Instant.parse("2026-10-07T03:00:00Z");
    Clock fixed = Clock.fixed(now, ZoneOffset.UTC);
    Coupon coupon = new Coupon(fixed);

    assert !coupon.isExpired(now.plusSeconds(1));
    assert coupon.isExpired(now);
    assert coupon.isExpired(now.minusSeconds(1));
  }
}

Wall clock กับ elapsed time เป็นคนละเรื่อง

Clock เหมาะกับวันเวลา business, timestamp และ expiry แต่การวัด duration ของ process ควรใช้ monotonic source เช่น System.nanoTime() เพราะ wall clock กระโดดได้จาก NTP หรือการปรับระบบ และห้ามใช้เวลาที่ client ส่งมาเป็นหลักฐาน authorization

กำหนด timezone ที่ boundary อย่างชัดเจน หากกติกาเป็น “สิ้นวันกรุงเทพฯ” ให้คำนวณด้วย ZoneId ของธุรกิจแล้วแปลงเป็น Instant ไม่ผสม default timezone ของเครื่อง

Recap

  • ฉีด Clock เข้า logic ที่พึ่งเวลาปัจจุบัน
  • ใช้ Clock.fixed ทดสอบก่อน/เท่ากับ/หลัง deadline
  • ใช้ monotonic time สำหรับ elapsed duration
  • กำหนด timezone ของธุรกิจ ไม่พึ่ง default เครื่อง