คู่มือเชิงลึกเกี่ยวกับ JavaScript Temporal API ซึ่งเป็นโซลูชันสมัยใหม่สำหรับการจัดการวันและเวลาอย่างมีประสิทธิภาพในบริบทนานาชาติที่หลากหลาย
JavaScript Temporal API: การจัดการวันและเวลาสมัยใหม่สำหรับผู้ชมทั่วโลก
อ็อบเจกต์ `Date` ของ JavaScript เป็นสิ่งที่สร้างความหงุดหงิดให้กับนักพัฒนามาอย่างยาวนาน คุณสมบัติที่เปลี่ยนแปลงได้ (mutability) API ที่ไม่สอดคล้องกัน และการรองรับไทม์โซนที่ไม่ดี ทำให้เกิดไลบรารีจำนวนมาก เช่น Moment.js และ date-fns เพื่อมาเติมเต็มช่องว่างเหล่านี้ แต่ตอนนี้ ด้วย Temporal API ทำให้ JavaScript มีโซลูชันสมัยใหม่ในตัวสำหรับการจัดการวันและเวลาด้วยความชัดเจนและความแม่นยำที่ดียิ่งขึ้น บทความนี้จะให้ภาพรวมที่ครอบคลุมของ Temporal API โดยเน้นที่คุณสมบัติ ประโยชน์ และการใช้งานในบริบทนานาชาติที่หลากหลาย
Temporal API คืออะไร?
Temporal API คืออ็อบเจกต์ส่วนกลาง (global object) ใหม่ใน JavaScript ที่ออกแบบมาเพื่อแก้ไขข้อบกพร่องของอ็อบเจกต์ `Date` โดยมี API ที่สะอาดและไม่เปลี่ยนรูป (immutable) สำหรับการทำงานกับวัน เวลา ไทม์โซน และระบบปฏิทิน ที่สำคัญคือ มีเป้าหมายเพื่อแสดงแนวคิดเกี่ยวกับวันและเวลาในลักษณะที่สอดคล้องกับการใช้งานและความคาดหวังในโลกแห่งความเป็นจริงมากขึ้น ทำให้การทำให้เป็นสากล (internationalization) ตรงไปตรงมามากขึ้น
คุณสมบัติหลัก:
- ความไม่เปลี่ยนรูป (Immutability): อ็อบเจกต์ Temporal จะไม่สามารถเปลี่ยนแปลงได้ หมายความว่าการดำเนินการต่างๆ เช่น การบวกวันหรือเดือน จะส่งคืนอ็อบเจกต์ใหม่แทนที่จะแก้ไขอ็อบเจกต์เดิม ซึ่งจะช่วยขจัดแหล่งที่มาของข้อผิดพลาดที่พบบ่อยและทำให้โค้ดเข้าใจได้ง่ายขึ้น
- API ที่ชัดเจน: Temporal มี API ที่สอดคล้องและใช้งานง่ายสำหรับการดำเนินการเกี่ยวกับวันและเวลาโดยทั่วไป
- การรองรับไทม์โซน: Temporal มีการรองรับไทม์โซนที่แข็งแกร่ง ช่วยให้คุณทำงานกับวันและเวลาในสถานที่ต่างๆ ได้โดยไม่มีความซับซ้อนเหมือนอ็อบเจกต์ `Date` แบบเก่า โดยใช้ฐานข้อมูลไทม์โซนของ IANA ซึ่งรับประกันข้อมูลที่ถูกต้องและเป็นปัจจุบัน
- ระบบปฏิทิน: นอกเหนือจากปฏิทินเกรกอเรียนแล้ว Temporal ยังรองรับระบบปฏิทินทางเลือก เพื่อตอบสนองความต้องการของวัฒนธรรมและภูมิภาคที่หลากหลาย
- ความแม่นยำที่เพิ่มขึ้น: Temporal ให้ความแม่นยำระดับนาโนวินาที (nanosecond) ซึ่งช่วยแก้ข้อจำกัดของอ็อบเจกต์ `Date` ที่มีความแม่นยำเพียงระดับมิลลิวินาที (millisecond)
อ็อบเจกต์พื้นฐานของ Temporal
Temporal API แนะนำอ็อบเจกต์ประเภทใหม่ๆ หลายอย่าง นี่คือส่วนสำคัญบางส่วน:
- `Temporal.PlainDate`: แทนวันที่ (ปี, เดือน, วัน) โดยไม่มีไทม์โซน
- `Temporal.PlainTime`: แทนเวลา (ชั่วโมง, นาที, วินาที, มิลลิวินาที, ไมโครวินาที, นาโนวินาที) โดยไม่มีวันที่หรือไทม์โซน
- `Temporal.PlainDateTime`: แทนวันที่และเวลาโดยไม่มีไทม์โซน
- `Temporal.ZonedDateTime`: แทนวันที่และเวลาพร้อมไทม์โซนที่ระบุ
- `Temporal.Instant`: แทนช่วงเวลาหนึ่งที่แน่นอน วัดเป็นนาโนวินาทีนับจาก Unix epoch (1 มกราคม 1970 UTC)
- `Temporal.TimeZone`: แทนไทม์โซน
- `Temporal.Duration`: แทนช่วงของเวลา (เช่น 2 ชั่วโมง 30 นาที)
- `Temporal.YearMonth`: แทนปีและเดือน
- `Temporal.MonthDay`: แทนเดือนและวัน
การทำงานกับวันที่
การสร้าง `Temporal.PlainDate`
ในการสร้าง `Temporal.PlainDate` คุณสามารถใช้ constructor:
const plainDate = new Temporal.PlainDate(2024, 10, 27); // ปี, เดือน (1-12), วัน
console.log(plainDate.toString()); // ผลลัพธ์: 2024-10-27
คุณยังสามารถใช้เมธอด `from` ซึ่งรับสตริงในรูปแบบ ISO 8601:
const plainDateFromString = Temporal.PlainDate.from('2024-10-27');
console.log(plainDateFromString.toString()); // ผลลัพธ์: 2024-10-27
การเข้าถึงส่วนประกอบของวันที่
คุณสามารถเข้าถึงส่วนประกอบแต่ละส่วนของวันที่ได้โดยใช้คุณสมบัติ เช่น `year`, `month` และ `day`:
console.log(plainDate.year); // ผลลัพธ์: 2024
console.log(plainDate.month); // ผลลัพธ์: 10
console.log(plainDate.day); // ผลลัพธ์: 27
การคำนวณทางคณิตศาสตร์กับวันที่
หากต้องการบวกหรือลบวัน สัปดาห์ เดือน หรือปี ให้ใช้เมธอด `plus` และ `minus` เมธอดเหล่านี้จะส่งคืนอ็อบเจกต์ `Temporal.PlainDate` ใหม่:
const nextWeek = plainDate.plus({ days: 7 });
console.log(nextWeek.toString()); // ผลลัพธ์: 2024-11-03
const lastMonth = plainDate.minus({ months: 1 });
console.log(lastMonth.toString()); // ผลลัพธ์: 2024-09-27
การเปรียบเทียบวันที่
คุณสามารถเปรียบเทียบวันที่ได้โดยใช้เมธอด `compare`:
const date1 = new Temporal.PlainDate(2024, 10, 27);
const date2 = new Temporal.PlainDate(2024, 11, 15);
console.log(Temporal.PlainDate.compare(date1, date2)); // ผลลัพธ์: -1 (date1 มาก่อน date2)
การทำงานกับเวลา
การสร้าง `Temporal.PlainTime`
ในการสร้าง `Temporal.PlainTime` ให้ใช้ constructor:
const plainTime = new Temporal.PlainTime(10, 30, 0); // ชั่วโมง, นาที, วินาที
console.log(plainTime.toString()); // ผลลัพธ์: 10:30:00
หรือใช้เมธอด `from` กับสตริงเวลาในรูปแบบ ISO 8601:
const plainTimeFromString = Temporal.PlainTime.from('10:30:00');
console.log(plainTimeFromString.toString()); // ผลลัพธ์: 10:30:00
การเข้าถึงส่วนประกอบของเวลา
console.log(plainTime.hour); // ผลลัพธ์: 10
console.log(plainTime.minute); // ผลลัพธ์: 30
console.log(plainTime.second); // ผลลัพธ์: 0
การคำนวณทางคณิตศาสตร์กับเวลา
const later = plainTime.plus({ minutes: 15 });
console.log(later.toString()); // ผลลัพธ์: 10:45:00
การทำงานกับวันและเวลาร่วมกัน
การสร้าง `Temporal.PlainDateTime`
คุณสามารถสร้าง `Temporal.PlainDateTime` ได้โดยตรงหรือโดยการรวม `Temporal.PlainDate` และ `Temporal.PlainTime` เข้าด้วยกัน:
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
console.log(plainDateTime.toString()); // ผลลัพธ์: 2024-10-27T10:30:00
const date = new Temporal.PlainDate(2024, 10, 27);
const time = new Temporal.PlainTime(10, 30, 0);
const combinedDateTime = date.toPlainDateTime(time);
console.log(combinedDateTime.toString()); // ผลลัพธ์: 2024-10-27T10:30:00
ไทม์โซน (Time Zones)
การจัดการไทม์โซนอย่างถูกต้องเป็นสิ่งสำคัญสำหรับแอปพลิเคชันที่ต้องติดต่อกับผู้ใช้ในสถานที่ต่างๆ Temporal API รองรับไทม์โซนอย่างแข็งแกร่งผ่านอ็อบเจกต์ `Temporal.ZonedDateTime` และ `Temporal.TimeZone`
การสร้าง `Temporal.ZonedDateTime`
ในการสร้าง `Temporal.ZonedDateTime` คุณต้องมี `Temporal.PlainDateTime` และตัวระบุไทม์โซน ตัวระบุไทม์โซนจะอิงตามฐานข้อมูลไทม์โซนของ IANA (เช่น `America/Los_Angeles`, `Europe/London`, `Asia/Tokyo`)
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
console.log(zonedDateTime.toString()); // ผลลัพธ์: 2024-10-27T10:30:00-07:00[America/Los_Angeles] (ค่า offset จะขึ้นอยู่กับกฎ DST)
อีกทางเลือกหนึ่งคือการสร้าง `Temporal.ZonedDateTime` จาก `Instant`
const instant = Temporal.Instant.fromEpochSeconds(1666866600); // ตัวอย่าง timestamp
const zonedDateTimeFromInstant = instant.toZonedDateTimeISO(timeZone); // ไทม์โซนเช่น 'America/Los_Angeles'
console.log(zonedDateTimeFromInstant.toString());
การแปลงระหว่างไทม์โซน
คุณสามารถแปลง `Temporal.ZonedDateTime` เป็นไทม์โซนอื่นได้โดยใช้เมธอด `withTimeZone`:
const newTimeZone = 'Europe/London';
const zonedDateTimeInLondon = zonedDateTime.withTimeZone(newTimeZone);
console.log(zonedDateTimeInLondon.toString()); // ผลลัพธ์: 2024-10-27T18:30:00+01:00[Europe/London]
การทำงานกับค่า Offset ของไทม์โซน
เมธอด `getOffsetStringFor` ของอ็อบเจกต์ `Temporal.TimeZone` จะให้ค่าสตริง offset สำหรับ `Temporal.Instant` ที่กำหนด:
const timeZoneObject = new Temporal.TimeZone(timeZone);
const offsetString = timeZoneObject.getOffsetStringFor(zonedDateTime.toInstant());
console.log(offsetString); // ผลลัพธ์: -07:00 (ขึ้นอยู่กับกฎ DST)
สิ่งสำคัญคือต้องใช้ตัวระบุไทม์โซนของ IANA ที่ถูกต้องเพื่อการคำนวณที่แม่นยำ ตัวระบุเหล่านี้ได้รับการดูแลและอัปเดตเป็นประจำเพื่อสะท้อนการเปลี่ยนแปลงของเวลาออมแสง (daylight saving time) และขอบเขตของไทม์โซน
ช่วงเวลา (Durations)
อ็อบเจกต์ `Temporal.Duration` แทนช่วงของเวลา สามารถใช้เพื่อบวกหรือลบออกจากวันและเวลาได้
การสร้าง `Temporal.Duration`
คุณสามารถสร้าง `Temporal.Duration` โดยใช้ constructor โดยระบุปี เดือน วัน ชั่วโมง นาที วินาที มิลลิวินาที ไมโครวินาที และนาโนวินาที:
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9); // ปี, เดือน, วัน, ชั่วโมง, นาที, วินาที, มิลลิวินาที, ไมโครวินาที, นาโนวินาที
console.log(duration.toString()); // ผลลัพธ์: P1Y2M3DT4H5M6.007008009S
หรือโดยใช้สตริงช่วงเวลาในรูปแบบ ISO 8601:
const durationFromString = Temporal.Duration.from('P1Y2M3DT4H5M6S');
console.log(durationFromString.toString()); // ผลลัพธ์: P1Y2M3DT4H5M6S
การบวกช่วงเวลาเข้ากับวันและเวลา
const plainDate = new Temporal.PlainDate(2024, 10, 27);
const duration = new Temporal.Duration(0, 0, 7); // 7 วัน
const newDate = plainDate.plus(duration);
console.log(newDate.toString()); // ผลลัพธ์: 2024-11-03
โปรดทราบว่าการบวกช่วงเวลาที่เกี่ยวข้องกับเดือนหรือปีเข้ากับวันที่ต้องพิจารณาอย่างรอบคอบ เนื่องจากจำนวนวันในหนึ่งเดือนหรือหนึ่งปีอาจแตกต่างกันไป
ระบบปฏิทิน (Calendar Systems)
Temporal API รองรับระบบปฏิทินที่แตกต่างกันนอกเหนือจากปฏิทินเกรกอเรียน ซึ่งเป็นสิ่งสำคัญสำหรับแอปพลิเคชันที่ต้องการจัดการวันที่ในบริบททางวัฒนธรรมที่หลากหลาย แม้ว่าการรองรับยังคงมีการพัฒนาอยู่ แต่ก็เป็นพื้นฐานสำหรับการขยายในอนาคต
การใช้ปฏิทินทางเลือก
หากต้องการใช้ปฏิทินที่เฉพาะเจาะจง คุณสามารถระบุได้เมื่อสร้างอ็อบเจกต์ Temporal:
const hebrewDate = new Temporal.PlainDate(5785, 1, 1, { calendar: 'hebrew' });
console.log(hebrewDate.toString()); // ผลลัพธ์ที่เฉพาะเจาะจงอาจแตกต่างกันไปขึ้นอยู่กับการใช้งานและการจัดรูปแบบ ณ เวลาที่เขียนบทความนี้ต้องใช้ polyfill ในหลายสภาพแวดล้อม
สำคัญ: การรองรับปฏิทินที่ไม่ใช่เกรกอเรียนอาจต้องใช้ polyfills หรือการรองรับจากเบราว์เซอร์/สภาพแวดล้อมที่เฉพาะเจาะจง โปรดตรวจสอบเอกสาร Temporal API และตารางความเข้ากันได้ของเบราว์เซอร์สำหรับข้อมูลล่าสุด
การจัดรูปแบบวันและเวลา
ในขณะที่ Temporal API มุ่งเน้นไปที่การจัดการวันและเวลา การจัดรูปแบบมักจะถูกจัดการโดยอ็อบเจกต์ `Intl.DateTimeFormat` ซึ่งเป็นส่วนหนึ่งของ Internationalization API อ็อบเจกต์ Temporal ทำงานร่วมกับ `Intl.DateTimeFormat` ได้อย่างราบรื่น
การใช้ `Intl.DateTimeFormat`
นี่คือวิธีจัดรูปแบบ `Temporal.PlainDate` โดยใช้ `Intl.DateTimeFormat`:
const plainDate = new Temporal.PlainDate(2024, 10, 27);
const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatter.format(plainDate)); // ผลลัพธ์: October 27, 2024
const formatterGerman = new Intl.DateTimeFormat('de-DE', { year: 'numeric', month: 'long', day: 'numeric' });
console.log(formatterGerman.format(plainDate)); // ผลลัพธ์: 27. Oktober 2024
คุณสามารถปรับแต่งตัวเลือกรูปแบบให้เหมาะกับความต้องการของคุณได้ อาร์กิวเมนต์แรกของ `Intl.DateTimeFormat` คือ locale ซึ่งจะกำหนดภาษาและธรรมเนียมของภูมิภาคที่ใช้ในการจัดรูปแบบ การใช้ locales ที่แตกต่างกัน (เช่น 'en-US', 'de-DE', 'fr-FR', 'ja-JP') จะให้ผลลัพธ์ในรูปแบบที่แตกต่างกัน
การจัดรูปแบบ `Temporal.ZonedDateTime`
การจัดรูปแบบ `Temporal.ZonedDateTime` ก็คล้ายกัน แต่คุณยังสามารถรวมข้อมูลไทม์โซนไว้ในผลลัพธ์ได้ด้วย:
const plainDateTime = new Temporal.PlainDateTime(2024, 10, 27, 10, 30, 0);
const timeZone = 'America/Los_Angeles';
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
const formatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZoneName: 'short' });
console.log(formatter.format(zonedDateTime)); // ผลลัพธ์: October 27, 2024, 10:30 AM PDT (ตัวย่อไทม์โซนขึ้นอยู่กับกฎ DST)
แนวทางปฏิบัติที่ดีที่สุดสำหรับการทำให้เป็นสากล
เมื่อทำงานกับวันและเวลาในบริบทสากล ควรคำนึงถึงแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้:
- ใช้ตัวระบุไทม์โซนของ IANA: ใช้ตัวระบุไทม์โซนของ IANA เสมอ (เช่น `America/Los_Angeles`, `Europe/London`) เพื่อการจัดการไทม์โซนที่แม่นยำ
- ตระหนักถึงเวลาออมแสง (Daylight Saving Time): เวลาออมแสง (DST) อาจส่งผลต่อค่า offset ของไทม์โซน Temporal API จะจัดการการเปลี่ยนแปลง DST โดยอัตโนมัติ
- ใช้ `Intl.DateTimeFormat` สำหรับการจัดรูปแบบ: ใช้อ็อบเจกต์ `Intl.DateTimeFormat` เพื่อจัดรูปแบบวันและเวลาตาม locale ของผู้ใช้
- พิจารณาระบบปฏิทิน: หากแอปพลิเคชันของคุณต้องการรองรับผู้ใช้ในบริบททางวัฒนธรรมที่แตกต่างกัน ให้พิจารณาใช้ระบบปฏิทินทางเลือก
- จัดเก็บวันและเวลาในรูปแบบ UTC: เมื่อจัดเก็บวันและเวลาในฐานข้อมูล แนวทางปฏิบัติที่ดีที่สุดคือการจัดเก็บในรูปแบบ UTC (Coordinated Universal Time) เพื่อหลีกเลี่ยงปัญหาเกี่ยวกับไทม์โซน จากนั้นจึงแปลงเป็นเวลาท้องถิ่นเพื่อการแสดงผล Temporal มีเมธอดสำหรับแปลงไปและกลับจาก UTC
- ทดสอบอย่างละเอียด: ทดสอบแอปพลิเคชันของคุณด้วยไทม์โซน, locales และระบบปฏิทินที่แตกต่างกันเพื่อให้แน่ใจว่าทำงานได้อย่างถูกต้องสำหรับผู้ใช้ทุกคน
การเปรียบเทียบ Temporal API กับอ็อบเจกต์ Date แบบดั้งเดิม
นี่คือตารางที่เน้นความแตกต่างที่สำคัญและข้อดีของ Temporal API เมื่อเทียบกับอ็อบเจกต์ `Date` แบบดั้งเดิม:
คุณสมบัติ | อ็อบเจกต์ `Date` แบบดั้งเดิม | Temporal API |
---|---|---|
การเปลี่ยนแปลงค่า (Mutability) | เปลี่ยนแปลงได้ (แก้ไขอ็อบเจกต์เดิม) | ไม่เปลี่ยนรูป (ส่งคืนอ็อบเจกต์ใหม่) |
การรองรับไทม์โซน | จำกัดและมักมีปัญหา | แข็งแกร่งและแม่นยำ อิงตามฐานข้อมูลไทม์โซนของ IANA |
API | ไม่สอดคล้องและใช้งานยาก | ชัดเจน สอดคล้อง และใช้งานง่าย |
ความแม่นยำ | มิลลิวินาที (Millisecond) | นาโนวินาที (Nanosecond) |
ระบบปฏิทิน | จำกัดเฉพาะเกรกอเรียน | รองรับระบบปฏิทินทางเลือก (พร้อมการสนับสนุนที่กำลังพัฒนา) |
การทำให้เป็นสากล | ต้องใช้ไลบรารีภายนอกเพื่อการทำให้เป็นสากลที่แข็งแกร่ง | รองรับในตัวและทำงานร่วมกับ `Intl.DateTimeFormat` ได้อย่างราบรื่น |
การรองรับของเบราว์เซอร์และ Polyfills
เนื่องจากเป็น API ที่ค่อนข้างใหม่ การรองรับ Temporal API ของเบราว์เซอร์ยังคงมีการพัฒนาอยู่ โปรดตรวจสอบตารางความเข้ากันได้ของเบราว์เซอร์ล่าสุด (เช่น บน MDN Web Docs) เพื่อดูว่าเบราว์เซอร์และสภาพแวดล้อมใดที่รองรับโดยกำเนิด สำหรับเบราว์เซอร์รุ่นเก่าหรือสภาพแวดล้อมที่ไม่มีการรองรับโดยกำเนิด คุณสามารถใช้ polyfills เพื่อให้มีฟังก์ชันการทำงานของ Temporal API ได้ ค้นหา "Temporal API polyfill" บนเว็บเพื่อหาตัวเลือกที่เหมาะสม
บทสรุป
JavaScript Temporal API แสดงถึงก้าวสำคัญในการจัดการวันและเวลาใน JavaScript คุณสมบัติความไม่เปลี่ยนรูป, API ที่ชัดเจน, การรองรับไทม์โซนที่แข็งแกร่ง และความสามารถของระบบปฏิทิน ทำให้เป็นเครื่องมือที่ทรงพลังสำหรับนักพัฒนาที่สร้างแอปพลิเคชันที่ต้องการทำงานกับวันและเวลาอย่างถูกต้องและเชื่อถือได้ในบริบทนานาชาติที่หลากหลาย แม้ว่าการรองรับของเบราว์เซอร์ยังคงมีการพัฒนาอยู่ แต่ประโยชน์ของ Temporal API ทำให้คุ้มค่าแก่การเรียนรู้และนำไปใช้สำหรับโครงการใหม่ๆ การนำ Temporal API มาใช้และปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดในการทำให้เป็นสากล จะช่วยให้คุณสร้างแอปพลิเคชันที่มอบประสบการณ์ด้านวันและเวลาที่ราบรื่นและแม่นยำสำหรับผู้ใช้ทั่วโลกได้