需要一个公式:自0001年1月1日上午12:00起提取秒数

时间:2010-01-27 05:56:36

标签: datetime math date formula

输入:自0001年1月1日起的秒数

输出:此期间的全年数

我开发了一种我认为不是最佳解决方案的算法。我认为应该有一个不涉及循环的解决方案。有关算法的信息,请参阅代码块1. A)确定天数和B)根据闰年从迭代年中逐步减去366或365,同时增加年总数

它并不像分割DayCount那么简单365.2425并截断,因为我们在1月1日遇到了失败点,0002(31536000秒/(365.2425 * 24 * 60 * 60))= 0.99934。

关于非循环方法从0001年1月1日上午12:00起提取年数的想法?

我需要解决这个问题,因为我需要一个长时间嵌入的日期(存储秒数),这样我就可以用1秒的精度跟踪年数超过1200万。

代码块1 - 从秒数(包括闰年)获得年数的低效算法

        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

编辑:我的解决方案是跳过在8位内嵌入日期的内存节省,并将每个值(秒到年)存储在单独的整数中。这会以内存为代价导致即时检索。

Edit2:第一次编辑错误(8位)

7 个答案:

答案 0 :(得分:20)

如果您需要 second 的准确性,您可能需要商业级日期时间套餐;使用简单的算法准确地完成它太复杂了。例如:

由于这些和更多的复杂性,你最好不要自己编写代码,除非你可以放松你需要准确度的约束第二次超过1200万年。

  

“1582年10月4日 - 阿维拉的圣特雷莎去世。第二天,10月15日,她被埋葬了。”

答案 1 :(得分:5)

维基百科有一篇关于Julian Date的文章,其中包含一个可以适应您需求的算法。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

您不需要循环,计算从0001年1月1日到unix epoch start(1970年1月1日00:00:00)的秒数,并保存到某处。然后从输入中减去它,然后使用任何可用的工具将unix时间戳(1970年1月1日的秒)转换为年,然后添加1970.我不太了解VB编程以发布详细指南。

答案 4 :(得分:0)

我认为这对你有用

function foo(seconds):
  count = seconds
  year = 0
  while (count > 0):
    if leap_year(year)
      count = count - 366
    else
      count = count - 365
    year ++
  return year

答案 5 :(得分:0)

以下假设公历将在即将到来的五百八十四亿五十亿年中继续生效。但是要准备好失望;随着我们的太阳开始膨胀,改变地球的轨道和一年的持续时间,日历可能最终被废弃,当地球落入太阳七十五亿年时,很可能会采取其他措施从现在开始。

顺便说一句,我甚至没有尝试在采用格里高利历之前处理日期。我只返回1582年10月15日之前发生日期的天数,并且需要能够表达那种返回值是GetDateFromSerial函数具有asString的唯一原因。参数。

Sub GetDateFromSerial(ByVal dateSerial As ULong, ByRef year As Long, ByRef month As Integer, ByRef dayOfMonth As Integer, ByRef secondsIntoDay As Integer, ByRef asString As String)
    Const SecondsInOneDay As ULong = 86400 ' 24 hours * 60 minutes per hour * 60 seconds per minute

    'Dim startOfGregorianCalendar As DateTime = New DateTime(1582, 10, 15)
    'Dim startOfGregorianCalendarInSeconds As ULong = (startOfGregorianCalendar - New DateTime(1, 1, 1)).TotalSeconds

    Const StartOfGregorianCalendarInSeconds As ULong = 49916304000

    secondsIntoDay = dateSerial Mod SecondsInOneDay

    If dateSerial < StartOfGregorianCalendarInSeconds Then
        year = -1
        month = -1
        dayOfMonth = -1

        Dim days As Integer = (StartOfGregorianCalendarInSeconds - dateSerial) \ SecondsInOneDay

        asString = days & IIf(days = 1, " day", " days") & " before the adoption of the Gregorian calendar on October 15, 1582"
    Else
        'Dim maximumDateValueInSeconds As ULong = (DateTime.MaxValue - New DateTime(1, 1, 1)).TotalSeconds
        Const MaximumDateValueInSeconds As ULong = 315537897600

        If dateSerial <= MaximumDateValueInSeconds Then
            Dim parsedDate As DateTime = DateTime.MinValue.AddSeconds(dateSerial)

            year = parsedDate.Year
            month = parsedDate.Month
            dayOfMonth = parsedDate.Day
        Else
            ' Move the date back into the range that DateTime can parse, by stripping away blocks of
            ' 400 years. Aim to put the date within the range of years 2001 to 2400.
            Dim dateSerialInDays As ULong = dateSerial \ SecondsInOneDay

            Const DaysInFourHundredYears As Integer = 365 * 400 + 97 ' Three multiple-of-4 years in each 400 are not leap years.

            Dim fourHundredYearBlocks As Integer = dateSerialInDays \ DaysInFourHundredYears

            Dim blocksToFactorInLater As Integer = fourHundredYearBlocks - 5

            Dim translatedDateSerialInDays As ULong = dateSerialInDays - blocksToFactorInLater * CLng(DaysInFourHundredYears)

            ' Parse the date as normal now.
            Dim parsedDate As DateTime = DateTime.MinValue.AddDays(translatedDateSerialInDays)

            year = parsedDate.Year
            month = parsedDate.Month
            dayOfMonth = parsedDate.Day

            ' Factor back in the years we took out earlier.
            year += blocksToFactorInLater * 400L
        End If

        asString = New DateTime(2000, month, dayOfMonth).ToString("dd MMM") & ", " & year
    End If
End Sub

Function GetSerialFromDate(ByVal year As Long, ByVal month As Integer, ByVal dayOfMonth As Integer, ByVal secondsIntoDay As Integer) As ULong
    Const SecondsInOneDay As Integer = 86400 ' 24 hours * 60 minutes per hour * 60 seconds per minute

    If (year < 1582) Or _
       ((year = 1582) And (month < 10)) Or _
       ((year = 1582) And (month = 10) And (dayOfMonth < 15)) Then
        Throw New Exception("The specified date value has no meaning because it falls before the point at which the Gregorian calendar was adopted.")
    End If

    ' Use DateTime for what we can -- which is years prior to 9999 -- and then factor the remaining years
    ' in. We do this by translating the date back by blocks of 400 years (which are always the same length,
    ' even factoring in leap years), and then factoring them back in after the fact.

    Dim fourHundredYearBlocks As Integer = year \ 400

    Dim blocksToFactorInLater As Integer = fourHundredYearBlocks - 5

    If blocksToFactorInLater < 0 Then blocksToFactorInLater = 0

    year = year - blocksToFactorInLater * 400L

    Dim dateValue As DateTime = New DateTime(year, month, dayOfMonth)

    Dim translatedDateSerialInDays As ULong = (dateValue - New DateTime(1, 1, 1)).TotalDays

    Const DaysInFourHundredYears As ULong = 365 * 400 + 97 ' Three multiple-of-4 years in each 400 are not leap years.

    Dim dateSerialInDays As ULong = translatedDateSerialInDays + blocksToFactorInLater * DaysInFourHundredYears

    Dim dateSerial As ULong = dateSerialInDays * SecondsInOneDay + secondsIntoDay

    Return dateSerial
End Function

答案 6 :(得分:0)

我知道这个问题现在已经过时了,但我经常看到这样的问题,而且这里没有任何简单的答案。

我的解决方案使用了写两个日期的旧技巧,就像它们是数字一样(例如'2013年12月12日'作为20131212),然后从另一个中减去一个并丢弃最后四位数。我在F#中挖出了我的实现,你可以将它粘贴到LinqPad来检查答案。也考虑到闰年等:

let dateFrom = new DateTime(1,1,1)

let dateTo = dateFrom.AddSeconds(100000000.0)

let yearsSince dateFrom dateTo =
    let intRepresentation (date: DateTime) = 
        date.ToString "yyyy.MMdd" |> Convert.ToDouble

    let dateToNum = intRepresentation dateTo
    let dateFromNum = intRepresentation dateFrom

    int (dateToNum - dateFromNum)

yearsSince dateFrom dateTo |> Dump

let dob = DateTime(1985, 4, 16)

let calculateAge = yearsSince dob

calculateAge DateTime.Today |> Dump

请注意,这非常天真:除了已经由.NET的DateTime类处理的更改之外,它不会考虑时区或历史时区更改。实际的grunt工作由DateTime.AddSeconds方法完成。希望这会有所帮助。