ข้าม​ไป​ยัง​เนื้อหา

ครึ่ง​ชั่วโมง​ที่​ไม่มี​อยู่​จริง

ร้าน​ของ​คุณ​อยู่​ใน Bangkok และ​ส่ง​ของ​ให้​ลูกค้า​ใน New York ด้วย ลูกค้า​ขอ​งอ​อเด​อร์ A-1042 เลือก slot ส่ง​ของ​เป็น 2026-03-08 02:30 ตาม​นาฬิกา​ของ​บ้าน​ตัว​เอง ซึ่ง​อยู่​ใน​โซน America/New_York

ระบบ​รับ​ค่า​นี้ แปลง​มัน​เป็น​เวลา​ที่​ใช้​คำนวณ​ต่อ แล้ว​ส่ง​ให้​ระบบ​จัด​รถ ไม่มี​บรรทัด​ไหน​แดง ไม่มี exception และ​ไม่มี​คำ​เตือน

คำถาม​ของ​บท​นี้​คือ เวลา​ที่​ลูกค้า​เลือก​มี​อยู่​จริง​หรือ​เปล่า คำ​ตอบ​คือ​ไม่มี และ​ใน​ห้า​ภาษา​ที่​วัด มี​ภาษา​เดียว​ที่​บอก​คุณ

📦 orderctl — ของ​ชิ้น​เดียวกัน​กับ​คอร์ส​ภาษา​อื่น​ใน​เสา​นี้

orderctl คือ​เครื่อง​มือ​ประมวล​ผล​ออ​เด​อร์​ที่​คอร์ส​ภาษา​ใน​เสา​นี้​สร้าง​ซ้ำ​ที​ละ​ภาษา repo ตัวอย่าง กำลัง​จัด​ทำ

ระหว่าง​นี้​ทุก​ค่า​ใน​บท​นี้​รัน​เอง​ได้​ด้วย npm run verify:time-semantics ซึ่ง​เรียก python3 scripts/time-semantics/probe.py · ไม่​ต้อง​มี Docker แต่​การ​รัน​ครั้ง​แรก ต้อง​ต่อ​เน็ต​เพื่อ​ดึง chrono กับ chrono-tz จาก crates.io

คืน​ที่ DST เริ่ม นาฬิกา​ของ New York ข้าม​ช่วง​หนึ่ง​ไป​ทั้ง​ช่วง และ 02:30 อยู่​ใน​ช่วง​นั้น

ประเทศไทย​ใช้ +07:00 ตลอด​ปี จึง​ไม่มี DST (daylight saving time หรือ​เวลา​ออม​แสง) แต่ America/New_York มี และ​วัน​ที่ 2026-03-08 คือ​วัน​ที่ DST ของ​ปี​นั้น​เริ่ม

คืน​นั้น offset ของ​โซน​เปลี่ยน​จาก -05:00 เป็น -04:00 นาฬิกา​จึง​ต้อง​กระโดด​ไป​ข้าง​หน้า​เท่า​ส่วน​ต่าง​นั้น เวลา​ช่วง​หนึ่ง​บน​หน้า​ปัด​ของ​คืน​นั้น​จึง​ไม่​เคย​เกิด​ขึ้น

ค่าที่​ลูกค้า​กรอก​คือ wall-clock timewall-clock timeเวลา​ที่​อ่าน​จาก​หน้า​ปัด​นาฬิกา​ของ​ที่​ใด​ที่​หนึ่ง คือ​วัน​ที่ ชั่วโมง และ​นาที โดย​ไม่มี offset หรือ​โซน​กำกับ · ค่า​หนึ่ง​ค่า​อาจ​ไม่​ตรง​กับ​จุด​ไหน​บน​เส้น​เวลา​เลย ตรง​กับ​สอง​จุด หรือ​ตรง​กับ​จุด​เดียว (เวลา​บน​หน้า​ปัด) คือ​วัน​ที่ ชั่วโมง และ​นาที ตาม​นาฬิกา​ของ​ที่​ใด​ที่​หนึ่ง ค่า​แบบ​นี้​ยัง​ไม่​ได้​ระบุ​ว่า​เป็น​จุด​ไหน​บน​เส้น​เวลา จนกว่า​จะ​มี​โซน​มา​กำกับ และ​โซน​นั้น​ตอบ​ได้​ว่า​ใช้ offset อะไร

จุด​บน​เส้น​เวลา​ที่​ไม่​ขึ้น​กับ​โซน​ของ​ใคร​เรียก​ว่า instantinstantจุด​หนึ่ง​จุด​บน​เส้น​เวลา​ที่​ไม่​ขึ้น​กับ​โซน​ของ​ใคร · เขียน​เป็น UTC ได้​ค่า​เดียว​เสมอ สอง​ระบบ​ที่​เก็บ instant เดียวกัน​จึง​หมาย​ถึง​เวลา​เดียวกัน ต่อ​ให้​หน้า​ปัด​ของ​แต่ละ​ที่​แสดง​คนละ​ค่า (จุด​หนึ่ง​บน​เส้น​เวลา) เขียน​เป็น UTC ได้​ค่า​เดียว ระบบ​จัด​รถ​ต้องการ instant ไม่ใช่ wall-clock time เพราะ​รถ​วิ่ง​ตาม​เวลา​จริง ไม่​ได้​วิ่ง​ตาม​ตัวเลข​บน​ฟอร์ม

ช่วง​ของ wall-clock time ที่​นาฬิกา​กระโดด​ข้าม​ไป​เรียก​ว่า DST gapDST gapช่วง​เวลา​บน​หน้า​ปัด​ที่​ไม่มี​อยู่​จริง เพราะ​นาฬิกา​กระโดด​ข้าม​ไป​ตอน​เริ่ม DST · ที่ America/New_York วัน​ที่ 2026-03-08 offset เปลี่ยน​จาก -05:00 เป็น -04:00 เวลา 02:30 ของ​วัน​นั้น​จึง​ไม่มี​อยู่​ใน​โซน​นั้น (ช่วง​เวลา​บน​หน้า​ปัด​ที่​ไม่มี​อยู่​จริง) 02:30 ของ​วัน​ที่ 2026-03-08 ที่ America/New_York อยู่​ใน gap นั้น จึง​ไม่​ตรง​กับ instant ไหน​เลย

flowchart LR
  A["ฟอร์มของลูกค้า · 2026-03-08 02:30"] --> B{"America/New_York มี 02:30 ของวันนั้นไหม"}
  B -->|"ไม่มี · นาฬิกากระโดดข้ามช่วงนั้นไป"| C["ไม่มี instant ให้แปลงไปหา"]
  C --> D["ภาษาต้องเลือกเองว่าจะทำอะไร"]
  D --> E["คืนเวลาค่าหนึ่งให้เงียบๆ"]
  D --> F["บอกผู้เรียกว่าไม่มีเวลานี้"]

คำ​บรรยาย​ภาพ: ปัญหา​ไม่​ได้​อยู่​ที่​การ​แปลง​ผิด แต่​อยู่​ที่​ไม่มี​อะไร​ให้​แปลง​ไป​หา ทุก​ภาษา​จึง​ต้อง​ตัดสิน​ใจ​แทน​ผู้​เรียก และ​สิ่ง​ที่​แยก​ห้า​ภาษา​ออก​จาก​กัน​คือ​กิ่ง​ที่​มัน​เลือก

แต่ละ​ภาษา​ได้​รับ 2026-03-08 02:30 ที่ America/New_York เหมือน​กัน แล้ว​คืน​ของ​กลับ​มา​คนละ​แบบ

ทุก​การ​เรียก​ข้าง​ล่าง​ตัด​มา​จาก runner ของ probe ใน scripts/time-semantics/runners/ และ​ค่า​ใน​คอม​เมน​ต์ คือ​ค่าที่ probe วัด​ได้ ไม่ใช่​ค่าที่​เขียน​จาก​ความ​จำ

ZonedDateTime.of รับ​เวลา​ที่​ไม่มี​อยู่​จริง​โดย​ไม่​โยน exception แล้ว​คืน​เวลา​ที่​เลื่อน​ไป​แล้ว

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง A)
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime gap = ZonedDateTime.of(2026, 3, 8, 2, 30, 0, 0, ny);
gap.toString(); // 2026-03-08T03:30-04:00[America/New_York]
gap.toInstant(); // 2026-03-08T07:30:00Z

Javadoc ของ ZonedDateTime.of เขียน​กฎ​นี้​ไว้​ตรงๆ ว่า​ใน​กรณี​ของ gap “the local date-time is adjusted to be later by the length of the gap” ค่า 02:30 จึง​กลาย​เป็น 03:30 และ offset กลาย​เป็น -04:00

Node เลื่อน​ไป​ข้าง​หน้า แต่​เฉพาะ​เมื่อ process อยู่​ใน New York

หัวข้อ​ที่​มีชื่อ​ว่า “Node เลื่อน​ไป​ข้าง​หน้า แต่​เฉพาะ​เมื่อ process อยู่​ใน New York”

Date สร้าง​เวลา​จาก​วัน​ที่​และ​ชั่วโมง​ได้​แค่​ใน​โซน​ของ process ซึ่ง​มา​จาก TZ ค่า​ของ Node ทุก​ค่า​ใน​บท​นี้​จึง​บอก TZ ที่​ใช้​รัน​กำกับ​ไว้​เสมอ

scripts/time-semantics/runners/shapes.mjs (เฉพาะ​ทรง A)
// เดือนของ Date เริ่มที่ 0 · เลข 2 คือเดือนมีนาคม
const gap = new Date(2026, 2, 8, 2, 30);
gap.toString();
gap.toISOString();

probe รัน file เดียวกัน​สอง​ครั้ง ต่าง​กัน​ที่ TZ อย่าง​เดียว

TZtoString()toISOString()
America/New_YorkSun Mar 08 2026 03:30:00 GMT-0400 (Eastern Daylight Time)2026-03-08T07:30:00.000Z
Asia/BangkokSun Mar 08 2026 02:30:00 GMT+0700 (Indochina Time)2026-03-07T19:30:00.000Z

แถว​แรก​ตรง​กับ Java ทั้ง wall-clock time และ instant ส่วน​แถว​ที่​สอง​ไม่​ได้​ตอบ​คำถาม​ของ​บท​นี้​เลย มัน​สร้าง 02:30 ของ Bangkok ซึ่ง​มี​อยู่​จริง เพราะ Bangkok ไม่มี gap ใน​คืน​นั้น

ผล​ของ​แถว​ที่​สอง​คือ server ที่​ตั้ง TZ=Asia/Bangkok สร้าง slot ของ​ลูกค้า​ใน New York ด้วย Date ไม่​ได้​ตั้งแต่​แรก code บรรทัด​เดียวกัน​ได้​คนละ instant ตาม​เครื่อง​ที่​รัน

สเปก ECMAScript หัวข้อ UTC ( t ) เขียน​กฎ​ของ​แถว​แรก​ไว้​ว่า​เวลา​ที่​ถูก​ข้าม​ไป “t is interpreted using the time zone offset before the transition” คือ​ใช้ -05:00 ซึ่ง​เป็น offset ก่อน​การ​เปลี่ยน แล้ว​จุด​ที่​ได้​ก็​แสดง​ผล​เป็น 03:30 ของ offset ใหม่

datetime ของ Python ไม่​ได้​เลื่อน​อะไร​เลย มัน​เก็บ 02:30 ไว้ แล้ว​แปะ offset ให้

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง A)
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
g = datetime(2026, 3, 8, 2, 30, tzinfo=ny)
g.isoformat() # 2026-03-08T02:30:00-05:00

2026-03-08T02:30:00-05:00 เป็น​สตริง​ที่​อ่าน​แล้ว​ดูถูก​ต้อง​ทุก​ตัว​อักษร แต่​ไม่มี​นาฬิกา​เรือน​ไหน ใน New York เคย​แสดง 02:30 คู่​กับ -05:00 ใน​วัน​นั้น

PEP 495 ซึ่ง​เป็น​ที่มา​ของ​ช่อง fold เขียน​กฎ​ไว้​ว่า​เมื่อ​เวลา​ตก​อยู่​ใน gap “the rules in effect before the transition should be used if fold=0” ค่า​ตั้ง​ต้น​ของ fold คือ 0 Python จึง​แปะ -05:00 ซึ่ง​เป็น offset ก่อน​การ​เปลี่ยน

ค่า​ของ Go ใน​หัวข้อ​ถัด​ไป​เป็น​ของ​เครื่อง​เดียว

อ่าน​ประโยค​นี้​ก่อน​อ่าน​ค่า​ของ Go · go doc time.Date เขียน​ไว้​ว่า

“Date returns a time that is correct in one of the two zones involved in the transition, but it does not guarantee which.”

ค่าที่ Go คืนให้​ใน​เวลา​ที่​ไม่มี​อยู่​จริง​จึง​ไม่ใช่​คำ​ตอบ​ของ Go ทั้ง​ภาษา มัน​เป็น​ค่า​ของ​เครื่อง​ที่​วัด และ​เครื่อง​อื่น​ตอบ​อีก​โซน​ได้​โดย​ไม่​ผิด​เอกสาร

scripts/time-semantics/runners/go/main.go (เฉพาะ​ทรง A)
ny, err := time.LoadLocation("America/New_York")
// ...
time.Date(2026, 3, 8, 2, 30, 0, 0, ny).Format(time.RFC3339)

เครื่อง​ที่ probe รัน​คือ go1.27.0 บน darwin/arm64 และ​ค่าที่​วัด​ได้​บน​เครื่อง​นั้น​คือ 2026-03-08T01:30:00-05:00

probe เก็บ​ค่า​นี้​ไว้​ใน​ช่อง unguaranteed ของ measured.json แล้ว​ไม่​ผูก​มัน​เป็น​ข้อ​อ้าง​สัก​ข้อ ด่าน​ของ CI รัน​บน linux/amd64 ซึ่ง​เป็น​คนละ​เครื่อง​กับ​ที่​วัด

สิ่ง​ที่​บท​นี้​พูด​ได้​เกี่ยว​กับ Go จึง​มี​ข้อ​เดียว คือ time.Date คืน​เวลา​ค่า​หนึ่ง​ให้​เงียบๆ ไม่มี error และ​ไม่มี​ช่อง​ให้​รู้​ว่า​เวลา​ที่​ส่ง​เข้าไป​ไม่มี​อยู่​จริง

chrono-tz ไม่​คืน​เวลา​ค่า​เดียว มัน​คืน enum ที่​บังคับ​ให้​ผู้​เรียก​จัดการ​ทุก​กรณี

scripts/time-semantics/runners/rs/src/main.rs (เฉพาะ​ทรง A)
use chrono::{LocalResult, NaiveDate, TimeZone};
use chrono_tz::America::New_York;
let gap = NaiveDate::from_ymd_opt(2026, 3, 8).unwrap().and_hms_opt(2, 30, 0).unwrap();
match New_York.from_local_datetime(&gap) {
LocalResult::None => { /* ค่าที่วัดได้ตกกิ่งนี้ */ }
LocalResult::Single(a) => { /* ... */ }
LocalResult::Ambiguous(a, b) => { /* ... */ }
}

ค่าที่​วัด​ได้​คือ LocalResult::None · เอกสาร​ของ chrono เขียน​ความ​หมาย​ของ variant นี้​ว่า “The local time does not exist because there is a gap in the local time.”

match ข้าง​บน​ไม่ compile ถ้า​ขาด​กิ่ง​ใด​กิ่ง​หนึ่ง​ใน​สาม​กิ่ง ผู้​เรียก​จึง​หนี​คำถาม​ว่า “ถ้า​เวลา​นี้​ไม่มี​จะ​ทำ​อะไร” ไม่​ได้ นี่​คือ​ราคา​ของ Rust ด้วย code ที่​แค่​อยาก​ได้​เวลา​ค่า​เดียว​ต้อง​เขียน​กิ่ง​เพิ่ม​อีก​สอง​กิ่ง

รวม​ห้า​คำ​ตอบ​ไว้​ใน​ตาราง​เดียว พร้อม​รุ่น​ของ tzdata ที่​แต่ละ​ภาษา​อ่าน​จริง

ภาษาการ​เรียกค่าที่​วัด​ได้tzdata ที่​ใช้
Java openjdk 25.0.4.1ZonedDateTime.of(...)2026-03-08T03:30-04:00[America/New_York]2026b · tzdb.dat ของ JDK
Node v24.19.0 ที่ TZ=America/New_Yorknew Date(2026, 2, 8, 2, 30)Sun Mar 08 2026 03:30:00 GMT-0400 (Eastern Daylight Time)2026b · ICU ที่​ฝัง​ใน node
Python 3.14.6datetime(..., tzinfo=ny)2026-03-08T02:30:00-05:002026c · /usr/share/zoneinfo ของ​ระบบ
Go go1.27.0 · darwin/arm64 เท่านั้นtime.Date(...)2026-03-08T01:30:00-05:00 · เอกสาร​ไม่​รับปาก2026c · /usr/share/zoneinfo ของ​ระบบ
Rust chrono-tz 0.10.4New_York.from_local_datetime(...)LocalResult::None2025b · ฝัง​ใน chrono-tz

แถว​ของ Java กับ Node ตรง​กัน​ทั้ง wall-clock time และ instant ที่​เหลือ​ต่าง​กัน​หมด บน​เครื่อง​ที่​วัด ห้า​ภาษา​จึง​ให้​สี่​คำ​ตอบ

ตัวเลข​สี่​นี้​นับ​รวม​ค่า​ของ Go ด้วย ถ้า​ตัด​แถว​ที่​เอกสาร​ไม่​รับปาก​ออก สิ่ง​ที่​รับปาก​ได้​คือ​สี่​ภาษา​ให้ สาม​คำ​ตอบ และ​มี​ภาษา​เดียว​ที่​บอก​ว่า​เวลา​นี้​ไม่มี​อยู่​จริง

ห้า​ภาษา​บน​เครื่อง​เดียวกัน​อ่าน tzdata สาม​รุ่น จาก​สี่​แหล่ง ตาราง​ที่​รวม​หลาย​ภาษา​จึง​ต้อง​พิมพ์​รุ่น​ของ​ทุก​ภาษา เพราะ​คำ​ตอบ​ของ​คอร์ส​นี้​ขึ้น​กับ​ข้อมูล​ของ​โซน​ด้วย ไม่​ได้​ขึ้น​กับ code อย่าง​เดียว

Python ไม่รู้​ว่า​ค่าที่​มัน​เก็บ​ไม่มี​อยู่​จริง จนกว่า​จะ​ส่ง​ค่า​นั้น​ไป UTC แล้ว​รับ​กลับ​มา

2026-03-08T02:30:00-05:00 ของ Python ดู​เหมือน​เวลา​ปกติ​ค่า​หนึ่ง วิธี​ที่​ทำให้​มัน​เผย​ตัว​คือ แปลง​ไป UTC แล้ว​แปลง​กลับ​มา​ที่​โซน​เดิม

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง A)
g.astimezone(timezone.utc).astimezone(ny).isoformat() # 2026-03-08T03:30:00-04:00

เวลา​ที่​ไป​แล้วก​ลับ​มา​ไม่ใช่​เวลา​เดิม 02:30 หาย​ไป และ 03:30-04:00 มา​แทนที่ ซึ่ง​เป็น wall-clock time เดียว​กับ​ที่ Java คืนให้​ตั้งแต่​ต้น

ค่าที่​มี​อยู่​จริง​จะ​ไป​แล้วก​ลับ​มา​เป็น​ค่า​เดิม​เสมอ เพราะ​การ​แปลง​จาก UTC กลับ​มา​ที่​โซน​ไม่มี​ทาง​ได้​เวลา​ใน gap PEP 495 เขียน​ข้อ​นี้​ไว้​ว่า “The fromutc() method should never produce a time in the gap.”

Java ก็​ตอบ​คำถาม​นี้​ได้ ถ้า​ถาม​มัน​ตรงๆ แทนที่​จะ​สร้าง ZonedDateTime

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง A)
ny.getRules().getValidOffsets(LocalDateTime.of(2026, 3, 8, 2, 30)).size(); // 0

offset ที่​ใช้ได้​กับ 02:30 ของ​วัน​นั้น​มี 0 ตัว คือ​ไม่มี​เลย Java รู้​ความ​จริง​ข้อ​นี้​มา​ตลอด แต่ ZonedDateTime.of ไม่​ได้​บอก​มัน ผู้​เรียก​ต้อง​รู้​เอง​ว่า​ต้อง​ถาม​อีก method หนึ่ง

ทุก​ภาษา​ที่​คืน​เวลา​ให้​เงียบๆ ส่ง​รถ​ไป​ตาม​เวลา​ที่​ลูกค้า​ไม่​ได้​กรอก

กลับ​ไป​ที่​ออ​เด​อร์ A-1042 ระบบ​ที่​เขียน​ด้วย Java หรือ Node ที่ TZ=America/New_York จะบัน​ทึก slot เป็น 03:30 ลูกค้า​กรอก 02:30 และ​ไม่มี​ใคร​บอก​ลูกค้า​ว่า​เวลา​ถูก​เลื่อน

ระบบ​ที่​เขียน​ด้วย Python จะ​เก็บ 02:30-05:00 ไว้ และ​แสดง 02:30 ให้​ลูกค้า​เห็น​ตาม​เดิม พอ​ค่า​นั้น​ผ่าน UTC ไป​ที่​ระบบ​จัด​รถ​แล้วก​ลับ​มา หน้า​จอ​ของ​คน​ขับ​จะ​แสดง 03:30-04:00

ถ้า​สอง service ใน​เส้น​ทาง​เดียวกัน​เขียน​คนละ​ภาษา slot เดียวกัน​จะ​แสดง​คนละ​เวลา​บน​สอง​หน้า​จอ และ server ที่​ตั้ง TZ=Asia/Bangkok สร้าง slot ของ New York ด้วย Date ไม่​ได้ มัน​ได้ 02:30 ของ Bangkok แทน ซึ่ง​คือ 2026-03-07T19:30:00.000Z คนละ​จุด​กับ slot ที่​ลูกค้า​เลือก

ราคา​ของ​การ​ให้​ภาษา​ตัดสิน​แทน

ทางออก​ไม่ใช่​การ​เลือก​ภาษา​ที่​เลื่อน​เวลา​ได้​ถูกใจ แต่​คือ​การ​ถาม​ก่อน​ว่า​เวลา​นี้​มี​อยู่​จริง​ไหม แล้ว​ตัดสิน​เอง​ว่า​จะ​ปฏิเสธ หรือ​จะ​ถาม​ลูกค้า​ใหม่

ราคา​คือ code ทุก​จุด​ที่​รับ​เวลา​จาก​ผู้​ใช้​ต้อง​มี​กิ่ง​ของ​เวลา​ที่​ไม่มี​อยู่​จริง ใน Rust คอม​ไพ​เลอ​ร์​บังคับ​กิ่ง​นั้น​ให้ ใน​อีก​สี่​ภาษา ไม่มี​ใคร​บังคับ และ​กิ่ง​นั้น​จะ​ไม่​ถูก​เขียน​จนกว่า​จะ​มี​ลูกค้า​ร้อง​เรียน

บท​นี้​ปิด​จุด​ที่​หนึ่ง อีก​ห้า​จุด​อยู่​ใน​บท​ถัด​ไป และ​ทุก​จุด​เงียบ​เหมือน​กัน

ธี​สิ​ส​ของ​คอร์ส​นี้​เป็น​ประโยค​เดียว คือ wall-clock time ค่า​หนึ่ง​ไม่​ได้​ระบุ​จุด​เดียว​บน​เส้น​เวลา มัน​อาจ​ไม่มี​จุด​เลย มี​สอง​จุด หรือ​มี​จุด​เดียว​ที่​แต่ละ​ภาษา​เลือก​ต่าง​กัน และ​ใน​ทรง​ที่ probe วัด มี​ภาษา​เดียว​จาก​ห้า​ภาษา​ที่​บอก​คุณ

จุด​ที่​หนึ่ง​คือ​จุด​ที่​บท​นี้​เพิ่ง​วัด คือ wall-clock time ที่​ไม่มี​จุด​บน​เส้น​เวลา​เลย สี่​ภาษา​ตอบ​มัน​กลับ​มา เป็น​เวลา​ค่า​หนึ่ง และ Rust เป็น​ภาษา​เดียว​ที่​ตอบ​ว่า​ไม่มี

บท​ที่ 2 ถอย​ไป​ที่​ค่าที่​สั้น​กว่า​นี้​อีก คือ​วัน​ส่ง​ของ​ที่​มี​แค่​วัน​ที่ ไม่มี​เวลา​และ​ไม่มี​โซน แล้ว​ถาม​ว่า​มัน​กลาย​เป็น คนละ​วัน​ได้​อย่างไร


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Go — package time, func Date (เข้า​ถึง 2026-10-02) — ที่มา​ของ​ประโยค “Date returns a time that is correct in one of the two zones involved in the transition, but it does not guarantee which.” · ตรง​กับ​ผล​ของ go doc time.Date บน go1.27.0

  • Java SE 25 — ZonedDateTime (เข้า​ถึง 2026-10-02) — ที่มา​ของ​กฎ​ของ ZonedDateTime.of ที่​เลื่อน​เวลา​ใน gap ไป​ข้าง​หน้า​เท่า​ความ​ยาว​ของ gap

  • ECMAScript — UTC ( t ) (เข้า​ถึง 2026-10-02) — ที่มา​ของ​กฎ​ที่ Date ตีความ​เวลา​ที่​ถูก​ข้าม​ไป​ด้วย offset ก่อน​การ​เปลี่ยน

  • PEP 495 — Local Time Disambiguation (เข้า​ถึง 2026-10-02) — ที่มา​ของ​กฎ​ที่ fold=0 ใช้ offset ก่อน​การ​เปลี่ยน​สำหรับ​เวลา​ใน gap และ​ของ​ข้อ​ที่ fromutc() ไม่มี​ทาง​คืน​เวลา​ใน gap

  • Python — zoneinfo (เข้า​ถึง 2026-10-02) — ที่มา​ของ ZoneInfo ที่​บท​นี้​ใช้ และ​ของ​ข้อ​ที่​โซน​ของ​มัน​รองรับ​ช่อง fold ตาม PEP 495

  • chrono 0.4.45 — LocalResult (เข้า​ถึง 2026-10-02) — ที่มา​ของ​ความ​หมาย​ของ LocalResult::None

  • ค่า​ทุก​ค่า​ใน​บท​นี้​มา​จาก scripts/time-semantics/measured.json ซึ่ง scripts/time-semantics/probe.py เขียน​เมื่อ 2026-10-02 บน darwin/arm64 · probe วัด​ทั้ง​ชุด​สาม​รอบ​ต่อ​การ​เรียก​หนึ่ง​ครั้ง และ Node รัน​ด้วย TZ ที่ probe ตั้ง​เอง​ทุก​ครั้ง

เช็กความเข้าใจ — บทที่ 1

ข้อ 1 / 3

ส่ง `2026-03-08 02:30` ที่ `America/New_York` เข้าห้าภาษา ภาษาไหนบอกผู้เรียกว่าเวลานี้ไม่มีอยู่จริง