I need to force the users to enter a date like this "ddmmYYYY" e.g : 14012022
The problem I'm facing is that I can't force this format just using this :
DateTime::createFromFormat('dmY', '14012022');
For example, DateTime::createFromFormat('dmY', '212022') for "02012022", it will not return false, although the date will still be wrong at the end.
Is there a way to force the user to pass the exact format that I expect, which is "ddmmYYYY" ?
I need 2 digits for the day, 2 digits for the month, and 4 digits for the year imperatively.
Thanks
How about an addition check by converting the date back to string?
$inputDate = '212022';
$date = DateTime::createFromFormat('dmY', $inputDate);
$valid = $date && $date->format('dmY') == $inputDate;
Since the manual says,
Letters that are used for parsing numbers allow a wide range of values, outside of what the logical range would be.
So string like '32132022' is valid input for createFromFormat, the above method will help you to avoid such date string.
Related
I'm toying with the idea with what happens when conflicting date systems are present in a database and when it happens it causes chaos in PHP. Case in point, if you have part of the database with dates that contains 01/01/2016 but at some other point it contains 1/1/2016, then the date system seems to break. In a proper environment I'd imagine it should be epoch but in this case it isn't.
The following code is messy and maybe I'm over thinking it. But this is what I have:
/*
*
* Dates ($dob) can appear as followed:
* 01-30-2016 | 1-30-2016 | 01-01-2016 | 01/30/2016 or any combination
*
*/
$chpos = 0; // Define Character Position Variable
$replace = false; // Should we replace the 0 after the first dash?
// If the date uses this format: 01/02/2016 then replace the / with - so it looks like 01-02-2016
$dob = preg_replace('/\s+/', '-', $dob);
// Let's find out if the dash occurs on the 3rd character or 4th. We do this so we can replace the 0 in 2-02-2016
// 01-34-6789 *String Array positions* 0-23-5678
if(substr($dob, 0, 3) == 0 && substr($dob, 0, 0) == 0){
$chpos = 3;
$replace == true;
} else if (substr($dob, 0, 0) != 0 && substr($dob, 0, 2) == 0){
$chpos = 2;
$replace == true;
} else {
$replace == false;
}
// Let's replace the 0 after the first dash if necessary
if($replace == true){
$dob = substr_replace($dob, '', $chpos);
}
// Let's replace the 0 from the beginning if necessary
if(substr($dob, 0, 1 ) == 0){
$dob = substr( $dob, 1 );
}
// Let's convert it to a usable date object
$birthday = new DateTime($dob);
// Now let's compare the time from now to when the birthdate happened
$interval = $birthday->diff(new DateTime);
// Return the data over
return $interval->y;
The issue with the code has to do with when it replaces the 0 on the left of things. I could swear the code should work but maybe I made a typo and just can't see it? I don't know, but it's not working at all. The message is:
Uncaught exception 'Exception' with message 'DateTime::__construct(): Failed to parse time string (2-17-1994) at position 0 (2): Unexpected character'
The line in reference is:
$birthday = new DateTime($dob);
My question here is:
Why does the date system break when it has leading zeros?
Why is it so complex to just parse the date?
Am I missing something or is it supposed to be this difficult?
Am I over-thinking this?
Thank you for your time!
Ignoring the issues of data sanitization (the dob should never have made it into the database in different formats; a unix timestamp would have been a better option)... Why not just do the following;
$timedob = strtotime($dob);
$birthday = date('Y-m-d',$timedob);
strtotime takes any string and converts it into a unix timestamp. The date function then converts that unix timestamp into the output format you want.
Note that strtotime interprets the input as follows; if the Month, day, year are separated by a slash '/' - it assumes that the date is in American m/d/y format. If it sees hyphens or periods (- or .), it assumes that the date is in European d-m-y format.
It is very easy to parse a date string in php, which goes to extraordinary lengths to best interpret your strings using functions like strtottime.
http://php.net/manual/en/function.strtotime.php
The problem you are having is that you should store dates in the database as the database date or datetime formats (or whatever equivalent there is for your database). Validation should be done before trying to enter them into the database, as well as testing that any database writes are not rejected for badly formatted date structures. There also should be further validation when converting to a date for output.
It has been a very long time since I have had to manipulate date strings manually like you are doing here, and there certainly is no need to do so. Especially since date pickers are so commonplace that will also format dates browser side via js before sending. (This does not remove the need for server side validation of course.)
If I were you, I would write a short script to convert all your dates to a proper date format for your database, and change the format of that database column to a date format. Then address anywhere that is trying to write strings as dates into the database in your application. In a well structured application this should only be being done in one model function, so should not take too long.
A final benefit of doing this properly, is you can now easily produce reports or query results such as every event that happened before so and so date or between two dates, or on a tuesday, etc.
I'm facing an issue with managinging dates, some dates pass others dont. I want to produce an insertable date for mysql. there are two possible types of post dates
yyyy-mm-dd //should go without conversion
m/d/yyyy // should be converted
I'm using this
$date = $_REQUEST['date'];
$date_regex = '/(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/';
if(preg_match($date_regex, $date)){
$date = DateTime::createFromFormat('m/d/Y',$date)->format('Y-m-d');}
problems
I realised this regex is failing for dates like
2/5/2013
but has been working for
12/12/2013
so I removed it BUT still
DateTime::createFromFormat('m/d/Y',$date)->format('Y-m-d');
is also failing for m/d/yyyy
This date thing has got my head spinning for the last 6 hours.
In this case, there is no need to use DateTime::createFromFormat because m/d/yyyy is one of the recognized date formats (see "American month, day and year"). Just convert it to a DateTime object and let the constructor handle the format and forget the regex:
$date = $_REQUEST['date'];
$datetime = new DateTime($date);
$datex = $datetime->format('Y-m-d');
The reason DateTime::createFromFormat('m/d/Y',$date) fails for dates like 2/5/2013 is because you are forcing it to be specifically 'm/d/Y' and that date does not fit that pattern. You can see a list of all date formats here. Specifically, m expects there to be a leading zero (like 02), so when you give it one without that, it won't recognize it. Same goes for d. In this case you would have to use n and j respectively. But, like I said, let the constructor do the hard work for you.
I would like to get date string filled with zeros for specified date format. I want to use it in case when there is no date given by external API.
Example:
define('DATE_FORMAT', 'Y-m-d H:i:s');
[...]
$date = SomeExternalApi::get_date();
// if date is given it's simple
if(!empty($date)){
$date_obj = DateTime::createFromFormat(SomeExternalApi::SOME_DATE_FORMAT, $date_string);
return $date_obj->format(DATE_FORMAT);
}else{
// SO UGLY BELOW! :( How to use DATE_FORMAT in this case?
return '0000-00-00 00:00:00';
}
I don't want to hardcode "zero string" like in example, because when I change DATE_FORMAT I would like to get zeros formatted new way.
Probably there is no simple way to format "zero string" of date, but maybe someone from this great community has better idea? :)
The best answer for my question is: DON'T GO THIS WAY.
You will never need empty date with zeros - it's super uncommon. The only one place where you can find it is database date field - if there is no date, you will get zeros format. But this will be always the same format, so you can define it as constant and condition as string.
Zeros as I asked are BAD because if you send such zeros through some API to any client like mobile app it has no chance to parse it - it will crash the app without catching errors.
Instead of having zeros date presentation use:
[when saving] put empty string in database, you can set date field to empty string and this will become zeros, do it with UPDATE mytable SET date = "" WHERE id = 2; and mytable.date field is MySQL timestamp in this example,
[when getting] you will get zeros with SELECT date FROM mytable WHERE id = 2;, so define it in your code as constant string 0000-00-00 00:00:00 and equal with returning value from db,
[when response of API] if date is empty return false instead of zeros. Mobile app can check if it's false and try to parse it in any other case. It's the best way to keep empty date and is super simple to check in any format by any language. Lot of simpler than trying to parse some strange zeros format.
Generally if I find date by condition like in example I return false and every single client using this API is happy with it:
if(empty($date)){return false;}
Dates and times in PHP are represented with a UNIX timestamp. The range of possible dates this can represent is not capable of representing the timestamp '0000-00-00 00:00:00' on a 32-bit system (it's over 2000 years ago). Also, actually creating a DateTime representing this single timestamp is probably pretty pointless, the values it represents are unchanging.
However, you could do this by defining absolute values for every sensible format character and using strtr() to convert them to their specific values.
Short example using your example input format:
$placeholders = array(
'Y' => '0000',
'm' => '00',
'd' => '00',
'H' => '00',
'i' => '00',
's' => '00'
);
return strtr(DATE_FORMAT, $placeholders);
However, this could start to get messy when you are dealing with the format elements that represent textual elements such as D. Personally I'd probably pick a default format and use that, as your code in the question shows.
I am amazed. I forgot to include strtotime but realized that it works in any case. Why does this work?
if($_POST['active_to'] == '' || $_POST['active_to'] >= '2038-01-19'){
$postproduct->active_to = '2025-07-31';
}else{
$postproduct->active_to = $_POST['active_to'];
}
Because YYYY-MM-DD format happens to sort in the same order when using string comparison.
If you were using, say, DD-MM-YYYY format, it wouldn't have worked. Similarly, if you were using YYYY-M-D format (where a leading zero isn't required), it also wouldn't work (because "2010-5-10" comes after "2010-12-10" in string ordering).
"2010-10-05" is greater than "2000-10-05" not because 2010 > 2000, but because "20" = "20" and "1" > "0". The first character that differs between the two strings happens to sort in the right order.
Because the string will be compared lexically character by character.
is 2 larger than 2?
if no, is 0 larger than 0?
if no, ...
You can directly compare the dates only for greater then less then or equal as it is a string.
As long as you have date in YYYY-MM-DD format only. Because YYYY-MM-DD will always change in increment format.
Please keep in mind about the format. Otherwise you have to use strtotime function.
You compare strings. In this operation, strings are converted to the numbers. If you have format Y-m-d, it works well because first is year which is the biggest value (Y*365 days) then month, and then days.
Result can be invalid in some cases because of not equal values of days in month, etc.
Ref: http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.types
IT works because you are doing a string comparison which will work in an or condition, so long as that condition is true.
I'm doing a date search filter where I have my date displayed as "j.n.Y G:i (26.6.2012 15:22)".
A user can enter the whole date or only a portion of it: "26.6","6.2012","6","15:22" are all valid inputs. Because I need to check this date in the database the format needs to be changed to the one of the database. For that I use:
$datum = '25.6.2012';
$date = DateTime::createFromFormat('j.n.Y',$datum);
echo $date->format('Y-m-d H:i');
Where I get an error if $datum is not in the format j.n.Y (if I only enter j.n or one of the above mentioned string portions i get an error).
A problem is also, for the entered string 'j.n.Y', i get the right output of the date, which also has the current time added to the date string (which was not in the initial date string). Example: I enter "22.6.2012", then I get the output "2012-06-22 15:33".
Can these two problems get fixed with existing php functions or should I make my own?
Help would be greatly appreciated.
You can list your acceptable data formats in an array, and loop around DateTime::createFromFormat() to see if any of the inputs produce an acceptable date:
$formats = array( 'j.n', 'j.n.Y');
$datum = '25.6.2012'; $date = false;
foreach( $formats as $format) {
$date = DateTime::createFromFormat( $format, $datum);
if( !($date === false)) break;
}
if( $date === false) {
echo "Invalid date!\n";
}
Finally, if you want to get rid of the current time in the newly created object and set the time to 00:00:00, just use the setTime() method on the date object:
// Sets the time to O hours, 0 minutes, 0 seconds
$date->setTime( 0, 0, 0);
For the first problem, you will need to write some code of your own because some of your acceptable inputs are not among the recognized input formats. Normalizing the input value will require you to fully parse it (a regular expression is a good way to start), and then you can call DateTime::createFromFormat without trouble.
For the second problem, putting an exclamation mark ! at the beginning of your format string would fix the time issue. From the documentation:
If format contains the character !, then portions of the generated
time not provided in format, as well as values to the left-hand side
of the !, will be set to corresponding values from the Unix epoch.
The Unix epoch is 1970-01-01 00:00:00 UTC.
However, since you are going to need to fully parse the input as mentioned above the matter is moot. Also note that the exclamation mark would cause missing values for year, month and day to use defaults that are probably undesirable.