PHP OOP DateTime ModifyDate decimal place errors - php

If I use the following code
$averageWeeks = 2.7;
$laidDate->modify('+'.$averageWeeks.' week');
I get the date 31st May 2014 returned, whereas if i use the following code
$averageWeeks = 3;
$laidDate->modify('+'.$averageWeeks.' week');
I get the date 3rd May 2014 which is right. Is there any way I can use decimal places in the date modify method?

Assuming you're starting at April 12th, then May 3rd would be 3 weeks and May 31st would be 7 weeks. So my guess is that when you pass "2.7", it is ignoring the "2." portion of that string.
You could consider calculating the number of days to add separately, such as:
$averageWeeks = 2.7;
$averageDays = floor($averageWeeks * 7);
$laidDate->modify('+'.$averageDays.' days');
Consider also that 2.7 weeks is 18.9 days. By taking the floor, We are adding 18 days. It doesn't make since to add fractional days here, unless you intend to change the time of day as well. If you did, you'd have to do some additional math, as it doesn't appear that either modify or add will support fractional values. You could do something like this:
$averageWeeks = 2.7;
$averageSeconds = floor($averageWeeks * 7 * 24 * 60 * 60);
$laidDate->modify('+'.$averageSeconds.' seconds');
But you might be fooling yourself if you think this time is accurate, since not every local day has exactly 24 hours in it, due to daylight saving time transitions.

Related

Split a Carbon period into full months and reminder in days for the start and end

What I have is:
$period = CarbonPeriod::create("03-09-2022", '1 month', "20-03-2023"); // 3rd of September and 20th of March
From this I can tell the number of months on which the period spans. But what I need is to have a way to calculate a salary based on that period knowing a monthly salary.
So what I'm actually trying to get is:
28 Days(for the first month) followed by 5 full months followed by 20 days (for the last month)
Is there any way I can get this from CarbonPeriod or CarbonInterval?
The following will give you a good approximation:
$salaryPerMonth = 1000;
$totalSalary = Carbon::create('03-09-2022')->floatDiffInRealMonths('20-03-2023') * $salaryPerMonth;
But unless you're working 7/7 days, the approach is not exact, you would need to take into account only business days (excluding, holidays, etc.)
If you want to go to this deeper precision level, take a look at https://github.com/kylekatarnls/business-day and https://github.com/kylekatarnls/business-time

Updating number values by year / month with php

I was wondering if there is a way to update a certain number value, per year?
Similar to how you would update a website date in the footer like this:
<?php echo date("Y"); ?>
I'm looking for a way to update a set number each year / month.
Example, say a website says they have been in business for 10 years. Then when the next year passes, I want PHP to automatically update the value to 11 years. Note, that I have to start with the number 10
Another example would be for age: John is 21. After a year passes I want PHP to update the site to say, John is 22.
Or Betty has worked here for 5 months.... After the next month passes it would update to 6, then once it hits 12 months is would change to 1 year then 2 years etc... Thats a little more involved, but you get the idea.
If someone knows how this can be achieved I would appreciate the help, or if you can point me in the right direction to solving this problem that would work too.
Yes, you need to understand two things - the current date and the date you are comparing it to. You then simply need to compare the too.
I would suggest using the PHP DateTime, DateInterval, etc. classes which greatly simply this for you.
For example:
$now = new DateTime('now');
$comparison_date = new DateTime('2005-01-01'); // roughly 10 years ago
$interval = $now->diff($comparison_date); // returns DateInterval object
$years_difference = $interval->y;
You can find your answer here:
How to calculate the difference between two dates using PHP?
By using strtotime() to convert two dates to unix time and calculate the number of seconds between them then reconvert to human readable.
To calculate the difference between to years you can use a simple sum like this:
<?php
$cur_year = date("Y");
$year = 2008;
$total_years = $cur_year - $year;
echo ($total_years);
?>
For months you could use a similar thing
Substract the initial Unix timestamp with the current timestamp (time() returns it), divide by 60 * 60 * 24 * 365, and round accordingly (you will probably want to floor() the years an user has been registered, and ceil() the years a bussiness have been working)
$start = 12345646;
$years = ceil((time() - $start) / 31536000.0); // Important: use a float
Note this uses "year" as a set of 365 days. It won't take into account leap years.

PHP "until date x" - Why are the 24 hours of the final day not included in the period?

I implemented a countdown in one of our projects. As this was my first countdown I just realized that if I tell PHP:
$timeleft = strtotime('2013-10-31') - strtotime('now');
$daysleft = floor( $timeleft/(24*60*60) );
I get the time left to 2013-10-31 00:00 instead of the full day.
Why is it this way? I expected 2013-10-31 23:59:59 or 2013-11-01 00:00
As an example from daily life: I have a rental contract for a car until 2013-10-31, or any contract, then I know that the rental period always includes the final day completely.
This might seem like a beginner's question but I would like to know the reason for this decision. Thank you for your ideas and experience.
PS: Is it the same in all programming languages?
Then give a time to strtotime as well.
$timeleft = strtotime('2013-10-31 23:59:59') - strtotime('now');
Why?
Because when you say:
strtotime('2013-10-31');
That means the exact timestamp when that date started, which is equivalent to saying:
strtotime('2013-10-31 00:00:00'); // hence the missing 24 hours
Edit
In fact, saying strtotime('now') is just like saying time(), so you can remove that as well. And have it like:
$timeleft = strtotime('2013-10-31 23:59:59') - time();
PS: Is it the same in all programming languages?
Its not a language limitation by any means, in any language. Its how you interpreted the function which generates time for you. After interpreting it correctly, send it the right values and it will work as you expect.
The reason for this will be the same as it is for money:
1 EURO = 1.00 EURO and not 1.50 EURO.
If the time of day matters, you should define it in both objects.
As dates can be ambiguous, varying on time zones, daylight savings - even the architecture of the system that its running on, it is recommended to use the built in DateTime object.
There are numerous methods that can be used to manipulate the date, such as DateTime::diff() without all the manual calculations (ie. floor( $timeleft/(24*60*60) );).
So they might offer you better flexibility and accuracy when comparing date intervals and/or adding them together.
$date = new DateTime('2013-10-31');
$now = new DateTime('today');
$diff = $date->diff($now);
$new = $date->add($diff);
echo $new->format('Y-m-d H:i'); // 2013-11-01 00:00

How to find if a date fits an interval in either PHP or MySQL?

Let's say I have a datetime, June 16 2011 at 7:00. I want to be able to check at, say, August 5 2011 at 7:00 and be able to tell that it is exactly a multiple of 1 day since the first date, whereas 7:01 would not count, since it is not an exact multiple.
Another test set: Let's say we have June 16 2011 at 7:00, and I want to check if a particular minute is within an interval of exactly 2 hours since then. So 9:00, 11:00, 13:00, etc. would count, but 9:30 and 10:00 would not. And this could continue for days and months - September 1 at 7:00 would still count as within every 2 hours. (And no, at the moment I don't know how I'm going to handle DST :D)
I thought about it for a moment and couldn't think of anything already existing in PHP or MySQL to do this easily but hell, it could, so I wanted to throw this up and ask before I start reinventing the wheel.
This is on PHP 5.1, sadly.
select *
from test
where datetimefield > '2011-06-16 07:00:00'
and
mod(timestampdiff(second,'2011-06-16 07:00:00',datetimefield),7200) = 0
This example will give you all the records greater than '2011-06-16 07:00:00' where the field is exactly a multiple of 2 hours.
Easiest would be to convert the date/time values into a unix timestamp and then simply do some subtraction/division:
2011-06-16 07:00:00 -> 1308229200
2011-08-05 07:00:00 -> 1312549200
2011-08-05 07:00:01 -> 1312549201
1312549200 - 1308229200 = 4320000 / 86400 = 50 (days)
1312549201 - 1308229200 = 4320001 / 86400 = 50.0000115...
So in other words:
if (($end_timestamp - $start_timestamp) % 864000)) == 0) {
... even multiple ...
}
Same would hold for the day/week comparisons. For months, this'll be out the window, since months aren't nice even figures to deal with.
MySQL Date functions:
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html
You can use TIME() to get just the time part of a date. If the time parts are the same it is an exact multiple.
For the two hour thing, one way to do it would be to get the minute/seconds part of the date, make sure those are equal, then make sure that the hour parts of the dates are both even or both odd. For more complicated integer (e.g. 5) hour multiples, you can "fake" doing a mod by dividing the hour parts and checking if the result is an int.
You can compare two DateTime objects via diff() method. Result is a DateInterval object - you can check the exact number of days/hours/minutes between two dates.
It's useless to write your own algorithms if you can use built-in functionality.

How do I calculate the number of seconds in a month in PHP?

How do I accurately determine the number of seconds in a month using PHP? Is the best way to take the number of seconds in a year and divide by 12?
Multiply the number of days in the month by 60 * 60 * 24.
Due to daylights savings... take a good datetime library in your language and calculate the difference between the first day of the month 0:00:00 and the first day of the next month 0:00:00 and extract the number of seconds.
How accurate do you need to be?
60 seconds * 60 minutes * 24 hours * Z days in the month gives you an accurate number for a given month.
If you need an average month go for number of seconds in the year and divide by twelve.
In some domains, such as billing or legal domains a 'month' might actually be exactly 30 days.
If you are working across multiple years or doing tight integration between disperse systems, you'll need to consult resource to determine leap seconds. For historical data this could be a table, but otherwise you'd be better suited by synchronizing to a trusted time source.
http://en.wikipedia.org/wiki/Leap_second
60 (seconds) * 60 (minutes) * 24 (hours) * ## (days in the month)
Given that there are 86,400 seconds in a day, you can multiply this number by the result of the DateTime.DaysInMonth function (in C#). The following function does just that:
public double SecondsInMonth(int year, int month)
{
return DateTime.DaysInMonth(year, month) * 86400;
}
E.g., find the seconds in the current month:
double secondsInCurrentMonth = SecondsInMonth(DateTime.Now.Year, DateTime.Now.Month);
Number of days in the given month * hours/day * minutes/hour * seconds/minute
is the best way.
If you're doing this in pure math it would be 60 * 60 * 24 * <number of days in month>.
What's the use case?
No, use the date API available for a particular lannguage and determine the number of days in the current month. Then calculate the number of seconds. Also take into account leap years.
Depends on if you want an average month or a specific month....your way gets an average. For a specific month count days and multiply by 86400 (seconds per 24.0 hour day)
This isn't really a programming question. Months have different lengths, so dividing the number of seconds in a year by 12 will give you nothing useful. It's easy to determine the days in a month - a simple lookup table plus a calcualation of leap years will do it. Then just multiply by the number of seconds in a day.
If you are being really precise you might need to include calculations of leap seconds, but since they are unpredictably assigned based on astronimical calculations, and not predictable in advance, I would probably ignore them.
Number of days vary in each month.Proper algorithm for this is to get number of days in moth and multiply it with 86400 (number of seconds in a day).You might also need average count or leap years calculation ...
The trivial answer is to find the number of days in the month and then multiply by 86400. That will work perfectly if you are dealing with dates and times in UTC. However, if you are using a local time zone then this approach yields a slightly incorrect result if that time zone observes daylight saving time. The error is somewhat small over a one month period, but will magnify if you need to make similiar calculation over short periods like a day. I definitely recommend doing all processing and storage in UTC, but depending on the application you will have to convert your UTC times to the local time zone that the end user is expecting. And it might even be plausible that you have to calculate durations using the local time zone. Again, use UTC as much as possible so that you avoid most of the problems.
I came up with this solution in C#. It is compatible with UTC and local time zones alike. You just have to tell the GetNumberOfSecondsInMonth which time zone you want the calculation to be based on. In my example I chose November of 2010 because here in Missouri we observe DST and there was one extra hour this month. Daylight saving time rules change so I used an API that pulls the DST information from the operating system so that the calculation will be correct for years prior to 2007 (that is when the United States expanded DST for most regions).
I should point out that my solution does not handle leap seconds in UTC. For me that is never an issue. But it would be easy to account for that by using a lookup table if you really needed ultra high precision timing.
public class Program
{
static void Main(string[] args)
{
int seconds = GetNumberOfSecondsInMonth(2010, 11, DateTimeKind.Local);
}
public static int GetNumberOfSecondsInMonth(int year, int month, DateTimeKind kind)
{
DateTime start = new DateTime(year, month, 1);
DateTime end = start.AddMonths(1);
int seconds = (int)(end - start).TotalSeconds;
if (kind == DateTimeKind.Local)
{
DaylightTime dt = TimeZone.CurrentTimeZone.GetDaylightChanges(year);
seconds = (dt.Start > start) ? seconds - 3600 : seconds;
seconds = (dt.End < end) ? seconds + 3600 : seconds;
}
return seconds;
}
}
It's a problem with years ang months as there is not a fixed number of days in them. But after a lot of thought I have figured out how to do it. It was not a good idea to calculate months with either 30 or 31 days in them, because it looks bad, for example converting from 1 year to months would give an answer of 11 months and 25 days if I had 30 days in each month, or 12 months and 5 days if I have 31 days in each month.
Instead I loop through a series of days per month: 30,30,31,30,31,30,31,30,31,30,31,30 which makes a total of 365 days in a year. So if I want the number of days in 4 months I add 30+30+31+30. And if I start with 23 months it would go through the loop almost twice (23 times 30 or 31). It's done in a while/until loop. For every 4 years I add 1 day, making it 366 days (the first 30 is changed to 31 in the list). It's rather complex but it works and the result looks better.

Categories