Format datetime from input string - php

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.

Related

STRTOTIME in php returning blank value

The dataset value is returning blank, no error on logfile.
$edate = trim($_POST['txtedate']); //user inputs date 12-01-2021
$int_effective_date = new DateTime(strtotime($edate));
echo "edate:- ".$edate."<br />";
echo "strtotime_edate:- ".strtotime($edate)."<br />";
echo "dateset:- ".strtotime($int_effective_date->format('Y/m/d'));
Result:
edate:- 2021-01-12
strtotime_edate:- 1610389800
dateset:-
To paraphrase #iainn: I'm not 100% sure why you're changing back and forth between DateTime objects and function calls to strtotime?
However, I can explain the most likely issue with your code...
strtotime
Firstly, let's clarify that 12-01-2021 is in the format (d-m-Y)? Hopefully it is, in which case PHPs strtotime function understands it correctly and produces a Unix timestamp (i.e. seconds passed since start of 1970)...
strtotime("12-01-2021");
// Output: 1610409600
// Notes:
// - Possible slight variations based on locale etc.
// - Lookup: date_default_timezone_set
// - This is with "UTC"
DateTime
You then pass that timestamp to DateTime but neglect to inform DateTime what kind of timestamp it is...
$int_effective_date = new DateTime(strtotime($edate));
// Is the same as...
$int_effective_date = new DateTime(1610409600);
However, DateTime doesn't see your timestamp as incorrect and tries to process it anyway...
In the format: HisYmd
But your input is too short for that so it only matches HisY
Time => 16:10
Year => 9600
Given the lack of data DateTime then fills in the blanks with today (example: 2021-02-05)
Day => 05
Month => 02
Which give you a complete timestamp of: 9600-02-05 16:10:40
strtotime from DateTime
Your next line of code then passes that timestamp back into a strtotime call...
echo "dateset:- ".strtotime($int_effective_date->format('Y/m/d'));
// Is the same as...
echo "dateset:- ".strtotime("9600/02/05");
Now, strtotime will always return something. Which means the first problem is that you're using echo which doesn't output (bool) false.
Try:
var_dump(strtotime("9600/02/05"));
You might ask, why doesn't that happen in the linked code example from #El_Vanja?
Answer
The answer to that, I believe, is that your PHP version is not up to date and anything over the 32 bit date range is going to return (bool) false from strtotime.
To fix this specific problem I suggest you update your PHP version (and OS if you haven't moved to 64 bit!)
However, further to that, I strongly suggest you stick to the DateTime object/class. It saves you from all of these annoying bugs if nothing else...
For reference:
echo strtotime( (new DateTime("#1610409600"))->format("Y-m-d") ); // Output: 1610409600
echo strtotime( (new DateTime("2021-01-12"))->format("Y-m-d") ); // Output: 1610409600

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

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.

How to solve PHP date() NULL value 1969?

cI use jQuery calendar date picker on my form. When the date is not filled, it always shows "1969-12-31" value. I did not want to show this value, 0000-00-00 is fine for me.
My MySQL date column is receive_dt DATE NOT NULL,
This is a snippet code from the PHP file to handle the form.
...
$rcv_dt = $_POST['receive_dt'];
list($year,$month,$day)=explode('/',$rcv_dt);
$timestamp=mktime(0,0,0,$year,$month,$day);
$receive_dt=date('Y-m-d',$timestamp);
..., receive_dt) VALUES (....,'$receive_dt')...
I've tried to do the strtotime() but no luck.
$receive_dt=date('Y-m-d', strtotime($rcv_dt));
I've even changed the MySQL reveive_dt column to DATE NULL, but still no luck.
Firstly, the fact that you're getting the end of 1969 rather than the beginning of 1970 (the "Unix epoch" begins at midnight on 1st Jan 1970) suggests you have some timezone-handling bug causing you to "lose" an hour, so just a heads-up on that.
Now, the reason you're seeing this at all, is that PHP's date formatting functions treat whatever input you give them as a number; if you give them an empty string, or null, this will be converted to the number 0, and interpreted as the beginning of the Unix epoch - 1st Jan 1970. MySQL will probably do something similar if you try to pass it an empty string or 0 when populating the column.
What you need to do is specifically detect this case - easy enough if your application should never actually have 1st Jan 1970 as input - and specifically insert a NULL into the database rather than formatting the date.
$invalid_dates='1969-12-31'; // anything before
$rcv_dt = $_POST['receive_dt'];
if(strtotime($invalid_dates) >= strtotime($rcv_dt))
{
$rcv_dt='0000-00-00'; // or $rcv_dt=date('Y-m-d'); // today
}
http://php.net/manual/en/function.checkdate.php
Learn to use the DateTime object. Using strtotime, mktime, and other integer based time formats is an outdated and bad approach.
$dt = date_create($_POST['receive_dt']);
if ($dt !== null)
{
echo $dt->format('Y-m-d H:i:s'); // insert this value
}
if you send $receive_dt as an empty string ..., receive_dt) VALUES (....,'')... (after checking the post variable is empty) then mysql will treat is as a null, otherwise php is sending a date of 0 which for mysql is the start date of the unix epoch.

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.

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