I'm not trying to do this with MySQL as I see is common. I am merely trying to create a PHP Script that will display information like this using the servers time and date as a reference:
Monday 2:30am to Tuesday 2:29am - Content A
Tuesday 2:30am to Wednesday 2:29am - Content B
Wednesday 2:30am to Thursday 2:29am - Content C
and so on....
Until every day of the week in covered, for some reason I can't seem to nail this and there doesn't seem to be and examples to build from. Is this for some reason not possible?
Edit
I've used the following code and it's proving to not be as reliable as it should be.
function isInTimeWindow($dayName, $startHour, $startMinute, $endHour, $endMinute)
{
$dayName = ucfirst($dayName);
list($dayNow, $timeNow) = explode('|', date('l|Hi'));
return (
ucfirst($dayName) == $dayNow and
$timeNow >= sprintf('%02d%02d', $startHour, $startMinute) and
$timeNow <= sprintf('%02d%02d', $endHour, $endMinute)
);
}
// tuesdays during day
if(isInTimeWindow('tuesday', 02, 30, 23, 00)) {
?>
CONTENT
<?php } ?>
you could use date(l) php manual date() function function to get the actual weekday in words (Monday, Tuesday, ... ) so you can check the weekday.
Use mktime function php manual mktime() function to convert your times ( Monday, 2:30am for example ) into timestamps and check if it is between current time by using time function php manual time() function
EDIT: Here's an example:
$today_start = mktime(2, 30, 0, date('m'), date('d'), date('Y'));
$today_end = mktime(2, 29, 0, date('m'), (date('d') + 1), date('Y'));
if ( $today_start < time() && $today_end > time() )
// code
mktime works like this: mktime( hour, minutes, seconds, month, day, year )
So long as you're happy using the server's time rather than the client's time this can be done using the getdate() function.
For example, if you just wanted to do it based on day of the week:
<?php
$now = getdate();
switch($now['wday']) {
case 0:
?>
Content A
<?
break;
etc.
Edit: here is how to do it in more detail:
getdate() provides you with information about the hour and minute parts too.
You could have a function like :
// Get's the 0-indexed day of the week offset so each "day" starts and 2:30am
function get_offset_day() {
$now = getdate();
if ($now['hours']*60 + $now['minutes'] < 150) { // less 2h 30m after midnight => still "yesterday"
$answer = $now['wday']-1;
}
else {
$answer = $now['wday'];
}
if ($answer < 0) return 6;
return $answer;
}
Related
In a site I am creating, I want to store member data and in that data I have a start/join date which is easy to get, but I want to automatically calculate the expiry date which I'm having problems with..
Basically all new members will expire on the last day of February each year, so if I join on say 1st Feb 2013 my expiry will be on 28/02/2014, if I join on 1/03/13 or 20/12/13 my expiry will still be on 28/02/2014. ((I don't mind too much about the 1 day that appears on leap years))
Does anyone know how I can work this out - I know it's probably something obvious but I just cant seem to grasp it! :/
(I'll be doing this in php)
Many thanks in advance,
Chris
Assuming that you have (or can get) their registration date in a Unix timestamp format, you could do the following:
function calculateExpiry($reg_date)
{
$next_year = strtotime('+1 year', $reg_date);
$feb_days = ((($next_year % 4) == 0) && ((($next_year % 100) != 0) || (($next_year % 400) == 0))) ? 29 : 28;
return strtotime($feb_days.' February '.$next_year);
}
This will always return the last day of February for the following year in a Unix timestamp, so you can format it how you like. I think this logic is suitable, see the following use cases:
Register: 01/01/2013, Returns: 28/02/2014
Register: 09/10/2013, Returns: 28/02/2014
Register: 31/12/2013, Returns: 28/02/2014
Register: 01/01/2014, Returns: 28/02/2015
This should do the trick too :).
function calculate_expiry( $rawDate ) {
// Convert data into usable timestamp
$signupDate = strtotime( $signupDate );
$cutoffYear = date('Y', $signupDate) + 1;
// Set the expiry to be the last day of Feb (the first day of March -1)
$expiryDate = new DateTime();
$expiryDate->setTimestamp( mktime( 0, 0, 0, 3, 1, $cutoffYear ) );
$expiryDate->sub( new DateInterval('P1D') );
}
Well, we will get the current signup date by doing...
$signup_date = date("m-d-Y"); // or time()
And then offset it by another year
$expire_date = date("m-d-Y", strtotime("+1 year"))
I am trying to get stripe to set a end_trial date on the next occurrence of whatever day of the month the user chooses. i.e. If today is the 16th and the user chooses the 15th I need the unix timestamp for the 15th of the next month. However if today was the 14th I need the timestamp for tomorrow.
I tried the solution found on this SO question Find the date for next 15th using php .
When i ran the code suggested in that question and substituted 15 for 31
$nextnth = mktime(0, 0, 0, date('n') + (date('j') >= 31), 31);
echo date('Y-m-d', $nextnth);
The result is 2013-03-03
I also tried this one Get the date of the next occurrence of the 18th .
The second one would actually give me 2013-03-31 when i ran it one 2013-1-31.
Both had unexpected results. Is february the problem? Any guidance will be much appreciated.
Here is a way to do it.
function nextDate($userDay){
$today = date('d'); // today
$target = date('Y-m-'.$userDay); // target day
if($today <= $userDay){
$return = strtotime($target);
}
else{
$thisMonth = date('m') + 1;
$thisYear = date('Y');
if($userDay >= 28 && $thisMonth == 2){
$userDay = 28;
}
while(!checkdate($thisMonth,$userDay,$thisYear)){
$thisMonth++;
if($thisMonth == 13){
$thisMonth = 1;
$thisYear++;
}
}
$return = strtotime($thisYear.'-'.$thisMonth.'-'.$userDay);
}
return $return;
}
// usage
echo date('Y-m-d',nextDate(29));
We get the user's choice and compare it today.
If today is less than or equal to user choice, we return the timestamp for this month.
If today is greater than user choice, we loop through dates, adding a month (or a year if it's $thisMonth hits 13). Once this date does exist again, we have our answer.
We check the dates using php's checkdate function, strtotime and date.
I really don't understand the question completely. You can easily determine the date for next 30 days for example
$next_ts = time() + 30 * 86400; // add 30 days to current timestamp
$next = date('Y-m-d', $next_ts); // format string as Y-m-d
echo $next;
If that is not what you need, please explain the problem.
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 know about the unwanted behaviour of PHP's function
strtotime
For example, when adding a month (+1 month) to dates like: 31.01.2011 -> 03.03.2011
I know it's not officially a PHP bug, and that this solution has some arguments behind it, but at least for me, this behavior has caused a lot waste of time (in the past and present) and I personally hate it.
What I found even stranger is that for example in:
MySQL: DATE_ADD('2011-01-31', INTERVAL 1 MONTH) returns 2011-02-28
or
C# where new DateTime(2011, 01, 31).AddMonths(1); will return 28.02.2011
wolframalpha.com giving 31.01.2013 + 1 month as input; will return Thursday, February 28, 2013
It sees to me that others have found a more decent solution to the stupid question that I saw alot in PHP bug reports "what day will it be, if I say we meet in a month from now" or something like that. The answer is: if 31 does not exists in next month, get me the last day of that month, but please stick to next month.
So MY QUESTION IS: is there a PHP function (written by somebody) that resolves this not officially recognized bug? As I don't think I am the only one who wants another behavior when adding / subtracting months.
I am particulary interested in solutions what also work not just for the end of the month, but a complete replacement of strtotime. Also the case strotime +n months should be also dealt with.
Happy coding!
what you need is to tell PHP to be smarter
$the_date = strtotime('31.01.2011');
echo date('r', strtotime('last day of next month', $the_date));
$the_date = strtotime('31.03.2011');
echo date('r', strtotime('last day of next month', $the_date));
assuming you are only interesting on the last day of next month
reference - http://www.php.net/manual/en/datetime.formats.relative.php
PHP devs surely don't consider this as bug. But in strtotime's docs there are few comments with solutions for your problem (look for 28th Feb examples ;)), i.e. this one extending DateTime class:
<?php
// this will give us 2010-02-28 ()
echo PHPDateTime::DateNextMonth(strftime('%F', strtotime("2010-01-31 00:00:00")), 31);
?>
Class PHPDateTime:
<?php
/**
* IA FrameWork
* #package: Classes & Object Oriented Programming
* #subpackage: Date & Time Manipulation
* #author: ItsAsh <ash at itsash dot co dot uk>
*/
final class PHPDateTime extends DateTime {
// Public Methods
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Calculate time difference between two dates
* ...
*/
public static function TimeDifference($date1, $date2)
$date1 = is_int($date1) ? $date1 : strtotime($date1);
$date2 = is_int($date2) ? $date2 : strtotime($date2);
if (($date1 !== false) && ($date2 !== false)) {
if ($date2 >= $date1) {
$diff = ($date2 - $date1);
if ($days = intval((floor($diff / 86400))))
$diff %= 86400;
if ($hours = intval((floor($diff / 3600))))
$diff %= 3600;
if ($minutes = intval((floor($diff / 60))))
$diff %= 60;
return array($days, $hours, $minutes, intval($diff));
}
}
return false;
}
/**
* Formatted time difference between two dates
*
* ...
*/
public static function StringTimeDifference($date1, $date2) {
$i = array();
list($d, $h, $m, $s) = (array) self::TimeDifference($date1, $date2);
if ($d > 0)
$i[] = sprintf('%d Days', $d);
if ($h > 0)
$i[] = sprintf('%d Hours', $h);
if (($d == 0) && ($m > 0))
$i[] = sprintf('%d Minutes', $m);
if (($h == 0) && ($s > 0))
$i[] = sprintf('%d Seconds', $s);
return count($i) ? implode(' ', $i) : 'Just Now';
}
/**
* Calculate the date next month
*
* ...
*/
public static function DateNextMonth($now, $date = 0) {
$mdate = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
list($y, $m, $d) = explode('-', (is_int($now) ? strftime('%F', $now) : $now));
if ($date)
$d = $date;
if (++$m == 2)
$d = (($y % 4) === 0) ? (($d <= 29) ? $d : 29) : (($d <= 28) ? $d : 28);
else
$d = ($d <= $mdate[$m]) ? $d : $mdate[$m];
return strftime('%F', mktime(0, 0, 0, $m, $d, $y));
}
}
?>
Here's the algorithm you can use. It should be simple enough to implement yourself.
Have the original date and the +1 month date in variables
Extract the month part of both variables
If the difference is greater than 1 month (or if the original is December and the other is not January) change the latter variable to the last day of the next month. You can use for example t in date() to get the last day: date( 't.m.Y' )
Had the same issue recently and ended up writing a class that handles adding/subtracting various time intervals to DateTime objects.
Here's the code:
https://gist.github.com/pavlepredic/6220041#file-gistfile1-php
I've been using this class for a while and it seems to work fine, but I'm really interested in some peer review. What you do is create a TimeInterval object (in your case, you would specify 1 month as the interval) and then call addToDate() method, making sure you set $preventMonthOverflow argument to true. The code will make sure that the resulting date does not overflow into next month.
Sample usage:
$int = new TimeInterval(1, TimeInterval::MONTH);
$date = date_create('2013-01-31');
$future = $int->addToDate($date, true);
echo $future->format('Y-m-d');
Resulting date is:
2013-02-28
Here is an implementation of an improved version of Juhana's answer above:
<?php
function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
$addMon = clone $currentDate;
$addMon->add(new DateInterval("P1M"));
$nextMon = clone $currentDate;
$nextMon->modify("last day of next month");
if ($addMon->format("n") == $nextMon->format("n")) {
$recurDay = $createdDate->format("j");
$daysInMon = $addMon->format("t");
$currentDay = $currentDate->format("j");
if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
$addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
}
return $addMon;
} else {
return $nextMon;
}
}
This version takes $createdDate under the presumption that you are dealing with a recurring monthly period, such as a subscription, that started on a specific date, such as the 31st. It always takes $createdDate so late "recurs on" dates won't shift to lower values as they are pushed forward thru lesser-valued months (e.g., so all 29th, 30th or 31st recur dates won't eventually get stuck on the 28th after passing thru a non-leap-year February).
Here is some driver code to test the algorithm:
$createdDate = new DateTime("2015-03-31");
echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;
$next = sameDateNextMonth($createdDate, $createdDate);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
foreach(range(1, 12) as $i) {
$next = sameDateNextMonth($createdDate, $next);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
}
Which outputs:
created date = 2015-03-31
next date = 2015-04-30
next date = 2015-05-31
next date = 2015-06-30
next date = 2015-07-31
next date = 2015-08-31
next date = 2015-09-30
next date = 2015-10-31
next date = 2015-11-30
next date = 2015-12-31
next date = 2016-01-31
next date = 2016-02-29
next date = 2016-03-31
next date = 2016-04-30
I have solved it by this way:
$startDate = date("Y-m-d");
$month = date("m",strtotime($startDate));
$nextmonth = date("m",strtotime("$startDate +1 month"));
if((($nextmonth-$month) > 1) || ($month == 12 && $nextmonth != 1))
{
$nextDate = date( 't.m.Y',strtotime("$initialDate +1 week"));
}else
{
$nextDate = date("Y-m-d",strtotime("$initialDate +1 month"));
}
echo $nextDate;
Somewhat similar to the Juhana's answer but more intuitive and less complications expected. Idea is like this:
Store original date and the +n month(s) date in variables
Extract the day part of both variables
If days do not match, subtract number of days from the future date
Plus side of this solution is that works for any date (not just the border dates) and it also works for subtracting months (by putting - instead of +).
Here is an example implementation:
$start = mktime(0,0,0,1,31,2015);
for ($contract = 0; $contract < 12; $contract++) {
$end = strtotime('+ ' . $contract . ' months', $start);
if (date('d', $start) != date('d', $end)) {
$end = strtotime('- ' . date('d', $end) . ' days', $end);
}
echo date('d-m-Y', $end) . '|';
}
And the output is following:
31-01-2015|28-02-2015|31-03-2015|30-04-2015|31-05-2015|30-06-2015|31-07-2015|31-08-2015|30-09-2015|31-10-2015|30-11-2015|31-12-2015|
function ldom($m,$y){
//return tha last date of a given month based on the month and the year
//(factors in leap years)
$first_day= strtotime (date($m.'/1/'.$y));
$next_month = date('m',strtotime ( '+32 day' , $first_day)) ;
$last_day= strtotime ( '-1 day' , strtotime (date($next_month.'/1/'.$y)) ) ;
return $last_day;
}
(fixed and working fine now, but if anyone still wants to refactor, leave a note)
This is a stripped down version of a function I have which iterates over a date range and assigns a unique integer to each...
When working with large datasets, running this several times over different date ranges, I'm getting a fatal error, assigning too much memory to the script and it dies in this loop...
Fatal error: Allowed memory size of 268435456 bytes exhausted
fixed, was an issue with the iteration not taking into account the potential daylight-savings-time
So, I was wondering if someone could recommend a more optimal way of generating this list of months/ints...
It must allow me to start the Int at whatever number I like and
<?php
// updated: 2010.11.04 with Qwerty's recommendations
// for fixing daylight savings time issue
function monthIterate($monthInt, $startDate, $stopDate) {
$epoch = $startMain = strtotime($startDate);
$stopMain = strtotime($stopDate);
while ($epoch <= $stopMain) {
// get the start/stop dates for "this month"
$start = date("Y-m-01", $epoch);
$stop = date("Y-m-t", $epoch);
// uniqueID for the month
$monthKey = "Month#-".str_pad($monthInt, 3, "0", STR_PAD_LEFT);
$months[$monthKey] = compact('start', 'stop');
// move forward in loop, +1 day should get us to the next month
$epoch = strtotime($stop);
$currentMonth = $nextMonth = date('m', $epoch);
while ($currentMonth == $nextMonth) {
$epoch = $epoch + 86400;
$nextMonth = date('m', $epoch);
}
$monthInt++;
}
return $months;
}
?>
Looks like your function goes in endless loop because of extra hour in light saving time.
echo date('Y-m-d H:i:s', strtotime('2010-10-31') + 60*60*2); // adding 2 hours
echo date('Y-m-d H:i:s', strtotime('2010-10-31') + 60*60*3); // adding 3 hours
both will output 2010-10-31 02:00:00. Thus strtotime('2010-10-31') + 86400 is actually 2010-10-31 23:00:00, but not next day.
So you should add more than 86400 seconds to be sure you switched to next day :-)
I think your array index gets too crazy.
ie :
[Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-Month#-123] => Array
(
[start] => 2011-12-01
[stop] => 2011-12-31
)
I would move your "$monthInt = "Month#-".str_pad($monthInt, 3, "0", STR_PAD_LEFT);" lines outside your loop.
<?php
function monthIterate($monthInt, $startDate, $stopDate) {
$monthInt = "Month#-".str_pad($monthInt, 3, "0", STR_PAD_LEFT); <--
$epoch = $startMain = strtotime($startDate);
$stopMain = strtotime($stopDate);
while ($epoch <= $stopMain) {
// get the start/stop dates for "this month"
$start = date("Y-m-01", $epoch);
$stop = date("Y-m-t", $epoch);
// uniqueID for the month
$months[$monthInt] = compact('start', 'stop');
// move forward in loop, +1 day should get us to the next month
$epoch = strtotime($stop) + 86400;
$monthInt++;
}
return $months;
}?>