Datetime 需要一个公式:从0001年1月1日凌晨12:00起的秒数中提取年数

Datetime 需要一个公式:从0001年1月1日凌晨12:00起的秒数中提取年数,datetime,math,date,formula,Datetime,Math,Date,Formula,输入:#自0001年1月1日起的秒数 产量:#在此期间的全年产量 我开发了一种算法,我认为它不是最优的解决方案。我认为应该有一个不涉及循环的解决方案。请参见代码块1了解算法,该算法A)确定天数,B)根据闰年从天数总数中迭代减去366或365,同时增加年份总数 这并不像将DayCount除以365.2425并截断那么简单,因为我们在0002年1月1日(31536000秒/(365.2425*24*60*60))=0.99934时遇到了一个故障点 从0001年1月1日凌晨12:00起,有没有关于从秒

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

产量:#在此期间的全年产量

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

这并不像将DayCount除以365.2425并截断那么简单,因为我们在0002年1月1日(31536000秒/(365.2425*24*60*60))=0.99934时遇到了一个故障点

从0001年1月1日凌晨12:00起,有没有关于从秒数中提取年份的非循环方法的想法

我需要弄清楚这一点,因为我需要一个嵌入在长时间(存储秒数)中的日期,这样我就可以以1秒的精度追踪年数,精确到1200多万年

代码块1-从秒开始计算年数(包括闰年)的算法效率低下。

Dim天,年为整数
“好几天
天=滴答声*(1/24)*(1/60)*(1/60)'滴答声=第1年1月1日起的秒数
“从一开始就开始计算以获得年份
年=0
虽然是真的
“如果闰年
如果(第4年模=0)和(第100年模=0)或(第400年模=0),则
如果天数>=366,那么“如果我们还有足够的天数来增加一年
年+=1
天数-=366天
其他的
退出时
如果结束
“如果不是闰年
其他的
如果天数>=365,那么“如果我们还有足够的天数来增加一年
年+=1
天数-=365天
其他的
退出时
如果结束
如果结束
结束时
回归年
编辑:我的解决方案是跳过将日期嵌入8位所节省的内存,并将每个值(从秒到年)存储在单独的整数中。这会导致以牺牲内存为代价的即时检索


Edit2:first edit(8位)中的打字错误Wikipeda有一篇文章介绍了一种算法,您可以根据自己的需要进行调整。

我认为这对您有用:

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
function foo(days):
  count = days
  year = 0
  while (count > 0):
    if leap_year(year)
      count = count - 366
    else
      count = count - 365
    year ++
  return year

如果你需要精确到第二秒,你会想要一个商业级的datetime软件包;这太复杂了,用一个简单的算法很难精确地完成。例如:

  • 很多人都注意到我们每4年有一个闰年,但是你知道吗,每年被100整除而不是被400整除都是不是闰年?这甚至在中国也引起了问题
  • 有些国家不实行夏时制,有些国家实行夏时制。这年复一年
  • 即使在有DST的国家,也可以任意选择不使用它
  • 1582年前使用
  • 年只有355天(或354天,取决于国家)
  • 有些国家会切换时区。一些国家
  • 然后是。一些管理机构武断地决定我们是否应该在每年的时钟上增加一秒(有时是两秒)。我们无法提前知道下一个闰秒是什么时候,也无法预测过去的闰秒
由于这些和更多的复杂性,最好不要自己编写代码,除非您可以在1200万年的时间里放松对精确度的限制,使其在第二秒就达到

1582年10月4日——奥维拉的圣特蕾莎去世。第二天,即10月15日,她被安葬


以下假设公历在未来的5845亿年内仍然有效。不过,要做好失望的准备;当我们的太阳开始膨胀,改变地球的轨道和一年的持续时间时,日历可能会被废弃,而当地球在75亿年后落入太阳时,很可能会采用其他的日历

顺便说一句,在采用公历之前,我甚至不想去处理日期。我只返回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
Sub-GetDateFromSerial(ByVal-dateSerial为ULong,ByRef-year为Long,ByRef-month为整数,ByRef-dayOfMonth为整数,ByRef-SecondsToday为整数,ByRef-asString为字符串)
Const SecondsInOneDay作为ULong=86400'24小时*60分钟/小时*60秒/分钟
'Dim startOfGregorianCalendar As DateTime=新日期时间(1582,10,15)
'Dim startofGregoriandarInSeconds为ULong=(startofGregoriandar-新日期时间(1,1,1))。总秒数
Const StartForegorianCalendarInSeconds为ULong=49916304000
secondsIntoDay=日期序列Mod secondsIntoDay
如果dateSerial如果dateSerial不需要循环,请计算从0001年1月1日到unix纪元开始(1970年1月1日00:00:00)的秒数,然后保存到某个地方。然后从您的输入中减去它,然后使用任何可用的工具将unix时间戳(1970年1月1日起的秒数)转换为年,然后添加1970。我不知道我是谁
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