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?
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);
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)
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".
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