How to include today when iterating a DatePeriod? - php

Why is today excluded from the returned values?
SELECT DATE(created) AS reg_date,
COUNT(*) AS user_reg_per_day
FROM users
WHERE created > (NOW() - INTERVAL 30 DAY)
GROUP BY reg_date
My query seems to be fine, but I use following PHP to fill in the gaps:
function generate_calendar_days() {
$end = date("Y-m-d");
$today = strtotime($end);
$start = strtotime("-30 day", $today);
$start = date('Y-m-d', $start);
$range = new DatePeriod(
DateTime::createFromFormat('Y-m-d', $start),
new DateInterval('P1D'),
DateTime::createFromFormat('Y-m-d', $end));
$filler = array();
foreach($range as $date) {
$d = $date->format('Y-m-d');
$filler[$d] = 0;
}
return $filler;
}
My guess is $today is not correct.

There is no reason your query should exclude data from the current day unless there is something odd with the way you are writing data to this table. Are you maybe not seeing it because you are not ordering your results (i.e. it is at bottom of result set)?
It would be giving partial day results for the day 30 days ago. As such, you might consider modifying the WHERE condition a bit:
SELECT DATE(created) AS reg_date,
COUNT(*) AS user_reg_per_day
FROM users
WHERE created >= DATE(DATE_SUB(NOW(),INTERVAL 30 DAY))
GROUP BY reg_date
ORDER BY reg_date DESC
The following is comments on update question, since it seems problem is in PHP code.
I do not fully understand why you would mix strtotime functionality with DateTime, DateInterval, DatePeriod. It is good to see that you are using those though as those are drastically underused by many developers.
That being said I might rewrite that function as:
function generate_calendar_days($start = 'today', $days = 30, $days_in_past = true) {
$dates = array();
try {
$current_day = new DateTime($start); // time set to 00:00:00
} catch (Exception $e) {
echo ('Failed with: ' . $e->getMessage());
return false;
}
$interval = new DateInterval('P1D');
if (true === $days_in_past) {
$interval->invert = 1; // make days step back in time
}
$range = new DatePeriod($current_day, $interval, $days);
foreach($range as $date) {
$dates[] = $date->format('Y-m-d');
}
return $dates;
}
Note that here I have added parameters to make your function more flexible. I also only return an array of date strings so as to make the the function more general purpose. You can leave how to work with the array of dates as an implementation detail outside the scope of this function.
Your zero-filled array can easily be constructed outside the function call like this:
$calendar = array_fill_keys(generate_calendar_days(), 0);

Your sentence is perfect, in fact SELECT (NOW() - INTERVAL 30 DAY) returns 2013-12-18 22:33:30. I experimented similar odd problems, and it was because our DDBB server had a different time configuration than our Apache Server, and it gaves us weird results.
Check your servers time configuration, (http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html)

You can see from the comments in the PHP manual that people have had the trouble including the end date when iterating a DatePeriod. There are various modifications suggested there that can help with that, but for what you're doing you don't really need the end date, since you're always just going back a set number of days from the current date.
You can include the end date by using the "recurrences" form of the DatePeriod constructor.
function generate_calendar_days(int $n): array
{
$range = new DatePeriod(new DateTime("-$n day"), new DateInterval('P1D'), $n);
foreach($range as $date) {
$filler[$date->format('Y-m-d')] = 0;
}
return $filler;
}
$days = generate_calendar_days(30);

Related

Date & time comparison operators >= not showing dates that are equal

I am trying to show events that occur either today or on a later date where today is specifically the problem.
public function getInspirationsMeetingIds()
{
$ids = [];
if (($inspirationMeetings = $this->getCustomField('meetings'))) {
foreach ($inspirationMeetings as $meeting) {
$row = new Inspiration($meeting['meeting']);
$dateFrom = $row->getCustomField('date');
if (strtotime($dateFrom) >= time()) {
$ids[] = $row->getId();
}
}
}
return $ids;
}
For some reason this will only show events that are greater than time() and not the events that are today, but then when i try this:
if (strtotime($dateFrom) <= time()) {
$ids[] = $row->getId();
}
Today's and older events are shown.
I think you need to add a timestamp to your datefrom.
Strtotime will add noon if time is omitted.
See this example https://3v4l.org/cYKO4
if (strtotime($dateFrom ) >= strtotime(date("Y-m-d 00:00"))) {
Will make it show all of datefrom
Edit added the 00:00 at the wrong side
Use the DateTime class http://php.net/manual/en/class.datetime.php
time() gives seconds since Jan 1st 1970. The chance that you hit the exact second is very small, so it will hardly ever match.
Instead, create a date with the time.
$date = new DateTime($dateFrom); // or DateTime::createFromFormat($format, $dateFrom);
$today = new DateTime();
if ($date >= $today) {
// should work
}

how may days are between two dates in specific year

I'm solving following task>
I have two dates - $start and $end and target year as $year.
dates are php DateTime objects, year is string.
add:dates comes acutaly from MySql field from this format 2017-02-01 15:00:00 ...
add2: if end date is null, I use todays date ...
I need to figure out how many days are between these two dates for specific year.
Also I need to round it for whole days, even if one minute in day should be counted as whole day ...
I can solve it by many many following ifs.
Expected results for values I used in example are
2016 is 0 days
2017 is 31 days
2018 is 32 days
2019 is 0 days
But are there any elegant php functions which can help me with this ?
What I did it seems to be wrong way and giving bad results - seems it counts full days only ...
Please see my code here >
<?php
$diff = True;
$start = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-01 23:05:00');
$end = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-03 00:05:00');
$year = '2017';
// start date
if ($start->format('Y')<$year)
{
$newstart = new DateTime('first day of January '. $year);
}
if ($start->format('Y')==$year)
{
$newstart = $start;
}
if ($start->format('Y')>$year)
{
$result = 0;
$diff = False;
}
// end date
if ($end->format('Y')>$year)
{
$newend = new DateTime('last day of December '. $year);
}
if ($end->format('Y')==$year)
{
$newend = $end;
}
if ($end->format('Y')<$year)
{
$result = 0;
$diff = False;
}
// count if diff is applicable
if ($diff)
{
$result = $newend->diff($newstart)->format("%a");
}
echo $result;
?>
But are there any elegant php functions which can help me with this ?
Read about DateTime::diff(). It returns a DateInterval object that contains the number of days (in $days) and by inspecting the values of $h, $i and $s you can tell if you have to increment it to round the result. You can also use min() and max() to crop the time interval to the desired year.
function getDays(DateTimeInterface $start, DateTimeInterface $end, $year)
{
// Extend the start date and end date to include the entire day
$s = clone $start; // Don't modify $start and $end, use duplicates
$s->setTime(0, 0, 0);
$e = clone $end;
$e->setTime(0, 0, 0)->add(new DateInterval('P1D')); // start of the next day
// Crop the input interval to the desired year
$s = min($s, new DateTime("$year-01-01 00:00:00"));
$year ++;
$e = max(new DateTime("$year-01-01 00:00:00"), $end); // start of the next year
if ($e <= $s) {
// The input interval does not span across the desired year
return 0;
}
// Compute the difference and return the number of days
$diff = $e->diff($s);
return $diff->days;
}
$d1 = strtotime('2017-05-15');
$d2 = strtotime('2017-05-31');
$div = 24 * 3600;
echo abs(($d2 - $d1) / $div); // 16 days
Just make sure and ONLY have the date part and you shouldn't have to deal with rounding.

php days between two dates list

Do you know what the problem is by looking at the code?
I would be happy if you helped me:
list($from_day,$from_month,$from_year) = explode(".","27.09.2012");
list($until_day,$until_month,$until_year) = explode(".","31.10.2012");
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
$iDateTo = mktime(0,0,0,$until_month,$until_day,$until_year);
while ($iDateFrom <= $iDateTo) {
print date('d.m.Y',$iDateFrom)."<br><br>";
$iDateFrom += 86400;
}
Date of writing the same problem 2 times
October (31) for writing 2 times in history draws the ends October 30th: (
27.09.2012
28.09.2012
...
26.10.2012
27.10.2012
[[28.10.2012]]
[[28.10.2012]]
29.10.2012
30.10.2012
Your problem is because you have set time to 00:00:00, set it to 12:00:00. That is because the Daylight saving time.
Stop using date() function, use Date and Time classes.
Solution (PHP >= 5.4):
$p = new DatePeriod(
new DateTime('2012-09-27'),
new DateInterval('P1D'),
(new DateTime('2012-10-31'))->modify('+1 day')
);
foreach ($p as $d) {
echo $d->format('d.m.Y') . "\n";
}
Solution (PHP < 5.4)
$end = new DateTime('2012-10-31');
$end->modify('+1 day');
$p = new DatePeriod(
new DateTime('2012-09-27'),
new DateInterval('P1D'),
$end
);
foreach ($p as $d) {
echo $d->format('d.m.Y') . "\n";
}
You have daylight savings time issues. Adding seconds from one timestamp to another is prone to problems around these sorts of edge conditions (leap days can be problematic is well), You should get in the habit of using PHP's DateTime and DateInterval objects. It makes working with dates a snap.
$start_date = new DateTime('2012-09-27');
$end_date = new DateTime('2012-10-31');
$current_date = clone $start_date;
$date_interval = new DateInterval('P1D');
while ($current_date < $end_date) {
// your logic here
$current_date->add($date_interval);
}
My idea for solving this would be something like this;
$firstDate = "27.09.2012";
$secondDate = "31.10.2012";
$daysDifference = (strtotime($secondDate) - strtotime($firstDate)) / (60 * 60 * 24);
$daysDifference = round($daysDifference);
for ($i = 0; $i <= $daysDifference; $i++)
{
echo date("d.m.Y", strtotime('+'.$i.' day', strtotime($firstDate))) . "<BR>";
}
This should solve your problem and be much easier to read (imho). I've just tested the code, and it outputs all dates and no doubles. It also saves you from all the daylight savings inconsistencies.
I don't know where you're from, but it's likely you're hitting daylight saving changeover in your timezone (it's Nov 4th where I live - exactly one week after Oct 28th). You can not rely on a day being exactly 86400 seconds long.
If you loop incrementing with mktime, you should be fine:
list($from_day,$from_month,$from_year) = explode(".","27.09.2012");
list($until_day,$until_month,$until_year) = explode(".","31.10.2012");
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
$iDateTo = mktime(0,0,0,$until_month,$until_day,$until_year);
while ($iDateFrom <= $iDateTo)
{
print date('d.m.Y',$iDateFrom)."<br><br>";
$from_day = $from_day + 1;
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
}
Even though $from_day will likely be going well over 31, mktime will make the math conversion for you. (ie 32 days in a 31 day month = day 1 of the next month)
EDIT: sorry, I had the incrementation in the wrong place.

Need to add number of months in date

I need a method for adding some number of months to any date in PHP. I know how to do this in MySQL but not in PHP. Here's my attempt:
MySQL:
SELECT DATE_ADD( '2011-12-29', INTERVAL 2
MONTH ) // Output "2012-02-29"
SELECT DATE_ADD( '2011-12-30', INTERVAL 2
MONTH ) // output "2012-02-29"
SELECT DATE_ADD( '2011-12-31', INTERVAL 2
MONTH ) // output "2012-02-29"
PHP:
$date = date_create('2011-12-29');
$date->modify("+1 month");
echo $date->format("Y-m-d");
// Output is "2012-01-29" -- this is correct
$date = date_create('2011-12-30');
$date->modify("+2 month");
echo $date->format("Y-m-d");
// Output is "2012-03-01" -- I need the answer like "2012-02-29"
$date = date_create('2011-12-31');
$date->modify("+2 month");
echo $date->format("Y-m-d");
// Output is "2012-03-02" -- I need the answer like "2012-02-29"
The MySQL output is correct. I need the same output in PHP.
If you use PHP5 >= 5.3, all you need to do is use
$date->modify("last day of +2 months");
as suggested in other answers. But if you use 5.2 you could try altering your code like this:
Class DateTimeM Extends DateTime
{
public function modify ($modify)
{
$day = $this->format ('d');
$buf = new DateTime ($this->format ('Y-m-01\TH:i:sO'));
$buf->modify ($modify);
if ($day > $buf->format ('t'))
{
$this->setDate ($buf->format ('Y'), $buf->format ('m'), $buf->format ('t'));
}
else
{
$this->setDate ($buf->format ('Y'), $buf->format ('m'), $day);
}
$this->setTime ($buf->format ('H'), $buf->format ('i'), $buf->format ('s'));
return $this;
}
}
$date = new DateTimeM ('2011-12-29');
$date->modify("+2 month");
echo $date->format("Y-m-d");
I suggest adding the class definition to a separate file and require_once() it. Switch from date_create() to using the new class's object constructor. The new class's modify() method will modify the date using the first day of the original given month instead of the last and check if the original given day of month is larger than the new month's number of days.
A benefit of this approach is that it will work for say $date->modify ('2 year 2 month') as well.
Here's a solution that might do the job for you:
function addMonths(DateTime $date, $months) {
$last = clone $date;
$last = $last->modify("last day of +$months months")->getTimestamp();
$default = clone $date;
$default = $default->modify("+$months months")->getTimestamp();
return $date->setTimestamp(min($last, $default));
}
$date = new DateTime('2011-12-31');
$laterDate = addMonths($date, 2);
This will work regardless of which day of the month you start with.
Hope it surely helps you.
I just try with adding days instead of adding months
$MonthAdded = strtotime("+60 days",strtotime('2011-12-31'));
echo "After adding month: ".date('Y-m-d', $MonthAdded)."<br>";
Output:
After adding month: 2012-02-29
Read the link Dagon posted in the comments to your question. Extrapolating on the answer there, I tried this and it works:
$d = new DateTime("2011-12-31");
$d->modify("last day of +2 months");
echo $d->format("Y-m-d");
// result is 2012-02-29
$d = new DateTime("2012-12-31");
$d->modify("last day of +2 months");
echo $d->format("Y-m-d");
// result is 2013-02-28

Fill Out The Gaps Between Two Times With PHP

I've got to write a loop that should start and end between two times. I know there are many ways to skin this cat, but I'd like to see a real programmers approach to this function.
Essentially I have Wednesday, for instance, that opens at 6:00pm and closes at 10:30pm.
I'm looking to write a loop that will give me a table with all of the times in between those two in 15 minute intervals.
So, I basically want to build a one column table where each row is
6:00pm
6:15pm
7:15pm
etc...
My two variables to feed this function will be the open time and the close time.
Now don't accuse me of "write my code for me" posting. I'll happily give you my hacked solution on request, I'd just like to see how someone with real experience would create this function.
Thanks :)
$start = new DateTime("2011-08-18 18:00:00");
$end = new DateTime("2011-08-18 22:30:00");
$current = clone $start;
while ($current <= $end) {
echo $current->format("g:ia"), "\n";
$current->modify("+15 minutes");
}
Try it on Codepad: http://codepad.org/JwBDOQQE
PHP 5.3 introduced a class precisely for this purpose, DatePeriod.
$start = new DateTime("6:00pm");
$end = new DateTime("10:30pm");
$interval = new DateInterval('PT15M');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $time) {
echo $time->format('g:ia'), PHP_EOL;
}
echo $end->format('g:ia'); // end time is not part of the period
$start = strtotime('2011-08-11 18:00:00');
for ($i = 0; $i < 20; $i++) {
echo date('g:ia', $start + ($i * (15 * 60))), '<br>';
}
I would go with the DateTime functions and increase the time by 15 minutes every loop-turn as long as the current time is lower then the end-time.
EDIT: as user576875 has posted
$start_date = '2019-07-30 08:00:00';
$end_date = '2019-09-31 08:00:00';
while (strtotime($start_date) <= strtotime($end_date)) {
echo "$start_date<br>";
$start_date = date ("Y-m-d H:i:s", strtotime("+1 hours", strtotime($start_date)));
}

Categories