בחלק הקודם סקרנו את היכולות של אובייקט Date והשימושים הרבים שהוא יכול לתת לנו. בחלק הזה נכיר ספריות שעוסקות בטיפול בזמן ואיך הן חוסכות לנו, ובכן, זמן. נקדים ונאמר שכמובן כולן ספריות קוד פתוח ללא תשלום.
רק רגע אחד! MomentJS
זאת בלי ספק הספריה הותיקה והפופולרית ביותר לטיפול בזמן. יש לה למעלה מ-46 אלף כוכבים בגיטהאב והמוני משתמשים. היא עושה את החיים הרבה יותר קלים בכל הנוגע לעיבוד של תאריכים, פירמוט והצגתם בשפה אנושית וגם פעולות מתמטיות שראינו במאמר הקודם.
בואו נכיר אותה טיפה יותר.
דבר ראשון ששמים לב אליו, הוא שאובייקט moment, שהוא המקבילה של JS Date אינו מתנהג כמו מחלקה, ולא יוצרים עותק שלו עם new, אלא ישירות:
const m1 = moment()
const m2 = moment('20220529')
m1.format()
// "2022-05-29T14:01:53+03:00"
m2.format()
// "2022-05-29T00:00:00+03:00"
אם במקרה שמתם לב לדבר נחמד, זה שהקריאה השניה השתמשה במספר החודש הנכון, ולא זה שמתחיל ב-0 כמו האובייקט הרגיל. זה מאד נוח ומונע הרבה בלבול.
המתודה הראשונה שמעניינת אותנו היא אכן format. היא מאפשרת לנו לראות את הזמן לפי הפרמטרים שמכניסים לה. בלעדיה האובייקט מציג את כל התכולה שלו וזה לא שימושי.
ניתן להכניס מגוון של פרמטרים לתוך הפורמט וקצרה היריעה מלסקור את כל האפשרויות, אבל הנה כמה:
m1.format("dd.MM.yy")
// "Su.05.2022"
m1.format("dddd.MM.YY")
// "Sunday.05.22"
m1.format("dddd Do, MMMM YYYY")
// "Sunday 29th, May 2022"
m1.format("[hello world] YYYY")
// "hello world 2022"
תוכלו לראות את המגוון המלא כאן.
אפשר גם לעשות פעולות מתמטיות על האובייקט באופן ידידותי.
m1.add(1, 'day').format()
// "2022-05-30T14:17:41+03:00"
m1.subtract(1, 'day').format()
// "2022-05-28T14:18:16+03:00"
יש גם פעולות השוואתיות שניתן לעשות בקלות, כמו לבדוק האם זמן מסויים נמצא לפני, אחרי, או בין זמנים אחרים:
moment('2010-10-20').isBetween('2010-10-19', '2010-10-25')
// true
או שאלות כגון האם זאת שנת שמיטה?
moment().isLeapYear()
האם זה שעון קיץ?
moment().isDST()
כמה שבועות יש בשנה?
moment().weeksInYear()
ועוד רבים וטובים.
על פניו מושלם, אלא שיש שני חסרונות לספרייה הזאת. הקטן שביניהם הוא העובדה שבדומה לאובייקט Date גם moment הוא מיוטאבילי, כלומר פעולות על אובייקט משנות אותו, מה שהופך אותו ללא פונקציונלי. אם רוצים לשמר את האובייקט המקורי יש לשכפל אותו תחילה:
moment().clone()
הבעיה הגדולה יותר היא ש… ובכן, שהספרייה הזאת גדולה. לפעמים כל מה שצריך זה כמה פונקציות בסיסיות ו-moment היא לא מודולרית. הכל או כלום. ולכן גם באתר הרשמי אחד הדברים הראשונים שתראו זה המלצה לעבור לספריה מודרנית יותר. אז בואו נעבור לספריה מודרנית יותר.
הלוקסוס של Luxon
לוקסון היא הממשיכה הטבעית של מומנט ולכן מומלצת על ידי הצוות. היא נמצאת כרגע בגירסה 2 ויש לה למעלה מ-12 אלף כוכבים בגיטהאב.
הרציונל של לוקסון היה לקחת את המבנה הייחודי והמיושן של מומנט ולהמיר אותו לתצורה מודרנית יותר. שני דברים מייחדים את הספריה הזאת: הראשון ייראה מוכר ומועיל במיוחד למי שמפתח בצד שרת. האובייקט הבסיסי נקרא DateTime. אובייקט מהסוג הזה קיים במגוון שפות ונחשב גם לתקן בבסיסי נתונים. לכן גם ה-API מנסה להידמות למוכר.
השינוי הנוסף נוגע לעובדה שלא ניתן לשנות את התכולה של אובייקט DateTime לאחר שנוצר. כל מתודה שמבצעת עליו מניפולציה מייצרת בפועל אובייקט חדש. תכונה זו תואמת סטנדרטים של תכנות פונקציונלי הנחשב למודרני יותר.
בואו נראה כמה שימושים של לוקסון.
עכשיו בלוקסונית:
DateTime.local()
זמן מוגדר מראש:
DateTime.fromObject({day: 22, hour: 12 }, { zone: 'America/Los_Angeles', numberingSystem: 'beng'})
DateTime.fromISO("2017-05-15")
כלים אשר חושפים פרטים נוספים:
DateTime.local().zoneName
// Asia/Jerusalem
DateTime.local().daysInMonth
// 31
DateTime.local().weekdayLong
// Sunday
יש גם התייחסות למיקום לטובת תצוגת הזמן:
DateTime.local().setLocale('he').toLocaleString({month: 'long', day: 'numeric'})
// 29 במאי
לוקסון מציעה גם התייחסות לזמן כיחידה ולא רק כייצוג של תאריך. זה יכול לעניין כאשר רוצים למדוד טווחי זמן, לדוגמה:
Duration.fromObject({ hours: 2, minutes: 7 }).as('minutes')
// 127
ובסה"כ מדובר על ספריה מודרנית יותר וראויה לשימוש.
אבל… הפיל. הפיל עדיין איתנו. גם לוקסון אינה מודולרית ולמען האמת שוקלת אף יותר ממומנט! ולכן אם יעילות וגודל הבאנדל של הפרוייקט שלכם משמעותי לכם תאלצו להשתמש במשהו אחר.
הגיע הזמן של date-fns
בואו נוותר על המתח הפעם. ספריית date-fns פותרת את בעיית גודל הבאנדל, כי היא מודולרית לגמרי. הגישה שלה פונקציונלית מלכתחילה והיא מורכבת מפונקציות-על אשר מבצעות מניפולציות על אובייקט ה-Date מיודענו משכבר הימים. אפשר להגיד עליה שהיא דומה ללודאש, או לייתר דיוק ל-lodash-es.
נראה שרוב מי שלא משתמש במומנט עבר אליה (נקרא לה דפנ"ס מעתה והלאה) במקום ללוקסון – יש לדפנ"ס יותר מכפול מספר הכוכבים בגיטהאב.
קודם כל חשוב להבין שכל פונקציה של ספריה זו מחייבת ייבוא בנפרד. ולכן אם נרצה להנות מיכולת הפירמוט שלה, אנחנו נייבא את הפונקציה המתאימה באופן כזה:
import { format } from "date-fns"
format(new Date(), "dd-MM-yyyy")
// 29-05-2022
כלי הפירמוט של דפנ"ס חזק מאד כצפוי ויש לו מגוון גדול של פרמטרים. מומלץ להסתכל על התיעוד הרשמי. לדוגמה אם אתם תוהים האם אנחנו לפני או אחרי הספירה, ובכלל איך קוראים ליום היום יש את זה:
format(new Date(), "G eeee")
// AD Sunday
וגם כמובן שניתן להשוות בין תאריכים, לדוגמה:
import { isEqual } from "date-fns"
isEqual(new Date(), new Date())
// true
ומעבר לצפוי יש כל מיני פונקציות שונות ומשונות, כמו זאת שמחזירה את רשימת השבועות בין שני תאריכים:
import { eachWeekOfInterval } from "date-fns"
eachWeekOfInterval({
start: new Date(2022, 0, 1),
end: new Date(2022, 0, 10)})
// [Sun Dec 26 2021 00:00:00 GMT+0200 (Israel Standard Time), Sun Jan 02 2022 00:00:00 GMT+0200 (Israel Standard Time), Sun Jan 09 2022 00:00:00 GMT+0200 (Israel Standard Time)]
אז מה עושים עכשיו?
אז למה בעצם נרצה להשתמש בספריות במקום ביכולות המובנות של Date? התשובה לכך היא שלפעמים באמת לא צריך ומפתחים פונים לפתרונות חיצוניים כי הם לא מכירים את היכולות המובנות. בשביל זה אנחנו כאן.
אבל כאשר רוצים לבצע פעולות מורכבות יותר, כאשר רוצים להשוות בין תאריכים, או לקבל מידע בצורה מתקדמת יותר ולהימנע מטעויות פיתוח, טוב שיש פתרונות שנבדקו מראש ואינם דורשים השגחה ובדיקה פנימית. וגם בשביל זה אנחנו כאן.
מה יכולים להיות השיקולים בין 3 החלופות שסקרנו במאמר זה?
מומנט היא קוד לגאסי. ולפעמים זה רע הכרחי, כי אין אפשרות ריאלית להחליף פתרונות בפרוייקטים קיימים.
לוקסון מיועדת למי שרגיל לכתוב בסטנדרטים של התעשיה ומוכן לשלם מחיר של כמה עשרות ק"ב מיותרים.
דפנ"ס תיתן פתרונות מצויינים למי שרוצה תכנות פונקציונלי ומינימום קוד נוסף על הקיים.
ומה איתכם? יש לכם זמן פנוי? מצויין. רוצו לכתוב קוד עכשיו, לפני שהיום יהפוך להיות מחר!