Sorting multiple date formats - php

In database I've got column date_of_premiere (type: INT). Passing the validation that column can store three "types" of values:
YYYY (e.g. 2016, when there's no info about specific month and day)
YYYYMM (e.g. 201511, when there's no info about specific day)
YYYYMMDD (e.g. 20151101)
I've got problem with sorting, becouse when I'm try to sort that column...
$query = $this->db->query(" SELECT * from table order by `date_of_premiere ");
... records with format YYYY will always first.
When I fetch the data, I can do some kind of workaround...
$dates =[];
while($row = $query->fetch_assoc())
{
if(strlen($row['date_of_premiere']) == 4)
{
$dates[] .= intval($row['date_of_premiere'] .'0000');
} else if (strlen($row['date_of_premiere']) == 6)
{
$dates[] .= intval($row['date_of_premiere'] .'00');
} else
{
$dates[] .= intval($row['date_of_premiere']);
}
}
...then I can do asort($dates)...
// input values: 2015, 2016, 201511, 20151101
// example output with correct sorting:
Array
(
[1] => 20150000
[0] => 20151100
[3] => 20151101
[2] => 20160000
)
... but I'm curious is there way to sort date_of_premiere using mysqli query only.

You can order by the dates' column casted as CHAR:
... ORDER BY CAST(date_of_premier AS CHAR(32))

Related

Substract a date compared to current date and show actual remaining days

This is my function below:
function Active()
{
............
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
if ($num_rows == -1)
{
$this->Error('ERROR');
$db->getError();
return;
}
$data = $db->doRead();
$data['Status'] = $num_rows == 0 ? '<:SHOW_PREMIUM_STATUS:>' : '<b><font size="2" color="red">Premium is active - <%Days_Remaining%> days remaining.</font></b>';
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => number_format($data['PremiumDays'])
);
$this->content = Template::Load('account-template', $replace);
}
PremiumDays column contains numbers like 10,15,30 etc.
PremiumStartTime contains a date in this format 2018-12-17 21:13:00
What I am trying to achieve is to show the actual days of premium remaining with days_remaining. So, I believe I need to substract from PremiumDays the days that passed since the premium started based on the second column PremiumStartTime.
Something like that I believe, however, I am not sure how to implement it correctly in PHP. Any help is greatly appreciated. Thank you in advance!
days_remaining = PremiumDays - (NumberOfDaysSincePremiumStarted(DateToday - PremiumStartTime))
To get the difference in PHP, you can create a DateInterval object using DateTime::diff between the current time (output of date_create()) and a DateTime object created from your PremiumStartTime variable. You can then access the days value of this object to get the total number of days from the PremiumStartTime to the current time. For example:
$data['PremiumStartTime'] = '2018-12-12 21:13:00';
$data['PremiumDays'] = 20;
$days_remaining = $data['PremiumDays'] - date_create($data['PremiumStartTime'])->diff(date_create())->days;
echo number_format($days_remaining);
Output:
13
Demo on 3v4l.org
Do it in the DB as follows:
//SQL Server
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime,
(PremiumDays - datediff(day,PremiumStartTime,getdate())) DaysRemaining
FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
//MySQL
$num_rows = $db->doQuery('SELECT PremiumDays, PremiumStartTime,
(PremiumDays - datediff(now(),PremiumStartTime)) DaysRemaining
FROM Premium WHERE AccountID = ?', $_SESSION['AccountID']);
Then:
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => $data['DaysRemaining']
);
Proof here: https://www.db-fiddle.com/f/xzuc89C9gQUHpdTTy9M4vf/0
Or if you really want to do it in PHP:
$replace = array
(
'account_status' => $data['Status'],
'days_remaining' => $data['PremiumDays'] - date_diff(date_create(), date_create($data['PremiumStartTime']))->days
);

MySQL order by time stored as HH:MM:SS

I have a varchar typed field which stores strings in this format HH:MM:SS i.e. 01:25:59 (and sometimes without HH part e.g. 25:59).
I want to have a descending order of results based on this time and for that I came with [str_to_date()][1] function and currently I'm using str_to_date($field_value,'%l:%i:%s') DESC to achieve this kind of sorting.
The odd thing is by using this format %l:%i:%s all posts having this field in MM:SS format are ordered correctly but those in HH:MM:SS aren't.
1-So if I have these values:
11:35
15:20
48:00
01:57:47
01:20:26
2-They are sorted as:
48:00
01:20:26
15:20
11:35
01:57:47
3-Which is wrong and should be:
01:57:47
01:20:26
48:00
15:20
11:35
As you see in (2) only times in format of HH:MM:SS are not placed correctly (DESC)
How can I have the right sorting?
What about this?
SELECT * FROM tbl
ORDER BY TIME_TO_SEC(IF(LENGTH(str_time)<6,CONCAT("00:",str_time),str_time)) DESC
fiddle demo: http://sqlfiddle.com/#!9/4b5da/3
This is your query:
SELECT IF(LENGTH( columnName ) >5, STR_TO_DATE(columnName, '%h:%i:%s'), STR_TO_DATE(columnName, '%i:%s')) as modDate
FROM `tableName` WHERE 1 order by modDate desc
SQL Fiddle: http://sqlfiddle.com/#!9/b6a52/1
wanna do something at application side ??? bit lengthy, but it works.
$tim_arr = array ('11:35', '15:20', '48:00', '01:57:47', '01:20:26');
$new_arr = array();
foreach ($tim_arr AS $tim){
$tim_chk = $key = '' ; $ntim_arr =array();
$tim_chk = substr_count($tim, ":");
$ntim_arr = explode(':',$tim);
if($tim_chk == 2){
$ntim = ( (int)$ntim_arr[0]*60 + (int)$ntim_arr[1] ).':'.$ntim_arr[2];
$key = ( (int)$ntim_arr[0]*60 + (int)$ntim_arr[1] );
}
else{
$ntim = $tim;
$key = $ntim_arr[0];
}
$new_arr[$key] = $ntim ;
}
krsort($new_arr);
foreach ($new_arr AS $tim)
{
$ntim_arr = explode(':',$tim);
if((int)$ntim_arr[0] >= 60){
echo str_pad(floor($ntim_arr[0] /60),2,"0",STR_PAD_LEFT).":".
str_pad($ntim_arr[0] %60,2,"0",STR_PAD_LEFT).":".$ntim_arr[1]."<br/>";
}
else{
echo $tim."<br/>";
}
}

Replacing redundant dates in DB query with symbol (MySQL/PHP)

I have a database query that displays a list of historic events in chronological order, like this:
(URL = MySite/Calendar/January_1)<br>
On this day in history...<br>
1968 - A volcano erupted.<br>
1968 - A country was invaded.<br>
1968 - Someone had a hit song.<br>
1970 - A famous person was born.
I'd like to know if there's a way to display a year just once, so the display looks like this:
1968 - A volcano erupted.<br>
• A country was invaded.<br>
• Someone had a hit song.<br>
1970 - A famous person was born.
Let's start with a database table (calendar_px) that lists the dates of various historic political events. The table has five fields -
1) N (a simple numerical key)
2) URL (values - such as May_1 - match page URL's)
3) Year (e.g. 1970, but the field type is INT, not Year, which only goes back to 1901)
4) Brief (some brief content)
5) Date (field type will be either date or datetime; I'm not actually using this field at the moment)
Here's what my code looks like (where $MyURL equals the page URL; e.g. January_1):
$stm = $pdo->prepare("SELECT Cal2.N, Cal2.URL, Cal2.Date, Cal2.Year, Cal2.Brief
FROM calendar_px Cal2
WHERE Cal2.URL = :MyURL
ORDER BY Cal2.Year");
$stm->execute(array(
'MyURL'=>$MyURL
));
while ($row = $stm->fetch())
{
$Year = $row['Year'];
$Brief[] = ''.$Year.' – '.$row['Brief'].'';
}
Then, I display a list of historic events like this...
echo join( $Brief, '<br>' );
I don't think it really changes anything, but I should mention that I have a similar set up on several websites; everything is the same except for the table names:
calendar_gw, calendar_gz, calendar_gs, calendar_px, calendar_sl
Accordingly, I've joined all five tables together with a UNION command. Here's a portion of the query:
$stm = $pdo->prepare("SELECT CGW.N, CGW.URL, CGW.Date, CGW.Year, CGW.Brief
FROM calendar_gw CGW
WHERE CGW.URL = :MyURL
UNION ALL
SELECT CGZ.N, CGZ.URL, CGZ.Date, CGZ.Year, CGZ.Brief
FROM calendar_gz CGZ
WHERE CGZ.URL = :MyURL
UNION ALL
SELECT CSL.N, CSL.URL, CSL.Date, CSL.Year, CSL.Brief
FROM calendar_sl CSL
WHERE CSL.URL = :MyURL
ORDER BY Year");
$stm->execute(array(
'MyURL'=>$MyURL
));
Anyway, my goal is the same; to replace redundant dates (years) with some sort of "dingbat" or symbol.
$prevYear = null;
while ($row = $stm->fetch())
{
$Year = $row['Year'];
if ($Year == $prevYear) {
$YearStr = '• ';
} else {
$YearStr = $Year . ' $#8211; ';
$prevYear = $Year;
}
$Brief[] = $YearStr.$row['Brief'];
}
P.S. You don't need to concatenate '' at each end of the string.
Looks like you need to group resultset before outputting:
$events = array();
while($row = $stm->fetch()){
$year = $row['Year']; // current year
if(!isset($events[$year]){ // if no such group
$events[$year] = array(); // create group
}
$events[$year][] = $row['Brief']; // add data to year group
}
// Output:
foreach($events as $year => $event){
echo $year, ':<br>'; // show year;
foreach($event as $data){
echo $data, '<br>'; // show row;
}
}
Also, you may change output to your model, easily:
foreach($events as $year => $event){
echo $year, ' • ', implode('<br>• ', $event);
}

multiple dates datetime

I am trying to display a table with PHP MYSQL where the FIELD is the DATE and rows are TIME from a MySQL database with unknown/infinite records, one with different TIMES for the same DATE, by querying it for the DateTime.
My mysql date stores the dateTime in the same column, but I am splitting this and trying to display them seperately. BUT I cannot seem to display the date only once and the time multiple times, it is just both.
$sql_result = mysqli_query($connection, "SELECT DATE(date_time) AS date_part, TIME(date_time) AS time_part FROM $table WHERE date_time LIKE '$date_input%'");
if (mysqli_num_rows($sql_result) == 0)
{
echo "<p>No bookings exist.</p>";
}
else {
echo "<h3>Results for booked " . $table . " Appointments:</h3>";
echo "<h3>" . $formattedDate ."</h3>";
while ($row = mysqli_fetch_array($sql_result))
{
echo $row['date_part'];
$array_time = array($row['time_part']);
foreach ($array_time as $time_output)
{
echo $time_output;
}
}
}
My output is like this:
2013-12-0809:00:002013-12-0810:00:002013-12-0811:00:002013-12-0812:00:002013-12-0814:00:002013-12-0815:00:002013-12-0816:00:002013-12-0817:00:002013-12-0909:00:002013-12-0809:00:00
But I would like it like this:
2013-12-08 09:00:0010:00:0011:00:0012:00:0014:00:0015:00:0016:00:0017:00:0009:00:000
2013-12-09 9:00:00
Hrrmm. Theres a bit of logic problem here:
while ($row = mysqli_fetch_array($sql_result))
{
echo $row['date_part'];
$array_time = array($row['time_part']); // HERE
foreach ($array_time as $time_output)
{
echo $row['time_part'];
}
}
$array_time will always have only one value, since $row['time_part'] only refers to ONE row (each iteration of while ($row = mysqli_fetch_array($sql_result)) reassigns a single row to $row)
try doing this first pass to define a workable array
while ($row = mysqli_fetch_array($sql_result))
{
$array[$row['date_part']][$row['time_part']] = 1; // the value assigned doesn't matter, all we wish is a definite hierarchy
}
this will give you an array like:
['2013-12-08']['9:08'] = 1
['12:30'] = 1
[23:17] = 1
['2013-12-09']['10:00'] = 1
[14:20] = 1
THEN! you can foreach through your result
foreach ($array as $date_part => $array_time)
{
echo $date_part . ' ';
foreach ($array_time as $time_part => $i) // again our $i is not used
{
echo $time_part;
}
echo '<br>'; // here it breaks line after all times for the date has been echoed
}
You need to GROUP in your query:
SELECT DATE(date_time) AS date_part, TIME(date_time) AS time_part
FROM $table
WHERE date_time LIKE '$date_input%'
GROUP BY date_part
I think I understand what you're trying to say, however, MySQL won't create a multidimensional array. You should try something like this:
$dates = array();
while ($row = mysqli_fetch_array($sql_result)) {
$dates[$row['date_part']][] = $row['time_part']
}
Then you could have something like this:
foreach ($dates as $key => $value) {
echo $key.'<br />';
foreach ($value as $time) {
echo $time.' - ';
}
echo '<br />';
}
which should look something like:
2013-09-01
09:00 - 09:30 - 10:20 - 11:00
2013-09-02
10:12 - 11:00 - 12:24 //etc
Hope this helps!
Two comments. First, it seems like you need an order by time so as to order your records.
SELECT DATE(date_time) AS `date_part`, TIME(date_time) AS `time_part`
FROM $table
WHERE date_time LIKE '$date_input%'
ORDER BY date_time ASC
Second, if I read your question correctly, it seems like you want to output your data into two columns, one with date and the other with all times for that date. You might consider using GROUP_CONCAT() to do this in SQL, making your output easy.
SELECT DATE(date_time) AS `date_part`, GROUP_CONCAT(TIME(date_time)) AS `times`
FROM $table
WHERE date_time LIKE '$date_input%'
GROUP BY `date_part`
ORDER BY date_time ASC
This would give output like
date_part times
2013-12-08 11:22:33,11:33:44,12:44:55
2013-12-09 12:00:00
With this approach, there would be no need to build a multi-dimensional array in PHP, as the data would come out of the database just the way you need it. That also means that you don't need to load the entire result set into memory in order to work with it (as you would have to do if creating multi-dimensional array).

Adding rows to an array in PHP

I have loaded an associative array of records from a MySQL database table.
The array consists of 1 to 7 rows representing one week of entries,
which might not have been entered for each day.
How can I insert blank rows into the array for the missing days
so that I can easily display the data in a table?
I don't need to update the database with the blanks.
Example:
Field1 Field2 Field3 Field4 .... Field#
Record[0]
Record[1]
Record[2]
Record[3]
Record[4]
Record[5]
Record[6]
Field4 is the date as yyyy-mm-dd
I load the array automatically using a start date and end date
Some weeks there will be a Sun, Tue, and Fri or Mon, Tue, Wed, Fri & Sat.
this is simple:
Do you know how many "Fields" you have for each day? let's say it's "num of fields"
$records = array_fill(0, 7, array_fill(0, <num of fields>, ''));
what this does is it creates a blank array from [0] to [6] and for each array element it inserts another array of "Fields", with "num of fields", each of which is set to an empty string ''.
Now that you have this, you read your data from the mysql table and if you selectively assign $records by index (i'm assuming), the rest of them will stay blank.
Keep in mind that you can reassign an element of $records array by using something like
$records[5] = array('New value', 'Field2 value');
which is what you do when you read data from mysql table.
Do you use some kind of index in your mysql table to correspond to the numbered day in a week?
comment here if you get stuck with mysql part.
If your array is associative, then when constructing the table why not just check for and skip the empty rows? As an example:
Example 1:
if ($row['Monday'] == '')
{
// draw blank template
}
else
{
// draw using live data
}
Based on added example (untested; for php 5.1 and above):
Example 2:
for($i = 0; $i < count($Record); $i++)
{
$recordDate = strtotime($Record[$i][field4]);
$dayOfWeek = $date('N', $recordDate);
switch ($dayOfWeek)
{
case '1':
// Monday
break;
case '2':
// Tuesday
break;
// and so on...
}
}
Edit
The above code assumed that your rows are in weekday order, with possible omissions. The problem with the first example, is that the array is not associative quite like the example. The problem with the second example, is that a missing weekday row results in a completely skipped output, which could provide a table like MTWFS (skipped Thursday).
So, you need to build a loop that draws each day of the week, and checks all of the rows for the appropriate day to draw. If the day is not found, an empty day is drawn:
Example 3:
$dayNames = {'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'};
// Loop that walks the days of the week:
for($d = 1; $d < 7; $d++)
{
// Loop that checks each record for the matching day of week:
$dayFound = false;
for($i = 0; $i < count($Record); $i++)
{
$recordDate = strtotime($Record[$i][field4]);
$dayOfWeek = $date('N', $recordDate);
// output this day name in your own formatting, table, etc.
echo $dayNames[$i];
if ($dayOfWeek == $d)
{
// output this day's data
$dayFound = true;
break;
}
if (!$dayFound)
{
// output a blank template
}
}
}
Edit 2
Ok it seems you are more interested in having a fully populated array of weekdays than an output routine (I was assuming you would just want to draw the tables in php or something). So this is my example of how to arrive at a 7-day array with no gaps:
Example 4:
$weekData = array(); // create a new array to hold the final result
// Loop that walks the days of the week, note 0-based index
for($d = 0; $d < 6; $d++)
{
// Loop that checks each record for the matching day of week:
$dayFound = false;
for($i = 0; $i < count($Record); $i++)
{
$recordDate = strtotime($Record[$i][field4]);
$dayOfWeek = $date('N', $recordDate);
// Add one to $d because $date('N',...) is a 1-based index, Mon - Sun
if ($dayOfWeek == $d + 1)
{
// Assign whatever fields you need to the new array at this index
$weekData[$d][field1] = $Record[$i][field1];
$weekData[$d][field2] = $Record[$i][field2];
$weekData[$d][field3] = $Record[$i][field3];
$weekData[$d][field4] = $Record[$i][field4];
// ...
break;
}
if (!$dayFound)
{
// Assign whatever default values you need to the new array at this index
$weekData[$d][field1] = "Field 1 Default";
// ...
}
}
}
Without having seen your current code (as requested in the comments by Felix Kling), my guess is that you will need to loop through your array, passing it to a function (or passing the array to the function) which checks Field4 and keeps track of which days of that week have data and fills in those missing days. This would be easier if the array was in order to start with (as you would only need to track the previous 'entry' rather than the entire week).
I'm a bit busy currently, here's some pseduo-code that will need to be expanded etc.
$formattedWeek = getFormattedWeek($inputArray);
function getFormattedWeek($input) {
$nextDay = 'Sunday';
foreach ($input as $entry) {
if (date-call-that-returns-day-of-the-week !== $nextDay) {
$output[] = 'add whatever you need to your array';
} else {
$output[] = $entry;
}
$nextDay = call-to-increase-the-day-or-loop-back-to-Sunday();
}
return $output;
}
You should get the picture.

Categories