How to validate dates without leading zero on day and month? - php

I need to validate a precise date format in php.
In my case the optimum date format should be: e.g. 1/1/2017 (without leading zeros) and the code should allow me to avoid the rest of date's format.
The following code is what i wrote but with no result:
if(preg_match("/([1-9]|[1-2][0-9]|3[0-1]-([1-9]|1[0-2])-^[0-9]{4})$/", $date){
// insert into etc. etc....
}else{
// update etc. etc....
}
The problem is that the code doesn't properly validate the date; it accepts every kind of date format.

I noticed that the OP's submitted answer was not bulletproof, so I started to research some of the functions mentioned in the comments under the question and some of my own thoughts.
I agree with apokryfos in that using regex to validate a date expression is not best practice. A pure regex solution is going to be an increasingly verbose and decreasingly comprehensible pattern because it will have to factor leap years both in the past and future. For this reason, I've investigated a few date functions that php has on offer.
date_parse_from_format() as suggested by Alex Howansky. Unfortunately, it will not be a stand-alone solution for this case because the format parameter obeys the syntax/rules of DateTime::createFromFormat():
See how d and j are grouped together, as are m and n. Consequently, this function will catch invalid dates, but not the unwanted leading zeros.
strptime() as suggested by Casimir et Hippolyte. This function affords matching days without leading zeros using %e. This function does not have a character that matches non-zero-leading month numbers. Furthermore, there can be hiccups with this function bases on the operating system.
check_date() seems like a logical choice, but again it will not flinch at zero-led numbers. Furthermore, this function requires a bit more data preparation than the others, because it requires the month, day, and year values to be individualized.
if(!DateTime::createFromFormat('j/n/Y', $date)) suggested by deceze regarding a slightly different scenario will not work in this case. createFromFormat() will go to great lengths to construct a valid date -- even a string like 99/99/9999 will make it through.
In an attempt to create the most concise expression, here is what I can offer as a seemingly bulletproof solution using a DateTime class:
if (($d=DateTime::createFromFormat('n/j/Y',$date)) && $date==$d->format('n/j/Y')){
// valid
}else{
// invalid
}
strtotime() can be used with a single condition for this case because the OP's date format uses slashes as a delimiter and this function will correctly parse the date expression "American-style".
This seems to be the simplest way to check for a valid date without leading zeros: Demo Link
if(date('n/j/Y',strtotime($date))==$date){
// valid
}else{
// invalid
}
If you are dealing with datetime expressions and don't wish to check the time portion, you can call this line before conditional line:
$date = explode(' ', $date)[0]; // create an array of two elements: date and time, then only access date
Demo Link
or
$date = strstr($date, ' ', true); // extract substring prior to first space

you can use with Carbon
$date->format('j')

Your date delimiter is / and not -, so add \/ to regex for /. And use ^ at the start of regex:
if(preg_match("/^([1-9]|[1-2][0-9]|3[0-1])\/([1-9]|1[0-2])\/([0-9]{4})$/", $date){

After several hours finally i found the correct answer. The correct pattern for php is to use single quote (') and not the double quote (") e.g.:
preg_match('/^([1-9]|[1-2][0-9]|3[0-1])\/([1-9]|1[0-2])\/([0-9]{4})/',$date)
this works fine for me... Thanks everyone especially to #Mohammad Hamedani 'cause the expression was correct but the f******g quote makes me gone crazy

Related

optional character in Regular Expression

I've found this REGEXP:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$
It's useful to validate a date in this format:
yyyy-mm-dd
with "-" as separator. It's perfect for me except that I'd like to make this:
1970-1-1
valid too, so I'm looking for a modification to have the zero optional in month and day block as first char. Could you please provide me some help?
If you want to have an optional zero, you can this RegEx (edited version of your own):
^[0-9]{4}-((?:0)?[1-9]|1[0-2])-((?:0)?[1-9]|[1-2][0-9]|3[0-1])$
To make a character ( or a range) optional in RegEx, put it in a parenthesis followed by a question mark:
[a-z] is mandatory
([a-z])? is optional, with capturing group
(?:[a-z])? without capturing group, just optional
Read more on Capturing group in RegEx
You could use something like so: ^[0-9]{4}-(([1-9])|(0[1-9]|1[0-2]))-(([1-9])|(0[1-9]|[1-2][0-9]|3[0-1]))$.
An example is available here.
Use your current regex just make the 0 optional 0?
^[0-9]{4}-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|3[0-1])$
DEMO
PHP already has a construct to extract dates; if you're using a regex to extract the date instead, "Now you have two problems"
PHP's DateTime::createFromFormat: "Returns new DateTime object formatted according to the specified format"
You can use it's d or j option to accept 1 or 2 digit days and it's m or n option to accept 1 or 2 digit months.
The format you'll want to use is: "Y-m-d"

Extract date from string in format "DD MMM YYYY"

I am trying to extract a date from a string variable and was hoping to get some help.
$editdate = "Content last modified on 17 May 2011 at 23:13";
from this string, I am trying to extract 17 May 2011, please keep in mind that the date will vary and the code needs to be able to extract any date in this format, DD MMM YYYY.
I thought of using preg_match to do this but I couldn't come up with a proper regex pattern that would extract the date properly.
Is this possible to do with regex or should I use a different function?
Thanks for the help !
Try:
$timestamp = strtotime( str_replace( array("Content last modified ", "at"), "", $editdate ) );
Which will leave you with an epoch time stamp that you can then output however you like using date()
This is possible with a regex. Given the format DD MMM YYYY you would need a regex that matches two (or one?) digits, then one space, three letters, one space and four digits.
That would look like:
$regex = '/(\d{2} [a-z]{3} \d{4})/i';
This can be optimized further.
Presuming the textual content of your string is always the same, and that it always ends with the time...
$editdate = substr($editdate, 25, -9); // 17 May 2011
However, this is very inflexible if the date format were ever to change.
Try this 'un:
preg_match('/(\d?\d [A-Za-z]+ \d\d\d\d) at (\d\d\:\d\d)/', $editdate, $matches);
print_r($matches);
$date = $matches[1];
$time = $matches[2];
I THINK that'll work in all cases (though it is pretty ugly).... :)
This might be the pattern that does the trick:
([0-9]){1}([0-9]){0,1}(\s.*\s)([0-9]){4}
Search for 1 digit then there might be another, followed by a space and character, a space and 4 digits for the year.

Any Elegant Ideas on how to parse this Dataset?

I'm using PHP 5.3 to receive a Dataset from a web service call that brings back information on one or many transactions. Each transaction's return values are delimited by a pipe (|), and beginning/ending of a transaction is delimited by a space.
2109695|49658|25446|4|NSF|2010-11-24 13:34:00Z 2110314|45276|26311|4|NSF|2010-11-24 13:34:00Z 2110311|52117|26308|4|NSF|2010-11-24 13:34:00Z (etc)
Doing a simple split on space doesn't work because of the space in the datetime stamp. I know regex well enough to know that there are always different ways to break this down, so I thought getting a few expert opinions would help me come up with the most airtight regex.
If each timestamp is going to have a Z at the end you can use positive lookbehind assertion to split on space only if it's preceded by a Z as:
$transaction = preg_split('/(?<=Z) /',$input);
Once you get the transactions, you can split them on | to get the individual parts.
Codepad link
Note that if your data has a Z followed a space anywhere else other than the timestamp, the above logic will fail. To overcome than you can split on space only if it's preceded by a timestamp pattern as:
$transaction = preg_split('/(?<=\d\d:\d\d:\d\dZ) /',$input);
As others have said, if you know for sure that there will be no Z characters anywhere other than in the date, you could just do:
$records = explode('Z', $data);
But if you have them elsewhere, you'll need to do something a bit fancier.
$regex = '#(?<=\d{2}:\d{2}:\d{2}Z)\s#i';
$records = preg_split($regex, $data, -1, PREG_SPLIT_NO_EMPTY);
Basically, that record looks for the time portion (00:00:00) followed by a Z. Then it splits on the following white-space character...
Each timestamp is going to have a Z at the end so explode it by 'Z '. You don't need a regular expression. There's no chance that the date has a Z after it only the time.
example
Use explode('|', $data) function

PHP: Time validation RegEx?

I am using following regex to validate date in format dd/mm/yyyy in php:
preg_match("/([0-9]{2})\/([0-9]{2})\/([0-9]{4})/", $e_startdate, $matches)
But what will be the regex to validate time in the format 10:20 PM or AM. I want a regex which will validate following format for me.
<number><number><colon><number><number><space><AM or PM>
Thanks
You can use the regex:
(0[0-9]|1[0-2]):[0-5][0-9] [AP]M
The following should validate the format you requested:
preg_match("/(\d{2}):(\d{2}) (AM|PM)/", $e_startdate, $matches);
Note that this is not necessarily a valid time. You could enter stuff like 32:62 AM. You need something like this for time validation:
preg_match("/(0?\d|1[0-2]):(0\d|[0-5]\d) (AM|PM)/i", $e_startdate, $matches);
Mind to match the whole thing case-insensitive (like i did in the second example). Otherwise lowercase am, pm, etc. are not going to work.
Regular Expression:
(([0-1][0-9])|([2][0-3])):([0-5][0-9]):([0-5][0-9])
Validates time in MySQL time format. 24 hour time colon separated hours:minutes:seconds hh:mm:ss
Matches: 09:30:00 | 17:45:20 | 23:59:59
Non-Matches: 24:00:00
/\d{2}:\d{2} (AM|PM)/
I started with elusive's answer, but needed a bit more flexibility. This one makes the minutes optional, as well as the space before AM/PM
/(0?\d|1[0-2])(:(0\d|[0-5]\d))?[ ]?(A|P)M/i

Regular Expression to match dates in YYYY-MM-DD format

I have a regular expression in PHP that looks for the date in the format of YYYY-MM-DD
What I have is: [\d]{4}-[\d]{2}-[\d]{2}
I'm using preg_match to test the date, the problem is that 2009-11-10 works, but 2009-11-1033434 works as well. It's been awhile since I've done regex, how do I ensure that it stops at the correct spot? I've tried doing /([\d]{4}-[\d]{2}-[\d]{2}){1}/, but it returns the same result.
Any help would be greatly appreciated.
What you need is anchors, specifically ^ and $. The former matches the beginning of the string, the latter matches the end.
The other point I would make is the [] are unnecessary. \d retains its meaning outside of character ranges.
So your regex should look like this: /^\d{4}-\d{2}-\d{2}$/.
^20[0-2][0-9]-((0[1-9])|(1[0-2]))-([0-2][1-9]|3[0-1])$
I added a little extra check to help with the issue of MM and DD getting mixed up by the user. This doesn't catch all date mixups, but does keeps the YYYY part between 2000 and 2029, the MM between 01 and 12 and the DD between 01 and 31
How do you expect your date to be terminated ?
If an end-of-line, then a following $ should do the trick.
If by a non-digit character, then a following negative assertion (?!\d) will similarly work.
you're probably wanting to put anchors on the expression.
i.e.
^[\d]{4}-[\d]{2}-[\d]{2}$
note the caret and dollar sign.
You probably want look ahead assertions (assuming your engine supports them, php/preg/pcre does)
Look ahead assertions (or positive assertions) allow you to say "and it should be followed by X, but X shouldn't be a part of the match). Try the following syntax
\d{4}-\d{2}-\d{2}(?=[^0-9])
The assertion is this part
(?=[^0-9])
It's saying "after my regex, the next character can't be a number"
If that doesn't get you what you want/need, post an example of your input and your PHP code that's not working. Those two items can he hugely useful in debugging these kinds of problems.
[\d]{4}-[\d]{2}-[\d]{2}?
where the question mark means "non-greedy"
You could try putting both a '^' and a '$' symbol at the start and end of your expression:
/^[\d]{4}-[\d]{2}-[\d]{2}$/
which match the start and the end of the string respectively.

Categories