summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorOscar NihlgÄrd <oscarnihlgard@gmail.com>2018-04-13 07:36:31 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-04-13 07:36:30 +0200
commitf6df2d9956a79eda048639107274ce60cbcf3542 (patch)
treece567080709419e0f04448335eba9ff24b1b3c15 /lib/pure
parent19a1cc914fb8a744243a9730f120f6e932134106 (diff)
downloadNim-f6df2d9956a79eda048639107274ce60cbcf3542.tar.gz
Sub second time resolution (#6978)
* Add deprecation warnings to recently deprecated procs

* Fix bad usage of the times module

* Introduce sub second resolution

* Fix usage of C's time()

* Switch to nanosecond resolution

* Make Time & Duration opaque again and fix some errors

* Change back to TimeInterval for shorthands

* Fix JS test

* Fix build error for windows

* Undeprecate epochTime

* Documentation and minor changes

* Lots of bugfixes and doc comments

* Attempt to make travis & appveyor green

* Fix edge cases for dealing with the local timezone

* Workaround JS backend overflow/underflow bug

* Use better workaround for not knowing the size of time_t

* Use all available timezones for tests

* Fix indentation

* Add procs for accessing the fractional part of a duration

* Order time units from smallest to largest since it makes more sense

* Include months and years in `TimeUnit`

* Review fix
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/oids.nim2
-rw-r--r--lib/pure/os.nim4
-rw-r--r--lib/pure/random.nim4
-rw-r--r--lib/pure/times.nim894
4 files changed, 639 insertions, 265 deletions
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 427a68964..26a55de6c 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -71,7 +71,7 @@ proc genOid*(): Oid =
   proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
   proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
 
-  var t = getTime().int32
+  var t = getTime().toUnix.int32
 
   var i = int32(atomicInc(incr))
 
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index c581996f6..c06a9bd79 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -225,10 +225,10 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
   ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
   ## modification time is later than `b`'s.
   when defined(posix):
-    result = getLastModificationTime(a) - getLastModificationTime(b) >= 0
+    result = getLastModificationTime(a) - getLastModificationTime(b) >= DurationZero
     # Posix's resolution sucks so, we use '>=' for posix.
   else:
-    result = getLastModificationTime(a) - getLastModificationTime(b) > 0
+    result = getLastModificationTime(a) - getLastModificationTime(b) > DurationZero
 
 proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
   ## Returns the `current working directory`:idx:.
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 97ad12b99..968a326d2 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -191,8 +191,8 @@ when not defined(nimscript):
   proc randomize*() {.benign.} =
     ## Initializes the random number generator with a "random"
     ## number, i.e. a tickcount. Note: Does not work for NimScript.
-    let time = int64(times.epochTime() * 1_000_000_000)
-    randomize(time)
+    let now = times.getTime()
+    randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanoseconds)
 
 {.pop.}
 
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index c53a300d2..2f1e0dfd6 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1,16 +1,18 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2017 Nim contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
 
-## This module contains routines and types for dealing with time.
-## This module is available for the `JavaScript target
-## <backends.html#the-javascript-target>`_. The proleptic Gregorian calendar is the only calendar supported.
+## This module contains routines and types for dealing with time using a proleptic Gregorian calendar.
+## It's is available for the `JavaScript target <backends.html#the-javascript-target>`_.
+##
+## The types uses nanosecond time resolution, but the underlying resolution used by ``getTime()``
+## depends on the platform and backend (JS is limited to millisecond precision).
 ##
 ## Examples:
 ##
@@ -25,10 +27,9 @@
 ##  echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
 ##  echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
 ##
-##  echo "epochTime() float value: ", epochTime()
-##  echo "cpuTime()   float value: ", cpuTime()
+##  echo "cpuTime()  float value: ", cpuTime()
 ##  echo "An hour from now      : ", now() + 1.hours
-##  echo "An hour from (UTC) now: ", getTime().utc + initInterval(0,0,0,1)
+##  echo "An hour from (UTC) now: ", getTime().utc + initDuration(hours = 1)
 
 {.push debugger:off.} # the user does not want to trace a part
                       # of the standard library!
@@ -38,12 +39,36 @@ import
 
 include "system/inclrtl"
 
+# This is really bad, but overflow checks are broken badly for
+# ints on the JS backend. See #6752.
+when defined(JS):
+  {.push overflowChecks: off.}  
+  proc `*`(a, b: int64): int64 =
+    system.`* `(a, b)
+  proc `*`(a, b: int): int =
+    system.`* `(a, b)
+  proc `+`(a, b: int64): int64 =
+    system.`+ `(a, b)
+  proc `+`(a, b: int): int =
+    system.`+ `(a, b)
+  proc `-`(a, b: int64): int64 =
+    system.`- `(a, b)
+  proc `-`(a, b: int): int =
+    system.`- `(a, b)
+  proc inc(a: var int, b: int) =
+    system.inc(a, b)
+  proc inc(a: var int64, b: int) =
+    system.inc(a, b)
+  {.pop.}
+
 when defined(posix):
   import posix
 
   type CTime = posix.Time
 
-  proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
+  var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
+
+  proc gettimeofday(tp: var Timeval, unused: pointer = nil) {.
     importc: "gettimeofday", header: "<sys/time.h>".}
 
   when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
@@ -74,16 +99,11 @@ type
   MinuteRange* = range[0..59]
   SecondRange* = range[0..60]
   YeardayRange* = range[0..365]
+  NanosecondRange* = range[0..999_999_999]
 
-  TimeImpl = int64
-
-  Time* = distinct TimeImpl ## Represents a point in time.
-                            ## This is currently implemented as a ``int64`` representing
-                            ## seconds since ``1970-01-01T00:00:00Z``, but don't
-                            ## rely on this knowledge because it might change
-                            ## in the future to allow for higher precision.
-                            ## Use the procs ``toUnix`` and ``fromUnix`` to
-                            ## work with unix timestamps instead.
+  Time* = object ## Represents a point in time.
+    seconds: int64
+    nanoseconds: NanosecondRange
 
   DateTime* = object of RootObj ## Represents a time in different parts.
                                 ## Although this type can represent leap
@@ -92,6 +112,8 @@ type
                                 ## but the ``DateTime``'s returned by
                                 ## procedures in this module will never have
                                 ## a leap second.
+    nanosecond*: NanosecondRange ## The number of nanoseconds after the second,
+                                 ## in the range 0 to 999_999_999.
     second*: SecondRange      ## The number of seconds after the minute,
                               ## normally in the range 0 to 59, but can
                               ## be up to 60 to allow for a leap second.
@@ -114,19 +136,36 @@ type
                               ## of the one in a formatted offset string like ``+01:00``
                               ## (which would be parsed into the UTC offset ``-3600``).
 
-  TimeInterval* = object ## Represents a duration of time. Can be used to add and subtract
-                         ## from a ``DateTime`` or ``Time``.
-                         ## Note that a ``TimeInterval`` doesn't represent a fixed duration of time,
+  TimeInterval* = object ## Represents a non-fixed duration of time. Can be used to add and subtract
+                         ## non-fixed time units from a ``DateTime`` or ``Time``.
+                         ## ``TimeInterval`` doesn't represent a fixed duration of time,
                          ## since the duration of some units depend on the context (e.g a year
                          ## can be either 365 or 366 days long). The non-fixed time units are years,
                          ## months and days.
+
+    nanoseconds*: int  ## The number of nanoseconds
+    microseconds*: int ## The number of microseconds
     milliseconds*: int ## The number of milliseconds
-    seconds*: int     ## The number of seconds
-    minutes*: int     ## The number of minutes
-    hours*: int       ## The number of hours
-    days*: int        ## The number of days
-    months*: int      ## The number of months
-    years*: int       ## The number of years
+    seconds*: int      ## The number of seconds
+    minutes*: int      ## The number of minutes
+    hours*: int        ## The number of hours
+    days*: int         ## The number of days
+    weeks*: int        ## The number of weeks
+    months*: int       ## The number of months
+    years*: int        ## The number of years
+
+  Duration* = object ## Represents a fixed duration of time.
+                     ## Uses the same time resolution as ``Time``.
+                     ## This type should be prefered over ``TimeInterval`` unless
+                     ## non-static time units is needed.
+    seconds: int64
+    nanoseconds: NanosecondRange                     
+                           
+  TimeUnit* = enum ## Different units of time.
+    Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years
+
+  FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
+                                             ## These are the units that can be represented by a ``Duration``.
 
   Timezone* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones.
                      ## The ``times`` module only supplies implementations for the systems local time and UTC.
@@ -136,11 +175,13 @@ type
     zoneInfoFromTz*:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
     name*: string ## The name of the timezone, f.ex 'Europe/Stockholm' or 'Etc/UTC'. Used for checking equality.
                   ## Se also: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-  ZonedTime* = object ## Represents a zooned instant in time that is not associated with any calendar.
+
+  ZonedTime* = object ## Represents a zoned instant in time that is not associated with any calendar.
                       ## This type is only used for implementing timezones.
-    adjTime*: Time ## Time adjusted to a timezone.
-    utcOffset*: int
-    isDst*: bool
+    adjTime*: Time  ## Time adjusted to a timezone.
+    utcOffset*: int ## Offset from UTC in seconds.
+                    ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``.
+    isDst*: bool    ## Determines whether DST is in effect.
 
 {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
     TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
@@ -150,14 +191,185 @@ const
   secondsInHour = 60*60
   secondsInDay = 60*60*24
   minutesInHour = 60
+  # The number of hectonanoseconds between 1601/01/01 (windows epoch)
+  # and 1970/01/01 (unix epoch).
+  epochDiff = 116444736000000000'i64
+
+const unitWeights: array[FixedTimeUnit, int64] = [
+  1'i64,
+  1000,
+  1_000_000,
+  1e9.int64,
+  secondsInMin * 1e9.int64,
+  secondsInHour * 1e9.int64,
+  secondsInDay * 1e9.int64,
+  7 * secondsInDay * 1e9.int64,
+]
+
+proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} =
+  ## Convert a quantity of some duration unit to another duration unit.
+  runnableExamples:
+    doAssert convert(Days, Hours, 2) == 48
+    doAssert convert(Days, Weeks, 13) == 1 # Truncated
+    doAssert convert(Seconds, Milliseconds, -1) == -1000
+  if unitFrom < unitTo:
+    (quantity div (unitWeights[unitTo] div unitWeights[unitFrom])).T
+  else:
+    ((unitWeights[unitFrom] div unitWeights[unitTo]) * quantity).T
+
+proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
+  ## Normalize a (seconds, nanoseconds) pair and return it as either
+  ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a
+  ## positive nanosecond part in the range ``NanosecondRange``.
+  result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds)
+  var nanoseconds = nanoseconds mod convert(Seconds, Nanoseconds, 1)
+  if nanoseconds < 0:
+    nanoseconds += convert(Seconds, Nanoseconds, 1)
+    result.seconds -= 1
+  result.nanoseconds = nanoseconds.int
+
+proc initTime*(unix: int64, nanoseconds: NanosecondRange): Time =
+  ## Create a ``Time`` from a unix timestamp and a nanosecond part.
+  result.seconds = unix
+  result.nanoseconds = nanoseconds
+
+proc nanoseconds*(time: Time): NanosecondRange =
+  ## Get the fractional part of a ``Time`` as the number
+  ## of nanoseconds of the second.
+  time.nanoseconds
+
+proc initDuration*(nanoseconds, microseconds, milliseconds,
+                   seconds, minutes, hours, days, weeks: int64 = 0): Duration =
+  let seconds = convert(Weeks, Seconds, weeks) +
+    convert(Days, Seconds, days) +
+    convert(Minutes, Seconds, minutes) +
+    convert(Hours, Seconds, hours) +
+    convert(Seconds, Seconds, seconds) +
+    convert(Milliseconds, Seconds, milliseconds) +
+    convert(Microseconds, Seconds, microseconds) +
+    convert(Nanoseconds, Seconds, nanoseconds)
+  let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
+    convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
+    nanoseconds mod 1_000_000_000).int
+  # Nanoseconds might be negative so we must normalize.
+  result = normalize[Duration](seconds, nanoseconds)
+
+proc weeks*(dur: Duration): int64 {.inline.} =
+  ## Number of whole weeks represented by the duration.
+  convert(Seconds, Weeks, dur.seconds)
+
+proc days*(dur: Duration): int64 {.inline.} =
+  ## Number of whole days represented by the duration.
+  convert(Seconds, Days, dur.seconds)
+  
+proc minutes*(dur: Duration): int64 {.inline.} =
+  ## Number of whole minutes represented by the duration.
+  convert(Seconds, Minutes, dur.seconds)
+
+proc hours*(dur: Duration): int64 {.inline.} =
+  ## Number of whole hours represented by the duration.
+  convert(Seconds, Hours, dur.seconds)
+
+proc seconds*(dur: Duration): int64 {.inline.} =
+  ## Number of whole seconds represented by the duration.
+  dur.seconds
+
+proc milliseconds*(dur: Duration): int {.inline.} =
+  ## Number of whole milliseconds represented by the **fractional**
+  ## part of the duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, milliseconds = 1)
+    doAssert dur.milliseconds == 1
+  convert(Nanoseconds, Milliseconds, dur.nanoseconds)
+
+proc microseconds*(dur: Duration): int {.inline.} =
+  ## Number of whole microseconds represented by the **fractional**
+  ## part of the duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, microseconds = 1)
+    doAssert dur.microseconds == 1
+  convert(Nanoseconds, Microseconds, dur.nanoseconds)
+
+proc nanoseconds*(dur: Duration): int {.inline.} =
+  ## Number of whole nanoseconds represented by the **fractional**
+  ## part of the duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, nanoseconds = 1)
+    doAssert dur.nanoseconds == 1
+  dur.nanoseconds
+
+proc fractional*(dur: Duration): Duration {.inline.} =
+  ## The fractional part of duration, as a duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, nanoseconds = 5)
+    doAssert dur.fractional == initDuration(nanoseconds = 5)
+  initDuration(nanoseconds = dur.nanoseconds)
+
+const DurationZero* = initDuration() ## Zero value for durations. Useful for comparisons.
+                                     ##
+                                     ## .. code-block:: nim
+                                     ## 
+                                     ##   doAssert initDuration(seconds = 1) > DurationZero
+                                     ##   doAssert initDuration(seconds = 0) == DurationZero
+
+proc `$`*(dur: Duration): string =
+  ## Human friendly string representation of ``dur``.
+  runnableExamples:
+    doAssert $initDuration(seconds = 2) == "2 seconds"
+    doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
+    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
+    doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
+  var parts = newSeq[string]()
+  var remS = dur.seconds
+  var remNs = dur.nanoseconds.int
+
+  # Normally ``nanoseconds`` should always be positive, but
+  # that makes no sense when printing.
+  if remS < 0:
+    remNs -= convert(Seconds, Nanoseconds, 1)
+    remS.inc 1
+
+  const unitStrings: array[FixedTimeUnit, string] = [
+    "nanoseconds", "microsecond", "millisecond", "second", "minute", "hour", "day", "week"
+  ]
+
+  for unit in countdown(Weeks, Seconds):
+    let quantity = convert(Seconds, unit, remS)
+    remS = remS mod convert(unit, Seconds, 1)
+
+    if quantity.abs == 1:
+      parts.add $quantity & " " & unitStrings[unit]
+    elif quantity != 0:
+      parts.add $quantity & " " & unitStrings[unit] & "s"
+
+  for unit in countdown(Milliseconds, Nanoseconds):
+    let quantity = convert(Nanoseconds, unit, remNs)
+    remNs = remNs mod convert(unit, Nanoseconds, 1)
+
+    if quantity.abs == 1:
+      parts.add $quantity & " " & unitStrings[unit]
+    elif quantity != 0:
+      parts.add $quantity & " " & unitStrings[unit] & "s"
+
+  result = ""
+  if parts.len == 1:
+    result = parts[0]
+  elif parts.len == 2:
+    result = parts[0] & " and " & parts[1]
+  else:
+    for part in parts[0..high(parts)-1]:
+      result.add part & ", "
+    result.add "and " & parts[high(parts)]
 
 proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} =
   ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``.
-  Time(unix)
+  runnableExamples:
+    doAssert $fromUnix(0).utc == "1970-01-01T00:00:00+00:00"
+  initTime(unix, 0)
 
 proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
   ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
-  t.int64
+  t.seconds
 
 proc isLeapYear*(year: int): bool =
   ## Returns true if ``year`` is a leap year.
@@ -177,7 +389,7 @@ proc getDaysInYear*(year: int): int =
 
 proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
   assert monthday <= getDaysInMonth(month, year),
-    $year & "-" & $ord(month) & "-" & $monthday & " is not a valid date"
+    $year & "-" & intToStr(ord(month), 2) & "-" & $monthday & " is not a valid date"
 
 proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
   ## Get the epoch day from a year/month/day date.
@@ -240,42 +452,154 @@ proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign
 proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
 
-proc `-`*(a, b: Time): int64 {.
-    rtl, extern: "ntDiffTime", tags: [], raises: [], noSideEffect, benign, deprecated.} =
-  ## Computes the difference of two calendar times. Result is in seconds.
-  ## This is deprecated because it will need to change when sub second time resolution is implemented.
-  ## Use ``a.toUnix - b.toUnix`` instead.
-  ##
-  ## .. code-block:: nim
-  ##     let a = fromSeconds(1_000_000_000)
-  ##     let b = fromSeconds(1_500_000_000)
-  ##     echo initInterval(seconds=int(b - a))
-  ##     # (milliseconds: 0, seconds: 20, minutes: 53, hours: 0, days: 5787, months: 0, years: 0)
-  a.toUnix - b.toUnix
-
-proc `<`*(a, b: Time): bool {.
-    rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect, borrow.}
+{. pragma: operator, rtl, noSideEffect, benign .}
+
+template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
+  normalize[T](a.seconds - b.seconds, a.nanoseconds - b.nanoseconds)
+
+template addImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
+  normalize[T](a.seconds + b.seconds, a.nanoseconds + b.nanoseconds)
+
+template ltImpl(a: Duration|Time, b: Duration|Time): bool =
+  a.seconds < b.seconds or (
+    a.seconds == b.seconds and a.nanoseconds < b.nanoseconds)
+
+template lqImpl(a: Duration|Time, b: Duration|Time): bool =
+  a.seconds <= b.seconds or (
+    a.seconds == b.seconds and a.nanoseconds <= b.seconds)
+
+template eqImpl(a: Duration|Time, b: Duration|Time): bool =
+  a.seconds == b.seconds and a.nanoseconds == b.nanoseconds
+
+proc `+`*(a, b: Duration): Duration {.operator.} =
+  ## Add two durations together.
+  runnableExamples:
+    doAssert initDuration(seconds = 1) + initDuration(days = 1) ==
+      initDuration(seconds = 1, days = 1)
+  addImpl[Duration](a, b)
+
+proc `-`*(a, b: Duration): Duration {.operator.} =
+  ## Subtract a duration from another.
+  runnableExamples:
+    doAssert initDuration(seconds = 1, days = 1) - initDuration(seconds = 1) ==
+      initDuration(days = 1)
+  subImpl[Duration](a, b)
+  
+proc `-`*(a: Duration): Duration {.operator.} =
+  ## Reverse a duration.
+  runnableExamples:
+    doAssert -initDuration(seconds = 1) == initDuration(seconds = -1)
+  normalize[Duration](-a.seconds, -a.nanoseconds)
+  
+proc `<`*(a, b: Duration): bool {.operator.} =
+  ## Note that a duration can be negative,
+  ## so even if ``a < b`` is true ``a`` might
+  ## represent a larger absolute duration.
+  ## Use ``abs(a) < abs(b)`` to compare the absolute
+  ## duration.
+  runnableExamples:
+    doAssert initDuration(seconds =  1) < initDuration(seconds = 2)
+    doAssert initDuration(seconds = -2) < initDuration(seconds = 1)
+  ltImpl(a, b)
+
+proc `<=`*(a, b: Duration): bool {.operator.} =
+  lqImpl(a, b)
+
+proc `==`*(a, b: Duration): bool {.operator.} =
+  eqImpl(a, b)
+
+proc `*`*(a: int64, b: Duration): Duration {.operator} =
+  ## Multiply a duration by some scalar.
+  runnableExamples:
+    doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
+  normalize[Duration](a * b.seconds, a * b.nanoseconds)
+
+proc `*`*(a: Duration, b: int64): Duration {.operator} =
+  ## Multiply a duration by some scalar.
+  runnableExamples:
+    doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
+  b * a
+
+proc `div`*(a: Duration, b: int64): Duration {.operator} =
+  ## Integer division for durations.
+  runnableExamples:
+    doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500)
+    doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1)
+  let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b)
+  normalize[Duration](a.seconds div b, (a.nanoseconds + carryOver) div b)
+
+proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
+  ## Computes the duration between two points in time.
+  subImpl[Duration](a, b)
+
+proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} =
+  ## Add a duration of time to a ``Time``.
+  runnableExamples:
+    doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1)
+  addImpl[Time](a, b)
+
+proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} =
+  ## Subtracts a duration of time from a ``Time``.
+  runnableExamples:
+    doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1)
+  subImpl[Time](a, b)
+
+proc `+=`*(a: var Time, b: Duration) {.operator.} =
+  ## Modify ``a`` in place by subtracting ``b``.
+  a = addImpl[Time](a, b)
+
+proc `-=`*(a: var Time, b: Duration) {.operator.} =
+  ## Modify ``a`` in place by adding ``b``.
+  a = subImpl[Time](a, b)
+
+proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
   ## Returns true iff ``a < b``, that is iff a happened before b.
+  ltImpl(a, b)
 
-proc `<=` * (a, b: Time): bool {.
-    rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect, borrow.}
+proc `<=` * (a, b: Time): bool {.operator, extern: "ntLeTime".} =
   ## Returns true iff ``a <= b``.
+  lqImpl(a, b)
 
-proc `==`*(a, b: Time): bool {.
-    rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect, borrow.}
+proc `==`*(a, b: Time): bool {.operator, extern: "ntEqTime".} =
   ## Returns true if ``a == b``, that is if both times represent the same point in time.
+  eqImpl(a, b)
+
+proc high*(typ: typedesc[Time]): Time =
+  initTime(high(int64), high(NanosecondRange))
+
+proc low*(typ: typedesc[Time]): Time =
+  initTime(low(int64), 0)
+
+proc high*(typ: typedesc[Duration]): Duration =
+  ## Get the longest representable duration.  
+  initDuration(seconds = high(int64), nanoseconds = high(NanosecondRange))
+
+proc low*(typ: typedesc[Duration]): Duration =
+  ## Get the longest representable duration of negative direction.
+  initDuration(seconds = low(int64))
+
+proc abs*(a: Duration): Duration =
+  runnableExamples:
+    doAssert initDuration(milliseconds = -1500).abs ==
+      initDuration(milliseconds = 1500)
+  initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanoseconds)
 
 proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
   ## Converts a broken-down time structure to
   ## calendar time representation.
   let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
-  result = Time(epochDay * secondsInDay)
-  result.inc dt.hour * secondsInHour
-  result.inc dt.minute * 60
-  result.inc dt.second
+  var seconds = epochDay * secondsInDay
+  seconds.inc dt.hour * secondsInHour
+  seconds.inc dt.minute * 60
+  seconds.inc dt.second
   # The code above ignores the UTC offset of `timeInfo`,
   # so we need to compensate for that here.
-  result.inc dt.utcOffset
+  seconds.inc dt.utcOffset
+  result = initTime(seconds, dt.nanosecond)
+
+proc `-`*(dt1, dt2: DateTime): Duration =
+  ## Compute the duration between ``dt1`` and ``dt2``.
+  dt1.toTime - dt2.toTime
 
 proc `<`*(a, b: DateTime): bool =
   ## Returns true iff ``a < b``, that is iff a happened before b.
@@ -290,9 +614,9 @@ proc `==`*(a, b: DateTime): bool =
   return a.toTime == b.toTime
 
 proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
-  let adjTime = zt.adjTime.int64
-  let epochday = (if adjTime >= 0: adjTime else: adjTime - (secondsInDay - 1)) div secondsInDay
-  var rem = zt.adjTime.int64 - epochday * secondsInDay
+  let s = zt.adjTime.seconds
+  let epochday = (if s >= 0: s else: s - (secondsInDay - 1)) div secondsInDay
+  var rem = s - epochday * secondsInDay
   let hour = rem div secondsInHour
   rem = rem - hour * secondsInHour
   let minute = rem div secondsInMin
@@ -308,6 +632,7 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
     hour: hour,
     minute: minute,
     second: second,
+    nanosecond: zt.adjTime.nanoseconds,
     weekday: getDayOfWeek(d, m, y),
     yearday: getDayOfYear(d, m, y),
     isDst: zt.isDst,
@@ -334,10 +659,11 @@ proc `==`*(zone1, zone2: Timezone): bool =
 
 proc toAdjTime(dt: DateTime): Time =
   let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
-  result = Time(epochDay * secondsInDay)
-  result.inc dt.hour * secondsInHour
-  result.inc dt.minute * secondsInMin
-  result.inc dt.second
+  var seconds = epochDay * secondsInDay
+  seconds.inc dt.hour * secondsInHour
+  seconds.inc dt.minute * secondsInMin
+  seconds.inc dt.second
+  result = initTime(seconds, dt.nanosecond)
 
 when defined(JS):
     type JsDate = object
@@ -366,14 +692,14 @@ when defined(JS):
     proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
 
     proc localZoneInfoFromUtc(time: Time): ZonedTime =
-      let jsDate = newDate(time.float * 1000)
+      let jsDate = newDate(time.seconds.float * 1000)
       let offset = jsDate.getTimezoneOffset() * secondsInMin
-      result.adjTime = Time(time.int64 - offset)
+      result.adjTime = time - initDuration(seconds = offset)
       result.utcOffset = offset
       result.isDst = false
 
     proc localZoneInfoFromTz(adjTime: Time): ZonedTime =
-      let utcDate = newDate(adjTime.float * 1000)
+      let utcDate = newDate(adjTime.seconds.float * 1000)
       let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
         utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
 
@@ -422,58 +748,52 @@ else:
 
   proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
 
-  proc toAdjTime(tm: StructTm): Time =
+  proc toAdjUnix(tm: StructTm): int64 =
     let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
-    result = Time(epochDay * secondsInDay)
+    result = epochDay * secondsInDay
     result.inc tm.hour * secondsInHour
     result.inc tm.minute * 60
     result.inc tm.second
 
-  proc getStructTm(time: Time | int64): StructTm =
-    let timei64 = time.int64
-    var a =
-      if timei64 < low(CTime):
-        CTime(low(CTime))
-      elif timei64 > high(CTime):
-        CTime(high(CTime))
-      else:
-        CTime(timei64)
-    result = localtime(addr(a))[]
+  proc getLocalOffsetAndDst(unix: int64): tuple[offset: int, dst: bool] =
+    var a = unix.CTime
+    let tmPtr = localtime(addr(a))
+    if not tmPtr.isNil:
+      let tm = tmPtr[]
+      return ((unix - tm.toAdjUnix).int, tm.isdst > 0)
+    return (0, false)
 
   proc localZoneInfoFromUtc(time: Time): ZonedTime =
-    let tm = getStructTm(time)
-    let adjTime = tm.toAdjTime
-    result.adjTime = adjTime
-    result.utcOffset = (time.toUnix - adjTime.toUnix).int
-    result.isDst = tm.isdst > 0
+    let (offset, dst) = getLocalOffsetAndDst(time.seconds)
+    result.adjTime = time - initDuration(seconds = offset)
+    result.utcOffset = offset
+    result.isDst = dst
 
   proc localZoneInfoFromTz(adjTime: Time): ZonedTime  =
-    var adjTimei64 = adjTime.int64
-    let past = adjTimei64 - secondsInDay
-    var tm = getStructTm(past)
-    let pastOffset = past - tm.toAdjTime.int64
+    var adjUnix = adjTime.seconds
+    let past = adjUnix - secondsInDay
+    let (pastOffset, _) = getLocalOffsetAndDst(past)
 
-    let future = adjTimei64 + secondsInDay
-    tm = getStructTm(future)
-    let futureOffset = future - tm.toAdjTime.int64
+    let future = adjUnix + secondsInDay
+    let (futureOffset, _) = getLocalOffsetAndDst(future)
 
     var utcOffset: int
     if pastOffset == futureOffset:
         utcOffset = pastOffset.int
     else:
       if pastOffset > futureOffset:
-        adjTimei64 -= secondsInHour
+        adjUnix -= secondsInHour
 
-      adjTimei64 += pastOffset
-      utcOffset = (adjTimei64 - getStructTm(adjTimei64).toAdjTime.int64).int
+      adjUnix += pastOffset
+      utcOffset = getLocalOffsetAndDst(adjUnix).offset
 
     # This extra roundtrip is needed to normalize any impossible datetimes
     # as a result of offset changes (normally due to dst)
-    let utcTime = adjTime.int64 + utcOffset
-    tm = getStructTm(utcTime)
-    result.adjTime = tm.toAdjTime
-    result.utcOffset = (utcTime - result.adjTime.int64).int
-    result.isDst = tm.isdst > 0
+    let utcUnix = adjTime.seconds + utcOffset
+    let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix)
+    result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanoseconds)
+    result.utcOffset = finalOffset
+    result.isDst = dst
 
 proc utcZoneInfoFromUtc(time: Time): ZonedTime =
   result.adjTime = time
@@ -485,18 +805,16 @@ proc utcZoneInfoFromTz(adjTime: Time): ZonedTime =
 
 proc utc*(): TimeZone =
   ## Get the ``Timezone`` implementation for the UTC timezone.
-  ##
-  ## .. code-block:: nim
-  ##  doAssert now().utc.timezone == utc()
-  ##  doAssert utc().name == "Etc/UTC"
+  runnableExamples:
+    doAssert now().utc.timezone == utc()
+    doAssert utc().name == "Etc/UTC"
   Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC")
 
 proc local*(): TimeZone =
   ## Get the ``Timezone`` implementation for the local timezone.
-  ##
-  ## .. code-block:: nim
-  ##  doAssert now().timezone == local()
-  ##  doAssert local().name == "LOCAL"
+  runnableExamples:
+   doAssert now().timezone == local()
+   doAssert local().name == "LOCAL"
   Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL")
 
 proc utc*(dt: DateTime): DateTime =
@@ -515,9 +833,30 @@ proc local*(t: Time): DateTime =
   ## Shorthand for ``t.inZone(local())``.
   t.inZone(local())
 
-proc getTime*(): Time {.tags: [TimeEffect], benign.}
-  ## Gets the current time as a ``Time`` with second resolution. Use epochTime for higher
-  ## resolution.
+proc getTime*(): Time {.tags: [TimeEffect], benign.} =
+  ## Gets the current time as a ``Time`` with nanosecond resolution.
+  when defined(JS):
+    let millis = newDate().getTime()
+    let seconds = convert(Milliseconds, Seconds, millis)
+    let nanos = convert(Milliseconds, Nanoseconds,
+      millis mod convert(Seconds, Milliseconds, 1).int)
+    result = initTime(seconds, nanos)
+  # I'm not entirely certain if freebsd needs to use `gettimeofday`.
+  elif defined(macosx) or defined(freebsd):
+    var a: Timeval
+    gettimeofday(a)
+    result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int))
+  elif defined(posix):
+    var ts: Timespec
+    discard clock_gettime(CLOCK_REALTIME, ts)
+    result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+  elif defined(windows):
+    var f: FILETIME
+    getSystemTimeAsFileTime(f)
+    let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100
+    let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch)
+    let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int
+    result = initTime(seconds, nanos)
 
 proc now*(): DateTime {.tags: [TimeEffect], benign.} =
   ## Get the current time as a  ``DateTime`` in the local timezone.
@@ -525,35 +864,39 @@ proc now*(): DateTime {.tags: [TimeEffect], benign.} =
   ## Shorthand for ``getTime().local``.
   getTime().local
 
-proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
-                   years: int = 0): TimeInterval =
+proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
+                       seconds, minutes, hours,
+                       days, weeks, months, years: int = 0): TimeInterval =
   ## Creates a new ``TimeInterval``.
   ##
   ## You can also use the convenience procedures called ``milliseconds``,
   ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##     let day = initInterval(hours=24)
-  ##     let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
-  ##     doAssert $(dt + day) == "2000-01-02T12-00-00+00:00"
+  runnableExamples:
+    let day = initTimeInterval(hours=24)
+    let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
+    doAssert $(dt + day) == "2000-01-02T12:00:00+00:00"
+  result.nanoseconds = nanoseconds
+  result.microseconds = microseconds
   result.milliseconds = milliseconds
   result.seconds = seconds
   result.minutes = minutes
   result.hours = hours
   result.days = days
+  result.weeks = weeks
   result.months = months
   result.years = years
 
 proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Adds two ``TimeInterval`` objects together.
+  result.nanoseconds = ti1.nanoseconds + ti2.nanoseconds
+  result.microseconds = ti1.microseconds + ti2.microseconds
   result.milliseconds = ti1.milliseconds + ti2.milliseconds
   result.seconds = ti1.seconds + ti2.seconds
   result.minutes = ti1.minutes + ti2.minutes
   result.hours = ti1.hours + ti2.hours
   result.days = ti1.days + ti2.days
+  result.weeks = ti1.weeks + ti2.weeks
   result.months = ti1.months + ti2.months
   result.years = ti1.years + ti2.years
 
@@ -565,11 +908,14 @@ proc `-`*(ti: TimeInterval): TimeInterval =
   ##     let day = -initInterval(hours=24)
   ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: -24, days: 0, months: 0, years: 0)
   result = TimeInterval(
+    nanoseconds: -ti.nanoseconds,
+    microseconds: -ti.microseconds,
     milliseconds: -ti.milliseconds,
     seconds: -ti.seconds,
     minutes: -ti.minutes,
     hours: -ti.hours,
     days: -ti.days,
+    weeks: -ti.weeks,
     months: -ti.months,
     years: -ti.years
   )
@@ -577,82 +923,16 @@ proc `-`*(ti: TimeInterval): TimeInterval =
 proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Subtracts TimeInterval ``ti1`` from ``ti2``.
   ##
-  ## Time components are compared one-by-one, see output:
+  ## Time components are subtracted one-by-one, see output:
   ##
   ## .. code-block:: nim
   ##     let a = fromUnix(1_000_000_000)
   ##     let b = fromUnix(1_500_000_000)
   ##     echo b.toTimeInterval - a.toTimeInterval
-  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
-  result = ti1 + (-ti2)
-
-proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDiff, absDiff: int64] =
-  ## Evaluates how many seconds the interval is worth
-  ## in the context of ``dt``.
-  ## The result in split into an adjusted diff and an absolute diff.
-
-  var anew = dt
-  var newinterv = interval
+  ##     # (nanoseconds: 0, microseconds: 0, milliseconds: 0, seconds: -40,
+  ##        minutes: -6, hours: 1, days: 5, weeks: 0, months: -2, years: 16)
 
-  newinterv.months += interval.years * 12
-  var curMonth = anew.month
-  # Subtracting
-  if newinterv.months < 0:
-    for mth in countDown(-1 * newinterv.months, 1):
-      if curMonth == mJan:
-        curMonth = mDec
-        anew.year.dec()
-      else:
-        curMonth.dec()
-      result.adjDiff -= getDaysInMonth(curMonth, anew.year) * secondsInDay
-  # Adding
-  else:
-    for mth in 1 .. newinterv.months:
-      result.adjDiff += getDaysInMonth(curMonth, anew.year) * secondsInDay
-      if curMonth == mDec:
-        curMonth = mJan
-        anew.year.inc()
-      else:
-        curMonth.inc()
-  result.adjDiff += newinterv.days * secondsInDay
-  result.absDiff += newinterv.hours * secondsInHour
-  result.absDiff += newinterv.minutes * secondsInMin
-  result.absDiff += newinterv.seconds
-  result.absDiff += newinterv.milliseconds div 1000
-
-proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
-  ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
-  ##
-  ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
-  ## month doesn't have enough days it, the month will be incremented and the monthday will be
-  ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
-  ## which will overflow and result in `1 December`.
-  ##
-  ## .. code-block:: nim
-  ##  let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
-  ##  doAssert $(dt + 1.months) == "2017-04-30T00:00:00+00:00"
-  ##  # This is correct and happens due to monthday overflow.
-  ##  doAssert $(dt - 1.months) == "2017-03-02T00:00:00+00:00"
-  let (adjDiff, absDiff) = evaluateInterval(dt, interval)
-
-  if adjDiff.int64 != 0:
-    let zInfo = dt.timezone.zoneInfoFromTz(Time(dt.toAdjTime.int64 + adjDiff))
-
-    if absDiff != 0:
-      let time = Time(zInfo.adjTime.int64 + zInfo.utcOffset + absDiff)
-      result = initDateTime(dt.timezone.zoneInfoFromUtc(time), dt.timezone)
-    else:
-      result = initDateTime(zInfo, dt.timezone)
-  else:
-    result = initDateTime(dt.timezone.zoneInfoFromUtc(Time(dt.toTime.int64 + absDiff)), dt.timezone)
-
-proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
-  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
-  dt + (-interval)
+  result = ti1 + (-ti2)
 
 proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
   ## Gets the current date as a string of the format ``YYYY-MM-DD``.
@@ -679,47 +959,139 @@ proc `$`*(m: Month): string =
       "November", "December"]
   return lookup[m]
 
+proc nanoseconds*(nanos: int): TimeInterval {.inline.} =
+  ## TimeInterval of ``nanos`` nanoseconds.
+  initTimeInterval(nanoseconds = nanos)
+
+proc microseconds*(micros: int): TimeInterval {.inline.} =
+  ## TimeInterval of ``micros`` microseconds.
+  initTimeInterval(microseconds = micros)
+
 proc milliseconds*(ms: int): TimeInterval {.inline.} =
-  ## TimeInterval of `ms` milliseconds
-  ##
-  ## Note: not all time procedures have millisecond resolution
-  initInterval(milliseconds = ms)
+  ## TimeInterval of ``ms`` milliseconds.
+  initTimeInterval(milliseconds = ms)
 
 proc seconds*(s: int): TimeInterval {.inline.} =
-  ## TimeInterval of `s` seconds
+  ## TimeInterval of ``s`` seconds.
   ##
   ## ``echo getTime() + 5.second``
-  initInterval(seconds = s)
+  initTimeInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
-  ## TimeInterval of `m` minutes
+  ## TimeInterval of ``m`` minutes.
   ##
   ## ``echo getTime() + 5.minutes``
-  initInterval(minutes = m)
+  initTimeInterval(minutes = m)
 
 proc hours*(h: int): TimeInterval {.inline.} =
-  ## TimeInterval of `h` hours
+  ## TimeInterval of ``h`` hours.
   ##
   ## ``echo getTime() + 2.hours``
-  initInterval(hours = h)
+  initTimeInterval(hours = h)
 
 proc days*(d: int): TimeInterval {.inline.} =
-  ## TimeInterval of `d` days
+  ## TimeInterval of ``d`` days.
   ##
   ## ``echo getTime() + 2.days``
-  initInterval(days = d)
+  initTimeInterval(days = d)
+
+proc weeks*(w: int): TimeInterval {.inline.} =
+  ## TimeInterval of ``w`` weeks.
+  ##
+  ## ``echo getTime() + 2.weeks``
+  initTimeInterval(weeks = w)
 
 proc months*(m: int): TimeInterval {.inline.} =
-  ## TimeInterval of `m` months
+  ## TimeInterval of ``m`` months.
   ##
   ## ``echo getTime() + 2.months``
-  initInterval(months = m)
+  initTimeInterval(months = m)
 
 proc years*(y: int): TimeInterval {.inline.} =
-  ## TimeInterval of `y` years
+  ## TimeInterval of ``y`` years.
   ##
   ## ``echo getTime() + 2.years``
-  initInterval(years = y)
+  initTimeInterval(years = y)
+
+proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDur: Duration] =
+  ## Evaluates how many nanoseconds the interval is worth
+  ## in the context of ``dt``.
+  ## The result in split into an adjusted diff and an absolute diff.
+  var months = interval.years * 12 + interval.months
+  var curYear = dt.year
+  var curMonth = dt.month
+  # Subtracting
+  if months < 0:
+    for mth in countDown(-1 * months, 1):
+      if curMonth == mJan:
+        curMonth = mDec
+        curYear.dec
+      else:
+        curMonth.dec()
+      let days = getDaysInMonth(curMonth, curYear)
+      result.adjDur = result.adjDur - initDuration(days = days)
+  # Adding
+  else:
+    for mth in 1 .. months:
+      let days = getDaysInMonth(curMonth, curYear)
+      result.adjDur = result.adjDur + initDuration(days = days)
+      if curMonth == mDec:
+        curMonth = mJan
+        curYear.inc
+      else:
+        curMonth.inc()
+
+  result.adjDur = result.adjDur + initDuration(
+    days = interval.days,
+    weeks = interval.weeks)
+  result.absDur = initDuration(
+    nanoseconds = interval.nanoseconds,
+    microseconds = interval.microseconds,
+    milliseconds = interval.milliseconds,
+    seconds = interval.seconds,
+    minutes = interval.minutes,
+    hours = interval.hours)
+
+proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
+  ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
+  ## in the order of their size, i.e first the ``years`` component, then the ``months``
+  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
+  ##
+  ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
+  ## month doesn't have enough days it, the month will be incremented and the monthday will be
+  ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
+  ## which will overflow and result in `1 December`.
+  ##
+  runnableExamples:
+    let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
+    doAssert $(dt + 1.months) == "2017-04-30T00:00:00+00:00"
+    # This is correct and happens due to monthday overflow.
+    doAssert $(dt - 1.months) == "2017-03-02T00:00:00+00:00"
+  let (adjDur, absDur) = evaluateInterval(dt, interval)
+
+  if adjDur != DurationZero:
+    var zInfo = dt.timezone.zoneInfoFromTz(dt.toAdjTime + adjDur)
+    if absDur != DurationZero:
+      let offsetDur = initDuration(seconds = zInfo.utcOffset)
+      zInfo = dt.timezone.zoneInfoFromUtc(zInfo.adjTime + offsetDur + absDur)
+      result = initDateTime(zInfo, dt.timezone)
+    else:
+      result = initDateTime(zInfo, dt.timezone)
+  else:
+    var zInfo = dt.timezone.zoneInfoFromUtc(dt.toTime + absDur)
+    result = initDateTime(zInfo, dt.timezone)
+
+proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
+  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
+  ## in the order of their size, i.e first the ``years`` component, then the ``months``
+  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
+  dt + (-interval)
+
+proc `+`*(dt: DateTime, dur: Duration): DateTime =
+  (dt.toTime + dur).inZone(dt.timezone)
+
+proc `-`*(dt: DateTime, dur: Duration): DateTime =
+  (dt.toTime - dur).inZone(dt.timezone)
 
 proc `+=`*(time: var Time, interval: TimeInterval) =
   ## Modifies `time` by adding `interval`.
@@ -1205,6 +1577,7 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
   dt.hour = 0
   dt.minute = 0
   dt.second = 0
+  dt.nanosecond = 0  
   dt.isDst = true # using this is flag for checking whether a timezone has \
       # been read (because DST is always false when a tz is parsed)
   while true:
@@ -1278,69 +1651,44 @@ proc toTimeInterval*(time: Time): TimeInterval =
   ##     echo a, " ", b  # real dates
   ##     echo a.toTimeInterval  # meaningless value, don't use it by itself
   ##     echo b.toTimeInterval - a.toTimeInterval
-  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
-  # Milliseconds not available from Time
+  ##     # (nanoseconds: 0, microseconds: 0, milliseconds: 0, seconds: -40,
+  ##        minutes: -6, hours: 1, days: 5, weeks: 0, months: -2, years: 16)
   var dt = time.local
-  initInterval(0, dt.second, dt.minute, dt.hour, dt.monthday, dt.month.ord - 1, dt.year)
+  initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour,
+    dt.monthday, 0, dt.month.ord - 1, dt.year)
 
 proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
-                   hour: HourRange, minute: MinuteRange, second: SecondRange, zone: Timezone = local()): DateTime =
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   nanosecond: NanosecondRange, zone: Timezone = local()): DateTime =
   ## Create a new ``DateTime`` in the specified timezone.
   assertValidDate monthday, month, year
-  doAssert monthday <= getDaysInMonth(month, year), "Invalid date: " & $month & " " & $monthday & ", " & $year
   let dt = DateTime(
     monthday:  monthday,
     year:  year,
     month:  month,
     hour:  hour,
     minute:  minute,
-    second:  second
+    second:  second,
+    nanosecond: nanosecond
   )
   result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
 
-when not defined(JS):
-  proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
-    ## gets time after the UNIX epoch (1970) in seconds. It is a float
-    ## because sub-second resolution is likely to be supported (depending
-    ## on the hardware/OS).
-
-  proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
-    ## gets time spent that the CPU spent to run the current process in
-    ## seconds. This may be more useful for benchmarking than ``epochTime``.
-    ## However, it may measure the real time instead (depending on the OS).
-    ## The value of the result has no meaning.
-    ## To generate useful timing values, take the difference between
-    ## the results of two ``cpuTime`` calls:
-    ##
-    ## .. code-block:: nim
-    ##   var t0 = cpuTime()
-    ##   doWork()
-    ##   echo "CPU time [s] ", cpuTime() - t0
-
-when defined(JS):
-  proc getTime(): Time =
-    (newDate().getTime() div 1000).Time
-
-  proc epochTime*(): float {.tags: [TimeEffect].} =
-    newDate().getTime() / 1000
+proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   zone: Timezone = local()): DateTime =
+  ## Create a new ``DateTime`` in the specified timezone.  
+  initDateTime(monthday, month, year, hour, minute, second, 0, zone)
 
-else:
+when not defined(JS):
   type
     Clock {.importc: "clock_t".} = distinct int
 
-  proc timec(timer: ptr CTime): CTime {.
-    importc: "time", header: "<time.h>", tags: [].}
-
   proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
 
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
 
-  proc getTime(): Time =
-    timec(nil).Time
-
   const
-    epochDiff = 116444736000000000'i64
     rateDiff = 10000000'i64 # 100 nsecs
 
   proc unixTimeToWinTime*(time: CTime): int64 =
@@ -1352,10 +1700,29 @@ else:
     result = CTime((time - epochDiff) div rateDiff)
 
   when not defined(useNimRtl):
-    proc epochTime(): float =
+    proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
+      ## gets time spent that the CPU spent to run the current process in
+      ## seconds. This may be more useful for benchmarking than ``epochTime``.
+      ## However, it may measure the real time instead (depending on the OS).
+      ## The value of the result has no meaning.
+      ## To generate useful timing values, take the difference between
+      ## the results of two ``cpuTime`` calls:
+      ##
+      ## .. code-block:: nim
+      ##   var t0 = cpuTime()
+      ##   doWork()
+      ##   echo "CPU time [s] ", cpuTime() - t0
+      result = toFloat(int(getClock())) / toFloat(clocksPerSec)
+    
+    proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
+      ## gets time after the UNIX epoch (1970) in seconds. It is a float
+      ## because sub-second resolution is likely to be supported (depending
+      ## on the hardware/OS).
+      ##
+      ## ``getTime`` should generally be prefered over this proc.
       when defined(posix):
         var a: Timeval
-        posix_gettimeofday(a)
+        gettimeofday(a)
         result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
       elif defined(windows):
         var f: winlean.FILETIME
@@ -1367,30 +1734,37 @@ else:
       else:
         {.error: "unknown OS".}
 
-    proc cpuTime(): float =
-      result = toFloat(int(getClock())) / toFloat(clocksPerSec)
+when defined(JS):
+  proc epochTime*(): float {.tags: [TimeEffect].} =
+    newDate().getTime() / 1000
 
 # Deprecated procs
 
+proc initInterval*(seconds, minutes, hours, days, months,
+                   years: int = 0): TimeInterval {.deprecated.} =
+  ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.                   
+  initTimeInterval(0, 0, 0, seconds, minutes, hours, days, 0, months, years)
+
 proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign, deprecated.} =
   ## Takes a float which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  Time(since1970)
+  let nanos = ((since1970 - since1970.int64.float) * convert(Seconds, Nanoseconds, 1).float).int
+  initTime(since1970.int64, nanos)
 
 proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprecated.} =
   ## Takes an int which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  Time(since1970)
+  fromUnix(since1970)
 
 proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
   ## Returns the time in seconds since the unix epoch.
   ##
-  ## **Deprecated since v0.18.0:** use ``toUnix`` instead
-  float(time)
+  ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
+  time.seconds.float + time.nanoseconds / convert(Seconds, Nanoseconds, 1)
 
 proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-time representation,
@@ -1414,7 +1788,8 @@ proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.}
   when defined(JS):
     return newDate().getTimezoneOffset() * 60
   elif defined(freebsd) or defined(netbsd) or defined(openbsd):
-    var a = timec(nil)
+    var a: CTime
+    discard time(a)
     let lt = localtime(addr(a))
     # BSD stores in `gmtoff` offset east of UTC in seconds,
     # but posix systems using west of UTC in seconds
@@ -1430,13 +1805,13 @@ proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
   dt.toTime
 
 when defined(JS):
-  var startMilsecs = getTime()
+  var start = getTime()
   proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
     ## get the milliseconds from the start of the program. **Deprecated since
     ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
-    when defined(JS):
-      ## get the milliseconds from the start of the program
-      return int(getTime() - startMilsecs)
+    let dur = getTime() - start
+    result = (convert(Seconds, Milliseconds, dur.seconds) +
+      convert(Nanoseconds, Milliseconds, dur.nanoseconds)).int
 else:
   proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
     when defined(macosx):
@@ -1444,9 +1819,6 @@ else:
     else:
       result = int(getClock()) div (clocksPerSec div 1000)
 
-proc miliseconds*(t: TimeInterval): int {.deprecated.} =
-  t.milliseconds
-
 proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
   ## Converts a Time to a TimeInterval.
   ##
@@ -1493,11 +1865,13 @@ proc timeToTimeInfo*(t: Time): DateTime {.deprecated.} =
   result = DateTime(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s)
 
 proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
+  ## **Warning:** This procedure is deprecated since version 0.18.0.
   getDayOfWeek(day, month.Month, year)
 
 proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
   ## Returns the day of the week enum from day, month and year,
   ## according to the Julian calendar.
+  ## **Warning:** This procedure is deprecated since version 0.18.0.
   # Day & month start from one.
   let
     a = (14 - month) div 12