diff options
author | coffeepots <coffeepots@users.noreply.github.com> | 2015-07-14 11:58:26 +0100 |
---|---|---|
committer | coffeepots <coffeepots@users.noreply.github.com> | 2015-07-14 11:58:26 +0100 |
commit | f09916d72034242a9bc4966ecf6f5590ed40d2cc (patch) | |
tree | 4f3eef0042fe208b38edd9546733b77b5b8224b6 | |
parent | 6c8e9da9ee8173091931980b64aa21a839fc1424 (diff) | |
download | Nim-f09916d72034242a9bc4966ecf6f5590ed40d2cc.tar.gz |
Procs to decode Time to TimeInfo & TimeInterval
Fills in the missing functionality of decoding Time to TimeInfo and TimeInterval, whilst also adding some procs to work with leap years and to get the day of the week based on a date.
-rw-r--r-- | lib/pure/times.nim | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e4d3f7494..efed1bbc0 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1039,6 +1039,126 @@ proc parse*(value, layout: string): TimeInfo = info.weekday = getLocalTime(timeInfoToTime(info)).weekday return info +# Leap year calculations are adapted from: +# from http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years +# The dayOfTheWeek procs are adapated from: +# from http://stason.org/TULARC/society/calendars/2-5-What-day-of-the-week-was-2-August-1953.html + +# Note: for leap years, start date is assumed to be 1 AD. +# counts the number of leap years up to January 1st of a given year. +# Keep in mind that if specified year is a leap year, the leap day +# has not happened before January 1st of that year. +proc countLeapYears(yearSpan: int): int = + (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1)/400)).int + +proc countDays(yearSpan: int): int = + (yearSpan - 1) * 365 + countLeapYears(yearSpan) + +# counts the number of years spanned by a given number of days. +proc countYears(daySpan: int): int = + ((daySpan - countLeapYears(daySpan div 365)) div 365) + +proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = + let days = daySpan - countLeapYears(daySpan div 365) + result.years = days div 365 + result.days = days mod 365 + +type + SecondScale = enum ssMinute, ssHour, ssDay, ssMonth, ssYear + +# these are very approximate beyond day +const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ + 60, # minute + 60*60, # hour + 60*60*24, # day + 60*60*24*30, # month (estimate) + 60*60*24*30*12] # year (estimate) + +const + epochStartYear = 1970 + leapYearsSinceEpoch* = countLeapYears(epochStartYear) + +proc dayOfWeek*(day, month, year: int): WeekDay = + # This is for the Gregorian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (day + y + (y div 4) - (y div 100) + (y div 400) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc dayOfWeekJulian*(day, month, year: int): WeekDay = + # This is for the Julian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc decodeTime*(t: Time): TimeInfo = + let + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + y = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, y) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, y) + + let + yd = daysRemaining + m = mon # month is zero indexed enum + md = days + # NB: month is zero indexed but dayOfWeek expects 1 indexed. + wd = dayOfWeek(days, mon.int + 1, y).Weekday + h = daySeconds div secondsIn[ssHour] + 1 + mi = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + s = daySeconds mod secondsIn[ssMinute] + result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) + +proc decodeTimeInterval*(t: Time): TimeInterval = + var + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + result.years = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, result.years) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, result.years) + + result.months = mon.int + 1 # month is 1 indexed int + result.days = days + result.hours = daySeconds div secondsIn[ssHour] + 1 + result.minutes = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + result.seconds = daySeconds mod secondsIn[ssMinute] + # Milliseconds not available from Time when isMainModule: # $ date --date='@2147483647' @@ -1122,3 +1242,13 @@ when isMainModule: assert "15:04:00" in $s.parse(f) when not defined(testing): echo "Kitchen: " & $s.parse(f) + var ti = decodeTime(getTime()) + echo "Todays date after decoding: ", ti + var tint = decodeTimeInterval(getTime()) + echo "Todays date after decoding to interval: ", tint + # checking dayOfWeek matches known days + assert dayOfWeek(21, 9, 1900) == dFri + assert dayOfWeek(1, 1, 1970) == dThu + assert dayOfWeek(21, 9, 1970) == dMon + assert dayOfWeek(1, 1, 2000) == dSat + assert dayOfWeek(1, 1, 2021) == dFri |