4

I am looking to validate the credit card expiry date in MM/YY format . I have no clue on how to validate , whether to go for Simple date format / Regex .

Appreciate your help.

Preethi
  • 2,112
  • 7
  • 38
  • 54
  • 2
    are you asking for a regex that will match MM/YY format, or are you asking if using a regex is a good idea in this case? – larissa Jul 17 '12 at 18:54

5 Answers5

20

Use SimpleDateFormat to parse a Date, then compare it with a new Date, which is "now":

String input = "11/12"; // for example
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/yy");
simpleDateFormat.setLenient(false);
Date expiry = simpleDateFormat.parse(input);
boolean expired = expiry.before(new Date());

Edited:

Thanks to @ryanp for the leniency aspect. The above code will now throw a ParseException if the input is not proper.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • To be on the safe side, you'll want to explicitly set the time (eg. 00:00 ... etc) to avoid failing in the extreme cases – TS- Jul 17 '12 at 19:04
  • 5
    The problem with that is that "42701/13" will still validate if you don't use `SimpleDateFormat.setLenient(false)`, and "1/13" will also validate, which isn't what my card looks like! – ryanp Jul 17 '12 at 19:05
  • It perfectly worked . I used the parse function but never uses setLenient(false) attribute so it was never workng – Preethi Jul 17 '12 at 19:48
  • 1
    I have used the same code to check expiry but if we pass any year beyond 34 .e.g 01/35, 01/41 etc, the expired result is true. The parse method change "01/41" to 01/1941. Have you guys experienced this? – amique Apr 15 '15 at 00:18
  • 1
    @amique The default cut over date is 1935, but you can set it to something else that works better for you: See [`SimpleDateFormat#get2DigitYearStart()`](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html#get2DigitYearStart()) and [`SimpleDateFormat#set2DigitYearStart(Date)`](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html#set2DigitYearStart(java.util.Date)) – Bohemian Apr 15 '15 at 03:49
10

Playing devil's advocate...

boolean validateCardExpiryDate(String expiryDate) {
    return expiryDate.matches("(?:0[1-9]|1[0-2])/[0-9]{2}");
}

which translates as:

...so this version requires zero-padded months (01 - 12). Add a ? after the first 0 to prevent this.

Community
  • 1
  • 1
ryanp
  • 4,905
  • 1
  • 30
  • 39
  • where are you checking if the date is in past or future , you just checking if this is valid number year and month – Antroid Mar 01 '19 at 05:01
  • 1
    This is very true! Ah, but what century does 01/19 represent? :) The point is, at some point you're going to have to verify the card details are correct by actually using them against a payment provider. This is enough to verify that the expiry date is in the right *format* - that the customer has likely entered them correctly, that they'll fit in the appropriate database column etc. I would imagine that this would be sufficient for most needs. – ryanp Mar 01 '19 at 09:17
  • For Kotlin you will need expiration.matches("(?:0[1-9]|1[0-2])/[0-9]{2}".toRegex()) – DevinM Jul 16 '20 at 11:49
2

java.time

To validate whether you’ve got a valid expiration date string:

    DateTimeFormatter ccMonthFormatter = DateTimeFormatter.ofPattern("MM/uu");
    String creditCardExpiryDateString = "11/21";
    try {
        YearMonth lastValidMonth = YearMonth.parse(creditCardExpiryDateString, ccMonthFormatter);
    } catch (DateTimeParseException dtpe) {
        System.out.println("Not a valid expiry date: " + creditCardExpiryDateString);
    }

To validate whether it denotes an expired credit card:

        if (YearMonth.now(ZoneId.systemDefault()).isAfter(lastValidMonth)) {
            System.out.println("Credit card has expired");
        }

Think about which time zone you want to use since the new month doesn’t begin at the same moment in all time zones. If you wanted UTC:

        if (YearMonth.now(ZoneOffset.UTC).isAfter(lastValidMonth)) {

If you wanted, for the sake of the example, Europe/Kiev time zone:

        if (YearMonth.now(ZoneId.of("Europe/Kiev")).isAfter(lastValidMonth)) {

Link: Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

Do you really need to use regex? Regex is really only suitable for matching characters, not dates. I think it would be far easier to just use simple date functions.

MRAB
  • 20,356
  • 6
  • 40
  • 33
0

I think that code will be better:

int month = 11;
int year = 2012;

int totalMonth = (year * 12) + month;
totalMonth++; // next month needed
int nextMonth = totalMonth % 12;
int yearOfNextMonth = totalMonth / 12;

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/yyyy");
simpleDateFormat.setLenient(false);
Date expiry = simpleDateFormat.parse(nextMonth + "/" + yearOfNextMonth);
boolean expired = expiry.before(new Date());

You need calculate next month because month displayed on credit card is the last month when card is valid.

  • 1
    In 2019 please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. – Ole V.V. Mar 14 '19 at 12:38