输入:自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位)
答案 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方法完成。希望这会有所帮助。