Having issues with a simple birthday system (PHP / MySQL) - php

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.

Related

php 7.4 how to expect date format "ddmmYYYY"

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.

PHP - Find file in Directory based on date /YEAR/MONTH/DAY - But not based on current date - static Datestamp

I have a variable pulled from a SQL DB which is in the "datetime" format of:
2017-02-22 16:24:12
I will be needing to find a file in this directory based on the results of the remaining code, but im not sure how to format the date variables for the directory.
Eg:
/basefolder/2017/02/22/File.extension
I have read lots about using date variables, but they are all based on the current date. My thoughts were that I could just strip the numbers like
/basefolder/"first 4 numbers"/"second 2 numbers"/"last 2 numbers"/
VS trying to do this with any sort of date/time functions. But perhaps there is a date fuction I can/should use.
I do know that the datetime will always be formatted as such, but it feels "hackish" to do it the first way. I could do it in bash in a second, but I'm not so good w/ PHP.
Thanks for any help!
PS: To give context, I will be getting 2 variables passed to this php script, phone number and date. I will then query the CDR table for the Unique ID of this call record based on that information, then I know the file will be in:
/monitor/2017/02/22/*uniqueID*
Which I will then pass back to the original script to have it download the file.
You can use the function strtotime to convert your date string to a timestamp, and then use the date function to create the date formatted as you like, and you can pass the timestamp as the second argument (it only uses current time if you pass only the format)
docs for strtotime: http://php.net/manual/en/function.strtotime.php
<?php
$dateString = '2017-02-22 16:24:12';
$timestamp = strtotime($dateString);
$datepath = date('Y/m/d',$timestamp);
$path = "/basefolder/$datepath/";
var_dump($path);
This outputs: string(20) "/basefolder/2017/02/22/"
This code could be shortened to:
$path = "/basefolder/".date('Y/m/d/',strtotime('2017-02-22 16:24:12'));
It sounds like you already found the date function but there is more info on it here: http://php.net/manual/en/function.date.php
Note that you have to do it in two stages, as if you include the /basefolder/ part in the format passed to date, then it will replace individual characters with various formats of the date - e.g.:
$datepath = date('/basefolder/Y/m/d',$timestamp);
Sets $datepath to something like bpm12US/Pacificf2017Wednesday22US/PacificWed, 22 Feb 2017 16:24:12 -0800/2017/02/22
b is left alone because it means nothing, but then a is replaced with pm because of the time, s is replaced with the seconds value, e is replaced with the time zone and so on.
date_parse_from_format() will convert from string to broken down time array:
<?php
$d = date_parse_from_format("Y-m-d H:i:s", "2017-02-22 16:24:12");
$ds = sprintf("/base/%d/%02d/%d", $d['year'], $d['month'], $d['day']);
echo $ds;
This is my final code snippit!
<?php
$dateString = '2017-02-22 16:24:12';
$base = "/monitor/";
$path = "$base".date('Y/m/d/',strtotime("$dateString"));
echo $path;
?>

Create empty date for specific format in PHP

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.

Format datetime from input string

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.

How can I work out if a date is on or before today?

My web application consists of library type system where books have due dates.
I have the current date displayed on my page, simply by using this:
date_default_timezone_set('Europe/London');
$date = date;
print $date("d/m/Y");
I have set 'date' as a variable because I'm not sure if it makes a difference when I use it in the IF statement you're about see, on my library books page.
On this page, I am simply outputting the due dates of the books, many have dates which have not yet reached todays date, and others which have dates greater than todays date.
Basically, all I want is the due date to appear bold (or strong), if it has passed todays date (the system displayed date). This is what I have and thought would work:
<?
if ($duedate < $date) {
echo '<td><strong>';
} else {
echo '<td>';
} ?>
<?php echo $date('d/m/Y', $timestamp);?></strong></td>
I have declared $timestamp as a var which converts the date of default MySQL format to a UK version. Can anyone help me out? I thought this would've been very straight forward!
try:
if (strtotime($duedate) < time()) {
// oooh, your book is late!
}
Instead of working with the formatted dates, work with their timestamps. Either convert them back with strtotime() or use time() instead of date. Timestamps can be compared like regular numbers, because that's what they just are.
Okay :) Let's start here:
$date = date; // Wrong!
print $date("d/m/Y");
The above only works because PHP thinks date is a constant. But since you didnt set this constant PHP will convert it to the string 'date'. So $date contains 'date'. Then, when calling $date() as a function, PHP evaluates $date's content, which is 'date' and uses that as the function name, e.g. date(). What you really wanted to do was just $date = date('d/m/y').
Here is how date works:
string date ( string $format [, int $timestamp ] )
First argument is the desired output format, the second argument is an optional timestamp for which the output will be generated. If omitted it will be now. The function returns the output as string.
I assume your $duedates are already formatted strings, e.g. 2010-04-06. So when you do $duedate < $date, you are really doing a string comparison, because both variables hold formatted strings, but not timestamps.
Timestamps on the other hand are just numbers. A timestamp is the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT). You can get the timestamp for the current date and time with the function time() and you can convert strings that represent dates with strtotime(). So when you want to compare your dates, do
if ( strtotime($duedate) < time() ) { // ... do something
And that's really all there is to it.

Categories