ממיר Unix timestamp
המרת חותמת זמן Unix לתאריך בכל אזור זמן, ותאריך בחזרה ל־epoch: שניות או מילישניות מזוהות לבד, לצד הצורה של ISO 8601 וכמה זמן עבר מאז.
מהי חותמת זמן Unix
חותמת זמן Unix — שנקראת גם זמן epoch או זמן POSIX — היא מספר יחיד שסופר כמה שניות עברו מאז 00:00:00 UTC ב־1 בינואר 1970, רגע המכונה ה־Unix epoch. מכיוון שמדובר במספר שלם אחד, בלי אזור זמן, בלי לוח שנה ובלי עיצוב, זהו הפורמט שמחשבים בוחרים בכל פעם שצריך לאחסן רגע בזמן, למיין אותו, להשוות אותו או לשלוח אותו ברשת. זה מה שעומד מאחורי העמודה "created_at" במסד הנתונים שלך, מאחורי ה־claims "iat" ו־"exp" ב־JWT, מאחורי ה־mtime של קובץ, ומאחורי חותמות הזמן כמעט בכל קובץ לוג שתקרא אי פעם.
המחיר הוא שמספר חשוף לא אומר דבר לבן אדם. 1716197600 הוא רגע תקין לחלוטין, אבל אי אפשר לדעת במבט אם מדובר ביום שלישי שעבר או בלפני שלוש שנים. התרגום הזה, לשני הכיוונים, הוא מה שהכלי הזה עושה.
שניות או מילישניות — העמימות שנושכת
המוסכמה המקורית של Unix סופרת שניות שלמות, וזה מה שמקבלים מ־date +%s במעטפת, מ־time() ב־PHP, ומ־time.time() בפייתון לאחר קיצוץ. ג׳אווהסקריפט, ג׳אווה ורבים אחרים סופרים מילישניות במקום: Date.now() מחזיר מספר גדול פי אלף. שניהם נקראים "חותמת זמן", והבלבול ביניהם הוא אחד מבאגי התאריכים הנפוצים ביותר שיש.
הכישלון שקט ולא רועש. קרא ערך במילישניות כשניות, והתאריך שלך ינחת עשרות אלפי שנים בעתיד; קרא ערך בשניות כמילישניות, והכול יתכווץ לינואר 1970. אף אחד מהם לא זורק שגיאה — פשוט מתקבל תאריך שגוי שנראה כמו תאריך אמיתי.
הכלי הזה מנחש לפי סדר הגודל של המספר, ואז אומר לך מה ניחש, ממש ליד התוצאה. שניות ה־epoch של היום הן בנות עשר ספרות והמילישניות של היום הן בנות שלוש-עשרה, ולכן הניחוש כמעט תמיד נכון — אבל "כמעט" אינו מספיק טוב עבור ערך שאתה עומד להדביק לדוח באג, ולכן הפרשנות תמיד גלויה ותמיד במרחק לחיצה אחת מהחלפה.
אזורי זמן, UTC ולמה "מקומי" הוא חמקמק
לחותמת זמן Unix אין אזור זמן. היא מזהה רגע: 1716197600 הוא 2024-05-20T09:33:20Z, ואותו רגע עצמו הוא בו-זמנית 10:33 בלונדון, 12:33 בירושלים ו־18:33 בטוקיו — בלונדון ובירושלים נוהג באותו יום שעון קיץ, וביפן אין שעון קיץ כלל. אזור זמן אינו חלק מהערך; הוא עדשה שדרכה מסתכלים על הערך.
לכן הכלי הזה מציג כמה עדשות בבת אחת. UTC הוא הייחוס הנייטרלי שכל שרת וכל לוג מסכימים עליו. הזמן המקומי שלך הוא מה שהמכשיר שלך עצמו מדווח, ומוצג עם שם האזור שלו (למשל Europe/Berlin) כדי שתמיד תדע איזו עדשה הפיקה אותו — הזיהוי מתבצע בדפדפן שלך, כך שמבקר בברלין רואה את שעון ברלין ומבקר בטוקיו רואה את שעון טוקיו. השורה השלישית היא כל אזור שתבחר ממסד הנתונים המלא של אזורי הזמן IANA, וזו השורה שתרצה כשאתה קורא לוג משרת שחי במקום אחר.
- UTC — הייחוס שכל המערכות מסכימות עליו, והדבר הנכון לאחסן ולתעד.
- מקומי — אותו רגע כפי שהמכשיר שלך רואה אותו, מסומן באזור שזוהה.
- אזור לבחירתך — לקריאת לוגים ומעקבים ממכונות במקומות אחרים.
- ISO 8601 — פורמט הטקסט לחילופי נתונים, למשל 2024-05-20T09:33:20.000Z.
- יחסי — "לפני 3 שעות", לתחושה מהירה של כמה הדבר עדכני.
ההיסטים מוצגים לצד כל זמן (+03:00, -04:00) משום שהם אינם קבועים: רוב האזורים זזים בשעה בשל שעון קיץ, כך שאותו אזור עצמו יכול להפיק היסטים שונים בתקופות שונות בשנה. תאריך סמוך למעבר הוא בדיוק המקום שבו חישוב ידני משתבש.
עבודה עם זמן epoch בפועל
לכל שפה ולכל מעטפת יש דרך משלהן להפיק ערכי epoch ולקרוא אותם. אלה כדאי לזכור:
date +%s # מעטפת: הזמן הנוכחי בשניות date -d @1716197600 # מעטפת (GNU): משניות בחזרה לתאריך Date.now() # ג׳אווהסקריפט: הזמן הנוכחי במילישניות new Date(1716197600 * 1000) # ג׳אווהסקריפט: משניות ל־Date time.time() # פייתון: שניות, כמספר עשרוני datetime.fromtimestamp(1716197600, tz=timezone.utc) SELECT EXTRACT(EPOCH FROM now()) -- PostgreSQL: שניות
כלל אצבע שחוסך את רוב הכאב סביב תאריכים: אחסן ושדר רגעים כ־UTC — או כמספר epoch שלם או כמחרוזת ISO 8601 — והמר לאזור מקומי רק ברגע האחרון, כשבאמת מציגים אותו למישהו. עיצוב מוקדם מדי הוא הדרך שבה באג של אזור זמן נאפה לתוך הנתונים שלך במקום להישאר בשכבת התצוגה.
עוד דבר שכדאי לדעת: מונה שניות מסוג 32 סיביות עם סימן נגמר ב־19 בינואר 2038, "בעיית שנת 2038". מערכות מודרניות משתמשות בערכי 64 סיביות והן בסדר, אבל התקנים משובצים ישנים ועמודות מסד נתונים מדור קודם הם בדיוק המקומות שבהם עוד אפשר להיתקל בה.
מהי epoch, ולמה לא כל חותמת זמן מתחילה ב־1970
epoch, במובן הזה, היא פשוט אפס שנבחר — הרגע שממנו מוגדר מונה להתחיל. ה־Unix epoch היא 00:00:00 UTC ב־1 בינואר 1970, והיא נבחרה משום שזה תאריך עגול ונוח ולא משום שנגזרה ממשהו: שום דבר בפורמט עצמו אינו מחייב דווקא את היום הזה. Unix המוקדמת ספרה שישיות שנייה מאפס קרוב הרבה יותר, ומדריך המהדורה השלישית אמר במפורש למה זה לא יחזיק מעמד — שלושים ושתיים סיביות של שישיות מבטיחות משבר כל 2.26 שנים, שהם כ־828 ימים. שניות שלמות באותן שלושים ושתיים סיביות מגיעות למאה שלושים ושש שנים, או לשישים ושמונה אם למונה יש סימן, וזה תאריך 2038 שלמעלה.
ההיסטוריה הזו היא הסיבה לבדוק מספר ארוך לפני שסומכים עליו. מערכות אחרות סופרות מאפסים אחרים ברזולוציות אחרות, וקריאה של אחד מהם כשניות Unix אינה מרחיקה אותך בכמה שעות — היא מרחיקה אותך במאות שנים. אלה הנפוצים ביותר:
- Windows FILETIME — פעימות של מאה ננו-שניות מאז 1 בינואר 1601 UTC. חלק בעשרה מיליון והחסר 11644473600 כדי לקבל שניות Unix; הרגע 1716197600 שמשמש לאורך המדריך הזה הוא 133606712000000000 בשיטה הזו.
- .NET DateTime.Ticks — אותה פעימה של מאה ננו-שניות, נספרת מתחילת שנת 1. ה־Unix epoch עצמה יושבת על 621355968000000000 פעימות, וזה המספר שיש להחסיר לפני החלוקה.
- תאריך הייחוס של Apple — שניות מאז 1 בינואר 2001 UTC, שבו משתמשים Cocoa ו־Core Data. הוסף 978307200 כדי לקבל שניות Unix: 1716197600 הוא 737890400 שם.
- מספרים סידוריים של Excel — ימים ולא שניות, ונספרים מ־30 בדצמבר 1899, משום ש־Excel מתייחס ל־1900 כשנה מעוברת והיא לא הייתה כזו.
- זמן GPS — שניות מאז 6 בינואר 1980, נספרות בלי לדלג אף פעם על שנייה מעוברת, כך שזמן GPS ו־UTC כבר אינם מסכימים.
האחרון הזה מצביע על משהו שנכון לגבי ערך ה־Unix עצמו: הוא אינו קריאת שעון עצר. POSIX מגדיר אותו כערך שמקרב את מספר השניות שעברו מאז ה־epoch, ודורש שכל יום ויום יימנה בדיוק 86400 שניות — כך שהשניות המעוברות שהוכנסו ל־UTC אינן נספרות כלל. ההפרש בין שתי חותמות זמן Unix הוא אפוא מספר שניות הלוח שביניהן ולא מספר השניות שבאמת עברו, והערך מובן בצורה הטובה ביותר כקידוד של קריאת לוח שנה ב־UTC. זו גם הסיבה ששום חותמת זמן אינה מציינת שנייה מעוברת: לקידוד פשוט אין משבצת לכזו.
הפיכת תאריך לערך epoch: המעטפת, ג׳אווהסקריפט ופייתון
הבלוק שלמעלה הופך ערך epoch למשהו קריא. הכיוון ההפוך — תאריך שכבר יש לך, כטקסט או כאוסף מספרים, שהופך לערך epoch — הוא המקום שבו חיים באגי אזורי הזמן, משום שכל אחד מאלה יניח בשקט אזור כשהטקסט אינו נוקב בשם אחד. הנה אותה עבודה בשלוש דרכים, על הרגע 2024-05-20T09:33:20Z של המדריך הזה. שורות המעטפת הן date של GNU; ה־date של BSD ושל macOS מקבל דגלים אחרים.
date -u -d '2024-05-20 09:33:20' +%s # 1716197600 — הדגל -u קורא את הטקסט כ־UTC date -d '2024-05-20 09:33:20 UTC' +%s # אותו דבר, עם שם האזור בתוך הטקסט date -d '2024-05-20 09:33:20' +%s # אף אחד מהם: אזור המכונה שלך, רגע אחר
Date.parse('2024-05-20T09:33:20Z') / 1000 // 1716197600 — ה־Z הוא מה שהופך את זה ל־UTC
new Date('2024-05-20T09:33:20Z').getTime() // אותו רגע במילישניות
Date.parse('2024-05-20 09:33:20') // בלי Z: אזור המכונה שלך, רגע אחרfrom datetime import datetime, timezone
int(datetime(2024, 5, 20, 9, 33, 20, tzinfo=timezone.utc).timestamp()) # 1716197600 — datetime מודע
int(datetime.fromisoformat('2024-05-20T09:33:20+00:00').timestamp()) # אותו דבר, מנותח ממחרוזת
int(datetime(2024, 5, 20, 9, 33, 20).timestamp()) # נאיבי: אזור המכונה שלךהתבנית זהה בכל שלושתן: נקוב בשם האזור, או שתירש את מה שהמכונה מוגדרת אליו. מספר שנכון במחשב הנייד שלך ומוטעה בכמה שעות אצל עמית הוא כמעט תמיד זה, וזו הסיבה שהכלי שלמעלה מציג את UTC ואת הקריאה המקומית שלך זו לצד זו במקום לבחור אחת עבורך. אם המספר כבר בידך ואתה רוצה לבדוק אותו בעין, הדבק אותו בשדה שבראש העמוד הזה.
שאלות נפוצות
- איך הכלי יודע אם המספר שלי הוא בשניות או במילישניות?
- לפי סדר הגודל שלו: ערכים מתחת ל־1e11 בקירוב נקראים כשניות, וגדולים מהם כמילישניות. כיום זה מפריד היטב בין שניות בנות עשר ספרות למילישניות בנות שלוש-עשרה. הפרשנות מוצגת ליד התוצאה וניתן להחליף אותה בלחיצה אחת, כך שהניחוש לעולם אינו מוסתר ממך.
- איזה אזור זמן נחשב "מקומי"?
- האזור שהמכשיר שלך עצמו מדווח עליו, מזוהה בדפדפן שלך ומוצג בשמו כדי שלא יישאר ספק. זה האזור שלך, לא של האתר — מבקר בברלין רואה את שעון ברלין ומבקר בטוקיו רואה את שעון טוקיו.
- אפשר להמיר תאריך בחזרה לחותמת זמן?
- כן. אותו שדה מקבל את שני הכיוונים: הקלד מספר ותקבל תאריך, הקלד תאריך כמו 2024-05-20 או 2024-05-20T09:33:20Z ותקבל בחזרה את ערך ה־epoch. אין מצב שצריך להחליף.
- האם הוא מטפל בתאריכים לפני 1970?
- כן. חותמות זמן שלפני ה־epoch הן פשוט שליליות — -86400 הוא 31 בדצמבר 1969 — וערכים שליליים מתקבלים ומומרים כרגיל.
- למה חותמת הזמן שלי מציגה תאריך שונה ממה שציפיתי?
- כמעט תמיד מאחת משתי סיבות: הפרשנות של שניות/מילישניות אינה מה שהנחת (בדוק את התווית והחלף אותה), או שאתה משווה ערך UTC מול ציפייה בזמן מקומי. הכלי מציג את שתי השורות זו לצד זו כדי שתראה במה מדובר.
- מהי בעיית שנת 2038?
- מערכות שמאחסנות שניות epoch במספר שלם של 32 סיביות עם סימן עוברות גלישה ב־19 בינואר 2038 ומתהפכות למספר שלילי, מה שמניב תאריכים ב־1901. כל מה שמשתמש בערכי 64 סיביות — כלומר כמעט כל דבר מודרני — אינו מושפע, אבל מערכות משובצות מדור קודם ועמודות מסד נתונים ישנות עדיין עלולות להיות בסיכון.
- למה חותמת הזמן שלי היא בת שמונה-עשרה ספרות?
- כי היא כנראה אינה חותמת זמן Unix. שניות Unix הן בנות עשר ספרות היום ומילישניות Unix בנות שלוש-עשרה; שמונה-עשרה היא איך שנראית פעימה של מאה ננו-שניות, וזה מה ש־Windows FILETIME סופר מ־1601 ומה ש־.NET סופר מתחילת שנת 1. שש-עשרה ספרות הן המקרה הנפוץ האחר — אותו רגע במיקרו-שניות. הפרק על ה־epoch שלמעלה נותן את הקבוע שיש להחסיר בכל מקרה.
- איך ממירים תאריך לחותמת זמן Unix בפייתון?
- בנה datetime מודע — כזה שנושא tzinfo — וקרא למתודת timestamp שלו, ואז קצץ למספר שלם. בבלוק הקוד שלמעלה יש את השורה, ואותו פרק מראה את אותה עבודה במעטפת ובג׳אווהסקריפט. כל הקושי הוא ב־tzinfo: השמט אותו ופייתון יקרא את המספרים שלך כזמן המקומי של המכונה ויחזיר ערך אחר בלי לומר לך.
- האם חותמת הזמן שאני מזין נשלחת לאנשהו?
- לא. הפירוש, ההמרה והעיצוב רצים כולם בדפדפן שלך באמצעות התמיכה המובנית של הפלטפורמה בתאריכים ובאזורי זמן. שום דבר שתקליד לא עוזב את המכשיר שלך.
כלים קשורים
- מחשבון chmod
המרת הרשאות קובץ בין אוקטלי, סימבולי ותיבות סימון.
- מחשבון CIDR ותת־רשתות
פירוק רשת, בדיקת הכלה, ופיצול לתת־רשתות.
- מסביר ביטויי cron
קריאה של ביטוי cron, ומתי הוא באמת רץ.
- מה כתובת ה־IP שלי
כתובות ה־IPv4 וה־IPv6 הציבוריות שלכם, ומי מהן נבחרה.