RegEx להתאמת תבנית תאריך ב- Java

1. הקדמה

ביטויים רגולריים הם כלי רב עוצמה להתאמת סוגים שונים של תבניות כאשר משתמשים בהם כראוי.

במאמר זה נשתמש java.util.regex חבילה כדי לקבוע אם נתון חוּט מכיל תאריך תקף או לא.

לקבלת מבוא לביטויים רגולריים, עיין במדריך API ל- Java Regular Expressions.

2. סקירה על פורמט תאריך

אנו נגדיר תאריך תקף ביחס ללוח השנה הגרגוריאני הבינלאומי. הפורמט שלנו יתאים לדפוס הכללי: YYYY-MM-DD.

בואו נכלול גם את המושג א לִקְפּוֹץ שנה שהיא שנה המכילה יום של 29 בפברואר. על פי לוח השנה הגרגוריאני, נתקשר לשנה לִקְפּוֹץ אם ניתן לחלק את מספר השנה באופן שווה על ידי 4 למעט אלה שאפשר לחלק אותם 100 אך כולל אלה הניתנים לחלוקה 400.

בכל המקרים האחרים, נתקשר לשנה רגיל.

דוגמאות לתאריכים תקפים:

  • 2017-12-31
  • 2020-02-29
  • 2400-02-29

דוגמאות לתאריכים לא חוקיים:

  • 2017/12/31: תוחם אסימונים שגוי
  • 2018-1-1חסר אפסים מובילים
  • 2018-04-31: ימים שגויים נחשבים לאפריל
  • 2100-02-29: השנה אינה קפיצת מדרגה ככל שהערך מתחלק 100אז פברואר מוגבל ל -28 יום

3. יישום פתרון

מכיוון שנתאים תאריך באמצעות ביטויים רגולריים, בואו נשרטט תחילה ממשק DateMatcher, המספק יחיד התאמות שיטה:

ממשק ציבורי DateMatcher {התאמות בוליאניות (תאריך מחרוזת); }

אנו נציג את היישום שלב אחר שלב בהמשך, ונבנה לקראת פיתרון מלא בסוף.

3.1. התאמה לפורמט הרחב

נתחיל ביצירת אב טיפוס פשוט מאוד לטיפול במגבלות הפורמט של המתאים שלנו:

class FormattedDateMatcher מיישם DateMatcher {דפוס סטטי פרטי DATE_PATTERN = Pattern.compile ("^ \ d {4} - \ d {2} - \ d {2} $"); @Override התאמות בוליאניות ציבוריות (תאריך מחרוזת) {החזר DATE_PATTERN.matcher (תאריך) .matches (); }}

כאן אנו מציינים זאת תאריך תקף חייב להיות מורכב משלוש קבוצות של מספרים שלמים המופרדים באמצעות מקף. הקבוצה הראשונה מורכבת מארבעה מספרים שלמים, כאשר לשתי הקבוצות הנותרות שני מספרים שלמים כל אחד.

תאריכי התאמה: 2017-12-31, 2018-01-31, 0000-00-00, 1029-99-72

תאריכים שאינם תואמים: 2018-01, 2018-01-XX, 2020/02/29

3.2. התאמה לפורמט התאריכים הספציפי

הדוגמה השנייה שלנו מקבלת טווחי אסימונים של תאריכים כמו גם את מגבלת העיצוב שלנו. לשם פשטות, הגבלנו את האינטרס שלנו לשנים 1900 - 2999.

כעת, כאשר התאמנו בהצלחה את תבנית התאריך הכללית שלנו, עלינו להגביל זאת עוד יותר - כדי לוודא שהתאריכים אכן נכונים:

^((19|2[0-9])[0-9]{2})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$

הנה הצגנו שלושה קבוצות של טווחי מספרים שלמים שצריכים להתאים:

  • (19|2[0-9])[0-9]{2} מכסה טווח שנים מוגבל על ידי התאמת מספר שמתחיל ב 19 אוֹ 2X ואחריו כמה ספרות.
  • 0[1-9]|1[012] תואם למספר חודש בטווח של 01-12
  • 0[1-9]|[12][0-9]|3[01] תואם מספר יום בטווח של 01-31

תאריכי התאמה: 1900-01-01, 2205-02-31, 2999-12-31

תאריכים שאינם תואמים: 1899-12-31, 2018-05-35, 2018-13-05, 3000-01-01, 2018-01-XX

3.3. התאמה ל- 29 בפברואר

על מנת להתאים נכון את שנות העוברת עלינו קודם לזהות מתי נתקלנו בשנה מעוברתואז וודא שנקבל את ה -29 בפברואר כתאריך תקף לאותן שנים.

מכיוון שמספר שנות המעבר בטווח המוגבל שלנו גדול מספיק עלינו להשתמש בכללי החלוקה המתאימים כדי לסנן אותם:

  • אם המספר שנוצר על ידי שתי הספרות האחרונות במספר מתחלק ב -4, המספר המקורי מתחלק ב -4
  • אם שתי הספרות האחרונות של המספר הן 00, המספר מתחלק ב 100

להלן פיתרון:

^((2000|2400|2800|(19|2[0-9](0[48]|[2468][048]|[13579][26])))-02-29)$

התבנית מורכבת מהחלקים הבאים:

  • 2000|2400|2800 תואם קבוצה של שנות מעוברות עם מחלק של 400 בטווח מוגבל של 1900-2999
  • 19|2[0-9](0[48]|[2468][048]|[13579][26])) תואם את כולם רשימה לבנה שילובים של שנים שיש להם מחלק של 4 ואין לך מחלק של 100
  • -02-29 התאמות 2 בפברואר

תאריכי התאמה: 2020-02-29, 2024-02-29, 2400-02-29

תאריכים שאינם תואמים: 2019-02-29, 2100-02-29, 3200-02-29, 2020/02/29

3.4. התאמה לימים כלליים של פברואר

כמו גם התאמה של 29 בפברואר בשנים מעוברות, עלינו להתאים גם את כל שאר ימי פברואר (1 - 28) בכל השנים:

^(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))$

תאריכי התאמה: 2018-02-01, 2019-02-13, 2020-02-25

תאריכים שאינם תואמים: 2000-02-30, 2400-02-62, 2018/02/28

3.5. התאמה לחודשים של 31 יום

החודשים ינואר, מרץ, מאי, יולי, אוגוסט, אוקטובר ודצמבר צריכים להתאים בין יום אחד ל -31 יום:

^(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))$

תאריכי התאמה: 2018-01-31, 2021-07-31, 2022-08-31

תאריכים שאינם תואמים: 2018-01-32, 2019-03-64, 2018/01/31

3.6. התאמה לחודשים של 30 יום

החודשים אפריל, יוני, ספטמבר ונובמבר צריכים להתאים בין יום אחד ל -30 יום:

^(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30))$

תאריכי התאמה: 2018-04-30, 2019-06-30, 2020-09-30

תאריכים שאינם תואמים: 2018-04-31, 2019-06-31, 2018/04/30

3.7. משחק תאריכים גרגוריאני

עכשיו אנחנו יכולים שלב את כל הדפוסים שלמעלה להתאמה יחידה כדי לקבל השלמה GregorianDateMatcher שמספק את כל האילוצים:

מחלקה GregorianDateMatcher מיישם את DateMatcher {דפוס סטטי פרטי DATE_PATTERN = Pattern.compile ("^ ((2000 | 2400 | 2800 | (19 | 2 [0-9] (0 [48] | [2468] [048] | [13579] [ 26]))) - 02-29) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2}) - 02- (0 [1-9] | 1 [ 0-9] | 2 [0-8])) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2}) - (0 [13578] | 10 | 12 ) - (0 [1-9] | [12] [0-9] | 3 [01])) $ "+" | ^ (((19 | 2 [0-9]) [0-9] {2 }) - (0 [469] | 11) - (0 [1-9] | [12] [0-9] | 30)) $ "); @Override התאמות בוליאניות ציבוריות (תאריך מחרוזת) {החזר DATE_PATTERN.matcher (תאריך) .matches (); }}

השתמשנו ב- הִתחַלְפוּת דמות “|” כדי להתאים לפחות אחד מארבעת הסניפים. לפיכך, התאריך התקף של פברואר תואם את הסניף הראשון של ה -29 בפברואר של שנה מעוברת או את הסניף השני של כל יום מיום 1 ל 28. תאריכי החודשים הנותרים תואמים את הסניף השלישי והרביעי.

מכיוון שלא אופטימיזנו דפוס זה לטובת קריאה טובה יותר, אל תהסס להתנסות באורכו.

ברגע זה סיפקנו את כל האילוצים שהצגנו בהתחלה.

3.8. הערה על ביצועים

ניתוח ביטויים רגולריים מורכבים עשוי להשפיע באופן משמעותי על ביצועי זרימת הביצוע. המטרה העיקרית של מאמר זה לא הייתה ללמוד דרך יעילה לבדיקת מחרוזת לחברותה במכלול התאריכים האפשריים.

שקול להשתמש LocalDate.parse () מסופק על ידי Java8 אם יש צורך בגישה אמינה ומהירה לאימות תאריך.

4. מסקנה

במאמר זה למדנו כיצד להשתמש בביטויים רגולריים לצורך התאמת התאריך המעוצב בקפדנות של לוח השנה הגרגוריאני על ידי מתן כללי פורמט, טווח ומשך חודשים גם כן.

כל הקוד המוצג במאמר זה זמין באתר Github. זהו פרויקט מבוסס Maven, כך שיהיה קל לייבא ולהפעיל אותו כפי שהוא.