Basically, I am trying to recreate PHP date's year functionality. Using the number of seconds since 1 January 1970, I am trying to get the year with out using a built in function. I had a an idea, but it did not work because of leap years. Can anyone give me a working formula that takes the seconds since 1970 and gets a year from it?
To find the year you need to deal with leaps.
The years from 1 are ordered as blocks of 4 years been the last of them one day longer, right? So you have blocks of:
seconds_block = 365*3 + 366 days = 126230400 seconds
seconds_year = 365 days = 31536000 seconds
1970 is the second year of its block so with this:
<?php
//test_year.php
$given_seconds = $argv[1];
$seconds_year = 31536000;
$seconds_block = 126230400;
$total_blocks_to_1968 = 492;
$actual_block = floor((($given_seconds + $seconds_year) / $seconds_block)) + $total_blocks_to_1968;
$actual_offset_from_last_block = ($given_seconds + $seconds_year) % $seconds_block;
$actual_year_of_the_block = min(floor($actual_offset_from_last_block / $seconds_year) + 1, 4);
$actual_year = $actual_block * 4 + $actual_year_of_the_block;
echo $actual_year;
Testing it...
$ php test_year.php 0
1970
$ php test_year.php 1
1970
$ php test_year.php -1
1969
$ php test_year.php 31536000
1971
$ php test_year.php 31535999
1970
$ php test_year.php 126230400
1974
$ php test_year.php 126230399
1973
More:
One year is leap if is divisible by 4 except those divisible by 100 (but not by 400).
function isLeap(year){
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)
}
EDIT: pseudocode formula
x = input // number of seconds since 1970
sy = 31536000 // seconds a non leap year
sb = 126230400 // seconds a block of 3 non leap years and one that is
actual_year = (floor(((x + sy) / sb)) + 492) * 4 +
(min(floor(((x + sy) % sb) / sy) + 1, 4));
Cheers
You can't ignore leap years.
You have year = 1970
Add numSecsInYear to that, increment the year
if year is leap, then 'eat' one more sec.
... until there is less that 1 year of seconds
then go month by month, day by day, hour by hour, min by min
also, year is leap if
- if ends with 00, if it gives no rest after dividing by 400
- else if it gives no rest after dividing by 4
You figure out the code :)
And yes, this isn't very much optimized :)
p.s. obviously, when going month by month, keep in mind that february has 29 days if year is leap :)
This is actually wrong, but a good enough approximation if you don't need the year to change exactly at the time of the new year. The idea is, the number of days in a year, in order for there to be leap years every 4 years, is 365.25 days.
$a = time();
$y = 1970 + floor($a / 60 / 60 / 24 / 365.25);
If you are using a UNIX-like system, you can use the system's date functionality to format times instead of reimplementing the PHP function:
date +%Y
gives the current year. You can then use the -d switch to format a custom date, rather than the current date:
date -d "UTC 1970-01-01 1287946333 secs +%Y"
gives "2010".
For years 1970 - 2038, you can use these equivalents (+/- a few minutes for the months and years):
Human readable time Seconds
1 minute 60 seconds
1 hour 3600 seconds
1 day 86400 seconds
1 week 604800 seconds
1 month (30.44 days) 2629743 seconds
1 year (365.24 days) 31556926 seconds
You can test your formulae here These equivalents can be off by enough minutes on key days (ie, Dec 31 / Jan 1) and are only good for epoch times away from boundaries.
If you want to be exact you need to deal with each and every leap year; either through a formula or through iteration.
This Perl code calculates the year from epoch seconds for any year +/- 130 or more years from 1970 (the Unix epoch). You need to know on your platform how big (32 bit or 64 bit) the epoch number is to know the span:
sub days_in_year {
my $year=shift;
my $leap =
($year % 400 == 0) ? 1
: ($year % 100 == 0) ? 0
: ($year % 4 == 0) ? 1
: 0
;
return (365+$leap);
}
sub epoch_to_year {
use integer;
my $t=shift;
my $ey=1970;
my $secs=$t;
if($t<0) {
while($secs<0) {
$secs+=days_in_year(--$ey)*24*60*60;
}
return $ey;
}
else {
while($secs>0) {
$secs-=days_in_year($ey++)*24*60*60;
}
return $ey if ($secs==0);
return $ey-1;
}
}
It is SLOW and you should use a library, but it you do not have one it will work. It is trivial to translate that to PHP. (sub => function, delete my, etc)
Related
How can I calculate the nearest hours to midnight time 00:00 regardless of date in PHP. For example:
If time is 22:00 then 2 hours are required to reach 00:00
If time is 04:00 then -4 hours are the nearest to reach 00:00
Currently I have the following PHP function:
<?php
$ts1 = strtotime('00:00');
$ts2 = strtotime('04:00');
$diff = ($ts1 - $ts2) / 3600;
?>
But this won't be helpful much in the above.
If you have the php Datetime class available you can calculate the difference between two DateTimes.
$time1 = new \DateTime('00:00');
$time2 = new \DateTime('04:00');
$diff = $time1->diff($time2, true);
$hourDifference = 0;
if ($diff->h < 12) {
$hourDifference = -$diff->h;
} elseif ($diff->h > 12) {
$hourDifference = 24 - $diff->h;
} else {
$hourDifference = 12; // kann be positive or negative
}
And you'll get a DateInverall object where you can access, hours, minuts, seconds and compare them with normal php operators.
If you'r not too interested in minutes;
1. Extract minutes.
check if minutes is > or <=30
if greater, 'store' 1
2. Extract hour
check if hour is greater than 12
if not, add 12 (store flag also to say it will be minus)
3. if greater (ref. Step 1), add 1 to extracted hour.
4. 24 - extracted hour is your interval.
Please note, this may be reduced/ simplified greatly.
Your interval (should) be correct to the nearest half hour
The answer depends on the date (not only the time). This is because of daylight saving time changes. For example might 02:59 being closer to 00:00 then 21:01 on the time where daylight saving time will set back hour.
I have a number of X days, let's say 700.
How can I output this 700 to years?
Eg. 700 = 1.11 years (1 year 11 months)
or 2.2 (2 years and two months)
... And so on
I don't care for leap years or such, just need an aproximate value.
Think about it...
700 days, 30 days in a month.
700/30 = 23.33 months. 12 months in a year.
23.33/12 = 1.944444... take the integer value (int)1.944444 to get 1 for the year.
23.33%12= 11.33... months. You can cast it to an int as well to get 11
Your result: 1 year, 11 months
try this one...
//86400 seconds per day
//31556926 seconds per year
$days = 700;
$timestamp = (86400 * $days) / 31556926;
echo $timestamp;
// = 1.9165364839402 years
use this site for referrence: http://www.epochconverter.com/ they have conversion per month, week, days.
//1.9165364839402
I have a SQL Datestamp like this: 2012-02-20 21:14:54
How would I print out the relative date and time in PHP?
e.g.
Occured: a few seconds ago
Occured: 4 minutes ago
Occured: 4 hours ago
Occured: Monday Jan 8th, 2012
After the hours I just want to print out the actual date
Found this after two seconds of Google http://www.mdj.us/web-development/php-programming/another-variation-on-the-time-ago-php-function-use-mysqls-datetime-field-type
In general you chose a unit of time like seconds, test if the time-difference is smaller then the max-value for this unit (60s) and if so, print out "$timeDifference $unit". If not you divide the difference by the units max-value and start over with the next higher unit (minutes).
Example:
$timeDif = 60*60*5 + 45; // == 5 hours 45 seconds
// 60 seconds in a minute
if ($timeDif < 60) // false
return "$timeDif second(s) ago";
// convert seconds to minutes
$timeDif = floor($timeDif / 60); // == 300 = 5 * 60
// 60 minutes in an hour
if ($timeDif < 60) // false
return "$timeDif minute(s) ago";
// convert minutes to hours
$timeDif = floor($timeDif / 60); // == 5
// 24 hours in a day
if ($timeDif < 24)
return "$timeDif hour(s) ago";
// ...
Here's what a MySQL solution might look like:
SELECT date_field, IF (DATEDIFF( NOW(), date_field) < 1, IF ( TIMEDIFF( NOW(), date_field ) < '01:00:00', CONCAT(MINUTE(TIMEDIFF(NOW(), date_field)), ' minutes'), CONCAT(HOUR(TIMEDIFF(NOW(), date_field )), ' hours')), date_field) AS occurred
FROM table
That is no so hard to code. Moreover is quite fun make it. Here a sample, Just put hands to work.
Start converting SQL Datestamp to unixtime like this:
$unixtime = strtotime('2012-02-20 21:14:54');
Editing the question.
I have SQL like this:
`table1`.`DateField` >= DATE_SUB(NOW(), INTERVAL {$days} DAY
Now 24 hours make a whole day. However, what if I want to do the query for the last 3 hours or so?
My table1.DateField is in the format of 2010-03-10 10:05:50.
Original post:
If I have this
1 hour
2 hours
3 hours
..
24 hours
How would I change it to days?
Thanks.
$hours = 80;
$hid = 24; // Hours in a day - could be 24, 8, etc
$days = round($hours/$hid);
if( $days < 0 )
{
echo "$hours hours";
}
else
{
echo "$days days";
}
This assumes you want the hours if it's less than 1 day. If not just remove the switch.
MySQL not only knows DAY as a unit for an interval but also HOUR, MINUTE, ....
see http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add
$x = 32;
$sql = "SELECT
x,y,z
FROM
foo
WHERE
`table1`.`DateField` >= NOW() - INTERVAL $x HOUR
";
As simple as:
if you want to convert the total of those hour to day:
Just sum the total of hours and that total must be divided by 24
(1 + 2 + 3 + 5) / 24
If you want to convert all of those hours to days:
Just divide by 24 every hours in your list
(1/24) (2/24) (3/24) (5/24)
How would you go about calculating the amount of months between two arbitrary dates? Given that even if just one day falls on a month, it is considered a full month.
Examples:
2010-01-01 - 2010-03-31 = three months
2010-06-15 - 2010-09-01 = four months
Et cetera. I thought of just dividing the difference of timestamps with 2592000 (average number of seconds in a month) but that seems hacky and prone to errors. And I'd like to keep it as fast as possible (needs to run thousands of times quick), so I guess using strtotime isn't optimal either?
If I am reading your question correctly, you would want to return "2" for January 31st and February 1st, because it spans both January and February, even though they are only 1 day apart.
You could work out (psuedocode):
monthno1 = (date1_year * 12) + date1_month;
monthno2 = (date2_year * 12) + date2_month;
return (monthno2 - monthno1) + 1;
This assumes that the second date is the later date.
Assuming the dates are in a known format:
function getMonths($start, $end) {
$startParsed = date_parse_from_format('Y-m-d', $start);
$startMonth = $startParsed['month'];
$startYear = $startParsed['year'];
$endParsed = date_parse_from_format('Y-m-d', $end);
$endMonth = $endParsed['month'];
$endYear = $endParsed['year'];
return ($endYear - $startYear) * 12 + ($endMonth - $startMonth) + 1;
}
This gives:
print(getMonths('2010-01-01', '2010-03-31')); // 3
print(getMonths('2010-06-15', '2010-09-01')); // 4