I am trying to organize an output of DATA in the following way:
Today, Yesterday and everything before Yesterday is present with a respective full DateTime minus the clock time of course. For a better understanding, have a look at the screenshot below:
I have written this code:
try{
$db = new PDO("mysql:host=" .$hostname. ";dbname=" .$database. "", "" .$user. "", "" .$pass. "");
$db->setAttribute(PDO::ATTR_PERSISTENT, true);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$notifications = $db->prepare("SELECT * FROM reports
ORDER BY timestamp DESC");
$notifications->execute();
$result = (object)$notifications->fetchAll(PDO::FETCH_OBJ);
echo "<pre>";
print_r($result);
echo "</pre>";
}
catch(PDOException $e)
{
echo $e->getMessage();
}
I am trying to figure out a way to split things up, for instance, "Today", "Yesterday", "Day before Yesterday" and so forth for as long as considered normal e.g. an entire month maybe.
*How would I structure this up correctly with a PDO prepared statement? *
[Today] => stdClass Object
(
[id] => 1
[timestamp] => 2015-04-09 13:20:05
[seen] => 0
)
[Yesterday] => stdClass Object
(
[id] => 2
[timestamp] => 2015-04-08 15:30:50
[seen] => 0
)
Clearly: I want to print everything with timestamp of TODAY. Next, everything with YESTERDAYS timestamp. And so forth.
SQL:
// Today
AND DATE(from_unixtime(comment_date)) = CURRENT_DATE
// Yesterday
AND DATE(from_unixtime(comment_date)) = DATE_SUB(CURRENT_DATE,INTERVAL 1 DAY)
// This week
AND YEARWEEK(from_unixtime(comment_date), 1) = YEARWEEK(CURRENT_DATE, 1)
// This month
AND YEAR(from_unixtime(comment_date)) = YEAR(CURRENT_DATE)
AND MONTH(from_unixtime(comment_date)) = MONTH(CURRENT_DATE)
Here's an example of how I would handle this. I wouldn't change your query - it's fine as is. Assuming you want to show everything in the database sorted from latest to earliest post. Let PHP handle the heavy lifting. I've purposely broken some things in to multiple lines instead of nesting the functions to make it easier to read. Condense as you see fit.
I'm not claiming this is the BEST way to do it. Only what I use.
//MOCK UP SOME DISPLAY DATA - YOU WOULD USE YOUR QUERY RESULT INSTEAD
$rows = array();
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s');
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 24, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 25, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 12, 26, 2014));
$rows[] = date('Y-m-d H:i:s', mktime(0, 0, 0, 3, 2, 2001));
//CREATE AN ARRAY OF THE REPLACEMENTS YOU WANT
$aArray = array();
$aArray[date('Y-m-d')] = 'Today';
$aArray[date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') - 1, date('Y')))] = 'Yesterday';
$aArray[date('Y-m-d', mktime(0, 0, 0, date('n'), date('j') - 2, date('Y')))] = 'Day before Yesterday';
$aArray['2014-12-25'] = 'Christmas 2014';
//INITIALIZE SOME VARIABLES
$cLastHeader = '';
$cCurrHeader = '';
//THIS WOULD BE YOUR QUERY RESULTS LOOP
foreach ($rows AS $nNull => $cDate) {
$cLookup = substr($cDate, 0, 10); //TRIM OUT THE TIME FROM CURRENT RECORD
//IS DATE IN ARRAY? IF NOT, FORMAT
if (isset($aArray[$cLookup])) {
$cCurrHeader = $aArray[$cLookup];
} else {
$cCurrHeader = $cLookup; //WOULD SHOW 'YYYY-MM-DD'
$cCurrHeader = date('F Y', strtotime($cLookup)); //WOULD SHOW 'MONTH YYYY'
}
//HAS HEADER CHANGED? IF SO PRINT
if ($cCurrHeader != $cLastHeader) {
$cLastHeader = $cCurrHeader;
print($cCurrHeader . "\n");
}
//PRINT RECORD
print("\t" . $cDate . "\n");
}
The output from this is :
Today
2015-05-28 18:40:35
2015-05-28 18:40:35
2015-05-28 18:40:35
Yesterday
2015-05-27 00:00:00
December 2014
2014-12-24 00:00:00
Christmas 2014
2014-12-25 00:00:00
December 2014
2014-12-26 00:00:00
March 2001
2001-03-02 00:00:00
SELECT IF( DATEDIFF( NOW( ) , `timestamp` ) =0, "Today", if( DATEDIFF( NOW( ) , `timestamp` ) =1, "Yesterday", DATE_FORMAT( `timestamp`, '%M %d' ) ) ) AS day,
id,timestamp,seen
FROM reports
ORDER BY timestamp DESC
Trying to solve your issue through query,check if it is helpful for you.
As per given screenshot, after today and yesterday this query will give you month and date.
What about get all the rows and checking the date in PHP then group the results which exists at the same time-frame in separate array like the following :
$notifications = $db->prepare("SELECT * FROM reports
ORDER BY timestamp DESC");
$notifications->execute();
$rows = $notifications->fetchAll(PDO::FETCH_ASSOC);
foreach($rows as $row ){
if ( date('Ymd') == date('Ymd', strtotime($row['timestamp'])) ){
$today_rows[] = $row;
}
else if (date('Ymd', strtotime('yesterday')) == date('Ymd', strtotime($row['timestamp'])) ){
$yesterday_rows[] = $row;
}
else if () ... etc
}
Now you can use $today_row , $yesterday_rows ... etc in your view and display them as you want.
There are multiple ways to approach this.
First, you can create separate calls and merge the data on php side.
Second, you can use subquery but I don't recommend subqueries as they are kind of tricky and can hog up a lot of resources.
Third, you can use unions. You create 3 separate queries to select based on different scenarios and then use union. This way, you will get all the results in one call.
SELECT *, 'today' as day FROM table WHERE DATE(from_unixtime(comment_date)) = CURRENT_DATE
UNION
SELECT *, 'yesterday' as day FROM table WHERE MONTH(from_unix_MONTH(CURRENT_DATE)) = MONTH(CURRENT_DATE)
So basically, unions are different select statements put together into one query. You can union as many select statements as you like. HOWEVER, note that you want to have all select statements return the SAME COLUMNS or it will error. You also might wanna read up on UNION ALL. There are some minor differences between UNION and UNION ALL but not very.
You should alter your MySQL query to get date difference as well.
SELECT *,DATEDIFF(NOW(),`timestamp`) as `dateDifference` FROM `reports` ORDER BY `dateDifference`
an example is provided here http://sqlfiddle.com/#!9/ecad0/6
Then run an if statement or a switch-case on dateDifference to organize your results the way you want.
Example of a suitable switch-case statement is given below
switch (true) {
case ($dateDifference===0):
echo "Today's reports!";
break;
case ($dateDifference===1):
echo "Yesterday's reports!";
break;
case ($dateDifference>1):
echo "Even older reports!";
break;
case ($dateDifference<0):
echo "An unexpected error occurred. Please contact your system administrator!";
}
Related
I'm building an array of the number of calls I have for a particular client from a mysql db on a running list of the last 30 days. The code I have so far works for adding the days that have calls to the array but I need a show a '0' for the days that have no calls (no entries in the db). Here is me code so far:
$query="SELECT COUNT(*) FROM my_db WHERE client_phone='clint_phone#' GROUP BY calldate";
$result = mysql_query($query);
$data = array();
while ($row = mysql_fetch_row($result)) {
$data[] = $row[0];
}
I just need a way to show if today I had 30 calls and yesterday I had 0, I need it to show [30,0]. What I have only would show [30].
EDIT * I have a mysql db will columns client_phone, calldate. Im looking to build a graph using the data in an array. Each point of the graph will represent a day and the number of calls for that client on that day. Im building the above query to populate that array. I'm trying to count backwards thirty days and feed the total calls for each day into the array.
EDIT 2* I've got it almost there. I'm getting a problem in the 'foreach' area. Below is the code with two print_r()'s to dump the array. The first one looks good, but the second one shows some array entries getting over-written that shouldn't be:
$query="SELECT calldate, COUNT(*) FROM my_db WHERE client_phone='phone#' and calldate>='20130101' AND calldate<='20130107' GROUP BY calldate ORDER BY calldate";
$result = mysql_query($query);
$data = array();
while ($row = mysql_fetch_array($result)) {
$data[$row['calldate']] = $row[1];
}
$startDate = '20130101';
$endDate = '20130107';
$dates = array();
for($current = $startDate; $current != $endDate; $current = date('Ymd', strtotime("$current +1 day"))) {
$dates[] = $current;
}
$dates[] = $endDate;
print_r ($data);
echo "<br />";
foreach($dates as $date){
if (in_array($date, $data)) {
// that date was found in your db_date array(therefore had queries)
}else{
$data[$date] = 0; //date was not found in your db_array so we set that date with no queries to zero
}
}
print_r ($data);
I run this in a browser and I get this:
Array ( [20130101] => 1 [20130104] => 6 [20130105] => 2 [20130106] => 1 [20130107] => 3 )
Array ( [20130101] => 0 [20130104] => 0 [20130105] => 0 [20130106] => 0 [20130107] => 0 [20130102] => 0 [20130103] => 0 )
The top output looks good, just missing a zero assigned to dates not in the data[] array. The second array has zero's in the missing dates, but other overwrited that shouldn't have been.
Thanks for any help!
Finding every date that is in that timespan and doing a task for that does not really that standard of a solution. But depending if your host allows cron-tasks; if you were able to use cron tasks to automatically insert a 0 into your database at 11:59pm for that date if no querys were made that day; you could simply extract all dates.
If you do not want to do it with cron tasks I think you can manage it this way...
$startDate = '2009-01-28';
$endDate = '2009-02-04';
$dates = array();
for($current = $startDate; $current != $endDate; $current = date('Y-m-d', strtotime("$current +1 day"))) {
$dates[] = $current;
$dates[] = $endDate;
}
then do this
foreach($dates as $date){
if (array_key_exists($date, $data)) {
// that date was found in your db_date array(therefore had queries)
}else{
$data[$date] = 0; //date was not found in your db_array so we set that date with no queries to zero
}
}
This may need some minor adjustments because I have not tested it; but this should work; if not something very close to it should. Hope this helps.
Ok, I've got a logistical question here more so than coding one, but a code example/solution might answer both, so here goes...
If I have a form that I pass in PHP with start_date and end_date (full on m/d/Y format) and I need to print out a table of all entries in mysql database that fall in that range (so from m1/d1/Y1 to m2/d2/Y2) grouped by month, how would I even go about doing that in an elegant way?
For example, if I pass start_date = 5/20/2012, end_date = 7/31/2012, I need the resulting table to show results in this format:
May 2012 | June 2012 | July 2012
.... | .... | .....
where the first column for May 2012 would show results from an mysql query like
SELECT ... WHERE signup_date >= 5/20/2012 AND signup_date <= 5/31/2012
and June 2012 would similarly show:
SELECT ... WHERE signup_date >= 6/1/2012 AND signup_date <= 6/30/2012
etc
So I am looking for a way to parse the start and end date into an array of dates properly arranged from the starting day of the month until the last day of the month, and if the end_date is a few months later then cover all the other months in-between in full (from 1st til last of that month), so that I can cycle through them in a for/while loop? So something like:
[0]['start'] => '5/20/2012'
[0]['end'] => '5/31/2012'
[1]['start'] => '6/1/2012'
[1]['end'] => '6/30/2012'
[2]['start'] => '7/1/2012'
[2]['end'] => '7/31/2012'
Any ideas?
You should be do achieve this with mktime() . Sample code below.
<?php
$startDate = '05/20/2012';
$endDate = '07/31/2012';
$curMonth = date("m", strtotime($endDate)); //End Month
$curYear = date("Y", strtotime($endDate)); //End Year
$months = array();
$months[] = array('start' => "$curMonth/01/$curYear", 'end' => $endDate);
while ("$curMonth/01/$curYear" > $startDate) {
$monthEnd = date("m/d/Y", mktime(0, 0, 0, $curMonth, 0, $curYear));
$curMonth = date('m', strtotime($monthEnd));
$curYear = date('Y', strtotime($monthEnd));
$monthStart = ($curMonth/01/$curYear > $startDate) ? $startDate : "$curMonth/01/$curYear";
$months[] = array('start' => $monthStart, 'end' => $monthEnd);
}
print_r(($months));
?>
try this:
$date = '5/20/2012';
$dateStart = date("m/01/y", strtotime($date));
$start = date("Y-m-d", strtotime($dateStart));
$end = date("Y-m-d", strtotime($dateStart. '+ 1 month - 1 day') );
echo $start;
echo $end;
See my comments first for mysql db date format.
written a bit in pseudo language
$sql="SELECT * FROM ... WHERE `signup_date`>='2012-5-20' AND `signup_date`<'2012-5-31' ORDER BY `signup_date`";
$result=mysql_query($sql);
$month_year=array() //3-dimensional array $month_year[year][month][primary_key];
$month_index=-1;
while($row=mysql_fetch_assoc($result)){
// find $month_of_the_date AND $year_of_the_date
//find different months ,corresponding years, and db primary key and keep track in the $month_year array
}
for( all the years in the array ){
for(all the corresponding months of the year in the array){
//**EDIT:** you MIGHT need other loops and arrays to collect distinct years and corresponding months together
// show the records with the primary keys corresponding to that month and year
}
}
Dates in PHP are a nightmare for me so please help me out fellow coders... I want to notify customers about the day their order will be delivered. It works like this:
I have 2 shipping zones, A & B. Orders for zone A are delivered each Monday, Wednesday & Friday, whereas zone B is on Tuesday, Thursday, Saturday. For each order, the delivery day is scheduled for the NEXT AVAILABLE day, depending on the zone. Please consider that if someone places an order on Monday the goods will be delivered on the NEXT available date, that would be Tuesday for zone B and Wednesday for zone A.
How can I calculate the NEXT AVAILABLE delivery date and notify the customer?
Thanks.
This will certainly not be the fastest or most clever answer, but it's going to be a pleasure to read the code.
Assuming we are shipping in zone A:
$dates = array(
new DateTime('next monday'), // magic!
new DateTime('next wednesday'),
new DateTime('next friday'),
);
// Seems to work because since PHP 5.2.2 DateTime objects
// can be compared with the < and > operators
$shippingDate = min($dates);
echo $shippingDate->format('Y-m-d');
You might want to take a look at the relative date formats available in PHP, this is the part where the "next monday" magic happens. For information on what you can do with $shippingDate, see the documentation on class DateTime.
Update
For completeness, here is a more old-school version which does not need PHP 5.3 and should also be faster (although speed is practically irrelevant here). I don't like it as much, because it's not easy to verify that it works correctly. In contrast to the version above, this one had a bug when I first wrote it. Simple is good.
$today = date('w');
// These values are from http://www.php.net/manual/en/function.date.php
$shippingDays = array(
1, // mon
3, // wed
5, // fri
);
// Each of these weekdays is how many days into the future?
foreach($shippingDays as &$day) {
$day = (7 + $day - $today) % 7;
}
// Get the earliest one, but not if it's today
// array_filter is used to remove a possible 0
$daysInFuture = min(array_filter($shippingDays));
$shippingDate = new DateTime('+'.$daysInFuture.' days');
echo $shippingDate->format('Y-m-d');
See it in action.
Try this:
// Info
$date = array(date("d"), date("m"), date("Y"));
$zone = "A";
// ------
$zones = array("A" => array(1 => "Monday",
3 => "Wednesday",
5 => "Friday")
,"B" => array(2 => "Tuesday",
4 => "Thursday",
6 => "Saturday"));
$found = false;
$days_plus = 1; // always next day
// Retrieve last day from the zone
end($zones[$zone]);
$last_day = key($zones[$zone]);
do {
$mk = mktime(0, 0, 0, $date[1], ($date[0] + $days_plus), $date[2]);
$week = date("w", $mk);
// if week not passed last day of zone
if ($week <= $last_day)
{
if (!isset($zones[$zone][$week]))
{
$days_plus++;
}
else
{
$found = true;
}
}
else
{
$days_plus++;
}
} while (!$found);
echo "Next date: " . date("d/m/Y - l", $mk);
$timestamp = strtotime('next Monday');
$date = date('Y-m-d', $timestamp);
I have a MySQL table with events in it. Each event has a datetime column that indicates when it starts.
I'm looking for a way to produce a string similar to this using PHP:
'event X starts in 2 hours'
Should also work for days, weeks and months:
'event X starts in 5 days/weeks/months'
You should have some variable with the number of seconds till your date available. My example function is below.
<?php
function timeRemaining($total) {
if (!$total || $total <= 0) return false;
// define your ranges here (desc order), the keys will go to output.
$elements = array(
"years" => 60*60*24*30*12,
"months" => 60*60*24*30,
"weeks" => 60*60*24*7,
"days" => 60*60*24,
"hours" => 60*60,
"minutes" => 60,
"seconds" => 1
);
// compute in a cycle to compress the code
$return = array();
foreach ($elements as $name => $dur) {
$return[$name] = floor($total / $dur);
$total -= $return[$name] * $dur;
}
// return data in the array form
return $return;
}
// how much till new year?
echo "<pre>",
print_r(timeRemaining(mktime(0,0,0,1,1,2012)-time()));
echo "</pre>";
?>
Just copy-paste into any php file for testing, as an example it returns the array with years, months etc remaining till new year. You can tailor the output for your needs by feeding the return value to another string-generating function, just don't forget to check the value against false, which'll mean the time has passed.
Please note that the months use a simplified 30-days range, and the year here is set to 360 days, not 365.25 as in the real world.
Hope it will be of use.
To use this I first changed the format of the date to:
Select DATE_FORMAT(date_of, '%d/%m/%Y') as example from tbl
The php Function:
<?php
function days_ago($time)
{
$today = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
$time_array = explode("/", $time);
if(count($time_array) < 2)
{
$time = "N/A";
}
else
{
$time = mktime(0, 0, 0, date($time_array[1]), date($time_array[0]), date($time_array[2]));
$daysAgo = $today - $time;
$time = ($daysAgo/86400);
}
return $time;
}
?>
I took the following snippet from the here.
<?php
$dateDiff = $date1 - $date2;
$fullDays = floor($dateDiff/(60*60*24));
$fullHours = floor(($dateDiff-($fullDays*60*60*24))/(60*60));
$fullMinutes = floor(($dateDiff-($fullDays*60*60*24)-($fullHours*60*60))/60);
echo "Differernce is $fullDays days, $fullHours hours and $fullMinutes minutes.";
?>
$date1 would be the database date, $date2 would be the current date. Does that help?
I need to work out how many different instances occur on a different day, from many different ranges. Probably best to explain it with an example.
18-JAN-09 to 21-JAN-09
19-JAN09 to 20-JAN-09
20-JAN-09 to 20-JAN-09
Using the three examples above, I need it to collect this information and display something a little like...
18th Jan: 1
19th Jan: 2
20th Jan: 3
21st Jan: 1
... I'll be grabbing the information from an Oracle database fwiw (hence the format above ^) and there will be hundreds, maybe thousands of records, so my lame attempt to do all sorts of loops and if statements would take forever to run.
Is there any fairly simple and efficient way of doing this? I'm really not too sure where to start unfortunately...
Thanks
You could use the method described in another SO:
SQL> WITH DATA AS (
2 SELECT to_date('18-JAN-09', 'dd-mon-rr') begin_date,
3 to_date('21-JAN-09', 'dd-mon-rr') end_date FROM dual UNION ALL
4 SELECT to_date('19-JAN-09', 'dd-mon-rr'),
5 to_date('20-JAN-09', 'dd-mon-rr') FROM dual UNION ALL
6 SELECT to_date('20-JAN-09', 'dd-mon-rr'),
7 to_date('20-JAN-09', 'dd-mon-rr') FROM dual
8 ),calendar AS (
9 SELECT to_date(:begin_date, 'dd-mon-rr') + ROWNUM - 1 c_date
10 FROM dual
11 CONNECT BY LEVEL <= to_date(:end_date, 'dd-mon-rr')
12 - to_date(:begin_date, 'dd-mon-rr') + 1
13 )
14 SELECT c.c_date, COUNT(d.begin_date)
15 FROM calendar c
16 LEFT JOIN DATA d ON c.c_date BETWEEN d.begin_date AND d.end_date
17 GROUP BY c.c_date
18 ORDER BY c.c_date;
C_DATE COUNT(D.BEGIN_DATE)
----------- -------------------
18/01/2009 1
19/01/2009 2
20/01/2009 3
21/01/2009 1
Either your DB engine or your PHP code is going to have to loop over the date range.
Here's some PHP code to do the summation. The day counts are stored by year-month to avoid having a huge array for a wide date range.
<?php
// Get the date ranges from the database, hardcoded for example
$dateRanges[0][0] = mktime(0, 0, 0, 1, 18, 2009);
$dateRanges[0][1] = mktime(0, 0, 0, 1, 21, 2009);
$dateRanges[1][0] = mktime(0, 0, 0, 1, 19, 2009);
$dateRanges[1][1] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][0] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][1] = mktime(0, 0, 0, 1, 20, 2009);
for ($rangeIndex = 0; $rangeIndex < sizeof($dateRanges); $rangeIndex++)
{
$startDate = $dateRanges[$rangeIndex][0];
$endDate = $dateRanges[$rangeIndex][1];
// Add 60 x 60 x 24 = 86400 seconds for each day
for ($thisDate = $startDate; $thisDate <= $endDate; $thisDate += 86400)
{
$yearMonth = date("Y-m", $thisDate);
$day = date("d", $thisDate);
// Store the count by year-month, then by day
$months[$yearMonth][$day]++;
}
}
foreach ($months as $yearMonth => $dayCounts)
{
foreach ($dayCounts as $dayNumber => $dayCount)
{
echo $yearMonth . "-" . $dayNumber . ": " . $dayCount . "<br>";
}
}
?>
You need a table with one row for each day
test_calendar:
Day
16.01.2009
17.01.2009
18.01.2009
19.01.2009
20.01.2009
21.01.2009
22.01.2009
23.01.2009
24.01.2009
25.01.2009
26.01.2009
table test contains bagin and finish of inctance:
DFROM DTILL
18.01.2009 21.01.2009
19.01.2009 20.01.2009
20.01.2009 20.01.2009
Here is a query you need:
select day, (select count(1)
from test where dfrom<=t.day and dtill>=t.day) from test_calendar t
where day>to_date('15.01.2009','DD.MM.YYYY')
order by day
Huge thanks for the solutions guys - managed to get it working using some of the SQL from above and also bits of PHP from the second solution.
Really appreciate it, cheers :)