So, uh, ok. This might get mathematical, so hope you brought your scientific calculator with you ;)
This is my problem:
Given an initial date (timestamp), time period period (seconds) and today's date (timestamp), I need to find the nearest date which coincides with the period*n plus the original/initial date.
So far, I got some stuff working nicely, such as the amount of "periods" between the initial and final(today's) date, which would be "2" in the demo above:
$initial=strtotime('2 April 1991');
$time=time();
$period=strtotime('+10 years',0);
$periods=round(($time-$initial)/$period);
The next thing I did was:
$range=$periods*$period;
And finally:
echo date('d M Y',$initial+$range);
Which wrote '03 April 2011'. How did it get to 3? (I suspect it's a leap year issue?)
You know that feeling when you're missing something small? I'm feeling it all over me right now....
Ok so if I understood what you are asking, you want to know the next date that will occurs in a given period of time (in your case, every 10 years starting from 2 April 1991, when will be the next date : 2 april 2011).
So, you should take a deeper look at the DateTime class in PHP that is wayyyy better to use for the dates because it is more accurate. You mix it with DateInterval that match exactly what you need :
<?php
$interval = new DateInterval('P10Y'); // 10 years
$initial = new DateTime('1991-04-02');
$now = new DateTime('now');
while ($now->getTimestamp() > $initial->getTimestamp()) {
$initial = $initial->add($interval);
}
echo $initial->format('d M Y'); // should return April 2, 2011 !
?>
Try this out:
$current = $initial = strtotime('2 April 1991');
$time_span = '+10 years';
while ($current < time())
{
$current = strtotime($time_span, $current);
}
echo date('d M Y', $current);
What happened:
+10 years from Year 0 (1970) will include 3 leap years '72, '76 and '80, but from '91 till '11 there are only five leap years '92, '96, '00, '04 and '08. You added that period twice, so because there weren't 6 leap years you got one extra day.
What you need to do:
Ad the period with strtotime one step at a time.
$period = "+10 years";
$newTime = $startingTime;
while(<condition>){
$newTime = strtotime($period, $newTime);
}
As a fix to cx42net's answer:
<?php
$initial = new DateTime('2 April 1991');
$now = new DateTime('now');
$interval = new DateInterval('P10Y');
$curDate = $initial;
while (true) {
$curDate = $curDate->add($interval);
$curDiff = $curDate->diff($now)->days;
if (isset($lastDiff) && ($curDiff > $lastDiff)) {
echo $lastDate->format('d M Y');
break;
} else {
$lastDate = clone $curDate;
$lastDiff = $curDiff;
}
}
Related
I want to find entries in a month range based on date of a month
like if a user registered on 20th of a month, the script should get entries in a range from last 20th to next 20th of the month.
I.e if the script is running on any day before 20th of April the range should be March 20 - April 20, and if its running on 20th April or after then the range should be April 20 - May 20.
I looked up relative formats but it only lists functions for day names and weeks etc.
Is there any way the relative date format works like last n to next n. where n= 1 to 31.
Can anyone help? Thanks
Based on comment from Cully, here is an implementation (it still feels too messy, maybe there is an easier way to do it). It may explain the question a bit more.
function getFromDate($myDate, $nDate)
{
// sub 1 day till date is $nDate
while(true)
{
if($myDate->format('d')==$nDate)
break;
$myDate->sub(new DateInterval('P1D'));
}
return $myDate;
}
function getToDate($myDate, $nDate)
{
// add 1 day till date is $nDate
while(true)
{
$myDate->add(new DateInterval('P1D'));
if($myDate->format('d')==$nDate)
break;
}
return $myDate;
}
$timestamp = 1602107066; // An example user registration date, 7 October 2021
$nDate = gmdate("d", $timestamp);
$fromDate = getFromDate(new DateTime('now'), $nDate);
$toDate = getToDate(new DateTime('now'), $nDate);
echo $fromDate->format('d M y')."\r\n"; // 7 May 2021 (run on May 22 2021)
echo $toDate->format('d M y'); // 7 June 2021 (run on May 22 2021)
Do you mean something like this? It might not be exactly what you want, but can you use it to create what you want?
<?php
$numberOfDaysIWant = 20;
// March 20th, 2021, but you can use any date you want
$startDate = new DateTimeImmutable('2021-03-20');
$myPastDates = [];
$myFutureDates = [];
$currentDate = $startDate;
for($i = 0; $i < $numberOfDaysIWant; $i++) {
$currentDate = $currentDate->sub('P1D');
$myPastDates []= $currentDate;
}
$currentDate = $startDate;
for($i = 0; $i < $numberOfDaysIWant; $i++) {
$currentDate = $currentDate->add('P1D');
$myFutureDates []= $currentDate;
}
var_dump($myPastDates, $myFutureDates);
It's unclear from your question, but it sounds like maybe you want to get the $numberOfDaysIWant value based on the date of the selected month. If so, you could use this to get it:
<?php
$startDate = new DateTimeImmutable('2021-03-20');
$numberOfDaysIWant = (int) $startDate->format('j'); // "j" gives you the day of the month, without leading zeroes
How to manipulate the date and exclude saturday and sunday?. The objective is, I need to create a cron job that will run and execute on datas that were created 5 days ago,"BUT", saturday and sunday shouldn't be included in that 5 days period.
here's what I have so far
$numdays = 5;
$today = strtotime('now');
$start = date('Y-m-d',strtotime('-'.$numdays.' day',$today));
echo $start;
if you try to run my code snippet above, it will show you the exact date 5 days ago 2016-02-10. But that one doesn't "exclude" saturday and sunday in the computation. it should be be 2016-02-08. So how to do that?
You can use PHP's date week of day, there are several versions, here is one using N:
<?php
$current = new DateTime();
$interval = new DateInterval('P1D');
$x = 5;
while ($x > 1) {
// Check if day of week is not saturday/sunday (1 => Monday ... 7 -> Sunday)
if ($current->format('N') >= 6) {
$x++;
}
$current->sub($interval);
$x--;
}
echo $current->format('Y-m-d') . PHP_EOL;
Example Run.
You can get a whole week and discard the weekends, keeping the furthest element in the array as a result.
$days = array_filter(array_map(function ($daysBack) {
$date = new \DateTimeImmutable("$daysBack days ago", new \DateTimeZone('UTC'));
return (!in_array($date->format('N'), [6, 7])) ? $date : null;
}, Range(1, 7)));
$fiveWorkingDaysAgo = end($days);
I have a database of events that are both "static" (on a specific day) and "recurring" (starting on a specific day, but set to be "recurring" either every week or every other week). I understand that there needs to be intervals of 7 and 14, but I don't know how to get to that point to find a date > today and spit it out.
so example;
I want to find the next upcoming recurring events and spit out their relevant dates. Side note: the data I'm stuck working with is in strings (I know, I know) of Ymd so 20150821 would be Aug 21st 2015.
if today was Aug 21 2015 and there's a recurring event for "Every other Friday" starting on Aug 7, it would be +14 which would get you to today, Friday Aug 21.
But say there was one for "Every Wednesday" starting Aug 19, I'd want to get the date for Wednesday, Aug 26 and spit that out.
This would need to work for infinite dates in the future, with the start dates never changing.
So running the script on Jan 1 2016, I'd need to know that the next "Every Wednesday" was Jan 6 2016.
pseudo code:
if(start_date < today_date) {
// start date is in the past
add recurring_inverval to start_date until result >= today_date
echo result
} elseif(start_date > today_date {
// start date is in the future
echo start_date
}
it's the adding until x that I'm lost at. not sure how to do that within an if statement.
also not sure if that's the best way to go about it. I know PHP can also do complicated strings and convert them to a date. like "Next Saturday"
I know you answered this yourself, however another option is to just use the modulus (remainder after devision) of the current date minus the start date to compute your next date. Here is a quick script to do just that :
<?php
function nextDate($start_date,$interval_days,$output_format){
$start = strtotime($start_date);
$end = strtotime(date('Y-m-d'));
$days_ago = ($end - $start) / 24 / 60 / 60;
if($days_ago < 0)return date($output_format,$start);
$remainder_days = $days_ago % $interval_days;
if($remainder_days > 0){
$new_date_string = "+" . ($interval_days - $remainder_days) . " days";
} else {
$new_date_string = "today";
}
return date($output_format,strtotime($new_date_string));
}
echo nextDate('20151210',14,'Ymd') . "<br />";
echo nextDate('20150808',14,'Ymd') . "<br />";
?>
You also don't want to return an early date if the "start date" is in the distant future. Code updated to prevent that.
solved it myself:
$begin = new DateTime($start_date); // start searching on the start date of the event
$end = new DateTime(date('Ymd')); // end on today's date
$end = $end->modify('+1 month'); // extend search to the next month
$interval = new DateInterval('P'. $freq_math .'D'); // intervals of Plus X Days - uses frequency math of 7 or 14 for every or every other
$daterange = new DatePeriod($begin, $interval, $end);
foreach($daterange as $date) {
if($date->format('Ymd') >= date('Ymd')) {
$next_recurring_date = $date->format('Ymd');
break;
}
}
echo $next_recurring_date;
Good Day,
I am trying to create a recurring date system that has the following:
nth day of nth month (2nd day of every 3rd month)
$this_months_friday = strtotime('+3 days +4 months');
the output of that will always be current day + 3 days of the 4th month.
how do I get it to display the nth day of the nth month?
since i also tried
$this_months_friday = strtotime('every 3 days +4 months');
and it did not return any result. Should i stick with strtotime on this one or move to DateTime function of php. though i wont still be able to formulate the proper argument for that kind of date sequence.
Any help would be greatly appreciated.
Thank You.
Probably better off using DateTime with a couple intervals:
$d = new DateTime();
$d->add(new DateInterVal('P' . $days . 'D'))->add('new DateInterVal('P' . $months . 'M'));
not sure what youre two example intervals are wanting.
You want an internval to start in 4 months, which then repeats every 3 days?
That'd be something more like
$d = new DateTime();
$d->add(new DateInterval('P4M')); // jump ahead 4 months immediately
$day3 = new DateInterval('P3D');
for ($i = 0; $i < 100; $i++) {
$d->add($day3); // jump ahead 3 days
... do something with this new date
}
for a basic recurring event, +4 months + 3 days, you'd simply have one interval:
$interval = new DateInteval('P4M3D'); // +4 months +3 days
$date = new DateTime();
while($some_condition) {
$date->add($interval);
do_something();
}
You can do this by saving the values in variables like that :
$day=3;
$month=4;
echo date("d-m-y",strtotime('+'.$day .'days' .'+'.$month.'months'));
Explanation:
7(july)+4 months = 11 month(November)
8 july+ 3 days = 11 july
Output:
11-11-13
NOTE: just for the example I have put the values hard coded, You can make them dynamic.
I need to find a way to get the date of the monday of a week based on another day. So for example, if wednesday is 2011-05-11, i need to somehow tell it that monday of that week was 2011-05-09. Can anyone help?
$res looks like this:
stdClass Object
(
[link_count] => 1
[day] => 2011-05-12
[weekday] => Thu
)
$days = array(1=> 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');
foreach($res as $r) {
$day = date('N', strtotime($r->day));
$r->weekday = $days[$day];
if($r->weekday == 'Mon') {
$r->weekstarting = $r->day;
} else {
// here's the problem right here, I need to find a way to tell it
// what it's starting day is
$r->weekstarting = ;
}
}
Seems like you have the weekday (like 'Thu'), so you need to get is position in your $days array.
You can get the key with $key=array_search($r->weekday, $days); (returns 4).
Now you know the difference from Monday, so you just have to create the date:
$tempArray=explode('-', $r->day);
$tempArray[2]-=$key-1;
$r->weekstarting = implode('-', $tempArray);
EDIT: Now I see that you already had the array index in $day. So you could use that value instead of the $key I had.
EDIT 2: If you don't want to lose the leading zeros in the day, you could use sprintf() on it .
$tempArray=explode('-', $r->day);
$tempArray[2]=sprintf('%02d', $tempArray[2]-($key-1));
$r->weekstarting = implode('-', $tempArray);
And finally the LAST UPDATE. I was thinking about deleting the whole post and rewriting it again, but I will leave it here for reference... somebody might learn from my mistake.
So the final code that will correctly adjust months also:
list($y, $m, $d)=explode('-', $r->day);
$timestamp=mktime(0,0,0,$m,$d-($day-1),$y);
$r->weekstarting = date('Y-m-d', $timestamp);
Actually this is what I wanted to avoid, converting to a timestamp, but I could not. The trick I'm using here is that mktime() will handle negative numbers for days and calculate the timestamp correctly.
This seems to work fine:
<?php
$dt = new DateTime("11 May 2011");
$dt->modify("last monday");
edit: I remember seeing reports that some people had problems with the "last" modifier (windows users maybe) so this should work if you have problems with that:
<?php
$dt = new DateTime("12 May 2011");
$days = (int)$dt->format('N') - 1;
$dt->modify("-{$days} days");
Please create a testfile and execute it:
<?php
$dates = array(
'11 May 2011',
'2011-04-26',
'2011-05-04',
);
foreach($dates as $date) {
$dt = new DateTime($date);
$dt->modify("last monday");
printf("For %s last monday is %s.\n", $date, $dt->format('Y-m-d'));
}
?>
it should output the following:
For 11 May 2011 last monday is 2011-05-09.
For 2011-04-26 last monday is 2011-04-25.
For 2011-05-04 last monday is 2011-05-02.
In case not, please add your output to your question above.
I'd do something like this:
$dow = date('N', strtotime($r->day)) - 1; // 0 will be Monday, 1 Tuesday, etc.
$start = mktime() - ($dow * 86400); // 86400 is one day in seconds
$startday = date('r', $start);