1

This regular expression validates timestamps e.g. 2018-02-12 00:55:22:

[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]

However, the timestamp should be validated step by step:

201 => true
201a => false
2018- => true
20189 => false

Is there a nice (short) regex extension?

......

Thomas Müller
  • 420
  • 4
  • 15
  • What do you mean by `step by step`? Regexes only match fully or don't match at all – rollstuhlfahrer Mar 03 '18 at 19:52
  • Possible duplicate of https://stackoverflow.com/questions/2524680/check-whether-the-string-is-a-unix-timestamp It should help you – nitrex Mar 03 '18 at 19:54
  • PHP is not a realtime language. You are not able to do this with JS too (however there is a [workaround here](https://stackoverflow.com/a/41580048/1020526)) What you are doing is called partial matching. – revo Mar 03 '18 at 20:01
  • Ajax :) User types 2 => true, User types 2a => false. And you can use html input pattern. – Thomas Müller Mar 03 '18 at 21:46

3 Answers3

1

Because your question has the javascript tag I am going to assume you are doing "step-by-step" validation like "onkeyup" or similar. The following pattern will validate your datetime string as it is being constructed (I'm including an empty string as valid so that no flag is triggered when empty; but you could change to \d{1,4} if you want to act on empty strings).

I am using \d whenever possible to reduce pattern length.

The x pattern modifier is in play with my dumped pattern, for easier reading. When you apply this to your project, you can compact it all and remove the x flag.

I am using non-capturing groups out of habit; since you are probably only matching, you can use capturing groups if you like.

Pattern Demo

Pattern:

~
^
(?:
\d{0,4}|
\d{4}-|
\d{4}-[01]|
\d{4}-(?:0[1-9]|1[0-2])|
\d{4}-(?:0[1-9]|1[0-2])-|
\d{4}-(?:0[1-9]|1[0-2])-[0-3]|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s[0-2]|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d)|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):[0-5]|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):[0-5]\d|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):[0-5]\d:|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):[0-5]\d:[0-5]|
\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[0-1])\s(?:2[0-3]|[01]\d):[0-5]\d:[0-5]\d
)
$
~
x
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • Thank you! I use the HTML pattern attribute. "If specified, the attribute's value must match the JavaScript Pattern production." https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute – Thomas Müller Mar 08 '18 at 20:58
0

You can combine it to get 2 overall information blocks.

Incrementally match the form delimiters - - : :
while allowing/matching bad segments.

In the end you get info on the form progress.
And also the form segments.

You test the form's progress via capture groups 2,4,6,8,10

You test the date/time elements via groups 1,3,5,7,9,11

Though, you only need to test the elements up to the maximum group in the form
progress.

^(?:(?:([0-9]{4})|\d*)(-(?:(0[1-9]|1[0-2])|\d*)(-(?:(0[1-9]|[1-2][0-9]|3[0-1])|\d*)([ ]+(?:(2[0-3]|[01][0-9])|\d*)(:(?:([0-5][0-9])|\d*)(:(?:([0-5][0-9])|\d*))?)?)?)?)?)$

Formatted

 ^    
 (?:
      (?:
           ( [0-9]{4} )                          # (1)
        |  \d* 
      )
      (                                     # (2 start)
           -
           (?:
                ( 0 [1-9] | 1 [0-2] )                 # (3)
             |  \d* 
           )
           (                                     # (4 start)
                -
                (?:
                     ( 0 [1-9] | [1-2] [0-9] | 3 [0-1] )   # (5)
                  |  \d* 
                )
                (                                     # (6 start)
                     [ ]+ 
                     (?:
                          ( 2 [0-3] | [01] [0-9] )              # (7)
                       |  \d* 
                     )
                     (                                     # (8 start)
                          :
                          (?:
                               ( [0-5] [0-9] )                       # (9)
                            |  \d* 
                          )
                          (                                     # (10 start)
                               :
                               (?:
                                    ( [0-5] [0-9] )                       # (11)
                                 |  \d* 
                               )
                          )?                                    # (10 end)
                     )?                                    # (8 end)
                )?                                    # (6 end)
           )?                                    # (4 end)
      )?                                    # (2 end)
 )
 $

segments via if the capture groups matched.

0

Regex is not the way to do this.

Heres a simple function. You use a good date in the correct format, strip off the number of characters from the front that have been entered and combine it with the vale entered, then check if its valid

function validateDate($date)
{
    $fakedate = "2018-02-12 00:55:22";
    $date .= substr($fakedate, strlen($date));
    $format = 'Y-m-d H:i:s';
    $d = DateTime::createFromFormat($format, $date);
    return $d && $d->format($format) == $date;
}

var_dump(validateDate('201')); bool(true)
var_dump(validateDate('201a')); bool(false)
var_dump(validateDate('2018-')); bool(true)
var_dump(validateDate('20189')); bool(false)
miknik
  • 5,748
  • 1
  • 10
  • 26