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

ครึ่ง​ชั่วโมง​ที่​เกิด​ขึ้น​สอง​ครั้ง

ลูกค้า​ขอ​งอ​อเด​อร์ A-1042 คน​เดิม​ใน New York สั่ง​ของ​อีก​ครั้ง คราว​นี้​เลือก slot ส่ง​ของ​เป็น 2026-11-01 01:30 ตาม​นาฬิกา​ของ​บ้าน​ตัว​เอง ใน​โซน America/New_York

ต่าง​จาก บท​ที่ 1 ตรง​ที่​เวลา​นี้​มี​อยู่​จริง ปัญหา​คือ​มัน​มี​อยู่​จริงสอง​ครั้ง และ​ฟอร์ม​ของ​ลูกค้า​ไม่มี​ช่อง​ให้​บอก​ว่า​หมาย​ถึง​ครั้ง​ไหน

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

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

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

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

คืน​ที่ DST จบ นาฬิกา​ของ New York ถอย​กลับ และ 01:30 ของ​คืน​นั้น​เกิด​ขึ้น​สอง​ครั้ง

2026-11-01 คือ​วัน​ที่ DST ของ America/New_York จบ​ใน​ปี​นั้น คืน​นั้น offset ของ​โซน​เปลี่ยน​จาก -04:00 กลับ​เป็น -05:00 นาฬิกา​จึง​ต้อง​ถอย​กลับ​เท่า​ส่วน​ต่าง​นั้น แล้ว​เดิน​ช่วง​เดิม​ซ้ำ​อีก​รอบ

ช่วง​ของ​เวลา​บน​หน้า​ปัด​ที่​เกิด​ขึ้น​สอง​ครั้ง​เรียก​ว่า DST overlapDST overlapช่วง​เวลา​บน​หน้า​ปัด​ที่​เกิด​ขึ้น​สอง​ครั้ง เพราะ​นาฬิกา​ถอย​กลับ​ตอน​จบ DST · เวลา​เดียวกัน​บน​หน้า​ปัด​จึง​ตรง​กับ​สอง instant และ​ต้อง​มี​ใคร​สัก​คน​เลือก​ว่า​หมาย​ถึง​ครั้ง​ไหน (ช่วง​เวลา​บน​หน้า​ปัด​ที่​เกิด​ขึ้น​สอง​ครั้ง) เป็น​คู่​ตรง​ข้าม​ของ gap ใน​บท​ที่ 1 ที่​นั่น​เวลา​ไม่มี​จุด​บน​เส้น​เวลา​เลย ที่​นี่​เวลา​มี​สอง​จุด

ครั้ง​แรก​ของ 01:30 ยัง​อยู่​ใน DST จึง​ใช้ -04:00 ส่วน​ครั้ง​ที่​สอง​เกิด​หลัง​นาฬิกา​ถอย​กลับ​แล้ว จึง​ใช้ -05:00 สอง​ค่า​นี้​เป็น​เวลา​บน​หน้า​ปัด​เดียวกัน​ทุก​ตัว​อักษร แต่​เป็น​คนละ instant

การ​ตัดสิน​ว่า​เวลา​บน​หน้า​ปัด​ที่​มี​สอง​ครั้ง​จะ​กลาย​เป็น instant ไหน​เรียก​ว่า disambiguationdisambiguationการ​ตัดสิน​ว่า​เวลา​บน​หน้า​ปัด​ที่​ไม่มี​อยู่​จริง หรือ​ที่​มี​สอง​ครั้ง จะ​กลาย​เป็น instant ไหน · บาง​ภาษา​เลือก​ให้​เงียบๆ บาง​ภาษา​ไม่​รับปาก​ว่า​จะ​เลือก​ฝั่ง​ไหน และ​บาง​ภาษา​คืน​ทุก​ทาง​เลือก​ให้​ผู้​เรียก​เป็น​คน​ตัดสิน (การ​ตัดสิน​ว่า​เวลา​นั้น​หมาย​ถึง​จุด​ไหน) ต้อง​มี​ใคร​สัก​คน​ทำ​มัน ถ้า​ผู้​เรียก​ไม่​ทำ ภาษา​จะ​ทำ​แทน

flowchart LR
  A["ฟอร์มของลูกค้า · 2026-11-01 01:30"] --> B{"America/New_York มี 01:30 ของวันนั้นกี่ครั้ง"}
  B -->|"ครั้งแรก · ยังอยู่ใน DST"| C["01:30 ที่ -04:00"]
  B -->|"ครั้งที่สอง · หลังนาฬิกาถอยกลับ"| D["01:30 ที่ -05:00"]
  C --> E["ภาษาเลือกให้หนึ่งครั้งแบบเงียบๆ"]
  D --> E
  C --> F["ภาษาคืนทั้งสองครั้งให้ผู้เรียกเลือก"]
  D --> F

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

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

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

Java เลือก​ครั้ง​แรก และ​มี​อีก method ให้​ขอ​ครั้ง​ที่​สอง

หัวข้อ​ที่​มีชื่อ​ว่า “Java เลือก​ครั้ง​แรก และ​มี​อีก method ให้​ขอ​ครั้ง​ที่​สอง”

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

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง B)
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime ov = ZonedDateTime.of(2026, 11, 1, 1, 30, 0, 0, ny);
ov.toString(); // 2026-11-01T01:30-04:00[America/New_York]
ov.withLaterOffsetAtOverlap().toString(); // 2026-11-01T01:30-05:00[America/New_York]

Javadoc ของ ZonedDateTime.of เขียน​กฎ​นี้​ไว้​ตรงๆ ว่า “In the case of an overlap, when clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to “summer”.” ค่าที่​ได้​จึง​เป็น -04:00

ครั้ง​ที่​สอง​ขอ​ได้​ด้วย withLaterOffsetAtOverlap() ซึ่ง​คืน -05:00 แต่​ผู้​เรียก​ต้อง​รู้​เอง​ว่า​เวลา​นี้​อยู่​ใน overlap และ​ต้อง​รู้​เอง​ว่า​ลูกค้า​หมาย​ถึง​ครั้ง​ที่​สอง ZonedDateTime.of ไม่​ได้​บอก​อะไร​เลย

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

scripts/time-semantics/runners/shapes.mjs (เฉพาะ​ทรง B)
// เดือนของ Date เริ่มที่ 0 · เลข 10 คือเดือนพฤศจิกายน
const ov = new Date(2026, 10, 1, 1, 30);
ov.toString();
ov.toISOString();

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

TZtoString()toISOString()
America/New_YorkSun Nov 01 2026 01:30:00 GMT-0400 (Eastern Daylight Time)2026-11-01T05:30:00.000Z
Asia/BangkokSun Nov 01 2026 01:30:00 GMT+0700 (Indochina Time)2026-10-31T18:30:00.000Z

แถว​แรก​ได้ GMT-0400 คือ​ครั้ง​แรก​เหมือน Java แถว​ที่​สอง​ไม่​ได้​ตอบ​คำถาม​ของ​บท​นี้ มัน​สร้าง 01:30 ของ Bangkok ซึ่ง​มี​ครั้ง​เดียว เพราะ Bangkok ไม่มี overlap ใน​คืน​นั้น

สเปก ECMAScript หัวข้อ UTC ( t ) เขียน​กฎ​ของ​แถว​แรก​ไว้​ว่า​เวลา​ที่​ซ้ำ “t is interpreted using the time zone offset before the transition” แล้ว​เขียน​ขั้น​ที่​ทำ​มัน​ไว้​ว่า “Let disambiguatedInstant be possibleInstants[0].” คือ​หยิบ​ตัว​แรก​ของ​รายการ​เสมอ

Python เลือก​ตาม​ช่อง fold ซึ่ง​ค่า​ตั้ง​ต้น​คือ​ครั้ง​แรก

หัวข้อ​ที่​มีชื่อ​ว่า “Python เลือก​ตาม​ช่อง fold ซึ่ง​ค่า​ตั้ง​ต้น​คือ​ครั้ง​แรก”

datetime ของ Python มี​ช่อง​หนึ่ง​ไว้​สำหรับ​คำถาม​นี้​โดย​เฉพาะ คือ foldfoldช่อง​ของ datetime ใน Python ที่​ระบุ​ว่า​เวลา​ที่​มี​สอง​ครั้ง​หมาย​ถึง​ครั้ง​ไหน · fold=0 คือ​ครั้ง​แรก fold=1 คือ​ครั้ง​ที่​สอง และ​ค่า​ตั้ง​ต้น​คือ 0 ผู้​ที่​ไม่​ได้​ตั้ง​มัน​จึง​ได้​ครั้ง​แรก​โดย​ไม่รู้​ตัว (ช่อง​ที่​ระบุ​ว่า​เวลา​ที่​มี​สอง​ครั้ง​หมาย​ถึง​ครั้ง​ไหน)

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง B)
from datetime import datetime
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
datetime(2026, 11, 1, 1, 30, tzinfo=ny).isoformat() # 2026-11-01T01:30:00-04:00
datetime(2026, 11, 1, 1, 30, fold=1, tzinfo=ny).isoformat() # 2026-11-01T01:30:00-05:00

เอกสาร datetime ของ Python เขียน​ความ​หมาย​ของ​ช่อง​นี้​ไว้​ว่า “The values 0 and 1 represent, respectively, the earlier and later of the two moments with the same wall time representation.”

PEP 495 ซึ่ง​เป็น​ที่มา​ของ​ช่อง​นี้​กำหนด​ให้ constructor รับ​มัน​เป็น “a new keyword-only argument called fold with the default value 0.” บรรทัด​แรก​ที่​ไม่​ได้​ตั้ง fold จึง​ได้​ครั้ง​แรก​โดย​ไม่รู้​ตัว

ราคา​ของ fold เหมือน​กับ withLaterOffsetAtOverlap() ของ Java คือ​ผู้​เรียก​ต้อง​รู้​เอง​ว่า​ต้อง​ตั้ง และ datetime(...) ไม่​โยน error เมื่อ​ไม่​ได้​ตั้ง

ค่า​ของ 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 (เฉพาะ​ทรง B)
ny, err := time.LoadLocation("America/New_York")
// ...
time.Date(2026, 11, 1, 1, 30, 0, 0, ny).Format(time.RFC3339)

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

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

ค่า​นี้​บังเอิญ​ตรง​กับ​อีก​สาม​ภาษา​บน​เครื่อง​ที่​วัด แต่​ความ​บังเอิญ​นั้น​ไม่ใช่​สิ่ง​ที่ code ของ​คุณ​พึ่ง​ได้ สิ่ง​ที่​บท​นี้​พูด​ได้​เกี่ยว​กับ Go คือ time.Date คืน Time ค่า​เดียว​ให้​เงียบๆ ไม่มี error และ​ไม่มี​ค่าที่​สอง​ให้​เห็น​ว่า​เวลา​นี้​มี​สอง​ครั้ง

chrono-tz ไม่​เลือก​ให้ มัน​คืน enum ตัว​เดียว​กับ​บท​ที่ 1 แต่​ตก​คนละ​กิ่ง

scripts/time-semantics/runners/rs/src/main.rs (เฉพาะ​ทรง B)
use chrono::{LocalResult, NaiveDate, TimeZone};
use chrono_tz::America::New_York;
let ov = NaiveDate::from_ymd_opt(2026, 11, 1).unwrap().and_hms_opt(1, 30, 0).unwrap();
match New_York.from_local_datetime(&ov) {
LocalResult::None => "None".to_string(),
LocalResult::Single(a) => format!("Single({})", a.to_rfc3339()),
LocalResult::Ambiguous(a, b) => format!("Ambiguous({}, {})", a.to_rfc3339(), b.to_rfc3339()),
}

runner พิมพ์​ค่าที่​วัด​ได้​เป็น Ambiguous(2026-11-01T01:30:00-04:00, 2026-11-01T01:30:00-05:00) ทั้ง​สอง​ครั้ง​มา​พร้อม​กัน และ code ที่​อยาก​ได้​เวลา​ค่า​เดียว​ต้อง​เลือก​เอง​ว่า​จะ​ใช้​ตัว​ไหน

เอกสาร​ของ chrono เขียน​ความ​หมาย​ของ variant นี้​ว่า “The local time is ambiguous because there is a fold in the local time. This variant contains the two possible results, in the order (earliest, latest).”

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

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

บน​เครื่อง​ที่​วัด สี่​ภาษา​ได้ -04:00 แต่​ตัวเลข​สี่​นี้​นับ​ค่า​ของ Go ด้วย ถ้า​ตัด​แถว​ที่​เอกสาร​ไม่​รับปาก​ออก สิ่ง​ที่​รับปาก​ได้​คือ Java, Node ที่ TZ=America/New_York และ Python ที่​ไม่​ตั้ง fold เลือก​ครั้ง​แรก ตาม​กฎ​ที่​เอกสาร​ของ​แต่ละ​ภาษา​เขียน​ไว้

ใน​ทรง​ที่ probe วัด Rust เป็น​ภาษา​เดียว​ที่​ไม่​เลือก​ให้ และ​คืน​ทั้ง​สอง​ค่า​ให้​ผู้​เรียก​เห็น

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

Java ตอบ​ได้​ว่า 01:30 ของ​วัน​นั้น​มี offset ที่​ใช้ได้​กี่​ตัว ถ้า​ถาม​มัน​อีก method หนึ่ง

ใน​บท​ที่ 1 คำถาม​เดียวกัน​นี้​ตอบ 0 สำหรับ​เวลา​ใน gap ลอง​ถาม​ซ้ำ​กับ​เวลา​ใน overlap

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

offset ที่​ใช้ได้​กับ 01:30 ของ​วัน​นั้น​มี 2 ตัว Javadoc ของ ZoneRules.getValidOffsets เขียน​ไว้​ว่า “Thus, for any given local date-time there can be zero, one or two valid offsets.”

ขนาด​ของ​รายการ​นี้​คือ​คำถาม​ที่ code รับ​เวลา​จาก​ผู้​ใช้​ควร​ถาม​ก่อน​สร้าง ZonedDateTime ทุก​ครั้ง แต่​ไม่มี​อะไร​บังคับ​ให้​ถาม ZonedDateTime.of ตอบ​ให้​ได้​โดย​ไม่​ต้อง​ถาม

ใน​ทรง​ที่ probe วัด สาม​ภาษา​ที่​เอกสาร​รับปาก​เลือก​ครั้ง​แรก​ให้​เงียบๆ และ​ลูกค้า​อาจ​หมาย​ถึง​ครั้ง​ที่​สอง

กลับ​ไป​ที่​ออ​เด​อร์ A-1042 สมมติ​ว่า​ลูกค้า​กรอก 01:30 หลัง​จาก​นาฬิกา​ที่​บ้าน​ถอย​กลับ​แล้ว ครั้ง​ที่​ลูกค้า​หมาย​ถึง​คือ 2026-11-01T01:30:00-05:00

ระบบ​ที่​เขียน​ด้วย Java, Node ที่ TZ=America/New_York หรือ Python ที่​ไม่​ตั้ง fold จะบัน​ทึก slot เป็น​ครั้ง​แรก คือ -04:00 รถ​ออก​ตาม​ครั้ง​แรก ซึ่ง​มา​ก่อน​ครั้ง​ที่​ลูกค้า​หมาย​ถึง​เท่า​ส่วน​ต่าง​ของ​สอง offset

ไม่มี​บรรทัด​ไหน​แดง ใบ​เสร็จ​แสดง 01:30 ตรง​กับ​ที่​ลูกค้า​กรอก​ทุก​ตัว​อักษร ความ​ผิด​อยู่​ที่ offset ซึ่ง​ลูกค้า​ไม่​เคย​เห็น​และ​ไม่​เคย​ถูก​ถาม

ต่าง​จาก gap ใน​บท​ที่ 1 ตรง​ที่​ครั้ง​นี้​ไม่มี​เวลา​ไหน​ถูก​เลื่อน ทุก​ภาษา​ที่​เลือก​ให้​คืน 01:30 ตาม​ที่​กรอก จึง​ไม่มี​อะไร​บน​หน้า​จอ​ให้​คน​ตรวจ​สังเกต​ได้​เลย

ราคา​ของ​การ​ถาม​ว่า​หมาย​ถึง​ครั้ง​ไหน

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

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

บท​นี้​ปิด​จุด​ที่​สี่ คือ​เวลา​ท้อง​ถิ่น​ที่​มี​สอง​ครั้ง และ​ใน​ทรง​ที่ probe วัด มี​ภาษา​เดียว​ที่​คืน​ทั้ง​สอง​ครั้ง​ให้​เห็น

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

บท​ที่ 1 วัด​กรณี​ที่​ไม่มี​จุด​เลย บท​นี้​วัด​กรณี​ที่​มี​สอง​จุด ใน​ทรง​ที่ probe วัด Java, Node ที่ TZ=America/New_York และ Python ที่​ไม่​ตั้ง fold เลือก​ครั้ง​แรก​ตาม​กฎ​ที่​เอกสาร​เขียน​ไว้ Go คืน​ค่าที่​เอกสาร​ไม่​รับปาก และ Rust เป็น​ภาษา​เดียว​ที่​คืน​ทั้ง​สอง​ค่า​ให้​ผู้​เรียก​เลือก

บท​ที่ 5 ข้าม​ไป​ที่​การ​บวก​เวลา​ข้าม​คืน​ที่​เปลี่ยน DST แล้ว​ถาม​ว่า​บวก​หนึ่ง​วัน​ตาม​ปฏิทิน​กับ​บวก​ระยะ​เวลา​เท่า​หนึ่ง​วัน​ให้​คำ​ตอบ​เดียวกัน​ไหม


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Go — package time, func Date (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ประโยค “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-03) — ที่มา​ของ​กฎ​ที่ ZonedDateTime.of ใช้ offset ตัว​แรก​ใน overlap และ​ของ withLaterOffsetAtOverlap()

  • Java SE 25 — ZoneRules (เข้า​ถึง 2026-10-03) — ที่มา​ของ getValidOffsets ที่​คืน​รายการ​ขนาด​ศูนย์ หนึ่ง หรือ​สอง

  • ECMAScript — UTC ( t ) (เข้า​ถึง 2026-10-03) — ที่มา​ของ​กฎ​ที่ Date หยิบ​ตัว​แรก​ของ​รายการ​เมื่อ​เวลา​ซ้ำ

  • Python — datetime.fold (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ความ​หมาย​ของ fold=0 กับ fold=1

  • PEP 495 — Local Time Disambiguation (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ค่า​ตั้ง​ต้น​ของ fold ใน constructor

  • chrono 0.4.45 — LocalResult (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ความ​หมาย​ของ LocalResult::Ambiguous และ​ลำดับ​ของ​สอง​ค่า​ใน​นั้น

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

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

ข้อ 1 / 3

ส่ง `2026-11-01 01:30` ที่ `America/New_York` เข้า `New_York.from_local_datetime` ของ `chrono-tz` ได้อะไร