I have a small script that splits out an amount of revenue over a span of days. I can't seem to wrap my head around allowing for any number of days. Currently I can only get it to work if I hard-set an array of %s for each amount of days, which obviously isn't going to work for large sets of day combinations.
If anyone has an idea as to how to accomplish this, I'd be ever grateful.
My Code:
<?php
//define variables
$ammount = 11320.00;
$start_date = "12/30/2011";
$days = 5;
//setup array of percents
$percent_array = array(3=>array(0 => 45, 1 => 30, 2 => 25), 4=>array(0 => 40, 1 => 30, 2 => 20, 3 => 10),4=>array(0 => 35, 1 => 25, 2 => 20, 3 => 10, 4 => 10));
//handle formatting the date for use in loop
$format = 'D m/d/Y';
$sDate = strtotime($start_date);
if ($usersTS !== false) {
for ($i = 0; $i < $days; $i++) {
echo date($format, strtotime('+' . $i . ' day', $sDate));
echo " : ";
echo number_format($ammount * ($percent_array[$days][$i] / 100), 2, ".", ",");
echo "<br />";
}
} else {
echo "No valid date supplied";
};
?>
Output:
Fri 12/30/2011 : 3,962.00
Sat 12/31/2011 : 2,830.00
Sun 01/01/2012 : 2,264.00
Mon 01/02/2012 : 1,132.00
Tue 01/03/2012 : 1,132.00
So you want to distribute the amount over X number of days such that the earlier days are higher weighted?
Sounds like you just need to find the right function. You can try y=1/(x+1) for example. Then solve for x number of days, and use each day as a percent sum of the y values.
well, I'm not quite sure by what amount you want to split for each day, but I think you are over thinking this problem? Couldn't you just divide the amount by the days? Or am I missing something... If you could post more info about your objective that would be helpful :)
Related
My mission is to display the correct day to the correct date for every month. I am so stuck, i would really appreciate some help or pointers in the right way. So far i get out all the days in October since it is october, but i also want to match the days in the loop to the correct date. Am i going this the wrong way?
All i manage to do is to display the same day on every date.
$months = date("n");
$monthsDays = array (
1 => 31,
2 => 28,
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31
);
$day_of_the_week = array (
1 => "Måndag",
2 => "Tisdag",
3 => "Onsdag",
4 => "Torsdag",
5 => "Fredag",
6 => "Lördag",
7 => "Söndag"
);
$dayInteger = date('N', time());
echo $day_of_the_week[$dayInteger];
$day_of_the_week = date("D");
$weekNumber = date("W");
$year = date("Y");
foreach($monthsDays as $key=>$value) {
if($key == $months) {
echo date("M")."<br>";
for($i = 1; $i <= $value; $i++) {
echo '<div class="displayDate">'.$i.'</div>';
}
}
}
Edit
I have deleted the previous answer for the sake of this, more elegant answer:
setlocale(LC_TIME, array('da_DA.UTF-8','da_DA#euro','da_DA','danish'));
$curYear = strftime('%Y');
?>
<h1><?= $curYear; ?></h1>
<?php
for ($month = 1; $month <= 12; $month++) {
$curMonth = strftime('%B', strtotime("01-{$month}-{$curYear}"));
$curMonth = ucfirst($curMonth);
$curMonth = utf8_encode($curMonth);
$totalDays = cal_days_in_month(CAL_GREGORIAN, $month, $curYear);
?>
<h2><?= $curMonth; ?></h2>
<?php for ($day = 1; $day <= $totalDays; $day++) { ?>
<?php
$monthName = ucfirst(strftime('%A', strtotime("{$day}-{$month}-{$curYear}")));
$monthName = ucfirst($monthName);
$monthName = utf8_encode($monthName);
?>
<div class="displayDate"><?= $day; ?> <?= $monthName; ?></div>
<?php } ?>
<?php } ?>
Explanation
There's a lot going on here so I will divulge:
setlocale is a function that will set the locale language to whatever is specified.
The first parameter is what functions are to be affected, the second parameter is the locale to change to. In your case that was Danish.
strftime is very similar to the date function, with the exception that it will return the date in the language set by the locale.
After that it's really just iterating over days and months.
When setting $curMonth, I use strtotime so that I can manipulate it to extract that date in the specified language. Originally, I used DateTime::createFromFormat, but that doesn't respect the locale that is set via setlocale, which is why I used this hack.
$totalDays will return the total number of days in the given month, these means we don't have to hardcode them. The advantages being leap years are accounted for, and if the days of the year change, you don't have to change anything! See cal_days_in_month for how to use this function.
<?= is the equivalent of <?php echo which is a lot easier to write and read - IMO!
The only other interesting things I used are utf8_encode and ucfirst.
The former will convert the string to UTF-8 which is almost a standard these days. The latter will just set the first letter of string to a capital letter.
Note: it might be a good idea to use this solution for setting a capital letter:
$curMonth = mb_convert_case($curMonth, MB_CASE_TITLE);
Thanks to #julp for this answer.
For an explanation of what it does see the documentation for mb_convert_case; but in essence it will simply convert the first letter to a capital regardless of the locale.
try this
echo cal_days_in_month(CAL_GREGORIAN, 8, 2018)
// 8 is month number
// 2018 is year
My mind seems to be going blank on this one.
I have to write something to figure out which date range todays day/month combination fits into.
I have a set amount of date ranges, which are:
$dateRanges = array(
1 => "16 January to 30 April",
2 => "1 May to 30 June",
3 => "1 July to 15 August",
4 => "16 August to 15 September",
5 => "15 September to 15 October",
6 => "16 October to 15 January"
);
All I'm trying to return is the array key of the range the current date fits into.
At the moment all thats going through my head is I'll have to set up a large if statement to look at the current date('j') and date('n') and match the results up. But surely thats pretty messy and not very efficient?
Any ideas of a better way to approach this problem would be much appreciated!
$today = time();
foreach ($dateRanges as $key => $range) {
list($start, $end) = explode(' to ', $range);
$start .= ' ' . date('Y'); // add 2011 to the string
$end .= ' ' . date('Y');
if ((strtotime($start) <= $today) && (strtotime($end) >= $today)) {
break;
}
}
$key will be either the index of the matching date range, or null/false.
This is a variation on Mark B's answer, but made more efficient by turning this into pure numeric comparisons:
function get_today () {
$dateRanges = array(
0 => 116, // 16th Jan
1 => 501, // 1st May
2 => 701, // 1st July ..etc..
3 => 816,
4 => 916,
5 => 1016
);
$today = (int) date('nd');
foreach ($dateRanges as $key => $date) {
if ($today < $date) {
$result = $key;
break;
}
}
return (empty($result)) ? 6 : $result;
}
Returns an integer matching the keys in your sample array
Create DateTime instances for the values in the array und use simple comparison operators like > and <
Just use strtotime to create a UNIX epoch, then use the inbuilt < and > operators.
http://www.php.net/manual/en/function.strtotime.php
$time_min = strtotime("17 January 2011");
$time_max = strtotime("30 April 2011");
if ($time >= $time_min && $time < $time_max)
{
echo "Time is within range!";
}
You can then just expand this to use the array of ranges you specified.
I'm am trying to calculate the hours for someone based on the number of hours worked and the time period in which they worked.
For example:
The shift patterns are 07:00 to 15:00 is 'morning', 15:00 to 23:00 is 'afternoons', and 23:00 to 07:00 is 'nights'.
Therefore if I started at 08:00 and finished at 17:30, this would mean that I get paid 7 'morning' hours and 2.5 'afternoon' hours.
I adapted a piece of code from a project I developed some time ago.
The function 'intersection' calculates the amount of time that overlaps between the two ranges s1-e1 and s2-e2.
Note that all the times are in seconds, and we add 3600*24 seconds when the time is in the next day.
<?php
function intersection($s1, $e1, $s2, $e2)
{
if ($e1 < $s2)
return 0;
if ($s1 > $e2)
return 0;
if ($s1 < $s2)
$s1 = $s2;
if ($e1 > $e2)
$e1 = $e2;
return $e1 - $s1;
}
$start = strtotime("07:00");
$end = strtotime("17:30");
// $end = strtotime("05:30") + 3600*24; // the work ended at 05:30 morning of the next day
$morning_start = strtotime("07:00");
$morning_end = strtotime("15:00");
$afternoon_start = strtotime("15:00");
$afternoon_end = strtotime("23:00");
$night_start = strtotime("23:00");
$night_end = strtotime("07:00") + 3600*24; // 07:00 of next day, add 3600*24 seconds
echo "morning: " . intersection( $start, $end, $morning_start, $morning_end ) / 3600 . " hours\n";
echo "afternoon: " . intersection( $start, $end, $afternoon_start, $afternoon_end ) / 3600 . " hours\n";
echo "night: " . intersection( $start, $end, $night_start, $night_end ) / 3600 . " hours\n";
You could have 3 counters, one for each shift. Then you'd need a way to increment by hour and for each hour you increment. You check if it is within each shift, and if it is within a certain one, then you increment that counter.
In the end, the values of each counter should be the amount you worked in each shift.
This is what I just threw together for fun.
There is much room for improvement and it can be easily extended to provide even more calculations, your imagination is the limit.
The array of shifts can be given named keys for readability, but I chose to remove them since 'night1' and 'night2' make no sense to me :-)
<?php
$shift_data = array(
array(
'rate' => 12.5,
'start' => strtotime('00:00:00'),
'end' => strtotime('07:00:00'),
),
array(
'rate' => 7.55,
'start' => strtotime('07:00:00'),
'end' => strtotime('15:00:00'),
),
array(
'rate' => 10,
'start' => strtotime('15:00:00'),
'end' => strtotime('23:00:00'),
),
array(
'rate' => 12.5,
'start' => strtotime('23:00:00'),
'end' => strtotime('07:00:00') + 86400, // next morning
),
);
function calculateWage($start, $end, $rate) {
$result = array();
$result['time']['seconds'] = $end - $start;
$result['time']['minutes'] = $result['time']['seconds'] / 60;
$result['time']['hours'] = $result['time']['minutes'] / 60;
$result['wages'] = $result['time']['hours'] * $rate;
//print_r($result);
return $result;
}
$shift_start = strtotime('08:00');
$shift_end = strtotime('17:30');
$shift_wages = 0;
foreach ($shift_data as $shift) {
if ($shift['start'] <= $shift_end) {
$start = ($shift_start <= $shift['start']) ? $shift['start'] : $shift_start;
$end = ($shift_end <= $shift['end']) ? $shift_end : $shift['end'];
$shift_wage = calculateWage($start, $end, $shift['rate']);
$shift_wages = $shift_wages + $shift_wage['wages'];
}
}
echo "\nTotal wages for today: $" . number_format($shift_wages, 2, '.', ',') . "\n\n";
?>
Here is another way: (I am assuming the start and end hours in unix timestamp)
Get the starting and ending day of employee
Get the work hours by a simple formula: $ending_hour - $starting_hour
If work hours is more than 8 hours, the employee should get paid for extra.
To calculate extra hours of next shift: $ending_hour - $next_shifts_starting_hour
My shift times are simplier: 22:00 -> 07:00 = nightwork, 07:00 -> 22:00 = daywork
But I got wrong answer with Javi R version if shifts are like 22:30 -> 08:00 (Result: day = 0 hours, night = 8:30 hours) and 20:00 -> 10:00 (Result: day = 2 hours, night = 9 hours) but right answer with shift time like 23:00 -> 07:00 (Result: day = 0 hours, night = 7 hours).
Sorry about that answer. I don't have enough rep but I need to get it work.
I have a problem that seems quite hard to solve so I hope someone will have a good solution for me:)
I have a PHP program with a list of reservation for many conference rooms of an hotel. I need to calculate how much time during the day at least 1 conference room is in use.
Here is an exemple:
Room 1: 10h 00 to 13h 00
Room 2: 11h 00 to 14h 00
Room 3: 15h 00 to 16h 00
With theses numbers, I need to calculate that the hotel is used during 5 hours (10h to 14h and 15h to 16h).
In the end, this tells me how many hours the hotel has to pay someone to check the rooms in case someone has a problem.
If you don't have a library that could help me, an algorithm could be a good start.
Algorithm outline:
Order you intervals by starting time.
Go through them and join neighbors that intersect. The intersection condition will be that the second interval's start time is before or equal to the first interval's end time. Because of the ordering only one loop will be needed.
At this point you have only disjoint intervals and you can sum their spans to get the total span.
And here is an implementation of that algorithm:
<?php
// assuming hh:mm format for all the dates //
$intervals = array(
array(
'start' => '15:30',
'end' => '16:00',
),
array(
'start' => '10:00',
'end' => '13:00',
),
array(
'start' => '15:00',
'end' => '16:09',
),
array(
'start' => '11:00',
'end' => '14:00',
),
);
// 1. sort the intervals by start date //
function mySortIntervals($a, $b){
return $a > $b;
}
usort($intervals, 'mySortIntervals');
// 2. merge adjoining intervals //
$active = 0;
$current = 1;
$length = count($intervals);
while($current < $length){
if($intervals[ $current ]['start'] <= $intervals[ $active ]['end']){
$intervals[ $active ]['end'] = max($intervals[ $active ]['end'], $intervals[ $current ]['end']);
unset($intervals[ $current ]);
}
else{
$active = $current;
}
$current++;
}
// 3. cout the total time //
$time = 0;
foreach($intervals as $interval){
$time += strtotime($interval['end']) - strtotime($interval['start']);
}
// output //
echo str_pad((int) ($time/60/60), 2, '0', STR_PAD_LEFT).':';
echo str_pad((int) (($time/60)%60), 2, '0', STR_PAD_LEFT);
?>
First of all, you need to define your interval or time span, for example every 15 minutes.
Why this? because you need to control the gaps caused by room that finish before time, or for some other reason.
The time span can NOT be more than 15 minutes.(Time is money).
Order a room (time span). It must check avaliable time span every time a request happen.
Once the cell (time span) is checked that time span will not be available anymore.
To order a room just select the hour (00 til 23) and the quarter (00; 15; 30; 45;)
It worked to me quite well for an ISP business. I think is not different for a hotel. Because the idea of time span is the same.
But of course you can look for some class if you don't want to write your code at all.
I need to work out how many different instances occur on a different day, from many different ranges. Probably best to explain it with an example.
18-JAN-09 to 21-JAN-09
19-JAN09 to 20-JAN-09
20-JAN-09 to 20-JAN-09
Using the three examples above, I need it to collect this information and display something a little like...
18th Jan: 1
19th Jan: 2
20th Jan: 3
21st Jan: 1
... I'll be grabbing the information from an Oracle database fwiw (hence the format above ^) and there will be hundreds, maybe thousands of records, so my lame attempt to do all sorts of loops and if statements would take forever to run.
Is there any fairly simple and efficient way of doing this? I'm really not too sure where to start unfortunately...
Thanks
You could use the method described in another SO:
SQL> WITH DATA AS (
2 SELECT to_date('18-JAN-09', 'dd-mon-rr') begin_date,
3 to_date('21-JAN-09', 'dd-mon-rr') end_date FROM dual UNION ALL
4 SELECT to_date('19-JAN-09', 'dd-mon-rr'),
5 to_date('20-JAN-09', 'dd-mon-rr') FROM dual UNION ALL
6 SELECT to_date('20-JAN-09', 'dd-mon-rr'),
7 to_date('20-JAN-09', 'dd-mon-rr') FROM dual
8 ),calendar AS (
9 SELECT to_date(:begin_date, 'dd-mon-rr') + ROWNUM - 1 c_date
10 FROM dual
11 CONNECT BY LEVEL <= to_date(:end_date, 'dd-mon-rr')
12 - to_date(:begin_date, 'dd-mon-rr') + 1
13 )
14 SELECT c.c_date, COUNT(d.begin_date)
15 FROM calendar c
16 LEFT JOIN DATA d ON c.c_date BETWEEN d.begin_date AND d.end_date
17 GROUP BY c.c_date
18 ORDER BY c.c_date;
C_DATE COUNT(D.BEGIN_DATE)
----------- -------------------
18/01/2009 1
19/01/2009 2
20/01/2009 3
21/01/2009 1
Either your DB engine or your PHP code is going to have to loop over the date range.
Here's some PHP code to do the summation. The day counts are stored by year-month to avoid having a huge array for a wide date range.
<?php
// Get the date ranges from the database, hardcoded for example
$dateRanges[0][0] = mktime(0, 0, 0, 1, 18, 2009);
$dateRanges[0][1] = mktime(0, 0, 0, 1, 21, 2009);
$dateRanges[1][0] = mktime(0, 0, 0, 1, 19, 2009);
$dateRanges[1][1] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][0] = mktime(0, 0, 0, 1, 20, 2009);
$dateRanges[2][1] = mktime(0, 0, 0, 1, 20, 2009);
for ($rangeIndex = 0; $rangeIndex < sizeof($dateRanges); $rangeIndex++)
{
$startDate = $dateRanges[$rangeIndex][0];
$endDate = $dateRanges[$rangeIndex][1];
// Add 60 x 60 x 24 = 86400 seconds for each day
for ($thisDate = $startDate; $thisDate <= $endDate; $thisDate += 86400)
{
$yearMonth = date("Y-m", $thisDate);
$day = date("d", $thisDate);
// Store the count by year-month, then by day
$months[$yearMonth][$day]++;
}
}
foreach ($months as $yearMonth => $dayCounts)
{
foreach ($dayCounts as $dayNumber => $dayCount)
{
echo $yearMonth . "-" . $dayNumber . ": " . $dayCount . "<br>";
}
}
?>
You need a table with one row for each day
test_calendar:
Day
16.01.2009
17.01.2009
18.01.2009
19.01.2009
20.01.2009
21.01.2009
22.01.2009
23.01.2009
24.01.2009
25.01.2009
26.01.2009
table test contains bagin and finish of inctance:
DFROM DTILL
18.01.2009 21.01.2009
19.01.2009 20.01.2009
20.01.2009 20.01.2009
Here is a query you need:
select day, (select count(1)
from test where dfrom<=t.day and dtill>=t.day) from test_calendar t
where day>to_date('15.01.2009','DD.MM.YYYY')
order by day
Huge thanks for the solutions guys - managed to get it working using some of the SQL from above and also bits of PHP from the second solution.
Really appreciate it, cheers :)