strtotime february bug - php

Okay, so I do understand that if I am running the strtotime function on a day that is the 29th or higher I will incur the February bug and get a result for March instead.
Also I get that if I set the date to Feb 1st, I can avoid the issue.
But here is the problem. I am rolling through the last 12 months of records to generate sales/billing numbers for tracking. How do I ask for all records in February when my loop looks like this?
setlocale( LC_MONETARY, 'en_US' );
$i = 0;
while( $i <= 11 ) {
$select = "SELECT * FROM `my_table` " .
"WHERE YEAR( billing_date ) = '" . date( 'Y', strtotime( -$i . ' month' )) . "' " .
" AND MONTH( billing_date ) = '" . date( 'm', strtotime( -$i . ' month' )) . "'";
$result = mysql_query( $select );
$num_rows = mysql_num_rows( $result );
$sales = 16 * $num_rows;
echo "<p align='center'>";
echo "Sales for " . date( 'M, Y', strtotime( '-' . $i . ' month' ) ) .
"&nbsp" . money_format( '%i', $sales );
echo "</p>";
$i++;
}
How do I avoid the February bug? Would it be an if statement? What would that look like?

Here is a pure SQL solution (if I correctly understand the scenario):
SELECT
YEAR(billing_date) AS billing_year,
MONTH(billing_date) AS billing_month,
16 * COUNT(*) AS sales /* Why x16?!!! */
FROM my_table
GROUP BY YEAR(billing_date), MONTH(billing_date)
This will calculate the sales directly in SQL, so in PHP you just need a display loop.

How do I ask for all records in February...
SELECT * FROM table WHERE MONTH(billing_date) = 2

February bug? It's not a bug. Normalizing dates is a (useful) feature! :-)
If you want the last day in February, ask for the 0th of March.

I don't know the "february bug", but you could try to use MySQL to subtract 1 month. Check
SELECT DATE_SUB(billing_date, INTERVAL 1 MONTH) FROM my_table
=> Manual

Related

mySQL PDO Prepared Statement Different Results from Non-Prepared Statement

My experience with PDO is somewhat limited and I've been stuck on this for a while. The issue is that when I run the code unprepared (because this has proven to be the only way I can debug PDO) I get the results I want. When I run it as a prepared statement, I get different results. See below:
Non-prepared code:
$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type='" . $p_sPostType . "'" .
" AND catID=" . $p_nCatID .
" AND serviceID=" . $p_nServiceID .
" AND serviceIdentity=" . $p_nServiceUserID .
" AND timestamp BETWEEN DATE_SUB(NOW(), $intervalString . ") AND NOW()";
$theQuery = $DB->Query($SQL);
echo "\r\n\r\nQuery:";
print_r($theQuery);
echo "\r\nResult:";
$result = $theQuery->fetch(PDO::FETCH_ASSOC);
print_r($result);
Non-prepared results:
Query:PDOStatement Object
(
[queryString] => SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type='pudding' AND catID=13 AND serviceID=1 AND serviceIdentity=3324848959 AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 DAY) AND NOW()
)
Result:Array
(
[counted] => 15
)
And now the prepared code:
$interval = array("hourly" => "1 HOUR", "daily" => "1 DAY", "weekly" => "7 DAY", "monthly" => "30 DAY", "yearly" => "1 YEAR");
$intervalString = "INTERVAL " . $interval[$p_sLimitType];
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type=:postType" .
" AND catID=:catID" .
" AND serviceID=:serviceID" .
" AND serviceIdentity=:serviceIdentity" .
" AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()";
// Execute the statement
try {
$stmt = $DB->prepare($SQL);
$stmt->bindParam(':postType', $p_sPostType, PDO::PARAM_STR, 30);
$stmt->bindParam(':catID', $p_nCatID, PDO::PARAM_INT);
$stmt->bindParam(':serviceID', $p_nServiceID, PDO::PARAM_INT);
$stmt->bindParam(':serviceIdentity', $p_nServiceUserID, PDO::PARAM_INT);
$stmt->bindParam(':interval', $intervalString, PDO::PARAM_STR, 30);
$result = $stmt->execute();
} catch(PDOException $e) {
mm_die($e->getMessage());
}
echo "\r\n\$SQL = $SQL";
// echo "\r\n\$p_nLimitValue = $p_nLimitValue\r\n";
echo "\r\nRow Count: " .$stmt->rowCount() . "\r\n";
And the prepared results:
$SQL = SELECT COUNT(*) as `counted` FROM tbl_transaction WHERE type=:postType AND siloID=:siloID AND serviceID=:serviceID AND serviceIdentity=:serviceIdentity AND timestamp BETWEEN DATE_SUB(NOW(), :interval) AND NOW()
Row Count: 0
Note "Row Count" being zero in the prepared statement. I've stared at this for more time than I care to admit. Can anyone see why one returns results and the other does not? Thanks!
The problem is that the second argument to DATE_SUB() must be an interval, but you're providing a string. The string "INTERVAL 1 HOUR is not automatically converted to the corresponding interval. You can only use a placeholder for the numeric part of the INTERVAL expression, not for the keywords.
Take the time units out of the associative array, and represent everything as hours.
$interval = array("hourly" => 1, "daily" => 24, "weekly" => 7*24, "monthly" => 30*24, "yearly" => 365*24);
Then you can do:
$SQL = "SELECT COUNT(*) as `counted` FROM tbl_transaction" .
" WHERE type=:postType" .
" AND catID=:catID" .
" AND serviceID=:serviceID" .
" AND serviceIdentity=:serviceIdentity" .
" AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL :interval HOUR) AND NOW()";
and
$stmt->bindParam(':interval', $interval[$p_sLimitType], PDO::PARAM_INT);
Placeholder could not represent an arbitrary part of query, but complete string or numeric literal only.
Thus you cannot bind a part of interval.
As long as you have your intervals whitelisted in the code as shown above, you may and you have to stick with old approach for interval.
It's not possible for a select count(*) to return 0 rows. It will always return AT LEAST one row, containing the count of the matched/found rows. Getting 0 rows means your query failed outright, and didn't return a result set, period.
Did you enable exceptions in PDO? By default it does "return false" for failure, and won't throw exceptions unless you explicitly enable them. if they're not enabled, then your try/catch is useless.

Calculating total hours out of database

I have a Log in PHP which shows all the things I've done.
the construction is like:
Date -- Things done -- Hours
I figured how to show all the info in a table. But I want to have a total hours in the bottom which calculated all the hours of the database.
I'm adding every day a new log (what I've done that day). But I wanna know how I can do this. I've been searching for a while now, without success..
I hope anyone can help me.
Thanks in advance.
This is how I display my code(short version):
<tr>
<td class="date"><?php echo $row['Date'] ?></td>
<td><?php echo $row['Detail'] ?></td>
<td><?php echo $row['Hours'] ?></td>
</tr>
So for clarification:
I make a new td which echo's the total hours by adding all the hours.
So if my hours are 8 + 8 + 8 + 8 + 4 + 5 in the hours table it will be shown as:
Total hours: 41 hours
To display the total sum of hours, you do this:
$db = new mysqli( DB_HOST, DB_USER, DB_PASS, DB_DATABASE );
$sql = 'SELECT SUM( `Hours` ) AS `total` FROM `table_name`';
$res = $db->query( $sql ) or die( 'MySQL error ' . $db->error );
$row = $res->fetch_assoc();
echo 'Total: ' . $row['total'];
If date is of typ datetime/date/timestamp you can get the total for every day like this (If the date is of type date, you do not need DATE(date)):
$sql = 'SELECT DATE(`date`) AS `day`, SUM(`Hours`) as `total` '
. 'FROM `table_name` GROUP BY DATE(`date`) ORDER BY `date`';
$res = $db->query( $sql ) or die( 'MySQL error ' . $db->error );
while( $row = $res->fetch_assoc() )
echo $row['day'] . ' has ' . $row['total'] . ' hours' . PHP_EOL;
If date is of typ int or PHP timestamp you can get the total for every day like this:
$sql = 'SELECT DATE( FROM_UNIXTIME(`date`) ) AS `day`, SUM(`Hours`) as `total` '
. 'FROM `table_name` GROUP BY DATE(FROM_UNIXTIME(`date`)) ORDER BY `date`';
$res = $db->query( $sql ) or die( 'MySQL error ' . $db->error );
while( $row = $res->fetch_assoc() )
echo $row['day'] . ' has ' . $row['total'] . ' hours' . PHP_EOL;

How to show records that falls on the date-range using if statement

I want to display all the records during August of the current year up to December of the current year. While it will not display on January up to June, Instead the Records of January up to June will be displayed.
How will I catch the date using IF statement?
Here is my code:
$year = date("Y");
$sem1_start = date("M-d-Y", strtotime("August, 01".$year));
$sem1_end = date("M-d-Y", strtotime("December, 31".$year));
$sem2_start = date("M-d-Y", strtotime("January, 01".$year));
$sem2_end = date("M-d-Y", strtotime("June, 15".$year));
//sample date from database
$date = '2015-10-15';
$date2 = date("M-d-Y", strtotime($date));
if($date2 >= $sem1_s && $date2 <= $sem1_e){
//Display all records
}
else if($date2 >= $sem2_s && $date2 <= $sem2_e){
//Display all records
}
I wanted to automatically display the records of the current semester (August-December) and when January comes, it will hide the records but will display the records of the second semester which is January up to June.
You could filter your sql result with the between statement. Something like
$query = "select * from db_table where datefield BETWEEN $startdate and $enddate order by datefield asc;
That way your results will always fall in range and are sorted to boot
I think, in the database I have a field to save valuable time
$time = time();
When I want to search from August of the current year to March 12 of the current year I will do the following
$timeSearchStart = strtotime(date('Y') . '-8-1 00:00:00');
$timeSearchEnd = strtotime(date('Y') . '-12-31 23:59:59');
$query = "SELECT * FROM `table` WHERE `fieldSaveTime` > '" . $timeSearchStart . "' && `fieldSaveTime` < '" . $timeSearchEnd . "'";

MySQL query produces correct results, but using PDO produces no results - why?

EDIT: I have changed the title of this question, as I am really getting no results, rather than a NULL result.
I am currently working with PDO on a project of mine, and am having a problem I can't seem to get past. A standard MySQL query (executed in phpMyAdmin) returns the results I want, but when I use PDO, it produces no results. I notice if I don't bind the values the query works, so would like to see what I am missing...
Below is the MySQL query:
SELECT AVG( demand_value )
FROM Demand
WHERE DATE = STR_TO_DATE( '06 Nov 2013', '%d %M %Y' )
AND TIME
BETWEEN STR_TO_DATE( '07:00:00', '%k:%i:%s' )
AND STR_TO_DATE( '10:00:00', '%k:%i:%s' );
Below is the PHP code (using PDO) trying to get the same results:
//Assume $date = '06 Nov 2013', $time_from = '07:00:00', $time_to = '10:00:00'
//Those values are parsed in from a custom function
$ave_btwn_times_stmt =
$connection->get_conn()->prepare("SELECT AVG( demand_value )
FROM Demand
WHERE date = STR_TO_DATE(:date, '%d %M %Y')
AND time BETWEEN STR_TO_DATE(:time_from, '%k:%i:%s')
AND STR_TO_DATE(:time_to, '%k:%i:%s')");
$ave_btwn_times_stmt->bindValue(':date', $date, PDO::PARAM_STR);
$ave_btwn_times_stmt->bindValue(':time_from', $time_from, PDO::PARAM_STR);
$ave_btwn_times_stmt->bindValue(':time_to', $time_to, PDO::PARAM_STR);
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . ": " . $ave_date_times[0][0] . ".";
If I implement the code below, I get the results I am after:
$ave_btwn_times_stmt =
$connection->get_conn()->prepare("SELECT AVG( demand_value )
FROM Demand
WHERE date = STR_TO_DATE('$date', '%d %M %Y')
AND time BETWEEN STR_TO_DATE('$from_time', '%k:%i:%s')
AND STR_TO_DATE('$to_time', '%k:%i:%s')");
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . ": " . $ave_date_times[0][0] . ".";
If anyone can help me out on this one I would be very grateful.
Your MySQL query threw this error
I'm sorry I'm afraid you can't do that.SQLSTATE[42S22]: Column not
found: 1054 Unknown column 'TIME' in 'where clause'
I have refactored your query removing BETWEEEN and replacing it with 2 comparison tests.
SELECT AVG( demand_value ) FROM Demand
WHERE DATE = STR_TO_DATE( '06 Nov 2013', '%d %M %Y' )
AND `time_from` >= STR_TO_DATE( `time_from`,'%k:%i:%s' )
AND `time_to` >= STR_TO_DATE(`time_to`,'%k:%i:%s' )
Using unnamed placeholders the following code provides required output.
$dbh = new PDO("mysql:host=$host;dbname=$database", $username, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$ave_btwn_times_stmt= $dbh->prepare("SELECT AVG( demand_value )
FROM Demand WHERE DATE = STR_TO_DATE( ?, '%d %M %Y' )
AND `time_from` >= STR_TO_DATE( ?,'%k:%i:%s' )
AND `time_to` >= STR_TO_DATE(?,'%k:%i:%s' )");
// Assign parameters
$ave_btwn_times_stmt->bindParam(1,$date);
$ave_btwn_times_stmt->bindParam(2,$time_from);
$ave_btwn_times_stmt->bindParam(3,$time_to);
$ave_btwn_times_stmt->execute();
$ave_date_times = $ave_btwn_times_stmt->fetchAll();
echo "Average on " . $date . ", between " . $time_from . " and " . $time_to . " = " . $ave_date_times[0][0] . ".";
}
catch(PDOException $e) {
echo "I'm sorry I'm afraid you can't do that.". $e->getMessage() ;// Remove or modify after testing
file_put_contents('PDOErrors.txt',date('[Y-m-d H:i:s]').", mapSelect.php, ". $e->getMessage()."\r\n", FILE_APPEND);
}
//Close the connection
$dbh = null;
After testing you should remove the error echo, or Try/Catch block and relying on PHP error handling instead

Count records from multiple tables in real-time

I want to count a record by current date from different tables and return as one row with different column in the new table. The code will update a record every three hours and insert new record if current date changes. I've current date and time data (2013-05-20 14:12:12) in "created_at" column. Here my current code:
require_once('./db_connect.php');
$dbcon = new db;
//test to see if a specific field value is already in the DB
public function in_table($table,$where) {
$query = 'SELECT * FROM ' . $table . ' WHERE ' . $where;
$result = mysqli_query($this->dbh,$query);
$this->error_test('in_table',$query);
return mysqli_num_rows($result) > 0;
}
//running in background
while (true) {
$select= "SELECT (SELECT CURDATE()) AS time," .
"(SELECT COUNT(tweet_id) FROM tweets WHERE created_at= 'CURDATE() %') AS total_count," .
"(SELECT COUNT(fid) FROM fun WHERE ftime= 'CURDATE() %') AS f_count," .
"(SELECT COUNT(sid) FROM sad WHERE stime= 'CURDATE() %') AS s_count";
$results = mysqli_query( $dbcon, $select );
while($row = mysqli_fetch_assoc($result)) {
$time = $row['time'];
$total = $row['total_count'];
$fcount = $row['f_count'];
$scount = $row['s_count'];
$field_values = 'time = "' . $time . '", ' . 'total_count = ' . $total . ', ' . 'fun_count = ' . $fcount . ', ' . 'sad_count = ' . $scount;
if ($dbcon->in_table('count','time= "' . $time . '"')) {
$update = "UPDATE count SET $field_values WHEN time= '$time'";
mysqli_query( $dbcon, $update );
}
else {
$insert = "INSERT INTO count SET $field_values";
mysqli_query( $dbcon, $insert );
}
}
//update record every 3 hour
sleep(10800);
}
With this code I can't get a count record. The result return | 2013-05-18 | 0 | 0 | 0 |. How can I correct this?
I not familiar with PHP, but you can retrieve the count of all records dated any time today using:
SELECT COUNT(tweet_id)
FROM tweets
WHERE created_at >= curDate()
AND created_at < date_add(curDate(), interval 1 day)
It is equivalent to saying
..
WHERE created_at >= (today at midnight *incusive*)
AND created_at < (tomorrow at midnight *exclusive*)
Update:
The advantage of this method is it is index friendly. While using WHERE DATE(Column) = currDate() works, it can prevent the database from using indexes on that column, making the query slower.
Replace the parts where you have this:
WHERE created_at= 'CURDATE() %'
with this:
WHERE DATE(created_at) = CURDATE()
Your existing WHERE clause is comparing created_at to the string constant CURDATE() %, and they'll never match.
You are comparing against created_at= 'CURDATE() %', which is looking for that exact string, not for the result of a function. If the field created_at is a date, it will never match.
And, you are doing that for all counts.

Categories