Formatting partial unknown dates with DateTime() from a database? - php

I have a database that has a few unknown months or days formatted like so: 1999-01-00 / 1999-00-00.
Now, I've been using DateTime() to format the days that are full, which works fine.
I'd like to have abbreviations for unknown days, but known months using $DateTime->format('M Y'); which outputs Sep 1999
and
$DateTime->format('Y'); for 1999 that is for unknown months and days.
DateTime() Doesn't allow 00 values for months/days, so what are some ways to get around this?

Just what I was looking for (and didn't want to hear).
Here's a basic php function for those of you who are lazy like me.
function formatDatabaseDate($date, $delimiter = '/')
{
// this function works only on mysql date fields,
// and takes into account partial dates.
if ($date != '' && $date != NULL)
{
$datePieces = explode('-', $date);
if (count($datePieces) == 3 &&
strlen($datePieces[0]) == 4 &&
strlen($datePieces[1]) == 2 &&
strlen($datePieces[2]) == 2)
{
$datestring = '';
$months['01'] = 'Jan';
$months['02'] = 'Feb';
$months['03'] = 'Mar';
$months['04'] = 'Apr';
$months['05'] = 'May';
$months['06'] = 'Jun';
$months['07'] = 'Jul';
$months['08'] = 'Aug';
$months['09'] = 'Sep';
$months['10'] = 'Oct';
$months['11'] = 'Nov';
$months['12'] = 'Dec';
if ($datePieces[2] != '00' && $datePieces[1] != '00')
{
$datestring = $datePieces[2] . $delimiter
. $months[$datePieces[1]] . $delimiter
. $datePieces[0];
}
else if ($datePieces[1] != '00')
{
$datestring = $months[$datePieces[1]] . $delimiter
. $datePieces[0];
}
else
{
$datestring = $datePieces[0];
}
return $datestring;
}
else
{
trigger_error('date is not in a valid mysql format');
return false;
}
}
else
{
trigger_error('empty date passed to format command');
return false;
}
}

Unfortunately, the DateTime class is pretty much a wrapper for a UNIX timestamp. (I said pretty much, I know there's more to it than just that.) The way that it works with dates were one of the elements is zero is simply to calculate it as a synonym for the previous period. (e.g.: April 0 is calculated to become March 31.)
Really, you just have to bypass PHP's built in date functionality and write your own. The good news is that you can indeed do this. MySQL will actually return values as simple strings, and, when it comes to dates, the natural response that the actual C libraries use is to return the date pretty much as an ISO 8601 string. (There's that phrase pretty much again.)
If I were writing this, I would read the value raw with mysql_fetch_row (or your own method) and check if all the elements (i.e.: year, month, day) are defined. If they are, use the DateTime method. If not, I would write a piece of code to pull apart the elements that are defined and format it accordingly.

Related

PHP Datetime check date with different format

I've got a problem with datetime condition checked.
First, I've a DateTime value that stored in database as 2018-05-08 15:54:40
But, I want to check only date is equal or not.
For example:
$DateInDatabase = 2018-05-08 15:54:40
$DateSpecific = 2018-05-08
if ($DateInDatabase == $DateSpecific) {
......
}
The question is how to check only date in $DateInDatabase
If you are only interested in the year-month-day, my suggestion would be something such as;
// As per comments, the second param in the date function MUST be a UNIX timestamp, so strtotime will resolve this
$dateInDB = date("Y-m-d", strtotime($DateInDatabase)); // format to XXXX-XX-XX
$dateToCheck = date("Y-m-d", strtotime($DateSpecific)); // format to XXXX-XX-XX
if ($dateInDb == $dateToCheck)
{
// They are the same
}
else
{
// The are different
}
As others have said, you can use direct string comparison also;
$DateInDatabase = "2018-05-08 15:54:40";
$DateSpecific = "2018-05-08";
// This function uses the params haystack, needle in that order
if (stristr($DateInDatabase, $DateSpecific))
{
// Match found
}
else
{
// No match found
}
You can use php date method to format date:
$DateInDatabase ='2018-05-08 15:54:40';
$DateSpecific = '2018-05-08';
if (date('Y-m-d', strtotime($DateInDatabase)) == $DateSpecific) {
echo 'ok';
} else {
echo 'not ok';
}
Read more about php date method from here
You can either directly match as string or parse as date and later format as string.
Given that $DateInDatabase = '2018-05-08 15:54:40' and $DateSpecific = '2018-05-08'
if (false !== strpos($DateInDatabase,$DateSpecific)) {
/* matched */
}
OR
$format = 'Ymd';
$DateInDatabase = (new DateTime($DateInDatabase))->format($format);
$DateSpecific = (new DateTime($DateSpecific))->format($format);
if ($DateInDatabase === $DateSpecific) {
/* matched */
}
This will check if your dates are equal. Be aware that PHP's date function has a limit in how far you can go for the year. The minimum is 01-01-1970 and the maximum is 19-01-2038 (d-m-Y format used here). The date is converted to an integer (strtotime). If those numbers are equal, the dates are equal.
$DateInDatabase = '2018-05-08 15:54:40'
$DateSpecific = '2018-05-08'
$newDate1 = date("Y-m-d", strtotime($DateInDatabase));
$newDate2 = date("Y-m-d", strtotime($DateSpecific));
if ($newDate1 == $newDate2) {
//equal
} else {
//not equal
}

Row highlighting based on due date using php

I'm relatively new to PHP, I've learned procedural PHP and I'm playing around with an app.
I've Looked around the site and found a few samples on date formatting and highlighting using PHP, HTML and CSS.
With the help found here and the PHP manual, I've put together some code to highlight 2 different rows among many others provided by a database that follow this criteria:
Anything due in 1 day (today, yesterday, last week, etc.) should color the table row red.
Anything 3 days out (between 1 day and 3 days in the future) should color the table row yellow.
Anything else should utilize the bootstrap "table-striped" styling.
Here is the code I've put together
//Today + 1 day
$oneDay = date('m/d/Y', strtotime("+1 day"));
//Today + 3 days
$threeDays = date('m/d/Y', strtotime("+3 days"));
//Database entry for comparison
$due = date_create($record['projected_delivery']);
$dueOneDay = date_create($oneDay);
$dueThreeDays = date_create($threeDays);
//Get the difference between the 2 dates
$diffOne = date_diff($due, $dueOneDay);
$diffThree = date_diff($due, $dueThreeDays);
if ($diffThree->format('%d') < 3) {
$highlight_css = " warning";
}elseif ($diffOne->format('%d') <= 1){
$highlight_css = " danger";
}else {
$highlight_css = "";
}
I then add the $highlight_css to the HTML.
So far some of the functionality is working. Proper highlighting is not added for:
Dates older than 1 day (i.e. yesterday, last week)
How can this functionality be achieved?
I would use this instead:
<?php
$dueDates =["06/05/2017","06/06/2017","06/07/2017","06/08/2017","06/09/2017","06/10/2017","06/11/2017","06/12/2017"];
$table = "<table class='table table-bordered'><thead><tr><th>Date</th></tr></thead><tbody>";
$today = date_create(date('m/d/Y')); // example: 06/07/2017
foreach ($dueDates as $dueStr) {
$due = date_create($dueStr);
$diff = date_diff($today, $due)->format("%r%a");
$highlight_css = "";
if ($diff > 1 && $diff <= 3) {
$highlight_css = " warning";
} elseif ($diff == 1) {
$highlight_css = " danger";
}
$table .= "<tr class='$highlight_css'><td>$dueStr</td></tr>";
}
$table .= "</tbody></table>";
?>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" >
<?php
echo $table;
$diffThree->format('%d')
will return a string instead of an integer. In order to compare it correctly with other numbers (3 and 1 in your case) you need to convert it to an int:
(int) $diffThree->format('%d')
and
(int) $diffOne->format('%d')
The issue
A DateInterval (the return value of date_diff()) has the number of days (actually stored in a property d, which could be used instead of getting a string with format()), which should always be a positive number. And for dates before today's date, the first date diff (i.e. $diffOne) will be 2+ days and the second date diff (i.e. $diffThreeDays) will be 4+ (see this playground example with debugging output above the table) so days before today's date will never be associated with either 'warning' or 'danger' according to the given logic.
A Solution
One approach (perhaps simpler) to comparing the dates is to use DateTime comparison
Note:
As of PHP 5.2.2, DateTime objects can be compared using comparison operators.1
So compare the DateTime variables (i.e. $due with $dueOneDay and $dueThreeDays) instead of the date differences.
In the logic below, you will notice that the order of the comparisons has been reversed, so the check for one day difference comes before the check for three days difference, in order to avoid the danger case never happening.
if ($due < $dueOneDay){
$highlight_css = " danger";
}else if ($due <= $dueThreeDays) {
$highlight_css = " warning";
}else {
$highlight_css = "";
}
With this approach, there is no need for the date_diff variables (i.e. $diffOne, $diffThree).
See a demonstration of this in this playground example.
1http://php.net/manual/en/datetime.diff.php#example-2524

date within range or not

I am trying to check whether user submitted date is within range or not.
<?php
$datesyntx = "/^(19|20)\d\d[\-\/.](0[1-9]|1[012])[\-\/.](0[1-9]|[12][0-9]|3[01])$/";
$paydate = "2015-03-01";
if (preg_match($datesyntx, $paydate)) {
if(strtotime($paydate) > strtotime('2015-04-23') && strtotime($paydate) < strtotime('2015-07-23')) {
}
}
?>
It is not working what actually I am trying to get.
Try this code :
$paydate = date('2015-03-01');
$DateBegin = date('2010-01-01');
$DateEnd = date('2016-01-01');
if (($paydate > $DateBegin) && ($paydate < $DateEnd))
{
echo "is between";
}
else
{
echo "not between!";
}
P.S: this question was answered before at stackoverflow:
PHP check if date between two dates
EDIT2: easy way .
No need to use regex in this scenario, use simple DateTime class to check whether user submitted date is within range or not.you can also use typical strtotime() to do this job. But DateTime class will be great fun for this.
<?php
$userSubmitted = new DateTime("2015-04-01");
$startDate=new DateTime("2014-02-01 20:20:00");
$endDate=new DateTime("2015-04-30 23:50:11");
if ($userSubmitted > $startDate && $userSubmitted < $endDate)
{
return true; #date is within the range of yours
}
return false; #date not within the range of yours

Convert user input to my date format

User enters the date as 8/1/11 or 08/1/11. Regardless of how the user inputs the date in my text field, I want to convert what they enter to my format of mm/dd/yyyy
So if the user enters 8/1/11 it would convert it to 08/01/2011.
Use strtotime() to transfer it into a time value, and then mktime() to transfer it to whatever format you want.
EDIT: Turns out it's not that simple.
You can't just print any date you'd like and transfer it to another, it'll be best to use some sort of UI on the client side to ensure the input matches, a great example is jQueryUI Datepicker
Check this.
It also checks if the date is valid (with checkdate) and converts years from short to long. When using short years, 0-69 is converted to 2000-2069 and 70-99 is converted to 1970-1999.
<?php
function cleanDate($input)
{
$parts = explode('/', $input);
if(count($parts) != 3) return false;
$month = (int)$parts[0];
$day = (int)$parts[1];
$year = (int)$parts[2];
if($year < 100)
{
if($year < 70)
{
$year += 2000;
}
else
{
$year += 1900;
}
}
if(!checkdate($month, $day, $year)) return false;
return sprintf('%02d/%02d/%d', $month, $day, $year);
// OR
$time = mktime(0, 0, 0, $month, $day, $year);
return date('m/d/Y', $time);
}
$test = array(
'08/01/2011', '8/1/11', '08/01/11', // Should all return 08/01/2011
'08/1/87', // Should return 08/01/1987
'32/1/93', '13', // Should fail: invalid dates
'02/29/2011', // Should fail: 2011 is not a leap year
'2/29/08'); // Should return 02/29/2008 (2008 is a leap year)
foreach($test as $t)
{
echo $t.' : '.(cleanDate($t) ?: 'false')."\n";
}
?>
Result:
08/01/2011 : 08/01/2011
8/1/11 : 08/01/2011
08/01/11 : 08/01/2011
08/1/87 : 08/01/1987
32/1/93 : false
13 : false
02/29/2011 : false
2/29/08 : 02/29/2008
<?php
$userInput = '08/1/11'; // or = '8/1/11' or = '08/01/11' or = '01/8/11'
$arr = explode('/', $userInput);
$formatted = sprintf("%1$02d", $arr[0]) . '/' . sprintf("%1$02d", $arr[1]) . '/20' . $arr[2];
?>
<?php
preg_match('#^(0?[1-9]|1[0-2])/(0?[1-9]|[12][0-9]|3[01])/(\d{2})$#',trim($date),$matches);
$month=str_pad($matches[1],2,'0',STR_PAD_LEFT);
$day=str_pad($matches[2],2,'0',STR_PAD_LEFT);
$year=$matches[3];
$result="{$month}/{$day}/{$year}";
?>
While #Truth is right that the user can do a lot to make it difficult, there IS actually a way to do a fairly decent job of parsing a date input box to work. It takes into account a variety of user input issues that may come up.
Note that it does make two assumptions:
That the local uses a date format of m/d/y
If the year is entered in short form, we are dealing with a 2000+ year
<?php
// Trim spaces from beginning/end
$date = trim(request($field));
// Allow for the user to have separated by spaces
$date = str_replace(" ", "/", $date);
// Allow for the user to have separated by dashes or periods
$date = str_replace("-", "/", str_replace(".", "/", trim($date)));
// Explode the date parts out to ensure a year
// Granted, this is geo-centric - you could adjust based on your locale
$dateparts = explode("/", $date);
// Check for a year. If not entered, assume this year
if (!isset($dateparts[2])) {$dateparts[2] = date("Y");}
// Force year to integer for comparison
$dateparts[2] = (int)$dateparts[2];
// Allow for user to use short year. Assumes all dates will be in the year 2000+
if ($dateparts[2] < 2000) {$dateparts[2]+= 2000;}
// Re-assemble the date to a string
$date = implode("/", $dateparts);
// Utilize strtotime and date to convert date to standard format
$date = date("m/d/Y", strtotime($date));
?>
Disclaimer: Yes, this is the verbose way of doing this, however I did so for clarity of the example, not for efficiency.

performing datetime related operations in PHP

How do you actually perform datetime operations such as adding date, finding difference, find out how many days excluding weekends in an interval? I personally started to pass some of these operations to my postgresql dbms as typically I would only need to issue one sql statement to obtain an answer, however, to do it in PHP way I would have to write a lot more code that means more chances for errors to occur...
Are there any libraries in PHP that does datetime operation in a way that don't require a lot of code? that beats sql in a situation where 'Given two dates, how many workdays are there between the two dates? Implement in either SQL, or $pet_lang' that is solved by making this query?
SELECT COUNT(*) AS total_days
FROM (SELECT date '2008-8-26' + generate_series(0,
(date '2008-9-1' - date '2008-8-26')) AS all_days) AS calendar
WHERE EXTRACT(isodow FROM all_days) < 6;
While for most datetime operations I would normally convert to Unixtime and perform addition subtraction etc. on the Unixtime integer, you may want to look at the Zend framework Zend_Date class.
This has a lot of the functionality you describe. Although Zend is billed as a "framework" it works exceptionally well as a class library to pick and chose elements from. We routinely include it in projects and then just pull in bits as and when we need them.
PHP5+'s DateTime object is useful because it is leap time and
daylight savings aware, but it needs some extension to really
solve the problem. I wrote the following to solve a similar problem.
The find_WeekdaysFromThisTo() method is brute-force, but it works reasonably quickly if your time span is less than 2 years.
$tryme = new Extended_DateTime('2007-8-26');
$newer = new Extended_DateTime('2008-9-1');
print 'Weekdays From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_WeekdaysFromThisTo($newer) ."\n";
/* Output: Weekdays From 2007-08-26 To 2008-09-01: 265 */
print 'All Days From '.$tryme->format('Y-m-d').' To '.$newer->format('Y-m-d').': '.$tryme -> find_AllDaysFromThisTo($newer) ."\n";
/* Output: All Days From 2007-08-26 To 2008-09-01: 371 */
$timefrom = $tryme->find_TimeFromThisTo($newer);
print 'Between '.$tryme->format('Y-m-d').' and '.$newer->format('Y-m-d').' there are '.
$timefrom['years'].' years, '.$timefrom['months'].' months, and '.$timefrom['days'].
' days.'."\n";
/* Output: Between 2007-08-26 and 2008-09-01 there are 1 years, 0 months, and 5 days. */
class Extended_DateTime extends DateTime {
public function find_TimeFromThisTo($newer) {
$timefrom = array('years'=>0,'months'=>0,'days'=>0);
// Clone because we're using modify(), which will destroy the object that was passed in by reference
$testnewer = clone $newer;
$timefrom['years'] = $this->find_YearsFromThisTo($testnewer);
$mod = '-'.$timefrom['years'].' years';
$testnewer -> modify($mod);
$timefrom['months'] = $this->find_MonthsFromThisTo($testnewer);
$mod = '-'.$timefrom['months'].' months';
$testnewer -> modify($mod);
$timefrom['days'] = $this->find_AllDaysFromThisTo($testnewer);
return $timefrom;
} // end function find_TimeFromThisTo
public function find_YearsFromThisTo($newer) {
/*
If the passed is:
not an object, not of class DateTime or one of its children,
or not larger (after) $this
return false
*/
if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
return FALSE;
$count = 0;
// Clone because we're using modify(), which will destroy the object that was passed in by reference
$testnewer = clone $newer;
$testnewer -> modify ('-1 year');
while ( $this->format('U') < $testnewer->format('U')) {
$count ++;
$testnewer -> modify ('-1 year');
}
return $count;
} // end function find_YearsFromThisTo
public function find_MonthsFromThisTo($newer) {
/*
If the passed is:
not an object, not of class DateTime or one of its children,
or not larger (after) $this
return false
*/
if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
return FALSE;
$count = 0;
// Clone because we're using modify(), which will destroy the object that was passed in by reference
$testnewer = clone $newer;
$testnewer -> modify ('-1 month');
while ( $this->format('U') < $testnewer->format('U')) {
$count ++;
$testnewer -> modify ('-1 month');
}
return $count;
} // end function find_MonthsFromThisTo
public function find_AllDaysFromThisTo($newer) {
/*
If the passed is:
not an object, not of class DateTime or one of its children,
or not larger (after) $this
return false
*/
if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
return FALSE;
$count = 0;
// Clone because we're using modify(), which will destroy the object that was passed in by reference
$testnewer = clone $newer;
$testnewer -> modify ('-1 day');
while ( $this->format('U') < $testnewer->format('U')) {
$count ++;
$testnewer -> modify ('-1 day');
}
return $count;
} // end function find_AllDaysFromThisTo
public function find_WeekdaysFromThisTo($newer) {
/*
If the passed is:
not an object, not of class DateTime or one of its children,
or not larger (after) $this
return false
*/
if (!is_object($newer) || !($newer instanceof DateTime) || $newer->format('U') < $this->format('U'))
return FALSE;
$count = 0;
// Clone because we're using modify(), which will destroy the object that was passed in by reference
$testnewer = clone $newer;
$testnewer -> modify ('-1 day');
while ( $this->format('U') < $testnewer->format('U')) {
// If the calculated day is not Sunday or Saturday, count this day
if ($testnewer->format('w') != '0' && $testnewer->format('w') != '6')
$count ++;
$testnewer -> modify ('-1 day');
}
return $count;
} // end function find_WeekdaysFromThisTo
public function set_Day($newday) {
if (is_int($newday) && $newday > 0 && $newday < 32 && checkdate($this->format('m'),$newday,$this->format('Y')))
$this->setDate($this->format('Y'),$this->format('m'),$newday);
} // end function set_Day
public function set_Month($newmonth) {
if (is_int($newmonth) && $newmonth > 0 && $newmonth < 13)
$this->setDate($this->format('Y'),$newmonth,$this->format('d'));
} // end function set_Month
public function set_Year($newyear) {
if (is_int($newyear) && $newyear > 0)
$this->setDate($newyear,$this->format('m'),$this->format('d'));
} // end function set_Year
} // end class Extended_DateTime
strtotime() is useful but it does have some odd behaviors that can pop-up from time to time if you are not just using it to convert a formatted date/time string.
things like "+1 month" or "-3 days" can sometimes not give you what you expect it to output.
For adding a date, you can use the method DateTime::add (Adds an amount of days, months, years, hours, minutes and seconds to a DateTime object), available from php 5.3.0 onwards.
To find the difference between two dates, there's the DateTime::diff method; but there doesn't seem to be a method for counting the working days between two dates.
PEAR::Date looks like it might have some useful functionality.
PEAR::Calendar might also be useful.
The easiest method is to use a timestamp, representing the number of seconds since January 1, 2008. With a timestamp type, you can do things like...
now = time();
tomorrow = now + 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds
Check out the documentation for time(), date() and mktime() on the php web pages. Those are the three methods that I tend to use the most frequently.
You can use a combination of strtotime, mktime and date todo the arithmetic
Here is an example which uses a combo todo some arithmetic http://rushi.wordpress.com/2008/04/13/php-print-out-age-of-date-in-words/ I'll reproduce the code here for simplicity
if ($timestamp_diff < (60*60*24*7)) {
echo floor($timestamp_diff/60/60/24)." Days";
} elseif ($timestamp_diff > (60*60*24*7*4)) {
echo floor($timestamp_diff/60/60/24/7)." Weeks";
} else {
$total_months = $months = floor($timestamp_diff/60/60/24/30);
if($months >= 12) {
$months = ($total_months % 12);
$years = ($total_months - $months)/12;
echo $years . " Years ";
}
if($months > 0)
echo $months . " Months";
}
?>
#Rushi I don't like strtotime() personally.. i don't know why but i discovered this morning that passing a string like this '2008-09-11 9:5 AM' to strtotime returns a false...
I don't think the code you provided solve the example problem 'Given two dates, how many workdays are there between the two dates? Implement in either SQL, or $pet_lang' and I haven't consider if I have a list of public holiday...
You can get number of days between two dates like this:
$days = (strtotime("2008-09-10") - strtotime("2008-09-12")) / (60 * 60 * 24);
And you can make function something like that (I don't have php installed in my work computer so i can't guarantee syntax is 100% correct)
function isWorkDay($date)
{
// check if workday and return true if so
}
function numberOfWorkDays($startdate, $enddate)
{
$workdays = 0;
$tmp = strtotime($startdate);
$end = strtotime($enddate);
while($tmp <= $end)
{
if ( isWorkDay( date("Y-m-d",$tmp) ) ) $workdays++;
$tmp += 60*60*24;
}
return $workdays;
}
If you don't like strtotime and you always have date in same format you can use explode function like
list($year, $month, day) = explode("-", $date);
I would strongly recommend using PHP 5.2's DateTime objects, rather than using UNIX timestamps, when doing date calculations. When you use the PHP date functions that return UNIX timestamps, you have a very limited range to work with (e.g. nothing before 1970).
If you have a look at http://php.net/date , you will find some examples of using mktime() to perform operations.
A simple example would be to workout what tomorrows date would be. You can do that by simply adding 1, to the day value in mktime() as follows:
$tomorrow = date("Y-m-d", mktime(0, 0, 0, date("m"), date("d") + 1, date("Y")));
So here, you will receive a date in the form of YYYY-MM-DD containing tomorrows date. You can also subtract days by simply replacing '+' with '-'. mktime() makes life a lot easier, and saves you from having to do nested if statements and other such troublesome coding.
to get working days/holidays, postgresql CTE ftw -- see http://osssmb.wordpress.com/2009/12/02/business-days-working-days-sql-for-postgres-2/

Categories