RegEx untuk mencocokkan Pola Tanggal di Jawa

1. Perkenalan

Ekspresi reguler adalah alat yang ampuh untuk mencocokkan berbagai jenis pola bila digunakan dengan tepat.

Pada artikel ini, kita akan menggunakan paket java.util.regex untuk menentukan apakah String yang diberikan berisi tanggal yang valid atau tidak.

Untuk pengenalan ekspresi reguler, lihat Panduan Untuk Java Regular Expressions API kami.

2. Gambaran Umum Format Tanggal

Kami akan menentukan tanggal yang valid terkait dengan kalender Masehi internasional. Format kita akan mengikuti pola umum: YYYY-MM-DD.

Mari kita sertakan juga konsep tahun kabisat yaitu tahun yang berisi hari tanggal 29 Februari. Menurut kalender Gregorian, kita akan menyebut tahun kabisat jika nomor tahun dapat dibagi rata dengan 4 kecuali yang habis habis dibagi 100 tetapi termasuk yang habis habis dibagi 400 .

Dalam semua kasus lain , kami akan menelepon satu tahun reguler .

Contoh tanggal yang valid:

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

Contoh tanggal tidak valid:

  • 2017/12/31 : pembatas token salah
  • 2018-1-1 : nol di depan hilang
  • 31-04-2018 : hari yang salah dihitung untuk bulan April
  • 2100-02-29 : tahun ini bukan lompatan karena nilainya dibagi 100 , jadi Februari dibatasi 28 hari

3. Menerapkan Solusi

Karena kita akan mencocokkan tanggal menggunakan ekspresi reguler, mari kita buat sketsa antarmuka DateMatcher , yang menyediakan metode pencocokan tunggal :

public interface DateMatcher { boolean matches(String date); }

Kami akan mempresentasikan penerapan langkah demi langkah di bawah ini, membangun solusi lengkap di bagian akhir.

3.1. Mencocokkan Format Luas

Kami akan mulai dengan membuat prototipe yang sangat sederhana yang menangani batasan format dari matcher kami:

class FormattedDateMatcher implements DateMatcher { private static Pattern DATE_PATTERN = Pattern.compile( "^\\d{4}-\\d{2}-\\d{2}$"); @Override public boolean matches(String date) { return DATE_PATTERN.matcher(date).matches(); } }

Di sini kami menetapkan bahwa tanggal yang valid harus terdiri dari tiga kelompok bilangan bulat yang dipisahkan oleh tanda hubung. Kelompok pertama terdiri dari empat bilangan bulat, dengan dua kelompok lainnya masing-masing memiliki dua bilangan bulat.

Tanggal yang cocok: 2017-12-31 , 2018-01-31 , 0000-00-00 , 1029-99-72

Tanggal tidak cocok: 01-01 , 2018-01-XX , 2020/02/29

3.2. Mencocokkan Format Tanggal Tertentu

Contoh kedua kami menerima rentang token tanggal serta batasan pemformatan kami. Untuk kesederhanaan, kami telah membatasi minat kami pada tahun 1900-2999.

Sekarang setelah kita berhasil mencocokkan format tanggal umum kita, kita perlu membatasi lebih jauh - untuk memastikan tanggalnya benar:

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

Di sini kami telah memperkenalkan tiga grup rentang bilangan bulat yang harus cocok:

  • (19|2[0-9])[0-9]{2}mencakup rentang tahun terbatas dengan mencocokkan angka yang dimulai dengan 19 atau 2X diikuti dengan beberapa digit.
  • 0[1-9]|1[012]cocok dengan angka bulan dalam kisaran 01-12
  • 0[1-9]|[12][0-9]|3[01]cocok dengan nomor hari dalam rentang 01-31

Tanggal yang cocok: 1900-01-01 , 2205-02-31 , 2999-12-31

Tanggal tidak cocok: 1899-12-31 , 2018-05-35 , 2018-13-05 , 3000-01-01 , 2018-01-XX

3.3. Cocok dengan 29 Februari

Untuk mencocokkan tahun kabisat dengan benar, pertama-tama kita harus mengidentifikasi kapan kita mengalami tahun kabisat , dan kemudian memastikan bahwa kita menerima 29 Februari sebagai tanggal yang valid untuk tahun-tahun itu.

Karena jumlah tahun kabisat dalam kisaran terbatas kita cukup besar, kita harus menggunakan aturan pembagian yang sesuai untuk memfilternya:

  • Jika bilangan yang dibentuk oleh dua digit terakhir dalam sebuah bilangan habis dibagi 4, bilangan aslinya habis dibagi 4
  • Jika dua digit terakhir dari nomor tersebut adalah 00, maka nomor tersebut habis dibagi 100

Inilah solusinya:

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

Polanya terdiri dari bagian-bagian berikut:

  • 2000|2400|2800mencocokkan serangkaian tahun kabisat dengan pembagi 400 dalam rentang terbatas 1900-2999
  • 19|2[0-9](0[48]|[2468][048]|[13579][26]))cocok dengan semua kombinasi daftar putih tahun yang memiliki pembagi 4 dan tidak memiliki pembagi 100
  • -02-29cocok dengan 2 Februari

Tanggal pertandingan : 2020-02-29 , 2024-02-29 , 2400-02-29

Tanggal tidak cocok: 2019-02-29 , 2100-02-29 , 3200-02-29 , 2020/02/29

3.4. Hari Umum Pencocokan Februari

Selain mencocokkan 29 Februari dalam tahun kabisat, kita juga perlu mencocokkan semua hari lain di bulan Februari (1 - 28) di semua tahun :

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

Tanggal pertandingan : 01-02-2018 , 2019-02-13 , 2020-02-25

Non-matching dates: 2000-02-30, 2400-02-62, 2018/02/28

3.5. Matching 31-Day Months

The months January, March, May, July, August, October, and December should match for between 1 and 31 days:

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

Matching dates: 2018-01-31, 2021-07-31, 2022-08-31

Non-matching dates: 2018-01-32, 2019-03-64, 2018/01/31

3.6. Matching 30-Day Months

The months April, June, September, and November should match for between 1 and 30 days:

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

Matching dates: 2018-04-30, 2019-06-30, 2020-09-30

Non-matching dates: 2018-04-31, 2019-06-31, 2018/04/30

3.7. Gregorian Date Matcher

Now we can combine all of the patterns above into a single matcher to have a complete GregorianDateMatcher satisfying all of the constraints:

class GregorianDateMatcher implements DateMatcher { private static Pattern 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 public boolean matches(String date) { return DATE_PATTERN.matcher(date).matches(); } }

We've used an alternation character “|” to match at least one of the four branches. Thus, the valid date of February either matches the first branch of February 29th of a leap year either the second branch of any day from 1 to 28. The dates of remaining months match third and fourth branches.

Since we haven't optimized this pattern in favor of a better readability, feel free to experiment with a length of it.

At this moment we have satisfied all the constraints, we introduced in the beginning.

3.8. Note on Performance

Parsing ekspresi reguler yang kompleks dapat secara signifikan memengaruhi kinerja aliran eksekusi. Tujuan utama artikel ini bukanlah untuk mempelajari cara yang efisien untuk menguji string untuk keanggotaannya dalam satu set semua tanggal yang mungkin.

Pertimbangkan untuk menggunakan LocalDate.parse () yang disediakan oleh Java8 jika diperlukan pendekatan yang andal dan cepat untuk memvalidasi tanggal.

4. Kesimpulan

Di artikel ini, kita telah mempelajari cara menggunakan ekspresi reguler untuk mencocokkan tanggal kalender Gregorian yang diformat ketat dengan memberikan aturan format, rentang, dan panjang bulan juga.

Semua kode yang disajikan dalam artikel ini tersedia di Github. Ini adalah proyek berbasis Maven, jadi semestinya mudah untuk mengimpor dan menjalankan apa adanya.