Complex date format - php

I am trying to come up with the most efficient and best way to accomplish this somewhat of a complex situation. I know that I could build this solution using probably around 5 if else statements, maybe more - however there must be a better way to accomplish what I want to.
So here's what I am trying to do. I have an events page on my website, and what I want to do is display the dates in a minimalistic way when possible. What I mean is the following:
Say I have 3 dates: May 5, May 6, May 7. I want to display it as: May 5 - 7.
However, there will be situations where the dates may be: May 5, May 7. In this case I would like to display it as: May 5 & 7.
However, there may also be situations where the dates may be: May 25, June 2. In this case I would like to display it as: May 25 & June 2.
However! There also may be situations where the dates may be: May 25, May 26, June 2. In this case it should display as: May 25 - 26 & June 2
Of course, there could just be a single date as well. But one other thing, it could be possible that there could be more than 3 dates as well, so it would be nice if it could work regardless of how many dates there are (IE loop through an array).
I know that we are suppose to make an attempt and show some code to debug, however I don't even know where to start with this, if this is too much for someone to put together - just giving me an idea of how to do something like this efficiently would be a huge help.
Thanks

//input data: sorted list of dates
$dates = array('May 5','May 6','May 7','May 30','Jun 2','Jun 3','Dec 11','Dec 12','Dec 14');
array_push($dates,false); //add an extra value so the last range gets printed
//initialize min & previous date as first date
$min_date = array_shift($dates);
$prev_date = $min_date;
$counter = 0; //keep count of # of days between min and max
$formatted_dates = array();
foreach($dates as $date) {
//if the difference in number of days between current date and min date
//is greater than counted number of days then we capture the current range
//and start a new one by resetting $min_date to $date and $counter to 0
if(!$date || ($counter + 1) < diff_in_days($min_date,$date)) {
if($counter == 0) { //format for 1 date
$formatted_dates[] = $min_date;
}
elseif($counter == 1) { //format for 2 dates
$formatted_dates[] = "$min_date & $prev_date";
}
elseif($counter > 1) { //format for > 2 dates
$formatted_dates[] = "$min_date - $prev_date";
}
$counter = 0;
$min_date = $date;
}
else {
$counter++;
}
$prev_date = $date;
}
//may also want to verify that neither formatted date contains an '&'
//so you don't end up with "May 11 & May 12 & June 1 & June 2" which may be confusing
if(count($formatted_dates) == 2) {
print implode(' & ',$formatted_dates);
}
else {
print implode("\n",$formatted_dates);
}
function diff_in_days($day1,$day2) {
$datetime1 = new DateTime($day1);
$datetime2 = new DateTime($day2);
$interval = $datetime1->diff($datetime2);
$ret = (int) $interval->format('%a');
return $ret;
}
Output
May 5 - May 7
May 30
Jun 2 & Jun 3
Dec 11 & Dec 12
Dec 14

Related

Recursive infinite daily dynamic loop array php

I am trying to display a number every day in a loop. After the last element is reached it needs to get to the first element again. This needs to happen daily. I have overworked my brains out but did not managed to solve it. Function needs to return current number by day/hour/minute, like . This is what I tried till now.
<?php
function recursive_daily_deals($i = 1) {
$current_date = strtotime(date('d-m-Y h:i:s'));
$dbs_date_1 = strtotime('29-06-2017 8:20:16');
$current_hour = date('h');
var_dump($current_hour);
$products = [
451,
455,
453
];
if ($i < count($products)) {
return recursive_daily_deals($i+1);
}
}
?>
EXPECTED output
> First day - June 29 2017
> It will appear 451
> Second day - June 30 2017
> It will appear 455
> 3rd day - July 1st 2017
> It will appear 453
> 4th Day - July 2nd 2017
> It will appear 453
> And start over
First you need to know how many days have been since the starting day. To do that you just need to sub the starting timestamp from the actual timestamp :
$dbs_date_1 = strtotime('29-06-2017 8:20:16');
$actualTimestamp = time();
$elapsedSec = $dbs_date_1 - $actualTimestamp;
// we need to get days from seconds
$elapsedDays = $elapsedSec / (3600*24);
$elapsedDays = floor($elapsedDays);
So when you have how many days have been since the starting day. We use floor() instead of round() because if the script runs after the half of the day it will return the number of days +1.
With this number of days we can have the number of cycles already done by dividing the number of elapsed days by the number of items in our array :
$nbItems = count($products);
$cycleCount = $elapsedDays / $nbItems;
$finishedCycles = floor($cycleCount);
We store the number of finished cycles by flooring the number of cycles. Then we just have to sub the days it took to complete those cycles from the elapsed days and we will get the position of the index.
$completeDays = $finishedCycles * $nbItems;
$actualPosition = $elapsedDays - $completeDays;
return $products[$actualPosition];
While this is a simplified version of the code originally posted, I believe it contains the kind of logic that the OP seeks, as follows:
<?php
$products = [
451,
455,
453
];
$modfactor = count($products);
$days = null;
$weekdays = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
for ($i=0, $max = 7; $i < $max; $i++) {
$days[$i] = $i % $modfactor;
}
foreach ($weekdays as $dex => $wday) {
echo "$wday, ";
echo $products[ $days[$dex] ], "\n";
}
See demo
Update: See demo here which makes use of array_map() and gets the current product ID, too.
While the loop is ever present, it is not infinite. If the number of products changes, then the modfactor changes, too. What stays constant are the days of the week. What makes the code work is taking advantage of a modulo operation.

Range check of a quarter and year?

I am kind of stuck on figuring out how to do this.
In my array that I am looping over, it has multiple arrays with the following fields year and quarter. Year is in numeric form like 2012 and quarter is in text form like Q1 or Q2
Now I am given a range for example
from: Q1 2013 to: Q3 2014
I have access to the numeric form of the quarters in the range.
So this is the variables I have from the example above: $quarter_from, $quarter_to, $year_from, and $year_to
I need to figure out if the current array in the loop that has the fields year and quarter is in the range of the range given.
How do I go about checking? Sure I can do a simple numeric check for years but quarters is a whole another story. For example the current array could have Q4 and 2013 and that's obviously in the range of the example, and the first case would pass by doing a year check. But for quarter, how do I check that?
I think you can do a concatenate of the quarter and year like this, assuming that the range that is selected logically makes sense (to comes later than from with respect to time).
from: 20122 => year 2012 and quarter 2
to: 20141 => year 2014 and quarter 1
So even though your quarter's value is in to is less than the quarter's value in from you still get a higher value in to because of the year.
This way you can now do a range check like this, by concatenating your year and quarter fields (do a substr() to get the second character of the string and convert to number then concatenate).
For example if 2013, Q4 shows up.
That looks like 20134 then do a range check like this
20122 <= 20134 <= 20141 which should work.
Wrote this in a hurry, it's more of a phsudo code but should give you some idea:
From: Q2 2013
Middle: Q1 2014 (checking if in range of From and To)
To: Q1 2014
Now we need to compare the years of from and middle and middle and to, and if the year is the same in either case you can split the quarter to get the number of the quarter and compare the quarters to see if the middle part is actually in the range.
fromQuarter = from.quarter.split("")[1] // Q2 => 2
middleQuarter = middle.quarter.split("")[1] // Q1 => 1
toQuarter = to.quarter.split("")[1] // Q1 => 1
If to.year >= middle.year && middle.year >= from.year
// Year range legit.
if(to.year == middle.year || from.year == middle.year)
// We need to check the quarters.
if(to.year == middle.year)
// Need to compare the quarter of the to year and the middle year.
if(toQuarter >= middleQuarter )
// In range.
else
// Out of range.
if(from.year == middle.year)
// Need to compare the quarter of the from year and the middle year.
if(fromQuarter <= middleQuarter)
// In range.
else
// Out of range.
else
// Between two distinct years, in range.
else
// Not in range.
I'll check this again soon to see if I made an logic error.
Try this:
function expand($quarter, $year) {
return $year * 4 + $quarter;
}
$quarter_from = 1; $year_from = 2013;
$quarter_to = 3; $year_to = 2014;
$quarter = 3; $year = 2014;
$n1 = expand($quarter_from, $year_from);
$n2 = expand($quarter_to, $year_to);
$n = expand($quarter, $year);
$is_in_range = $n >= $n1 && $n <= $n2;

Populate Drop Down with Time using Time Range from Database

I am trying to populate a drop-down menu with time in 30 minute intervals (ability to change to different intervals would be nice too).
I am currently using the following code to populate the drop down (Scrounged this up online from searching around).
$sqlHours = "SELECT Open, Close FROM TimeTable";
$rsSQLHours = odbc_exec($conn, $sqlHours);
$strOpen = trim(odbc_result($rsSQLHours, "Open"));
$strClose = trim(odbc_result($rsSQLHours, "Close"));
$DeliHourOpen = substr($strOpen, 0, 2);
$DeliMinOpen = substr($strOpen, 3, 2);
$DeliHourClose = substr($strClose, 0, 2);
$DeliMinClose = substr($strClose, 3, 2);
for($hours=$DeliHourOpen; $hours<=$DeliHourClose; $hours++)
for($mins=$DeliMinOpen; $mins<60; $mins+=30)
echo '<option value='.$hours.':'.$mins.'>'.date('h:i A', strtotime($hours.':'.$mins)).'</option>'; ?>
Edit: I am storing the times in the database in 24h format, such as 08:00:00 or 22:00:00. I am using the date() just to format the displayed output in an AM/PM fashion for ease of use by users.
I am having issues when the Close Time is 20:00 it will go up to 8:30 PM in the drop-down. If I remove the = from <=$DeliHourClose then it will only display 7:30 PM. I need it to Display up to whatever the Close Time is.
The fields in the database are 'Time' in the in format 'H:i:s'.
Also, although the drop-down can be populated with a range of times from Open to Close, I need it to start at whatever the current time is + 30 minutes.
So if the Open Time is 8:00 AM, and it is 7:00 AM I want to see 8:00 AM as the first time in the drop-down. But if it is 9:00 AM, the first option in the drop-down needs to be 9:30 AM.
I have the general idea that it needs some sort of if/else to compare current time to the times in the drop-down, but I am not sure how to go about it, with the format I am using now for the drop-down.
I would prefer to have a more manageable format if possible.
Is there an easier/better way to generate a range of times with intervals that may be changed? And then populate the drop-down with the appropriate times?
Any help or suggestions would be greatly appreciated.
Using a Microsoft SQL Database.
Edit: There are multiple locations that will be stored in the table. I will add a WHERE Location = XXX clause once I get it working and add more locations to the table. Currently there is only one location, so no WHERE clause.
I am using time datatype instead of datetime as I do not want a y/m/d attached to the open/close times.
You need to generate your time stamp using time() so you can get the unix timestamp and then convert it as you wish, this way you'll be able to do time addition and add seconds straight to the given unix timestamp.
Ressource : http://fr.php.net/time
Edit : Just so we're clear and to explain it further : UNIX timestamp is the number of seconds since the 1st of january 1970, so echo time(); will return 1390934768, you just need to process it from there as the docs shows.
This code whille return this as an array : 8
8.5
9
9.5
10
10.5
11
11.5
12
12.5
13
13.5
14
14.5
15
15.5
16
16.5
17
17.5
18
18.5
19
<?php
//Function returning unix time from simple time stamp (8 or 17.5 or whatever)
function ttounix($t) {
$tunix = $t * 60 * 60;
return $tunix;
}
//Function returning simple timestamp from unix timestamp
function unixtot($u) {
$tt = $u / 60 / 60;
return $tt;
}
$gap = 30 * 60; //gap in seconds
$t1 = 8; //opening time from db
$t2 = 19; //closing time from db
//setting vars
$n = 0;
$count = array();
//Getting processed time stamps into vars
$o = ttounix($t1);
$c = ttounix($t2);
//Populating the array
while ( $o <= $c ) {
$count[$n] = $o;
$o += $gap;
$n++;
}
//Output
foreach ($count as $output) {
echo unixtot(intval($output)) . '<br />';
}
?>

How can I divide a date range by number of months?

My dilemma is that if I request more than 6 months or so ( I do not know the approximate number ) from my webservices ( which gets called via JS ), I get nothing back. In other words, I have to limit it to 6 months.
So let's consider this scenario:
$a = strtotime('June 3, 2011');
$b = strtotime('June 3, 2012');
I need to split this up by 6 months in order to make 2 distinct web servicerequests so that each call requests 6 months.
So with $a and $b, I need to split these up into as many date ranges as possible when taking the amount of months total divided by 6.
The first date range I need is from June 1, 2011 to November 31, 2011. The second date range I need is from December 1, 2011 to July 1, 2012.
The idea I had in mind was finding the number of months, then if it was greater than the limit variable 6, do a loop and increment the initial date by 6 months per loop.
Pseudo-code ( I actually initially wrote it in JS but figured it'd be easier to do in PHP because I wouldn't have to deal with multiple asynchronous request behaviour ):
var numMonths = monthDiff ( a, b ), ret = [], limit = 6, loopLimit = Math.ceil( numMonths / limit ), ranges = [];
if ( numMonths > limit ) {
for ( var i = 0; i<loopLimit; i++ ) {
var start = new Date(b);
var end = new Date ( b.setMonth( b.getMonth() + limit ) );
ranges.push( start, end );
}
}
Does anyone know of a succinct way of doing this? Can anyone spot any programmatic flaws in this?
Try:
$a = strtotime("June 3, 2011 00:00:00Z");
$b = strtotime("June 3, 2012 00:00:00Z");
fetchAll($a,$b);
function fetchAll($a,$b) {
$fetchLimit = "6 months"; // or, say, "180 days"; a string
if ($b <= strtotime(gmdate("Y-m-d H:i:s\Z",$a)." +".$fetchLimit)) {
// it fits in one chunk
fetchChunk($a,$b);
}
else { // chunkify it!
$lowerBound = $a;
$upperBound = strtotime(gmdate("Y-m-d H:i:s\Z",$a)." +".$fetchLimit);
while ($upperBound < $b) { // fetch full chunks while there're some left
fetchChunk($lowerBound,$upperBound);
$lowerBound = $upperBound;
$upperBound = strtotime(gmdate("Y-m-d H:i:s\Z",$lowerBound)." +".$fetchLimit);
}
fetchChunk($lowerBound,$b); // get last (likely) partial chunk
}
}
function fetchChunk($a,$b) {
/* insert your function that actually grabs the partial data */
//
// for test, just display the chunk range:
echo gmdate("Y-m-d H:i:s\Z",$a)." to ".gmdate("Y-m-d H:i:s\Z",$b)."<br>";
}
...where $fetchLimit in fetchAll() is any duration string parseable by strtotime(). You could then keep appending the output of each fetchChunk() to an initially blank variable which is later returned by fetchAll().
This example fetches two six-month "chunks", as expected. Changing $b to one day later adds a third chunk containing only that extra day:
2011-06-03 00:00:00Z to 2011-12-03 00:00:00Z
2011-12-03 00:00:00Z to 2012-06-03 00:00:00Z
2012-06-03 00:00:00Z to 2012-06-04 00:00:00Z
Of course, PHP 5.3 has somewhat more elegant time functions like DateTime::diff, but the code above should work fine in PHP 5.2.x.
The only way to do this accurately if a month means X days is to have a method that can perform day calculation.
Unless by 6 months you mean 6 literal months.... Do this in a loop:
Subtract 6 from the month portion, and decrement year and add 12 to month if your month number is negative. Then determine which is later, the start date or your decremented date.
If your start date is later, then use that date. If your decremented date is later, then pack that date range and loop up and start the check over.
Another method is:
$a = strtotime("2018-12-01");
$b = strtotime("2020-12-01");
$makeArray = array();
function addDate($date){
return strtotime(gmdate("Y-m-d", $date)." +6 months");
}
array_push($makeArray, array("start" => date("Y-m-d", $a), "end" => date("Y-m-d", addDate($a))));
while ($c < $b) {
$a = addDate($a);
$c = addDate($a);
if ($c >= $b) {
$c = $b;
}
array_push($makeArray, array("start" => date("Y-m-d", $a), "end" => date("Y-m-d", $c)));
}
print_r($makeArray);

Determine if a week is odd or even

I have debugged this legacy code, and would like a sanity check on it.
The purpose of it is to allow someone to choose a delivery frequency for shipping a product. If someone wants their product Every Other Week, the system needs to determine if they should get an order next week, or two weeks from now. We call it A week, or B Week.
Keep in mind I did not write this, I am just trying to make sense of it and would like some help evaluating its accuracy:
if (date("l") == "Monday" ) {
$start = 0;
} else if (date("l") == "Tuesday" || date("l") == "Wednesday" || date("l") == "Thursday" || date("l") == "Friday" || date("l") == "Saturday"|| date("l") == "Sunday") {
$start = -1;
}
// if changing to every other week set to next week's a/b-ness
$a_week_tid = 34;
$b_week_tid = 35;
$every_other_week_frequency_id = 32;
if ($delivery_frequency == $every_other_week_frequency_id) {
$julian = (int) (strtotime('Monday +' . $start . ' week') / 86400);
$julian_week = ($julian-4) / 7;
if ($julian_week % 2) {
$today_a_or_b = $b_week_tid;
$next_week_a_or_b = $a_week_tid;
$a_or_b_week_string = '(A Week)';
} else {
$today_a_or_b = $a_week_tid;
$next_week_a_or_b = $b_week_tid;
$a_or_b_week_string = '(B Week)';
}
} else {
$next_week_a_or_b = NULL;
$a_or_b_week_string = NULL;
}
This code is not commented or documented. The part that confuses me is:
Why is 4 subtracted from Julian, then divided by 7?
If today is Monday, $julian_week is 2129, and 2129 % 2 evaluates TRUE. Is that correct?
Is this even how it should be done? Can't I rewrite this using date('w') a lot easier?
Yeah using date would totally be easier, plus it takes into account leap years, daylight saving time, all that extra stuff you don't want to have to deal with.
if (date('W')%2==1)
That's SOOOO much easier to maintain than the above.
I don't believe you can use date("W") in this case. According to the ISO calculation, on occasion, there will be years with 53 weeks. In those years, Week 53 is followed by Week 01, both odd numbers, and an A/B calculation based on Even/Odd ISO week number would result in two successive A or B weeks.
The original calculation determines the number of days from the UNIX epoch of the present Monday, or of the most recent Monday if today is not a Monday. The -4 causes the A/B week labels to change on Thursdays. Even/oddness of a week is determined from a fixed date (the Unix Epoch), so there will be no discontinuity in the oscillation of A/B-ness using the original code.
The ISO standard for week one in a year is that it is the week that the first Thursday of the year falls. This is the reason for the 4 subtracted from the Julian date. The week number is then found by dividing by 7.
Again the ISO standard implies that week number cannot be greater than 53. I don't understand how your figure of 2129 can arise. However the div operator will not evaluate TRUE for this figure. Checking the div operator on the week number is the way of determining whether you are in week a or b. If it is before Thursday, it is quite likely that the number will be 1 less than you anticipate.
The coding looks fairly good to me, though I have not stepped through all of it. It does look correct.
Using W on consecutive Fridays, mod by 2. Both lines output 1. So doing it this way will occasionally fail.
echo date('W',strtotime('2016-01-01'))%2;
echo date('W',strtotime('2016-01-08'))%2;
Just a simple way.
<?php
$weekNumber = date("W");
echo 'Week number:',$weekNumber;
if($weekNumber&1) {
echo '<strong>Week A.</strong>';
} else {
echo '<strong>Week B.</strong>';
}
?>
$day = '2019-11-10';
$date = new DateTime($day);
$dayOfMonth = $date->format("j"); // month days 1 - 30
$weekNumber = ceil($dayOfMonth / 7); // get the week number
if ($weekNumber % 2 == 0) { //if week number is even
echo "Even Week";
} else {
echo "Odd Week";
}
**// output Even Week**

Categories