views:

293

answers:

4

Input: # of seconds since January 1st, of Year 0001

Output: # of Full years during this time period

I have developed an algorithm that I do not think is the optimal solution. I think there should be a solution that does not involve a loop. See Code Block 1 for the algorithm which A) Determines the quantity of days and B) Iteratively subtracts 366 or 365 depending on Leap Years from the Day Total while incrementing the Year Total

It's not as simple as Dividing DayCount by 365.2425 and truncating, because we hit a failure point at on January 1, 0002 (31536000 Seconds / (365.2425 * 24 * 60 * 60)) = 0.99934.

Any idea on a non-looping method for extracting years from a quantity of seconds since January 1, 0001 12:00 AM?

I need to figure this out because I need a date embedded in a long (which stores seconds) so that I can track years out to 12+ million with 1-second precision.

Code block 1 - Inefficient Algorithm to get Years from Seconds (Including Leap Years)

        Dim Days, Years As Integer

        'get Days
        Days = Ticks * (1 / 24) * (1 / 60) * (1 / 60) 'Ticks = Seconds from Year 1, January 1

        'get years by counting up from the beginning
        Years = 0
        While True
            'if leap year
            If (Year Mod 4 = 0) AndAlso (Year Mod 100 <> 0) OrElse (Year Mod 400 = 0) Then
                If Days >= 366 Then 'if we have enough days left to increment the year
                    Years += 1
                    Days -= 366
                Else
                    Exit While
                End If
                'if not leap  year
            Else
                If Days >= 365 Then 'if we have enough days left to increment the year
                    Years += 1
                    Days -= 365
                Else
                    Exit While
                End If
            End If
        End While

        Return Years

Edit: My solution was to skip the memory savings of embedding a date within 8 bits and to store each value (seconds through years) in separate integers. This causes instant retrievals at the expense of memory.

Edit2: Typo in first edit (8bits)

+2  A: 

Wikipeda has an article on Julian Date with an algorithm, and links to another which you could adapt to your needs.

Carlos Gutiérrez
This may work, I'll look into it a bit more. If I can get a O(1) algorithm to go Seconds since 1900 -> Julian -> Gregorian, then we'll be set (assuming it's quicker than 2000-10,000,000 iterations through the above loop)
hamlin11
+1  A: 
Const TICKS_PER_YEAR As Long = 315360000000000
Function YearsSinceBeginningOfTimeUntil(ByVal d As DateTime) As Integer
    Return Math.Floor(d.Ticks / TICKS_PER_YEAR)
End Function
Jay
Ticks per year is closer to 31556952 which is slightly more than the ticks per non leapyear, so we fall just short of achieving the next integer, thus Floor gives us the previous year number.
hamlin11
Have you tried the function?
Jay
A: 

I think that this will work for you

function foo(seconds):
  count = seconds
  year = 0
  while (count > 0):
    if leap_year(year)
      count = count - 366
    else
      count = count - 365
    year ++
  return year
danny
I was hoping to avoid a loop
hamlin11
Although this is cleaner
hamlin11
I think there are more than 366 seconds in a year
BlueRaja - Danny Pflughoeft
+2  A: 

If you need accuracy to the very second, you'll probably want a commercial-grade datetime package; it's just too complex to do accurately with a simple algorithm. For instance:

  • Many people have noted that we have leap years every 4 years, but did you know that every year divisible by 100 but not by 400 is not a leap-year?
  • Years before 1582 use a slightly different calendar
  • The year 1582 itself only had 355 days

Because of these and more complications, you are better off not writing the code yourself, unless you can relax the constraint that you need accuracy to the very second over 12-million years.

"October 4, 1582 – Saint Teresa of Ávila dies. She is buried the next day, October 15."

BlueRaja - Danny Pflughoeft