1

I am trying to validate birthdays using the following regex:

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

This regex works when tested in an online regex tester, but when I try to use this regex inside my firebase rules, Firebase seems to not accept it. I also tried doubling my backslashes and still no luck.

This is my firebase rule:

".validate": "newData.isString() && newData.val().matches(/^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/)"

This is the error I get on Firebase: "Illegal regular expression, unescaped ^, ^ can only appear at the end of regular expressions"

How can I tweak this regex to get it working on Firebase?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Ryan Dailey
  • 286
  • 1
  • 6
  • 13
  • I tried doubling the backslashes and still no luck. – Ryan Dailey May 28 '18 at 18:23
  • I'm still getting the exact same error :( – Ryan Dailey May 28 '18 at 18:35
  • Sorry, I forgot to remove one `^`. See https://regex101.com/r/3pJKH8/2. The point is that you cannot repeat `^` and `$` inside the pattern, `^` and `$` are only allowed once at the beginning (`^`) and at the end (`$`) – Wiktor Stribiżew May 28 '18 at 18:52
  • This expression gets accepted by firebase with no errors, but when I try to pass in a birthday, the regex rejects it. – Ryan Dailey May 28 '18 at 19:07
  • It should work, see [this demo](https://regex101.com/r/3pJKH8/3). Do you pass it as a string? – Wiktor Stribiżew May 28 '18 at 19:09
  • Yeah I do pass it as a string. Is it possible that Firebase has trouble reading expressions that have a "?:" inside of it? The reason I bring this up, is that originally my username regex validation had a ?: inside of it, which was causing the exact same problem (always reject the string). I had to use another expression that didn't have ?: in order to fix that problem. – Ryan Dailey May 28 '18 at 19:16
  • That is what I have been checking now at https://firebase.google.com/docs/reference/security/database/regex. Looks like no non-capturing groups are allowed in Firebase regex. So, replace `(?:` with `(`. It will require the backreference re-adjusting. – Wiktor Stribiżew May 28 '18 at 19:18
  • Does this require me adding a ? at the end of the bracket? Can you show me how it should look like? – Ryan Dailey May 28 '18 at 19:25
  • See [this regex version #4](https://regex101.com/r/3pJKH8/4). – Wiktor Stribiżew May 28 '18 at 19:27
  • I tried v4 and it still rejects the birthday I pass in. This is what I try to pass in: "01/04/1998" – Ryan Dailey May 28 '18 at 19:32
  • Try replacing `\\15` with `[-\\/.]`, does it work then? If not, use [this one](https://regex101.com/r/3pJKH8/5) since it means backreferences are not supported either. It will also match `04/04-1989` though, as it is not possible to match the same delimiter (well, it is possible, but you will have to write a much longer regex). – Wiktor Stribiżew May 28 '18 at 19:43
  • Thanks so much, replacing the //15 did the trick! Please update your answer so I can accept it :) – Ryan Dailey May 28 '18 at 19:49

1 Answers1

1

You need to do two things here:

  • make sure all backslashes are doubled
  • turn all non-capturing groups into capturing ones and re-adjust the backreferences (note that redundant capturing groups should be eliminated) (note you can't use \15 as backreference, it seems only 1 to 9 backreferences are supported)
  • re-vamp the pattern so that the ^ start of string anchor appeared at the beginning and $ only at the end of the regular expression (otherwise, you will get the illegal regex exception). It is easy to do here as your pattern is of the ^a1$|^a2$|^a3$ type, and it is equal to ^(?:a1|a2|a3)$.

The pattern should look like

newData.val().matches(/^((31([-\\/.])(0?[13578]|1[02])\\3|(29|30)([-\\/.])(0?[13-9]|1[0-2])\\6)(1[6-9]|[2-9]\\d)?\\d{2}|29([-\\/.])0?2\\9((1[6-9]|[2-9]\\d)?(0[48]|[2468][048]|[13579][26])|(16|[2468][048]|[3579][26])00)|(0?[1-9]|1\\d|2[0-8])([-\\/.])(0?[1-9]|1[0-2])[-\\/.](1[6-9]|[2-9]\\d)?\\d{2})$/)

Note that I also turned (\/|-|\.) into ([-\/.]) (since a character class is more efficient than plain alternation with single-char alternatives) and remove a comma from [1,3-9] - it looks like a typo, you wanted to match 1 or a digit from 3 to 9, I believe, and a , is treated literally in a character class.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • @RyanDailey I updated with a bit more precise version since you probably do not want to match `14/02-1954`, only `14/02/1954` or `14-02-1954`. – Wiktor Stribiżew May 28 '18 at 20:25
  • Thanks for the update! I tried using the updated regex and it is giving me an error, same error as I was getting before. – Ryan Dailey May 30 '18 at 18:55
  • @RyanDailey I have only split one alternative into 3. It is the only way to make your original regex work as required. – Wiktor Stribiżew May 30 '18 at 19:40
  • Is there a way to make this work without getting any errors in my Firebase console? As of now, the new regex answer you have submitted does not work. – Ryan Dailey May 30 '18 at 21:40
  • @RyanDailey Ok, I rolled back to the previous version. I have no access to a Firebase console, so I cannot enhance it more. Still, [this more precise pattern](https://regex101.com/r/3pJKH8/8) looks correct taking into account all the restrictions we have come across. – Wiktor Stribiżew May 31 '18 at 19:10