tags:

views:

135

answers:

5
+1  Q: 

Seconds to Year

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?

+1  A: 
playcat
how is this not useful, is pseudocode is what user was asking for?
playcat
Not necessarily the best impl. pseudo code, but I like: *You can't ignore leap years*.
pst
:) well, i'm sure it's not the best implementation, but it's somewhat 'school' implementation, and it sounded as something that user might need
playcat
+1  A: 

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);
cambraca
A gross approximation, but I'll take it and cross my fingers.
pst
365.2425 is a better approximation.
drewk
+1  A: 

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)

drewk
Still a gross approximation, but why not :-)
pst
+1  A: 

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".

lonesomeday
+2  A: 

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

Alfonso de la Osa
only works from 1968 -> 2100...
drewk
+1 @drewk, i was wrong
Alfonso de la Osa
I would not say yours was "wrong" necessarily. Just limited in its range. The range of 1968 -> 2100 may be all that is needed by the OP. A "complete" version would check what year the locale adopted the Gregorian calendar! (Leap year in the modern sense date to the Gregorian reforms that took 300+ years to fully adopt worldwide.) So there is only degrees of accurate, no?
drewk