I'm trying to make a script which penalizes a user daily after a time stamp.
For the first day, it will penalize 1 point, second day 2 points, third 4 points, fourth 8 point, 16, 32, 64 and so on.
How would I go about auto generating a strtotime and the multiplications?
I really don't even know what i'm looking for is called at this point which makes searching hard, sorry if this has been posted.
Let's suppose, that $timestamp1 is the first day timestamp and the $timestamp2 is now timestamp. Then:
$difference = abs(strtotime($timestamp2)-strtotime($timestamp1));
$days = floor($difference / (60*60*24));
$penalty = pow(2,$days);
echo "{$days} left, so your penalty is: {$penalty}";
Sample results:
0 left, so your penalty is: 1
1 left, so your penalty is: 2
2 left, so your penalty is: 4
3 left, so your penalty is: 8
4 left, so your penalty is: 16
5 left, so your penalty is: 32
6 left, so your penalty is: 64
...
Try this
$initial_date = '2012-10-20';
$initial_datetime = new DateTime($initial_date);
$today_datetime = new DateTime();
$days_passed = $initial_datetime->diff($today_datetime)->format('%a');
$penalty = pow(2, ($days_passed-1));
echo $penalty;
Related
I have some code that loops through each day within a 10 year range starting on a specific date. The date should only be added if it meets the selected criteria from a form.
The form has fields for months and weekdays to be selected. Within each weekday there are options for every, first, second, etc. I'm adding an 'every other' option to each weekday along with a start date field.
I am trying to determine the best way to check if the current date is within the 'every other' criteria based on the start date.
After looking at other questions, I couldn't find an ideal answer that took into account year that have 53 weeks. I was able to come up with some test code that seems to be giving me accurate results, but I'm wondering if there is a simpler way to perform this check.
Test Code:
// set start date
$date = new \DateTime('2019-12-15');
// get start date week of year and modulus
$start_week = date('W', $date->getTimestamp());
$start_mod = $start_week % 2;
// set end time (10 years from start date)
$end_time = strtotime('+10 years', $date->getTimestamp());
// init previous year and week modifier
$prev_year = false;
$week_modifier = 1;
// each day in range
while($date->getTimestamp() <= $end_time){
// get year
$y = $date->format('Y');
// previous year doesn't match current year
if($prev_year != $y){
// previous year set
if($prev_year){
// get number of weeks in year
$weeks_in_year = date('W', mktime(0, 0, 0, 12, 28, $prev_year));
// year has odd number of weeks
if($weeks_in_year % 2){
// increment week modifier
$week_modifier++;
}
}
// update previous year
$prev_year = $y;
}
// get week of year
$w = $date->format('W') + $week_modifier;
// check if meets every other criteria (based on start date)
$every_other = false;
if( ($w % 2) == $start_mod ){
$every_other = true;
}
// print date if it is part of every other Tuesday
if($date->format('w') == 2 && $every_other){
echo $date->format('Y-m-d');
echo '<br/>';
}
// increment day
$date->modify('+1 day');
}
Note 1: 2020 is the next year in which there are 53 weeks.
Note 2: I had a typo in this test code that was incrementing the week modifier instead of initializing it to 0. It would make more sense to me that this code would work if the modifier was initialized to be 0, but instead it only works when initialized to an odd number.
Since the "every other" is evaluated in a continuous cycle, you might just keep track of the days:
$odd = [ true, true, true, true, true, true, true ];
...
// Flip the appropriate day of the week.
$odd[date('w')] = !$odd[date('w')];
// Or start with all 1's, then $odd[date('w')] ^= 1;
if ($odd[date('w')]) {
// This is the "not-other" day
}
Modular arithmetic
This day is $w and we mark it:
$odd[$w] = !$odd[$w];
Now we advance by an unknown number of days $d. We need to properly flip all the days in this interval.
One way to do that is to cycle through all the days. But it's clear that this can't be necessary - we have seven days in a week, and they are either odd or even; even if we flipped them all, it would be seven updates. Cycling through one year would be 365 or 366 updates.
On the one hand, the 365-cycle solution has the advantage of simplicity. Is running 7 flips instead of 3652 really worth our while? If it is not, we're done. So let's suppose it is; but this evaluation should be re-done for every project.
So note that if we advanced by 1 day, we need do nothing. If we advance by 2 days, day[w+1] must be flipped. If we advance by 5 days, days from w+1 to w+4 need to be flipped. In general, days from w+1 to w+d-1 need to be flipped:
for ($i = 1; $i < $w+$d; $i++) {
$odd[$i % 7] = !$odd[$i % 7];
}
But now notice that if we advanced by 15 days, we would again need do nothing, as if we had advanced of only 1 day, since every day of the week would find itself being flipped twice:
d need to flip w+...
1 (none)
2 1
3 1, 2
4 1, 2, 3
5 1, 2, 3, 4
6 1, 2, 3, 4, 5
7 1, 2, 3, 4, 5, 6
8 1, 2, 3, 4, 5, 6, 0 (or 7)
9 2, 3, 4, 5, 6, 0
10 3, 4, 5, 6, 0
11 4, 5, 6, 0
12 5, 6, 0
13 6, 0
14 0
15 (none)
So here's a very reasonable compromise: if we need to advance by X days, treat it as if we had to advance by (X % 14) days. So now we will run at most 13 updates. Stopping now means that our code is the trivial version, enhanced by a strategically placed "% 14". We went from 3652 to 14 updates for a ten-year interval, and the best we could hope for was 7 updates. We got almost all of the bang, for very little buck.
If we want to settle for nothing but the best, we go on (but note that the additional arithmetic might end up being more expensive than the saving in updates from the worst value of 13 to, at best, zero. In other words, doing additional checks means we will save at best 13 updates; if those checks cost more than 13 updates, we're better off not checking and going through blindly).
So we start flipping at dIndex 1 if (1 < d%14 < 9), or (d%7) if (d%14 >=9 ). And we end at (d%14)-1 if (d%14)<8, 0 otherwise. If d%14 is 1, the start (using a simplified rule 1: d%14 < 9) is 1, the end is at 0, and since 0 is less than 1, the cycle would not even start. This means that the simplified rule should work:
// increase by d days
d1 = (d%14) < 9 ? 1 : (d%7);
d2 = (d%14) < 8 ? (d%14-1) : 7;
for (dd = d1; dd <= d2; dd++) {
odd[(w+dd)%7)] = !odd[(w+dd)%7)];
}
The above should flip correctly the "every other XXX" bit doing at most 7 writes, whatever the value of d. The approximate cost is about 6-7 updates, so if we're doing this in memory, it's not really that worth it, on average, if compared with the "% 14" shortcut. If we're flipping with separated SQL queries to a persistency layer, on the other hand...
(And you really want to check out I didn't make mistakes...)
This supposed to be easier to solve or google the answer, but I just can't get it well done. May be I'm just stuck:
This is what I tried:
$now = time();
// i.e Improve police arriving time from 15 mins to 10 mins
$array_ini = explode(':',$ini_value); // "00:15:00" in my example (15 mins)
$array_desired = explode(':',$desired_value); // "00:10:00" in my example
$ini = $now-mktime($array_ini[0],$array_ini[1],$array_ini[2]);
$des = $now-mktime($array_desired[0],$array_desired[1],$array_desired[2]);
$percent = (1-$ini/$des)*100;
But all I get is .47% as improvement and my logic says that it really is a 33% improvement.
What am I doing wrong?
It's much easier to just deal with minutes:
$ini_mins = 15;
$desired_mins = 10;
$improvement_mins = $ini_mins - $desired_mins;
$percent = ($improvement_mins / $ini_mins) * 100;
print_r($percent);
It is indeed straight forward and easier to just deal with minutes, as Ryan said in his answer.
But to add to it, what you are doing wrong - you are deducting unix time of 01 Jan 1970 00:10:00 and 01 Jan 1970 00:15:00 from say unix time of 15 Sep 2015 19:00:00. Of course the percentage difference between these two numbers would be small. You are doing something like this
num1 = 100000 - 10
num2 = 100000 - 15
percentage num1/num2 is wrong way to find percentage diff between 10 and 15; and also it is going to be much smaller than 33%.
plus you have a code bug. The array is called $array_desired but you reference $array_des in mktime.
Ok. I guess that Amit opened my mind.
Based on his comment, I post the right answer:
// This line is not needed any more
//$now = time();
// i.e Improve police arriving time from 15 mins to 10 mins
$array_ini = explode(':',$ini_value); // "00:15:00" in my example (15 mins)
$array_desired = explode(':',$desired_value); // "00:10:00" in my example
// Time must to be based on Jan, 1 1970
// Hours are from 1 to 23, so must be increased by 1
$ini = mktime($array_ini[0]+1,$array_ini[1],$array_ini[2],1,1,1970);
$des = mktime($array_desired[0]+1,$array_desired[1],$array_desired[2],1,1,1970);
$percent = (1-$des/$ini)*100;
I want to calculate how many months have passed since an X1 date to an X2 date, and to calculate how much will I have to pay with a given monthly interest rate.
The script should: Get the amount of a debt (capital), the month and year when the debt was originated and up until it should get calculated. Not the day, just the month and the year.
And the output should be the total generated in interests, and the total months between the two dates.
I have these initial variables:
$tMonth = $_POST['tmes'];
$tYear = $_POST['tanio'];
$interes = $_POST['interes'];
$fMonth = $_POST['fmes'];
$fYear = $_POST['fanio'];
$capital = $_POST['capital'];
And this is what I've done:
if($_SERVER['REQUEST_METHOD']=='POST') {
//I try and obtain how many months do I have between the two months
$mesesEnAnios = (($tYear - $fYear) -1) * 12;
$mesesScattered = (12 - $fMonth) + $tMonth;
$mesesTotales = $mesesEnAnios + $mesesScattered;
//Then I do calculate the interest I'll have to pay
$totalCapital = $capital * ($interes * $mesesTotales) / 100;
echo 'Son $'.$totalCapital.' en '.$mesesTotales.' meses.';
...
I've tried this script and it works. But I don't know much about PHP (nor math) and I don´t know if this will always work.
I've researched other solutions here at SO, but I think mine is a little easier - at least I do understand it :) - maybe it´s because it's not correct?
No, it is wrong.
$mesesEnAnios = (($tYear - $fYear) -1) * 12;
That is for example: (2013 - 2012 - 1) = 0
0 * 12 = 0, while its obvious 12.
Then you do
$mesesScattered = (12 - $fMonth) + $tMonth;
So, lets say we have 05-2013 (month - year) and 05-2012. The result should be: 12 months. Lets assume you fixed the first line I pointed out, we get 12 months from that. On your second line, you get 12 - 5, which is 7. Result 7+12 = 19.. which is again obvious wrong.
To make it easy, ill just give you the code..
$months = (($tYear - $fYear ) * 12) + ($tMonth- $fMonth);
You first check if there are any extra's years and do this amount * 12 for the 12 months we have. 0 years give 0 months, 1 year gives 12 months etc. After that we check the months. If the first value is 8 months and the second value is 6 months, we have 2 months in total and add this value on top of the value we got from the years calculation.
That is the month part, for the other part I cannot help you since I do not know the calculation. Yet assuming you have 0 experience in math and PHP (like you said), I would recommend to manually calculate it, and compare it with the result from the script.
In PHP I need to do a count of 15 minutes within a number of hours and minutes. But I need it to round down if less or equal to 7 minutes and round up if over 7 minutes.
So I have something like;
$totalHours = 10;
$totalMinutes = 46;
The hours is easy enough to work out the number of 15 minutes;
$totalHours = 10*4;
But the minutes is giving me some grief.
Some examples are;
If Minutes = 7 then answer is 0.
If Minutes = 8 then answer is 1.
If Minutes = 18 then answer is 1.
If Minutes = 37 then answer is 2.
If Minutes = 38 then answer is 3.
I would be really grateful if someone could do me a nice function for this. A signature of (int TotalHours, int TotalMinutes) would be great.
Thanks,
Mike
Try this code:
<?php
echo round($n/15);
?>
// where n is your minute number i.e. 7, 8, 18, 37, 38 etc
Calculate the number of minutes as (60 * hours + minutes), then add 8, divide by 15, and truncate the result by a call to int().
To round to the nearest X: Divide by x, round to nearest int, multiply by x.
$nearest = round($minutes / 15) * 15;
But in your case, if you just want a count of that, you can simply skip the last step.
$numberOfQuarterHours = round($minutes / 15);
You could try modulo operation.
PHP.net MOD
Just divide by 15 and get the remaining part. There you could apply test for 7 < x < 8
I have this project that has a set price for a certain amount of hours, I need to add more money to the overall total for each hour after 5.
My script already calculated the time by counting the hours:
Which outputs like, "15" or "9.5" or "3.5" or "7" etc.
Let's say that 5 hours is £50, how would I add an additional £15 for every hour over the 5 hour limit.
This includes if a user going over by ".5"
(So 5.5 hours would be £65 and 6 would be £65)
Any help would be great, Thanks!
Subtract 5 from the number of hours, ceil() / round() the number and multiply by 15?
You ceil() if you want any part of an hour to be charged, whereas you round() if you want to charge only if the fraction of the hour is .5 or higher.
$hours = 5.5;
$amount = 50 + ceil(max(0,$hours-5)) * 15; # 65
I assume that anything belove 5 hours is 50.
$price = $val <= 5 ? 50 : (50 + (int) ceil($val-5) * 15)
Assuming I've understood you correctly, are you looking for something like this:
$total = 50;
if($hours > 5) {
$total += ceil($hours - 5) * 15;
}