summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@googlemail.com>2015-08-08 20:56:14 +0100
committerDominik Picheta <dominikpicheta@googlemail.com>2015-08-08 20:56:14 +0100
commit34ca9dd5861d5504bbbeb71469318c8c8caba6d7 (patch)
treeb5407a18ced4924d66b71cd742103fa25c07da24
parent4f8d982d5b0944f7f2bfc905c428ed81c3bd75bd (diff)
parentf9d909bb947749666c0241f64bd8adef8c071a9b (diff)
downloadNim-34ca9dd5861d5504bbbeb71469318c8c8caba6d7.tar.gz
Merge pull request #3114 from coffeepots/patch-1
Procs to decode Time to TimeInfo & TimeInterval
-rw-r--r--lib/pure/times.nim126
1 files changed, 126 insertions, 0 deletions
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 78629af71..d9abbed1a 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1052,6 +1052,117 @@ 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)
+
+proc countYears(daySpan: int): int =
+  # counts the number of years spanned by a given number of days.
+  ((daySpan - countLeapYears(daySpan div 365)) div 365)
+
+proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] =
+  # counts the number of years spanned by a given number of days and the remainder as days.
+  let days = daySpan - countLeapYears(daySpan div 365)
+  result.years = days div 365
+  result.days = days mod 365
+
+const
+  secondsInMin = 60
+  secondsInHour = 60*60
+  secondsInDay = 60*60*24
+  epochStartYear = 1970
+
+proc getDayOfWeek*(day, month, year: int): WeekDay =
+  ## Returns the day of the week enum from day, month and year.
+  # 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 getDayOfWeekJulian*(day, month, year: int): WeekDay =
+  ## Returns the day of the week enum from day, month and year, according to the Julian calender.
+  # 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
+  result = d.WeekDay
+
+proc timeToTimeInfo*(t: Time): TimeInfo =
+  ## Converts a Time to TimeInfo.
+  let
+    daysSinceEpoch = t.int div secondsInDay
+    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
+    daySeconds = t.int mod secondsInDay
+
+    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 = getDayOfWeek(days, mon.int + 1, y).Weekday
+    h = daySeconds div secondsInHour + 1
+    mi = (daySeconds mod secondsInHour) div secondsInMin
+    s = daySeconds mod secondsInMin
+  result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s)
+
+proc timetoTimeInterval*(t: Time): TimeInterval =
+  ## Converts a Time to a TimeInterval.
+  var
+    daysSinceEpoch = t.int div secondsInDay
+    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
+    daySeconds = t.int mod secondsInDay
+
+  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 secondsInHour + 1
+  result.minutes = (daySeconds mod secondsInHour) div secondsInMin
+  result.seconds = daySeconds mod secondsInMin
+  # Milliseconds not available from Time
 
 when isMainModule:
   # $ date --date='@2147483647'
@@ -1137,3 +1248,18 @@ when isMainModule:
   assert "15:04:00" in $s.parse(f)
   when not defined(testing):
     echo "Kitchen: " & $s.parse(f)
+    var ti = timeToTimeInfo(getTime())
+    echo "Todays date after decoding: ", ti
+    var tint = timeToTimeInterval(getTime())
+    echo "Todays date after decoding to interval: ", tint
+  # checking dayOfWeek matches known days
+  assert getDayOfWeek(21, 9, 1900) == dFri
+  assert getDayOfWeek(1, 1, 1970) == dThu
+  assert getDayOfWeek(21, 9, 1970) == dMon
+  assert getDayOfWeek(1, 1, 2000) == dSat
+  assert getDayOfWeek(1, 1, 2021) == dFri
+  # Julian tests
+  assert getDayOfWeekJulian(21, 9, 1900) == dFri
+  assert getDayOfWeekJulian(21, 9, 1970) == dMon
+  assert getDayOfWeekJulian(1, 1, 2000) == dSat
+  assert getDayOfWeekJulian(1, 1, 2021) == dFri