בעבר, כדי לחשב את הזמן שחלף או שנותר בין אירועים (כמו תפוגה של תקופת ניסיון, ספירה לאחור של מינוי או חלונות SLA) ולהציג את פרקי הזמן האלה בפורמט מקומי וקריא לאנשים, היה צריך לבצע חישוב ידני או להשתמש בספריות חיצוניות. האובייקט Date הקודם עוקב רק אחרי אלפיות השנייה של התקופה, ולכן חישובים של חודשים קלנדריים, מעברים לשעון קיץ ואיזון יחידות עלולים לגרום לשגיאות.
API של Temporal משלב את Temporal.ZonedDateTime עם Temporal.Duration ו-Intl.DurationFormat כדי לספק הפרשי זמן בטוחים לשעון קיץ, איזון יחידות שניתן להגדרה, השוואות משך זמן בהתאם ללוח השנה ועיצוב מותאם לשוק המקומי.
איך להטמיע
- קבלת נקודות קצה (endpoints) של
Temporal.ZonedDateTime: משתמשים ב-Temporal.Now.zonedDateTimeISO()כדי לקבל את השעה הנוכחית באזור הזמן של המערכת, או מעבירים מחרוזת מפורשת של אזור זמן של IANA (Temporal.Now.zonedDateTimeISO(timeZone)) כשאזור הזמן של אירוע היעד שונה מברירת המחדל של המערכת של המשתמש. - מחשבים את ההפרשים באמצעות
.since()ו-.until():- אפשר להשתמש ב-
now.since(start, options)כדי לחשב את הזמן שחלף מאז אירוע בעבר. - אפשר להשתמש ב-
now.until(end, options)כדי לחשב את הזמן שנותר עד לאירוע עתידי. - מעבירים את
largestUnit(למשל,'year','month','day','hour') ואתsmallestUnit(למשל,'minute') כדי לאזן ולעגל את הערך שלTemporal.Durationשמתקבל.
- אפשר להשתמש ב-
- איזון או השוואה של אובייקטים עצמאיים של
Temporal.Duration:- אפשר ליצור משכי זמן נפרדים באמצעות
Temporal.Duration.from({ minutes: 90 })ולאזן מחדש יחידות באמצעותduration.round({ largestUnit: 'hour' })(צריך לספק אתrelativeToכשמאזנים בין חודשים קלנדריים, שנים או מעברים לשעון קיץ). - השוואה בין שני משכי זמן באמצעות
Temporal.Duration.compare(d1, d2, { relativeTo })– צריך להעבירPlainDateאוZonedDateTimeבתורrelativeToבכל פעם שאחד ממשכי הזמן מכיל יחידות קלנדריות (years,months,weeks) או חוצה את גבולות שעון הקיץ.
- אפשר ליצור משכי זמן נפרדים באמצעות
- פורמט לבני אדם לעומת מכונות:
- כדי להציג למשתמשים טקסט מקומי, צריך להשתמש ב-
duration.toLocaleString(locale, options)(שמפנה ל-Intl.DurationFormat). - צריך להשתמש ב-
duration.toString()רק למחרוזות של משך זמן בפורמט ISO 8601 שניתן לקריאה על ידי מכונה (כמוP1M2DT4H).
- כדי להציג למשתמשים טקסט מקומי, צריך להשתמש ב-
דוגמה: הפרש בין תאריכי תפוגה של תקופת ניסיון ועיצוב מקומי
// 1. Get the current time in an explicit target IANA time zone (or omit the
// argument to use the system's current time zone).
const tz = 'America/New_York';
const now = Temporal.Now.zonedDateTimeISO(tz);
// 2. Combine separate date and time strings directly.
// Do not build an ISO string with template literals; chain Temporal methods instead.
const startDateStr = '2025-01-01';
const startTimeStr = '12:00:00';
const endDateStr = '2025-01-31';
const endTimeStr = '12:00:00';
const start = Temporal.PlainDate.from(startDateStr).toPlainDateTime(startTimeStr).toZonedDateTime(tz);
const end = Temporal.PlainDate.from(endDateStr).toPlainDateTime(endTimeStr).toZonedDateTime(tz);
// 3. Calculate differences using .since() and .until()
// ZonedDateTime differences default to largestUnit: 'hour' (days are not a fixed
// length across DST, so Temporal won't balance into days unless asked). Pass
// largestUnit: 'day' or a calendar unit ('week', 'month', 'year') when you want a
// human-scale breakdown instead of e.g. PT720H. smallestUnit rounds away
// seconds and sub-second noise so the formatted string stays readable.
const timeActive = now.since(start, { largestUnit: 'year', smallestUnit: 'minute' });
const timeRemaining = now.until(end, { largestUnit: 'year', smallestUnit: 'minute' });
// 4. Format durations for display with .toLocaleString() (Intl.DurationFormat)
// instead of manually concatenating unit numbers and suffixes. 'en-US' is used
// here for deterministic output; pass undefined to use the user's locale.
console.log(`Active: ${timeActive.toLocaleString('en-US', { style: 'narrow' })}`);
console.log(`Remaining: ${timeRemaining.toLocaleString('en-US', { style: 'long' })}`);
// Use .toString() for ISO 8601 serialization (e.g., API payloads or <time datetime="...">)
console.log(`ISO 8601: ${timeRemaining.toString()}`);
// 5. Check status via .sign, and compare durations with Temporal.Duration.compare()
const isExpired = timeRemaining.sign < 0;
// Pass relativeTo when comparing durations that may include calendar months/years or DST shifts
const isPastHalfway = Temporal.Duration.compare(timeActive, timeRemaining, { relativeTo: now }) > 0;
דוגמה: איזון משכי הזמן של מודעות Standalone (round ו-Intl.DurationFormat)
// Convert a raw unit count (e.g., 90 minutes) into a balanced breakdown (1 hour, 30 minutes)
const rawDuration = Temporal.Duration.from({ minutes: 90 });
const balanced = rawDuration.round({ largestUnit: 'hour' });
// Format via duration.toLocaleString() or directly with Intl.DurationFormat
const formatter = new Intl.DurationFormat('en-US', { style: 'long' });
console.log(formatter.format(balanced)); // "1 hour, 30 minutes"
הטמעה אסטרטגית ושיטות מומלצות
- מומלץ להשתמש ב-
Temporal.ZonedDateTimeלחישובים שקשורים לאירועים בעולם האמיתי שמתרחשים באזורי זמן ספציפיים (כמו חידושי מינויים או ספירה לאחור של אירועים), ולהעבירtimeZoneמפורש אלTemporal.Now.zonedDateTimeISO(timeZone)כשמבצעים חישובים באזור זמן שונה מברירת המחדל של המערכת. - מומלץ לשלב מחרוזות נפרדות של תאריך ושעה עם שיטות Temporal, למשל
Temporal.PlainDate.from(dateStr).toPlainDateTime(timeStr).toZonedDateTime(tz)אוTemporal.PlainDate.from(dateStr).toZonedDateTime({ timeZone: tz, plainTime: timeStr }), במקום להוסיף מחרוזת ISO כמו`${dateStr}T${timeStr}`. - כן מעבירים את
largestUnit(למשל'day','month','year') ב-.since(),.until()או.round()כשרוצים לאזן משכי זמן מעבר לשעות. ברירת המחדל של שיטות ההפרש שלZonedDateTimeהיא'hour'. - מומלץ להשתמש ב-
Temporal.Duration.compare(d1, d2, { relativeTo })כשמשווים בין שני משכי זמן, ולספק עוגן שלrelativeToבכל פעם שאחד ממשכי הזמן כולל יחידות של לוח שנה (months,years) או ימים שמושפעים מאזור הזמן. - מומלץ לעצב משכי זמן לתצוגה למשתמשים באמצעות
duration.toLocaleString()אוnew Intl.DurationFormat(locale, { style })('long','short','narrow'או'digital') במקום לבצע אינטרפולציה ידנית של${duration.years}y ${duration.months}m.... - כן משתמשים ב-
duration.toString()למחרוזות של משך זמן בפורמט ISO 8601 שניתן לקריאה על ידי מכונה (למשל,PT1H30M), אף פעם לא כטקסט שגלוי למשתמשים. - מומלץ לעבד
PlainTimeבתורHH:MM(למשל, בשביל<input type="time">) באמצעות.toPlainTime().toString({ smallestUnit: 'minute' })במקום המחרוזת.slice(0, 5). - אל תשנו מופעים באופן ישיר. אובייקטים מסוג
Temporalהם לא ניתנים לשינוי. פעולות כמו.add(),.subtract(),.round()או.with()מחזירות מופע חדש. - אל תחזרו לחישובים של
Dateאו.getTime()בשביל ההפרש עצמו. ל-Dateאין חישובים של לוח שנה שמודעים לאזור הזמן, והדיוק שלו הוא רק במילישניות. - מומלץ להשתמש במאפיין
.signהמקורי שלTemporal.Duration(אוTemporal.ZonedDateTime.compare()) המחושב כדי לבדוק אם משך הזמן מייצג נקודת זמן בעבר או שפג תוקפה (sign < 0) או התחלה בהמתנה בעתיד.
אסטרטגיית גיבוי
תמיכה ב-Temporal בדפדפנים ופתרונות חלופיים
בדפדפנים שעדיין לא תומכים ב-API המקורי של Temporal, צריך להשתמש בזיהוי תכונות ולטעון באופן דינמי את temporal-polyfill (~15 KB min+gz).
חשוב לזכור שייבוא של temporal-polyfill לא מקצה באופן אוטומטי את האובייקט Temporal להיקף הגלובלי. אם הקוד שלכם מסתמך על globalThis.Temporal, צריך להקצות אותו באופן מפורש.
// Check if Temporal is supported natively
(async () => {
if (typeof Temporal === 'undefined') {
// Load the polyfill conditionally
const module = await import('https://esm.sh/temporal-polyfill');
globalThis.Temporal = module.Temporal;
// Extend Date.prototype if needed
Date.prototype.toTemporalInstant = module.toTemporalInstant;
}
initializeApp();
})();