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

หนึ่ง​วัน​ไม่ใช่ 24 ชั่วโมง

ลูกค้า​ขอ​งอ​อเด​อร์ A-1042 อยู่​ใน New York และ​ได้ slot ส่ง​ของ​เป็น 2026-03-07 12:00 ตาม​นาฬิกา​ของ​บ้าน​ตัว​เอง ซึ่ง​อยู่​ใน​โซน America/New_York

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

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

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

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

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

“บวก​หนึ่ง​วัน” กับ “บวก 24 ชั่วโมง” เป็น​สอง​คำ​สั่ง​ที่​ต่าง​กัน และ​คืน​ที่ DST เริ่ม​คือ​คืน​ที่​ความ​ต่าง​นั้น​โผล่​ออก​มา

slot เดิม​คือ 2026-03-07T12:00:00-05:00 ใน บท​ที่ 1 เรา​เห็น​แล้ว​ว่า​คืน​ถัด​มา​เป็น​คืน​ที่ DST ของ America/New_York เริ่ม และ offset เปลี่ยน​จาก -05:00 เป็น -04:00

การ​บวก​แบบ​แรก​คือ calendar arithmeticcalendar arithmeticการ​บวก​ลบ​ตาม​ปฏิทิน เช่น​บวก​หนึ่ง​วัน​แล้ว​ได้​เวลา​เดิม​บน​หน้า​ปัด​ของ​วัน​ถัด​ไป · ถ้า​ข้าม​คืน​ที่​เปลี่ยน DST ระยะ​ที่​ผ่าน​ไป​จริง​จะ​ไม่ใช่ 24 ชั่วโมง (การ​บวก​ลบ​ตาม​ปฏิทิน) มัน​เดิน​บน​หน้า​ปัด คือ​เลื่อน​วัน​ที่​ไป​หนึ่ง​วัน​แล้ว​คง​ชั่วโมง​กับ​นาที​ไว้​ตาม​เดิม จาก​นั้น​ค่อย​ถาม​โซน​ว่า​วัน​ใหม่​ใช้ offset อะไร ลูกค้า​ที่​บอก​ว่า “พรุ่ง​นี้ เวลา​เดิม” หมาย​ถึง​การ​บวก​แบบ​นี้

การ​บวก​แบบ​ที่​สอง​คือ duration arithmeticduration arithmeticการ​บวก​ลบ​ระยะ​ที่​ผ่าน​ไป​จริง​บน​เส้น​เวลา เช่น​บวก 24 ชั่วโมง · ถ้า​ข้าม​คืน​ที่​เปลี่ยน DST เวลา​บน​หน้า​ปัด​ที่​ได้​จะ​ไม่​ตรง​กับ​การ​บวก​หนึ่ง​วัน และ​ไม่ใช่​ทุก​ภาษา​ที่​ทำ​แบบ​นี้​เมื่อ​คุณ​สั่ง​บวก​เป็น​ชั่วโมง (การ​บวก​ระยะ​ที่​ผ่าน​ไป​จริง) มัน​เดิน​บน​เส้น​เวลา คือ​เลื่อน instant ไป​ตาม​ระยะ​ที่​สั่ง แล้ว​ค่อย​แปลง​กลับ​มา​เป็น​เวลา​บน​หน้า​ปัด งาน​ที่​ต้องการ​ระยะ​จริง เช่น​กำหนด​ว่า​รถ​ต้อง​ออก​จาก​คลัง​ภายใน​ระยะ​หนึ่ง ต้องการ​การ​บวก​แบบ​นี้

flowchart LR
  A["slot เดิม · 2026-03-07 12:00 ที่ America/New_York · offset -05:00"] --> B{"บวกแบบไหน"}
  B -->|"หนึ่งวันตามปฏิทิน"| C["เดินบนหน้าปัด · ได้ 12:00-04:00"]
  B -->|"24 ชั่วโมงบนเส้นเวลา"| D["เดินบนเส้นเวลา · ได้ 13:00-04:00"]
  B -->|"timedelta ของ Python ไม่ว่าจะสั่งเป็นวันหรือชั่วโมง"| E["เดินบนหน้าปัด · ได้ 12:00-04:00"]
  E --> F["ระยะที่ผ่านไปจริง 23:00:00"]

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

Java Go Rust และ Node ที่ TZ=America/New_York มี API คนละ​ตัว​สำหรับ​สอง​คำถาม​นี้ และ​ได้​สอง​คำ​ตอบ​ที่​ต่าง​กัน

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

scripts/time-semantics/runners/Shapes.java (เฉพาะ​ทรง C)
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime s = ZonedDateTime.of(2026, 3, 7, 12, 0, 0, 0, ny);
s.plusDays(1).toString(); // 2026-03-08T12:00-04:00[America/New_York]
s.plus(Duration.ofHours(24)).toString(); // 2026-03-08T13:00-04:00[America/New_York]

Javadoc ของ ZonedDateTime.plus(long, TemporalUnit) แบ่ง​หน่วย​ไว้​สอง​พวก “Date units operate on the local time-line.” และ “Time units operate on the instant time-line.” วัน​เป็น​หน่วย​ของ​วัน​ที่ ส่วน​ชั่วโมง​เป็น​หน่วย​ของ​เวลา

plus(Duration.ofHours(24)) ส่ง​งาน​ต่อ​ให้ Duration และ Javadoc ของ plus(TemporalAmount) เขียน​ว่า​ตัว amount “typically calls back to plus(long, TemporalUnit)”

Javadoc ของ plusHours เขียน​ผล​ของ​การ​แบ่ง​นั้น​ไว้​ตรงๆ ว่า “thus adding one day is not the same as adding 24 hours.” ค่าที่ probe วัด​ได้​ตรง​กับ​ประโยค​นั้น plusDays(1) ได้ 12:00 และ plus(Duration.ofHours(24)) ได้ 13:00 ทั้ง​คู่​อยู่​ที่ offset -04:00

scripts/time-semantics/runners/go/main.go (เฉพาะ​ทรง C)
ny, err := time.LoadLocation("America/New_York")
// ...
s := time.Date(2026, 3, 7, 12, 0, 0, 0, ny)
s.AddDate(0, 0, 1).Format(time.RFC3339) // 2026-03-08T12:00:00-04:00
s.Add(24 * time.Hour).Format(time.RFC3339) // 2026-03-08T13:00:00-04:00

เอกสาร​ของ AddDate เขียน​ว่า “calendrical periods like days don’t have fixed durations” และ “AddDate uses the Location of the Time value to determine these durations.” ส่วน Add บวก time.Duration ซึ่ง​เป็น​ระยะ​คงที่ จึง​เดิน​บน​เส้น​เวลา

ค่า​ของ Go ใน​บท​นี้​ไม่​อยู่​ใน​ช่อง unguaranteed

time.Date ใน​บท​ที่ 1 คืน​ค่าที่​เอกสาร​ไม่​รับปาก เพราะ 02:30 อยู่​ใน gap ส่วน 12:00 ของ​ทั้ง​สอง​วัน​ใน​บท​นี้ ไม่​อยู่​ใน gap และ​ไม่​อยู่​ใน​ช่วง​ที่​มี​สอง​ครั้ง ค่า​ของ AddDate กับ Add ข้าง​บน​จึง​เป็น​ข้อ​อ้าง​ที่ probe ผูก​ไว้ ไม่ใช่​ค่า​ของ​เครื่อง​เดียว

แต่​เอกสาร​ของ AddDate เขียน​ไว้​ด้วย​ว่า “AddDate normalizes its result in the same way that Date does” ถ้า​การ​บวก​วัน​ไป​ตกใน gap ก็​จะ​กลับ​ไป​เจอ​คำถาม​เดียว​กับ​บท​ที่ 1 ซึ่ง probe ไม่​ได้​วัด​ใน​ทรง​นี้

scripts/time-semantics/runners/rs/src/main.rs (เฉพาะ​ทรง C)
use chrono::{Days, Duration, TimeZone};
use chrono_tz::America::New_York;
let s = New_York.with_ymd_and_hms(2026, 3, 7, 12, 0, 0).unwrap();
s.checked_add_days(Days::new(1)).unwrap().to_rfc3339(); // 2026-03-08T12:00:00-04:00
(s + Duration::hours(24)).to_rfc3339(); // 2026-03-08T13:00:00-04:00

Duration ใน​บรรทัด​นี้​เป็น​ของ chrono ไม่ใช่​ของ std ชื่อ​ชนิด​สอง​ตัว​บอก​ความ​ต่าง​ไว้​เอง Days คือ​จำนวน​วัน​บน​ปฏิทิน ส่วน Duration คือ​ระยะ​บน​เส้น​เวลา

เอกสาร​ของ checked_add_days เขียน​ว่า​มัน “Add a duration in Days to the date part of the DateTime.” และ​คืน None ถ้า “The local time at the resulting date does not exist or is ambiguous” runner ของ probe เรียก unwrap() ตรง​นั้น เพราะ 12:00 ของ​วัน​ใหม่​มี​อยู่​จริง​และ​มี​ครั้ง​เดียว

scripts/time-semantics/runners/shapes.mjs (เฉพาะ​ทรง C)
// เดือนของ Date เริ่มที่ 0 · เลข 2 คือเดือนมีนาคม
const s = new Date(2026, 2, 7, 12, 0);
const d = new Date(s);
d.setDate(d.getDate() + 1);
const h = new Date(s.getTime() + 864e5);

Date ไม่มี API สำหรับ​สอง​คำถาม​นี้​ตรงๆ runner จึง​ใช้​สอง​ท่า​ที่ code จริง​มัก​เขียน​กัน setDate เปลี่ยน​วัน​ที่​ใน​โซน​ของ process ส่วน 864e5 คือ​จำนวน​มิลลิ​วินาที​ของ 24 ชั่วโมง ที่​บวก​เข้าไป​บน​ค่าที่ getTime() คืน

สเปก ECMAScript หัวข้อ Date.prototype.setDate เขียน​ขั้น​ตอน​ไว้​ว่า “Set tv to LocalTime(tv).” ก่อน​เปลี่ยน​วัน แล้ว​จึง​แปลง​กลับ​ด้วย UTC(newDate) การ​บวก​วัน​ของ Date จึง​เดิน​บน​หน้า​ปัด​ของ​โซน​ของ process

TZการ​เรียกtoString()toISOString()
America/New_YorksetDate(getDate() + 1)Sun Mar 08 2026 12:00:00 GMT-0400 (Eastern Daylight Time)2026-03-08T16:00:00.000Z
America/New_YorkgetTime() + 864e5Sun Mar 08 2026 13:00:00 GMT-0400 (Eastern Daylight Time)2026-03-08T17:00:00.000Z

สอง​แถว​นี้​ได้ instant คนละ​ค่า แถว​แรก​ตรง​กับ​การ​บวก​หนึ่ง​วัน​ของ Java Go และ Rust ทั้ง wall-clock time และ offset ส่วน​แถว​ที่​สอง​ตรง​กับ​การ​บวก 24 ชั่วโมง​ของ​สาม​ภาษา​นั้น

เครื่อง​ที่​ตั้ง TZ=Asia/Bangkok มอง​ไม่​เห็น​ความ​ต่าง​นี้

probe รัน file เดียวกัน​ซ้ำ​ที่ TZ=Asia/Bangkok และ​ได้​ค่า​นี้​ทั้ง​สอง​แถว

  • toString() ได้ Sun Mar 08 2026 12:00:00 GMT+0700 (Indochina Time)
  • toISOString() ได้ 2026-03-08T05:00:00.000Z

สอง​การ​บวก​ให้ instant เดียวกัน เพราะ Date สร้าง 12:00 ของ Bangkok ไม่ใช่​ของ New York และ Bangkok ไม่มี DST ใน​คืน​นั้น code บรรทัด​เดียวกัน​จึง​แสดง​ความ​ต่าง​ของ​บท​นี้​บน​เครื่อง​หนึ่ง และ​ไม่​แสดง​บน​อีก​เครื่อง​หนึ่ง

ใน​ทรง​ที่ probe วัด การ​บวก timedelta เข้า​กับ aware datetime ของ Python เดิน​บน​หน้า​ปัด ต่อ​ให้​สั่ง​เป็น​ชั่วโมง

ค่าที่​มี tzinfo ติด​มา​เรียก​ว่า aware datetimeaware datetimedatetime ที่​มี tzinfo ติด​มา จึง​แปลง​เป็น instant ได้ · aware ไม่​ได้​แปล​ว่า​มีชื่อ​โซน​เสมอ เพราะ tzinfo อาจ​เป็น​แค่ offset คงที่​ที่​ไม่รู้​เรื่อง DST เลย (datetime ที่​แปลง​เป็น instant ได้) slot เดิม​ของ​ลูกค้า​เป็น​ค่า​แบบ​นี้ เพราะ​มัน​มี ZoneInfo("America/New_York") ติด​มา

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง C)
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
ny = ZoneInfo("America/New_York")
s = datetime(2026, 3, 7, 12, 0, tzinfo=ny)
(s + timedelta(days=1)).isoformat() # 2026-03-08T12:00:00-04:00
(s + timedelta(hours=24)).isoformat() # 2026-03-08T12:00:00-04:00

สอง​บรรทัด​ได้​สตริง​เดียวกัน​ทุก​ตัว​อักษร และ​สตริง​นั้น​ตรง​กับ wall-clock time และ offset ที่​อีก​สี่​ภาษา​ได้​จาก​การ​บวกหนึ่ง​วัน ผู้​ที่​สั่ง timedelta(hours=24) เพราะ​ต้องการ​ระยะ​จริง​จึง​ได้การ​บวก​ตาม​ปฏิทิน​โดย​ไม่มี​อะไร​เตือน

เหตุผล​มี​สอง​ชั้น ชั้น​แรก​อยู่​ที่ timedelta เอง เอกสาร​ของ​มัน​เขียน​ว่า “Only days, seconds and microseconds are stored internally.” และ “An hour is converted to 3600 seconds.” แล้ว​ค่า​ทั้งหมด “are then normalized so that the representation is unique” ชั่วโมง​ที่​ส่ง​เข้าไป​จึง​ไม่​ได้​ถูก​เก็บ​เป็น​ชั่วโมง

ชั้น​ที่​สอง​อยู่​ที่​การ​บวก เอกสาร​ของ datetime เขียน​ว่า​ผล​ของ datetime + timedelta มี tzinfo เดิม และ “Note that no time zone adjustments are done even if the input is an aware object.” Python จึง​เลื่อน​ค่า​บน​หน้า​ปัด แล้ว​ให้​โซน​เลือก offset ของ​วัน​ใหม่

probe วัด​ระยะ​ของ​การ​บวก 24 ชั่วโมง​ใน Python ด้วย​การ​แปลง​ทั้ง​สอง​ค่า​ไป UTC ก่อน​ลบ และ​ได้ 23:00:00

scripts/time-semantics/runners/shapes.py (เฉพาะ​ทรง C)
str((s + timedelta(hours=24)).astimezone(timezone.utc) - s.astimezone(timezone.utc)) # 23:00:00

runner แปลง​ไป UTC ก่อน​ลบ เพราะ​เอกสาร​เขียน​กฎ​ของ​การ​ลบ​สอง​ค่าที่​มี tzinfo เดียวกัน​ไว้​ว่า “the tzinfo attributes are ignored” และ “No time zone adjustments are done in this case.” ถ้า​ลบ​ตรงๆ ผล​จะ​เป็น​ระยะ​บน​หน้า​ปัด ไม่ใช่​ระยะ​บน​เส้น​เวลา

ค่าที่​ได้​คือ 23:00:00 ผู้​เรียก​สั่ง​บวก 24 ชั่วโมง แต่​ระยะ​ที่​ผ่าน​ไป​จริง​ตาม​ที่ API คืน​สั้น​กว่า​นั้น เพราะ​คืน​นั้น​นาฬิกา​ของ New York กระโดด​ข้าม​ช่วง​หนึ่ง​ไป และ​การ​บวก​บน​หน้า​ปัด​ไม่​ได้​นับ​ช่วง​นั้น

รวม​ค่า​ของ​สอง​การ​บวก​จาก​ห้า​ภาษา พร้อม​รุ่น​ของ tzdata ที่​แต่ละ​ภาษา​อ่าน​จริง

ภาษาบวก​หนึ่ง​วัน​ตาม​ปฏิทินบวก 24 ชั่วโมงtzdata ที่​ใช้
Java openjdk 25.0.4.1plusDays(1) → 2026-03-08T12:00-04:00[America/New_York]plus(Duration.ofHours(24)) → 2026-03-08T13:00-04:00[America/New_York]2026b · tzdb.dat ของ JDK
Go go1.27.0AddDate(0, 0, 1) → 2026-03-08T12:00:00-04:00Add(24 * time.Hour) → 2026-03-08T13:00:00-04:002026c · /usr/share/zoneinfo ของ​ระบบ
Node v24.19.0 ที่ TZ=America/New_YorksetDate(getDate() + 1) → 2026-03-08T16:00:00.000ZgetTime() + 864e5 → 2026-03-08T17:00:00.000Z2026b · ICU ที่​ฝัง​ใน node
Rust chrono-tz 0.10.4checked_add_days(Days::new(1)) → 2026-03-08T12:00:00-04:00+ Duration::hours(24) → 2026-03-08T13:00:00-04:002025b · ฝัง​ใน chrono-tz
Python 3.14.6+ timedelta(days=1) → 2026-03-08T12:00:00-04:00+ timedelta(hours=24) → 2026-03-08T12:00:00-04:002026c · /usr/share/zoneinfo ของ​ระบบ

ทุก​แถว​วัด​บน darwin/arm64 ใน​ทรง​ที่ probe วัด สี่​แถว​แรก​ให้​สอง​การ​บวก​เป็น​คนละ​จุด​บน​เส้น​เวลา และ​แถว​ของ Python เป็น​แถว​เดียว​ที่​สอง​ช่อง​เท่า​กัน

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

ภาษา​ที่​แยก​สอง​คำถาม​ผิด​ทาง​หนึ่ง และ Python ผิด​อีก​ทาง​หนึ่ง ทั้ง​สอง​ทาง​ไม่มี​บรรทัด​ไหน​แดง

กลับ​ไป​ที่​ออ​เด​อร์ A-1042 ถ้า​คน​เขียน​ระบบ​เลื่อน slot ด้วย​การ​บวก 24 ชั่วโมง​ใน Java Go Rust หรือ Node ที่ TZ=America/New_York slot ใหม่​จะ​เป็น 13:00 ตาม​นาฬิกา​ของ​ลูกค้า ลูกค้า​ขอ 12:00 และ​รถ​จะ​มา​ถึง​ช้า​กว่า​ที่​ลูกค้า​รอ

ถ้า​คน​เขียน​รู้​เรื่อง​นี้​แล้ว​เปลี่ยน​ไป​บวก​หนึ่ง​วัน slot ใหม่​จะ​เป็น 12:00-04:00 ตรง​กับ​ที่​ลูกค้า​ขอ แต่​ทีม​เดียวกัน​อาจ​มี​อีก​งาน​หนึ่ง​ที่​ต้องการ​ระยะ​จริง และ​งาน​นั้น​ต้อง​ใช้ API อีก​ตัว

ทีม​ที่​เขียน​ด้วย Python เจอ​ปัญหา​กลับ​ด้าน การ​เลื่อน slot ได้ 12:00-04:00 ถูก​ต้อง ไม่​ว่า​จะ​สั่ง​เป็น​วัน​หรือ​ชั่วโมง แต่​งาน​ที่​สั่ง timedelta(hours=24) เพื่อ​นับ​ระยะ​จริง​จะ​ได้​ระยะ 23:00:00 แทน

ราคา​ของ​การ​เลือก​คำถาม​ให้​ถูก

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

ราคา​คือ​ทุก​จุด​ที่​บวก​ลบ​เวลา​ต้อง​ตอบ​คำถาม​นี้​เอง ใน Java Go และ Rust ชื่อ API แยก​สอง​คำถาม​ไว้​ให้ ส่วน​ใน Python คำ​ว่า hours=24 ที่​บวก​เข้า​กับ aware datetime บอก​สิ่ง​ที่ API ไม่​ได้​ทำ และ​ไม่มี​ใคร​เตือน​จนกว่า​จะ​มี​คืน​ที่ offset เปลี่ยน

บท​นี้​ปิด​จุด​ที่​ห้า การ​บวก​เวลา​ค่า​เดียวกัน​ด้วย​สอง​ความ​หมาย​ข้าม​คืน​ที่ offset เปลี่ยน

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

จุด​ที่​ห้า​คือ​จุด​ที่​บท​นี้​เพิ่ง​วัด slot เดิม​เป็น​จุด​เดียว​บน​เส้น​เวลา แต่​คำ​ว่า “พรุ่ง​นี้” พา​ไป​ได้​สอง​จุด ใน​ทรง​ที่ probe วัด สี่​ภาษา​แยก​สอง​จุด​นั้น​ด้วย API คนละ​ตัว (Node ที่ TZ=America/New_York) และ Python พา​ไป​จุด​เดียว​ทั้ง​สอง​คำ​สั่ง

บท​ที่ 6 ถาม​คำถาม​ที่​ต่อ​จาก​นี้ ถ้า slot ใหม่​ต้อง​เดิน​ทาง​ผ่าน JSON ไป​ที่​ระบบ​อื่น ชื่อ​โซน​ที่​ทำให้​การ​บวก​หนึ่ง​วัน​ได้ offset ที่​ถูก​ต้อง​จะ​ไป​ถึง​ปลาย​ทาง​หรือ​เปล่า


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้
  • Python — datetime objects (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ประโยค “Note that no time zone adjustments are done even if the input is an aware object.” และ​ของ​กฎ​การ​ลบ​สอง​ค่าที่​มี tzinfo เดียวกัน

  • Python — timedelta objects (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ข้อ​ที่ timedelta เก็บ​แค่ days, seconds และ microseconds และ​แปลง​ชั่วโมง​เป็น​วินาที

  • Java SE 25 — ZonedDateTime (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ประโยค “Date units operate on the local time-line.” กับ “Time units operate on the instant time-line.” ของ plus(long, TemporalUnit) และ​ประโยค “adding one day is not the same as adding 24 hours.” ของ plusHours

  • Go — package time, func (Time) AddDate (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ข้อ​ที่ AddDate ใช้ Location ของ​ค่า​เวลา​กำหนด​ความ​ยาว​ของ​วัน และ normalize ผล​แบบ​เดียว​กับ Date

  • ECMAScript — Date.prototype.setDate (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ขั้น​ตอน​ที่ setDate แปลง​ค่า​เป็น​เวลา​ท้อง​ถิ่น​ก่อน​เปลี่ยน​วัน

  • chrono 0.4.45 — DateTime::checked_add_days (เข้า​ถึง 2026-10-03) — ที่มา​ของ​ข้อ​ที่ checked_add_days บวก​ที่​ส่วน​วัน​ที่ และ​คืน None เมื่อ​เวลา​ท้อง​ถิ่น​ของ​วัน​ใหม่​ไม่มี​อยู่​จริง​หรือ​มี​สอง​ครั้ง

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

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

ข้อ 1 / 3

`s = datetime(2026, 3, 7, 12, 0, tzinfo=ny)` โดย `ny` คือ `ZoneInfo` ของ `America/New_York` แล้วสั่ง `(s + timedelta(hours=24)).isoformat()` ได้อะไร