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

offset ไม่ใช่​โซน

ออ​เด​อร์ A-1042 มี slot ส่ง​ของ​ที่​คลัง​สินค้า​ใน New York ได้​รับ​มา​เป็น​สตริง "2026-03-07T12:00-05:00" สตริง​นี้​มี​วัน​ที่ มี​เวลา และ​มี offset ครบ ดู​เหมือน​ไม่มี​อะไร​ขาด

แล้ว​ลูกค้า​โทร​มา​ขอ​เลื่อน slot ไป​วัน​ถัด​ไป เวลา​เดิม คลัง​จึง parse สตริง​นั้น บวก​หนึ่ง​วัน แล้ว​ส่ง​ให้​ระบบ​จัด​รถ ไม่มี​บรรทัด​ไหน​แดง และ​ไม่มี exception

คำถาม​ของ​บท​นี้​คือ “เวลา​เดิม” ของ​วัน​ถัด​ไป​คือ​เวลา​ไหน เมื่อ​สิ่ง​ที่​อยู่​ใน​มือ​มี​แค่ offset คำ​ตอบ​ขึ้น​กับ​ว่า​คุณ​บวก​บน offset หรือ​บวก​บน​โซน และ​ใน​ทรง​ที่ probe วัด สอง​ทาง​นี้​ได้​คนละ​จุด​บน​เส้น​เวลา

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

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

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

-05:00 บอก​ว่า​จุด​นี้​ห่าง​จาก UTC เท่าไร แต่​ไม่​ได้​บอก​ว่า​วัน​ถัด​ไป​ที่​เดียวกัน​จะ​ห่าง​เท่าไร

-05:00 ท้าย​สตริง​คือ UTC offsetUTC offsetระยะ​ห่าง​จาก UTC ณ จุด​หนึ่ง​บน​เส้น​เวลา · offset ถูก​ต้อง​เฉพาะ​จุด​นั้น​จุด​เดียว มัน​ไม่รู้​ว่า​วัน​ถัด​ไป​โซน​เดียวกัน​จะ​ใช้ offset อะไร การ​บวก​เวลา​บน offset จึง​ไม่​เดิน​ตาม DST (ระยะ​ห่าง​จาก UTC ณ จุด​หนึ่ง​บน​เส้น​เวลา) มัน​ถูก​ต้อง​สำหรับ 2026-03-07 12:00 ที่ New York และ​ถูก​ต้อง​แค่​จุด​นั้น

สิ่ง​ที่​รู้​ว่า​วัน​ถัด​ไป​จะ​ใช้ offset อะไร​คือ time zonetime zoneชุด​กฎ​ที่​บอก​ว่า​แต่ละ​ช่วง​ของ​เส้น​เวลา​ใช้ offset อะไร ระบุ​ด้วย​ชื่อ​อย่าง America/New_York หรือ Asia/Bangkok · ต่าง​จาก offset ตรง​ที่​มัน​รู้​ว่า DST เริ่ม​และ​จบ​วัน​ไหน และ​รู้​ว่า​ใน​อดีต​โซน​นั้น​เคย​ใช้ offset อะไร (ชุด​กฎ​ที่​บอก​ว่า​แต่ละ​ช่วง​ของ​เส้น​เวลา​ใช้ offset อะไร) ระบุ​ด้วย​ชื่อ​อย่าง America/New_York ไม่​ได้​ระบุ​ด้วย​ตัวเลข

ชื่อ​โซน​กับ​กฎ​ของ​มัน​มา​จาก IANA tz databaseIANA tz databaseฐาน​ข้อมูล​กฎ​ของ​โซน​เวลา​ที่ IANA ดูแล และ​เป็น​ที่มา​ของ​ชื่อ​โซน​อย่าง Asia/Bangkok · แต่ละ​ภาษา​ได้​มัน​มา​คนละ​ทาง บาง​ภาษา​ฝัง​สำเนา​ไว้​ใน​ตัว บาง​ภาษา​อ่าน​จาก​ระบบ​ปฏิบัติ​การ (ฐาน​ข้อมูล​กฎ​ของ​โซน​เวลา​ที่ IANA ดูแล) หน้า​ของ IANA เขียน​ว่า​ฐาน​ข้อมูล​นี้ “is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules.”

Javadoc ของ ZoneId แยก ID ของ​โซน​ไว้​สอง​ชนิด:

  • “Fixed offsets - a fully resolved offset from UTC/Greenwich, that uses the same offset for all local date-times”
  • “Geographical regions - an area where a specific set of rules for finding the offset from UTC/Greenwich apply”

บท​ที่ 1 วัด​ไว้​แล้ว​ว่า​วัน​ที่ 2026-03-08 คือ​วัน​ที่ DST ของ America/New_York เริ่ม slot ใบ​นี้​อยู่​ก่อน​คืน​นั้น​หนึ่ง​วัน การ​บวก​หนึ่ง​วัน​จึง​ข้าม​คืน​ที่​โซน​เปลี่ยน offset พอดี

flowchart TB
  A["สตริงจากสาย · 2026-03-07T12:00-05:00"] --> B["parse ได้ offset -05:00 ติดมา"]
  B --> C["บวกหนึ่งวันบน offset"]
  B --> D["แปลงเป็นโซน America/New_York ก่อน แล้วบวกหนึ่งวัน"]
  C --> E["2026-03-08 12:00-05:00 · offset เดิม เพราะ offset ไม่มีกฎ"]
  D --> F["2026-03-08 12:00-04:00 · กฎของโซนรู้ว่า DST เริ่มคืนนั้น"]
  E --> G["คนละจุดบนเส้นเวลา"]
  F --> G

คำ​บรรยาย​ภาพ: สตริง​เดียวกัน​แยก​เป็น​สอง​กิ่ง​ตั้งแต่​ตอน​ที่​เลือก​ว่า​จะ​บวก​บน​อะไร กิ่ง​ซ้าย​ไม่มี​ข้อมูล​พอ​จะ​รู้​ว่า DST เริ่ม​คืน​นั้น ส่วน​กิ่ง​ขวา​ต้อง​ถาม​กฎ​ของ​โซน​ก่อน​จะ​ตอบ

สี่​ภาษา parse slot ใบ​เดียวกัน แล้ว​บวก​หนึ่ง​วัน​สอง​ทาง คือ​บน​ค่าที่ parse มา และ​บน​โซน​ที่​แปลง​ไป

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

runner ของ Go กับ Rust parse "2026-03-07T12:00:00-05:00" ซึ่ง​มี​ช่อง​วินาที ส่วน runner ของ​อีก​สาม​ภาษา parse "2026-03-07T12:00-05:00" snippet ข้าง​ล่าง​ใช้​สตริง​ตาม​ที่ runner ของ​แต่ละ​ภาษา​เขียน​ไว้

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง G)
ZoneId ny = ZoneId.of("America/New_York");
OffsetDateTime off = OffsetDateTime.parse("2026-03-07T12:00-05:00");
ZonedDateTime z = off.atZoneSameInstant(ny);
off.plusDays(1).toString(); // 2026-03-08T12:00-05:00
z.plusDays(1).toString(); // 2026-03-08T12:00-04:00[America/New_York]
off.plusDays(1).toInstant().equals(z.plusDays(1).toInstant()); // false

Javadoc ของ OffsetDateTime เขียน​ความ​ต่าง​นี้​ไว้​ใน​สอง​ประโยค​ติด​กัน: “OffsetDateTime adds to the instant the offset from UTC/Greenwich, which allows the local date-time to be obtained. ZonedDateTime adds full time-zone rules.”

OffsetDateTime จึง​บวก​หนึ่ง​วัน​แล้ว​คง -05:00 ไว้ ส่วน ZonedDateTime ถาม​กฎ​ของ America/New_York แล้ว​ได้ -04:00 และ equals ของ instant ทั้ง​สอง​ตอบ false

scripts/time-semantics/runners/go/main.go (เฉพาะ​ทรง G)
o, err := time.Parse(time.RFC3339, "2026-03-07T12:00:00-05:00")
// ...
z := o.In(ny)
o.Location().String() // ""
o.AddDate(0, 0, 1).Format(time.RFC3339) // 2026-03-08T12:00:00-05:00
z.AddDate(0, 0, 1).Format(time.RFC3339) // 2026-03-08T12:00:00-04:00
o.AddDate(0, 0, 1).Equal(z.AddDate(0, 0, 1)) // false

ชื่อ location ของ​ค่าที่ parse มา​เป็น​สตริง​ว่าง เอกสาร​ของ time.Parse อธิบาย​ที่มา​ไว้​ว่า “Otherwise it records the time as being in a fabricated location with time fixed at the given zone offset.”

คำ​ว่า “Otherwise” ใน​ประโยค​นั้น​มี​เงื่อนไข​นำ​หน้า เอกสาร​เขียน​ว่า “if the offset corresponds to a time zone used by the current location (Local), then Parse uses that location and zone in the returned time.”

ผล​ของ time.Parse จึง​ขึ้น​กับ Local ของ​เครื่อง​ที่​รัน​ด้วย probe วัด​ได้​สตริง​ว่าง​บน​เครื่อง​ที่​วัด และ​ไม่​ได้​วัด​เครื่อง​ที่ Local เป็น America/New_York บท​นี้​จึง​ไม่​บอก​ว่า​เครื่อง​แบบ​นั้น​จะ​ได้​ค่า​อะไร

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง G)
p = datetime.fromisoformat("2026-03-07T12:00-05:00")
z = p.astimezone(ny)
repr(p.tzinfo) # datetime.timezone(datetime.timedelta(days=-1, seconds=68400))
(p + timedelta(days=1)).isoformat() # 2026-03-08T12:00:00-05:00
(z + timedelta(days=1)).isoformat() # 2026-03-08T12:00:00-04:00
(p + timedelta(days=1)) == (z + timedelta(days=1)) # False

fromisoformat คืน tzinfo เป็น datetime.timezone ไม่ใช่ ZoneInfo

เอกสาร​ของ datetime เขียน​ข้อ​จำกัด​ของ timezone ไว้​ตรงๆ ว่า “Objects of this class cannot be used to represent time zone information in the locations where different offsets are used in different days of the year or where historical changes have been made to civil time.”

ny ใน​บรรทัด​ที่​สอง​คือ ZoneInfo("America/New_York") ตัว​เดียว​กับ​ที่​บท​ที่ 1 ใช้

scripts/time-semantics/runners/rs/src/main.rs (เฉพาะ​ทรง G)
let o = DateTime::parse_from_rfc3339("2026-03-07T12:00:00-05:00").unwrap();
let z = o.with_timezone(&New_York);
let a1 = o.checked_add_days(Days::new(1)).unwrap();
let b1 = z.checked_add_days(Days::new(1)).unwrap();
a1.to_rfc3339(); // 2026-03-08T12:00:00-05:00
b1.to_rfc3339(); // 2026-03-08T12:00:00-04:00
a1 == b1; // false

เอกสาร​ของ chrono เขียน​ชนิด​ที่​ได้​ไว้​ใน​ประโยค​แรก​ของ method ว่า “Parses an RFC 3339 date-and-time string into a DateTime<FixedOffset> value.” ชนิด​ของ o จึง​บอก​ตั้งแต่​ตอน​คอม​ไพล์​ว่า​มัน​มี​แค่ offset

ต่าง​จาก​บท​ที่ 1 ตรง​ที่ Rust ไม่​ได้​คืน enum ให้​เลือก เวลา 12:00 ของ​วัน​ที่ 2026-03-08 มี​อยู่​จริง​ใน​ทั้ง​สอง​กิ่ง คำถาม​ของ​บท​นี้​ไม่ใช่​ว่า​เวลา​มี​อยู่​ไหม แต่​คือ​บวก​บน​อะไร

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

ภาษาบวก​หนึ่ง​วัน​บน​ค่าที่ parse มาบวก​หนึ่ง​วัน​บน​โซน America/New_Yorkจุด​เดียวกัน​ไหมtzdata ที่​ใช้
Java openjdk 25.0.4.12026-03-08T12:00-05:002026-03-08T12:00-04:00[America/New_York]false2026b · tzdb.dat ของ JDK
Go go1.27.02026-03-08T12:00:00-05:002026-03-08T12:00:00-04:00false2026c · /usr/share/zoneinfo ของ​ระบบ
Python 3.14.62026-03-08T12:00:00-05:002026-03-08T12:00:00-04:00False2026c · /usr/share/zoneinfo ของ​ระบบ
Rust chrono-tz 0.10.42026-03-08T12:00:00-05:002026-03-08T12:00:00-04:00false2025b · ฝัง​ใน chrono-tz

ใน​ทรง​ที่ probe วัด สี่​ภาษา​ตอบ​ตรง​กัน​ทั้ง​สาม column บวก​บน offset ได้ -05:00 บวก​บน​โซน​ได้ -04:00 และ​สอง​ค่า​นี้​เป็น​คนละ​จุด​บน​เส้น​เวลา

column ที่​สอง​ใช้​แค่ offset ที่มา​กับ​สตริง ส่วน column ที่​สาม​ต้อง​ถาม​กฎ​ของ​โซน ซึ่ง​สี่​ภาษา​นี้​อ่าน​จาก tzdata สาม​รุ่น จาก​สาม​แหล่ง ตาราง​ที่​รวม​หลาย​ภาษา​จึง​ต้อง​พิมพ์​รุ่น​ของ​ทุก​ภาษา

ตาราง​นี้​ไม่มี​แถว​ของ Node เพราะ Node ไม่มี​ค่า​ให้​ใส่​ใน column ที่​สอง หัวข้อ​ถัด​ไป​บอก​เหตุผล

Date ใช้ -05:00 แค่​ตอน parse แล้ว​บวก​วัน​ใน​โซน​ของ process จึง​ได้​คำ​ตอบ​ตาม TZ ของ​เครื่อง

scripts/time-semantics/runners/shapes.mjs (เฉพาะ​ทรง G)
const p = new Date("2026-03-07T12:00-05:00");
const p1 = new Date(p);
p1.setDate(p1.getDate() + 1);
p.toISOString();
p1.toISOString();

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

TZp.toISOString()p1.toISOString() หลัง​บวก​หนึ่ง​วัน
America/New_York2026-03-07T17:00:00.000Z2026-03-08T16:00:00.000Z
Asia/Bangkok2026-03-07T17:00:00.000Z2026-03-08T17:00:00.000Z

column แรก​ตรง​กัน​ทั้ง​สอง TZ ที่ probe วัด แต่​หลัง​บรรทัด​แรก -05:00 ไม่​ได้​อยู่​ใน p อีก​แล้ว สเปก ECMAScript เขียน​ว่า “The DateValue internal slot is the time value represented by this Date.”

time value ใน​สเปก​เดียวกัน​คือ “an integral Number representing an instant in time to millisecond precision” Date จึง​เก็บ​แค่ instant ไม่มี​ช่อง​ให้ offset หรือ​ชื่อ​โซน

พอ​สั่ง setDate ขั้น​ตอน​ใน​สเปก​เขียน​ว่า “Set tv to LocalTime(tv).” ก่อน​จะ​บวก​วัน คือ​แปลง​เป็น​เวลา​ท้อง​ถิ่น ใน​โซน​ของ process แล้ว​ค่อย​บวก ใน probe โซน​นั้น​คือ TZ ที่ probe ตั้ง runner รายงาน resolvedTz ตรง​กับ TZ ทั้ง​สอง​ครั้ง โซน​นั้น​ไม่​ได้​มา​จาก​สตริง​ที่ parse มา

ผล​คือ code เดียวกัน อินพุต​เดียวกัน ได้​สอง instant ตาม​เครื่อง​ที่​รัน ทั้ง​สอง​แถว​บวก​วัน​ใน​โซน​ของ process ตาม​ขั้น​ตอน LocalTime ของ​สเปก ไม่ใช่​บน -05:00 เพราะ offset นั้น​หาย​ไป​ตั้งแต่​บรรทัด​แรก

ชื่อ​ชนิด​มี​คำ​ว่า Zoned แต่ ZonedDateTime.parse ของ​สตริง​ที่​มี​แค่ offset ได้ offset ไม่​ได้​โซน

ทาง​ลัด​ที่​ดู​ปลอดภัย​คือ parse สตริง​ด้วย ZonedDateTime ตรงๆ แทน OffsetDateTime เพราะ​ชื่อ​ชนิด​สัญญา​ว่า​มี​โซน

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง G)
ZonedDateTime pz = ZonedDateTime.parse("2026-03-07T12:00-05:00");
pz.getZone().toString(); // -05:00
String.valueOf(pz.getZone() instanceof ZoneOffset); // true
pz.plusDays(1).toString(); // 2026-03-08T12:00-05:00

getZone() คืน ZoneOffset ซึ่ง​เป็น ZoneId ชนิด fixed offset และ plusDays(1) ได้ 2026-03-08T12:00-05:00 ตรง​กับ OffsetDateTime ทุก​ตัว​อักษร ไม่ใช่ 12:00-04:00 ของ​โซน

Javadoc ของ ZonedDateTime.parse ยก​ตัวอย่าง​อินพุต​เป็น “2007-12-03T10:15:30+01:00[Europe/Paris]” ชื่อ​โซน​อยู่​ใน​วงเล็บ​เหลี่ยม​ท้าย​สตริง สตริง​จาก​สาย​ของ​บท​นี้​ไม่มี​วงเล็บ​นั้น ZonedDateTime จึง​ไม่มี​ชื่อ​โซน​ให้​ใช้

ZonedDateTime ไม่​ได้​มี​โซน​เสมอ มัน​ถือ ZoneId ซึ่ง​เป็น​ได้​ทั้ง​ชื่อ​โซน​และ offset คงที่ ถ้า​จะ​รู้​ว่า​ตัว​ที่​ถือ​อยู่​เป็น​แบบ​ไหน ต้อง​ถาม getZone() เหมือน​ที่ probe ถาม

คลัง​ที่​บวก​บน offset ส่ง​รถ​ไป​ถึง​ตอน​ที่​นาฬิกา​ของ​ลูกค้า​ไม่​ได้​แสดง​เวลา​ที่​ลูกค้า​ขอ

กลับ​ไป​ที่​ออ​เด​อร์ A-1042 คลัง​ที่​เขียน​ตาม​สัญชาตญาณ​จะ parse สตริง​จาก​สาย บวก​หนึ่ง​วัน แล้ว​ได้ 2026-03-08T12:00:00-05:00 ทุก​ภาษา​ใน​ตาราง​ข้าง​บน​เห็น​ตรง​กัน​ว่า​นี่​ไม่ใช่​จุด​เดียว​กับ 12:00 ของ​โซน

probe วัด​ท่อ​นี้​ซ้ำ​ใน​ท่อ​ของ​บท​ที่ 8 ซึ่ง​เขียน​ด้วย Python แล้ว​อ่าน​ค่าที่​ได้​ด้วย​ชื่อ​โซน​ของ​ลูกค้า นาฬิกา​ของ​ลูกค้า​ที่ instant นั้น​แสดง 2026-03-08T13:00:00-04:00

ลูกค้า​ขอ 12:00 ของ​วัน​ถัด​ไป และ​รถ​มา​ถึง​ตอน 13:00 ตาม​นาฬิกา​ของ​ลูกค้า​เอง ไม่มี​ระบบ​ไหน​ใน​ท่อ​แดง เพราะ​ทุก​ค่าที่​ผ่าน​มือ​เป็น​เวลา​ที่​ถูก​ต้อง​ตาม offset ที่​มัน​ถือ

ราคา​ของ​การ​ถือ​โซน​แทน offset

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

ราคา​มี​สอง​ข้อ:

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

บท​นี้​ปิด​ครึ่ง​แรก​ของ​จุด​ที่​สาม คือ offset ที่​ถูก​ใช้​คำนวณ​ต่อ​ราวกับ​เป็น​โซน

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

บท​นี้​เติม​อีก​ด้าน​หนึ่ง สตริง​ที่​มี offset ระบุ​จุด​เดียว​ได้​จริง แต่​จุด​นั้น​จุด​เดียว พอ​บวก​วัน​บน offset ใน​ทรง​ที่ probe วัด Java Go Python และ Rust ได้​คนละ​จุด​กับ​การ​บวก​บน​โซน และ​ไม่มี​ภาษา​ไหน​แดง

Node ไม่​ได้​เข้า​ตาราง​เดียว​กับ​อีก​สี่​ภาษา เพราะ​มัน​ไม่​เก็บ offset ไว้​เลย และ​ให้​สอง​คำ​ตอบ​ตาม TZ ของ​เครื่อง

บท​ที่ 4 กลับ​ไป​ที่ wall-clock time ค่า​เดียว คือ 01:30 ของ​คืน​ที่ DST จบ แล้ว​ถาม​ว่า​ภาษา​ไหน​บอก​คุณ​ว่า​มัน​มี​สอง​ครั้ง


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Java SE 25 — ZoneId (เข้า​ถึง 2026-10-03) — ที่มา​ของ​การ​แยก ID สอง​ชนิด คือ fixed offset กับ geographical region

  • Java SE 25 — OffsetDateTime (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ประโยค​ที่​ว่า OffsetDateTime เพิ่ม offset ส่วน ZonedDateTime เพิ่ม​กฎ​ของ​โซน​ทั้ง​ชุด

  • Java SE 25 — ZonedDateTime.parse (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ตัวอย่าง​อินพุต​ที่​มีชื่อ​โซน​ใน​วงเล็บ​เหลี่ยม

  • Go — package time, func Parse (เข้า​ถึง 2026-10-03) — ที่มา​ของ fabricated location และ​ของ​เงื่อนไข​ที่ Parse ใช้ Local เมื่อ offset ตรง​กับ​โซน​ของ​เครื่อง

  • Python — datetime, timezone objects (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ข้อ​จำกัด​ที่ timezone แทน​โซน​ที่​ใช้ offset ต่าง​กัน​ใน​แต่ละ​วัน​ของ​ปี​ไม่​ได้

  • chrono 0.4.45 — DateTime::parse_from_rfc3339 (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ชนิด DateTime<FixedOffset> ที่ parser คืนให้

  • ECMAScript — Properties of Date Instances, Time Values and Time Range และ Date.prototype.setDate (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ข้อ​ที่ Date เก็บ​แค่ time value และ​ของ​ขั้น​ตอน LocalTime ใน setDate

  • IANA — Time Zones (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ประโยค​ที่​ว่า tz database ถูก​ปรับ​ตาม​การ​เปลี่ยนแปลง​ของ UTC offset และ​กฎ DST

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

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

ข้อ 1 / 3

parse `"2026-03-07T12:00-05:00"` ด้วย `OffsetDateTime` แล้วสั่ง `plusDays(1)` ได้อะไร