summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorGULPF <oscarnihlgard@gmail.com>2017-12-18 23:11:28 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-12-18 23:11:28 +0100
commita879973081e2c29d64e9fb9d8e539aa980533b10 (patch)
treea6ba8d2e6c072ee3beffa99447538c05b451cc9a /lib/pure
parent07fe1aa655dc75eec1a4cf4c697615b5642e8a7c (diff)
downloadNim-a879973081e2c29d64e9fb9d8e539aa980533b10.tar.gz
Better times module (#6552)
* First work on better timezones

* Update tests to new api.
Removed tests for checking that `isDst` was included when formatting, since `isDst` no longer affects utc offset (the entire utc offset is stored directly in `utcOffset` instead).

* Deprecate getLocaltime & getGmTime
* Add `now()` as a shorthand for GetTIme().inZone(Local)
* Add FedericoCeratto's timezone tests (#6548)
* Run more tests in all timezones

* Make month enum start at 1 instead of 0
* Deprecate getDayOfWeekJulian
* Fix issues with gc safety
* Rename TimeInfo => DateTime
* Fixes #6465
* Improve isLeapYear
* FIx handling negative adjTime

* Cleanup:
- deprecated toSeconds and fromSeconds, added fromUnix and toUnix instead (that returns int64 instead of float)
- added missing doc comments
- removed some unnecessary JS specific implementations

* Fix misstake in JS `-` for Time

* Update usage of TimeEffect
* Removed unecessary use of `difftime`
* JS fix for local tz
* Fix subtraction of months
* Fix `days` field in `toTimeInterval`
* Style and docs
* Fix getDayOfYear for real this time...
* Fix handling of adding/subtracting time across dst transitions
* Fix some bad usage of the times module in the stdlib
* Revert to use proper time resoultion for seeding in random.nim
* Move deprecated procs to bottom of file
* Always use `epochTime` in `randomize`
* Remove TimeInterval normalization
* Fixes #6905

* Fix getDayOfWeek for year < 1
* Export toEpochDay/fromEpochDay and change year/month/monthday order
* Add asserts for checking that the monthday is valid
* Fix some remaining ambiguous references to `Time`
* Fix ambiguous reference to Time
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/cookies.nim6
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim7
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim4
-rw-r--r--lib/pure/oids.nim2
-rw-r--r--lib/pure/os.nim35
-rw-r--r--lib/pure/osproc.nim16
-rw-r--r--lib/pure/random.nim8
-rw-r--r--lib/pure/times.nim1531
8 files changed, 839 insertions, 770 deletions
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index 07b37c7d4..8f16717ac 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -51,7 +51,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
   if secure: result.add("; Secure")
   if httpOnly: result.add("; HttpOnly")
 
-proc setCookie*(key, value: string, expires: TimeInfo,
+proc setCookie*(key, value: string, expires: DateTime,
                 domain = "", path = "", noName = false,
                 secure = false, httpOnly = false): string =
   ## Creates a command in the format of
@@ -63,9 +63,9 @@ proc setCookie*(key, value: string, expires: TimeInfo,
                    noname, secure, httpOnly)
 
 when isMainModule:
-  var tim = Time(int(getTime()) + 76 * (60 * 60 * 24))
+  var tim = fromUnix(getTime().toUnix + 76 * (60 * 60 * 24))
 
-  let cookie = setCookie("test", "value", tim.getGMTime())
+  let cookie = setCookie("test", "value", tim.utc)
   when not defined(testing):
     echo cookie
   let start = "Set-Cookie: test=value; Expires="
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 35cdace09..8827f239f 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -277,15 +277,16 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
   var events = {Event.Timer}
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = fdi.uint
+
   if oneshot:
-    new_ts.it_interval.tv_sec = 0.Time
+    new_ts.it_interval.tv_sec = posix.Time(0)
     new_ts.it_interval.tv_nsec = 0
-    new_ts.it_value.tv_sec = (timeout div 1_000).Time
+    new_ts.it_value.tv_sec = posix.Time(timeout div 1_000)
     new_ts.it_value.tv_nsec = (timeout %% 1_000) * 1_000_000
     incl(events, Event.Oneshot)
     epv.events = epv.events or EPOLLONESHOT
   else:
-    new_ts.it_interval.tv_sec = (timeout div 1000).Time
+    new_ts.it_interval.tv_sec = posix.Time(timeout div 1000)
     new_ts.it_interval.tv_nsec = (timeout %% 1_000) * 1_000_000
     new_ts.it_value.tv_sec = new_ts.it_interval.tv_sec
     new_ts.it_value.tv_nsec = new_ts.it_interval.tv_nsec
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 3e2ec64a8..af5aa15df 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -452,10 +452,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
 
   if timeout != -1:
     if timeout >= 1000:
-      tv.tv_sec = (timeout div 1_000).Time
+      tv.tv_sec = posix.Time(timeout div 1_000)
       tv.tv_nsec = (timeout %% 1_000) * 1_000_000
     else:
-      tv.tv_sec = 0.Time
+      tv.tv_sec = posix.Time(0)
       tv.tv_nsec = timeout * 1_000_000
   else:
     ptv = nil
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 60b53dbe0..427a68964 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -88,7 +88,7 @@ proc generatedTime*(oid: Oid): Time =
   var tmp: int32
   var dummy = oid.time
   bigEndian32(addr(tmp), addr(dummy))
-  result = Time(tmp)
+  result = fromUnix(tmp)
 
 when not defined(testing) and isMainModule:
   let xo = genOid()
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a59134007..87f6def29 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -173,33 +173,33 @@ proc findExe*(exe: string, followSymlinks: bool = true;
         return x
   result = ""
 
-proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last modification time.
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_mtime
+    return fromUnix(res.st_mtime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64)
     findClose(h)
 
-proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last read or write access time.
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_atime
+    return fromUnix(res.st_atime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64)
     findClose(h)
 
-proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
+proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s creation time.
   ##
   ## **Note:** Under POSIX OS's, the returned time may actually be the time at
@@ -208,12 +208,12 @@ proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
   when defined(posix):
     var res: Stat
     if stat(file, res) < 0'i32: raiseOSError(osLastError())
-    return res.st_ctime
+    return fromUnix(res.st_ctime.int64)
   else:
     var f: WIN32_FIND_DATA
     var h = findFirstFile(file, f)
     if h == -1'i32: raiseOSError(osLastError())
-    result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
+    result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64)
     findClose(h)
 
 proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
@@ -1443,7 +1443,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
     winlean.sleep(int32(milsecs))
   else:
     var a, b: Timespec
-    a.tv_sec = Time(milsecs div 1000)
+    a.tv_sec = posix.Time(milsecs div 1000)
     a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
     discard posix.nanosleep(a, b)
 
@@ -1481,16 +1481,17 @@ type
     size*: BiggestInt # Size of file.
     permissions*: set[FilePermission] # File permissions
     linkCount*: BiggestInt # Number of hard links the file object has.
-    lastAccessTime*: Time # Time file was last accessed.
-    lastWriteTime*: Time # Time file was last modified/written to.
-    creationTime*: Time # Time file was created. Not supported on all systems!
+    lastAccessTime*: times.Time # Time file was last accessed.
+    lastWriteTime*: times.Time # Time file was last modified/written to.
+    creationTime*: times.Time # Time file was created. Not supported on all systems!
 
 template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## Transforms the native file info structure into the one nim uses.
   ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
   ## or a 'Stat' structure on posix
   when defined(Windows):
-    template toTime(e: FILETIME): untyped {.gensym.} = winTimeToUnixTime(rdFileTime(e)) # local templates default to bind semantics
+    template toTime(e: FILETIME): untyped {.gensym.} =
+      fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics
     template merge(a, b): untyped = a or (b shl 32)
     formalInfo.id.device = rawInfo.dwVolumeSerialNumber
     formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
@@ -1522,9 +1523,9 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
     formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
     formalInfo.size = rawInfo.st_size
     formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
-    formalInfo.lastAccessTime = rawInfo.st_atime
-    formalInfo.lastWriteTime = rawInfo.st_mtime
-    formalInfo.creationTime = rawInfo.st_ctime
+    formalInfo.lastAccessTime = fromUnix(rawInfo.st_atime.int64)
+    formalInfo.lastWriteTime = fromUnix(rawInfo.st_mtime.int64)
+    formalInfo.creationTime = fromUnix(rawInfo.st_ctime.int64)
 
     result.permissions = {}
     checkAndIncludeMode(S_IRUSR, fpUserRead)
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index f0542ea98..1625845d1 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -1060,10 +1060,10 @@ elif not defined(useNimRtl):
         var tmspec: Timespec
 
         if timeout >= 1000:
-          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_sec = posix.Time(timeout div 1_000)
           tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
         else:
-          tmspec.tv_sec = 0.Time
+          tmspec.tv_sec = posix.Time(0)
           tmspec.tv_nsec = (timeout * 1_000_000)
 
         try:
@@ -1109,20 +1109,20 @@ elif not defined(useNimRtl):
         var b: Timespec
         b.tv_sec = e.tv_sec
         b.tv_nsec = e.tv_nsec
-        e.tv_sec = (e.tv_sec - s.tv_sec).Time
+        e.tv_sec = e.tv_sec - s.tv_sec
         if e.tv_nsec >= s.tv_nsec:
           e.tv_nsec -= s.tv_nsec
         else:
-          if e.tv_sec == 0.Time:
+          if e.tv_sec == posix.Time(0):
             raise newException(ValueError, "System time was modified")
           else:
             diff = s.tv_nsec - e.tv_nsec
             e.tv_nsec = 1_000_000_000 - diff
-        t.tv_sec = (t.tv_sec - e.tv_sec).Time
+        t.tv_sec = t.tv_sec - e.tv_sec
         if t.tv_nsec >= e.tv_nsec:
           t.tv_nsec -= e.tv_nsec
         else:
-          t.tv_sec = (int(t.tv_sec) - 1).Time
+          t.tv_sec = t.tv_sec - posix.Time(1)
           diff = e.tv_nsec - t.tv_nsec
           t.tv_nsec = 1_000_000_000 - diff
         s.tv_sec = b.tv_sec
@@ -1154,10 +1154,10 @@ elif not defined(useNimRtl):
             raiseOSError(osLastError())
 
         if timeout >= 1000:
-          tmspec.tv_sec = (timeout div 1_000).Time
+          tmspec.tv_sec = posix.Time(timeout div 1_000)
           tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
         else:
-          tmspec.tv_sec = 0.Time
+          tmspec.tv_sec = posix.Time(0)
           tmspec.tv_nsec = (timeout * 1_000_000)
 
         try:
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 7edd93c08..de419b9fb 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -190,12 +190,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.
-    when defined(JS):
-      proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
-      randomize(getMil times.getTime())
-    else:
-      let time = int64(times.epochTime() * 1_000_000_000)
-      randomize(time)
+    let time = int64(times.epochTime() * 1_000_000_000)
+    randomize(time)
 
 {.pop.}
 
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index c1d6c3e53..dcc817b7b 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -10,27 +10,26 @@
 
 ## This module contains routines and types for dealing with time.
 ## This module is available for the `JavaScript target
-## <backends.html#the-javascript-target>`_.
+## <backends.html#the-javascript-target>`_. The proleptic Gregorian calendar is the only calendar supported.
 ##
 ## Examples:
 ##
 ## .. code-block:: nim
 ##
 ##  import times, os
-##  var
-##    t = cpuTime()
+##  let time = cpuTime()
 ##
 ##  sleep(100)   # replace this with something to be timed
-##  echo "Time taken: ",cpuTime() - t
+##  echo "Time taken: ",cpuTime() - time
 ##
-##  echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm")
+##  echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
 ##  echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
 ##
 ##  echo "epochTime() float value: ", epochTime()
 ##  echo "getTime()   float value: ", toSeconds(getTime())
 ##  echo "cpuTime()   float value: ", cpuTime()
-##  echo "An hour from now      : ", getLocalTime(getTime()) + 1.hours
-##  echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
+##  echo "An hour from now      : ", now() + 1.hours
+##  echo "An hour from (UTC) now: ", getTime().utc + initInterval(0,0,0,1)
 
 {.push debugger:off.} # the user does not want to trace a part
                       # of the standard library!
@@ -40,132 +39,85 @@ import
 
 include "system/inclrtl"
 
-type
-  Month* = enum ## represents a month
-    mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
-  WeekDay* = enum ## represents a weekday
-    dMon, dTue, dWed, dThu, dFri, dSat, dSun
-
-when defined(posix) and not defined(JS):
-  when defined(linux) and defined(amd64):
-    type
-      TimeImpl {.importc: "time_t", header: "<time.h>".} = clong
-      Time* = distinct TimeImpl ## distinct type that represents a time
-                                ## measured as number of seconds since the epoch
-
-      Timeval {.importc: "struct timeval",
-                header: "<sys/select.h>".} = object ## struct timeval
-        tv_sec: clong  ## Seconds.
-        tv_usec: clong ## Microseconds.
-  else:
-    type
-      TimeImpl {.importc: "time_t", header: "<time.h>".} = int
-      Time* = distinct TimeImpl ## distinct type that represents a time
-                                ## measured as number of seconds since the epoch
-
-      Timeval {.importc: "struct timeval",
-                header: "<sys/select.h>".} = object ## struct timeval
-        tv_sec: int  ## Seconds.
-        tv_usec: int ## Microseconds.
+when defined(posix):
+  import posix
 
-  # we cannot import posix.nim here, because posix.nim depends on times.nim.
-  # Ok, we could, but I don't want circular dependencies.
-  # And gettimeofday() is not defined in the posix module anyway. Sigh.
+  type CTime = posix.Time
 
   proc posix_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):
     var timezone {.importc, header: "<time.h>".}: int
-    proc tzset(): void {.importc, header: "<time.h>".}
     tzset()
 
 elif defined(windows):
   import winlean
 
   # newest version of Visual C++ defines time_t to be of 64 bits
-  type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
+  type CTime {.importc: "time_t", header: "<time.h>".} = distinct int64
   # visual c's c runtime exposes these under a different name
-  var
-    timezone {.importc: "_timezone", header: "<time.h>".}: int
-
-  type
-    Time* = distinct TimeImpl
+  var timezone {.importc: "_timezone", header: "<time.h>".}: int
 
+type
+  Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
+                ## the month number in the range ``[1..12]``.
+    mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
 
-elif defined(JS):
-  type
-    TimeBase = float
-    Time* = distinct TimeBase
-
-  proc getDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getTime(t: Time): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
-  proc getTimezoneOffset(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getUTCDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc getYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
-  proc parse(t: Time; s: cstring): Time {.tags: [], raises: [], benign, importcpp.}
-  proc setDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setTime(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setUTCSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc setYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
-  proc toGMTString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
-  proc toLocaleString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
+  WeekDay* = enum ## Represents a weekday.
+    dMon, dTue, dWed, dThu, dFri, dSat, dSun
 
-type
-  TimeInfo* = object of RootObj ## represents a time in different parts
-    second*: range[0..61]     ## The number of seconds after the minute,
+  MonthdayRange* = range[1..31]
+  HourRange* = range[0..23]
+  MinuteRange* = range[0..59]
+  SecondRange* = range[0..60]
+  YeardayRange* = range[0..365]
+
+  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.
+
+  DateTime* = object of RootObj ## Represents a time in different parts.
+                                ## Although this type can represent leap
+                                ## seconds, they are generally not supported
+                                ## in this module. They are not ignored,
+                                ## but the ``DateTime``'s returned by
+                                ## procedures in this module will never have
+                                ## a leap second.
+    second*: SecondRange      ## The number of seconds after the minute,
                               ## normally in the range 0 to 59, but can
-                              ## be up to 61 to allow for leap seconds.
-    minute*: range[0..59]     ## The number of minutes after the hour,
+                              ## be up to 60 to allow for a leap second.
+    minute*: MinuteRange      ## The number of minutes after the hour,
                               ## in the range 0 to 59.
-    hour*: range[0..23]       ## The number of hours past midnight,
+    hour*: HourRange          ## The number of hours past midnight,
                               ## in the range 0 to 23.
-    monthday*: range[1..31]   ## The day of the month, in the range 1 to 31.
+    monthday*: MonthdayRange  ## The day of the month, in the range 1 to 31.
     month*: Month             ## The current month.
-    year*: int                ## The current year.
+    year*: int                ## The current year, using astronomical year numbering
+                              ## (meaning that before year 1 is year 0, then year -1 and so on).
     weekday*: WeekDay         ## The current day of the week.
-    yearday*: range[0..365]   ## The number of days since January 1,
+    yearday*: YeardayRange    ## The number of days since January 1,
                               ## in the range 0 to 365.
-                              ## Always 0 if the target is JS.
-    isDST*: bool              ## Determines whether DST is in effect.
-                              ## Semantically, this adds another negative hour
-                              ## offset to the time in addition to the timezone.
-    timezone*: int            ## The offset of the (non-DST) timezone in seconds
-                              ## west of UTC. Note that the sign of this number
-                              ## is the opposite of the one in a formatted
-                              ## timezone string like ``+01:00`` (which would be
-                              ## parsed into the timezone ``-3600``).
-
-  ## I make some assumptions about the data in here. Either
-  ## everything should be positive or everything negative. Zero is
-  ## fine too. Mixed signs will lead to unexpected results.
-  TimeInterval* = object ## a time interval
+    isDst*: bool              ## Determines whether DST is in effect.
+                              ## Always false for the JavaScript backend.
+    timezone*: Timezone       ## The timezone represented as an implementation of ``Timezone``.
+    utcOffset*: int           ## The offset in seconds west of UTC, including any offset due to DST.
+                              ## Note that the sign of this number is the opposite
+                              ## 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,
+                         ## 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.
     milliseconds*: int ## The number of milliseconds
     seconds*: int     ## The number of seconds
     minutes*: int     ## The number of minutes
@@ -174,92 +126,394 @@ type
     months*: int      ## The number of months
     years*: int       ## The number of years
 
+  Timezone* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones.
+                     ## The ``times`` module only supplies implementations for the systems local time and UTC.
+                     ## The members ``zoneInfoFromUtc`` and ``zoneInfoFromTz`` should not be accessed directly
+                     ## and are only exported so that ``Timezone`` can be implemented by other modules.
+    zoneInfoFromUtc*: proc (time: Time): ZonedTime {.nimcall, tags: [], raises: [], benign .}
+    zoneInfoFromTz*:  proc (adjTime: Time): ZonedTime {.nimcall, 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.
+                      ## This type is only used for implementing timezones.
+    adjTime*: Time ## Time adjusted to a timezone.
+    utcOffset*: int
+    isDst*: bool
+
 {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
-    TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
+    TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
 
-proc getTime*(): Time {.tags: [TimeEffect], benign.}
-  ## gets the current calendar time as a UNIX epoch value (number of seconds
-  ## elapsed since 1970) with integer precission. Use epochTime for higher
-  ## resolution.
-proc getLocalTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
-  ## converts the calendar time `t` to broken-time representation,
-  ## expressed relative to the user's specified time zone.
-proc getGMTime*(t: Time): TimeInfo {.tags: [TimeEffect], raises: [], benign.}
-  ## converts the calendar time `t` to broken-down time representation,
-  ## expressed in Coordinated Universal Time (UTC).
+const
+  secondsInMin = 60
+  secondsInHour = 60*60
+  secondsInDay = 60*60*24
+  minutesInHour = 60
 
-proc timeInfoToTime*(timeInfo: TimeInfo): Time
-    {.tags: [TimeEffect], benign, deprecated.}
-  ## converts a broken-down time structure to
-  ## calendar time representation. The function ignores the specified
-  ## contents of the structure members `weekday` and `yearday` and recomputes
-  ## them from the other information in the broken-down time structure.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``toTime`` instead.
+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)
 
-proc toTime*(timeInfo: TimeInfo): Time {.tags: [TimeEffect], benign.}
-  ## converts a broken-down time structure to
-  ## calendar time representation. The function ignores the specified
-  ## contents of the structure members `weekday` and `yearday` and recomputes
-  ## them from the other information in the broken-down time structure.
+proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
+  t.int64
 
-proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign.}
-  ## Takes a float which contains the number of seconds since the unix epoch and
-  ## returns a time object.
+proc isLeapYear*(year: int): bool =
+  ## Returns true if ``year`` is a leap year.
+  year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
 
-proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign.} =
-  ## Takes an int which contains the number of seconds since the unix epoch and
-  ## returns a time object.
-  fromSeconds(float(since1970))
+proc getDaysInMonth*(month: Month, year: int): int =
+  ## Get the number of days in a ``month`` of a ``year``.
+  # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
+  case month
+  of mFeb: result = if isLeapYear(year): 29 else: 28
+  of mApr, mJun, mSep, mNov: result = 30
+  else: result = 31
 
-proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.}
-  ## Returns the time in seconds since the unix epoch.
+proc getDaysInYear*(year: int): int =
+  ## Get the number of days in a ``year``
+  result = 365 + (if isLeapYear(year): 1 else: 0)
+
+proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
+  assert monthday <= getDaysInMonth(month, year),
+    $year & "-" & $ord(month) & "-" & $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.
+  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
+  assertValidDate monthday, month, year  
+  # Based on http://howardhinnant.github.io/date_algorithms.html
+  var (y, m, d) = (year, ord(month), monthday.int)
+  if m <= 2:
+    y.dec
+
+  let era = (if y >= 0: y else: y-399) div 400
+  let yoe = y - era * 400
+  let doy = (153 * (m + (if m > 2: -3 else: 9)) + 2) div 5 + d-1
+  let doe = yoe * 365 + yoe div 4 - yoe div 100 + doy
+  return era * 146097 + doe - 719468
+
+proc fromEpochDay*(epochday: int64): tuple[monthday: MonthdayRange, month: Month, year: int] =
+  ## Get the year/month/day date from a epoch day.
+  ## The epoch day is the number of days since 1970/01/01 (it might be negative).  
+  # Based on http://howardhinnant.github.io/date_algorithms.html
+  var z = epochday
+  z.inc 719468
+  let era = (if z >= 0: z else: z - 146096) div 146097
+  let doe = z - era * 146097
+  let yoe = (doe - doe div 1460 + doe div 36524 - doe div 146096) div 365
+  let y = yoe + era * 400;
+  let doy = doe - (365 * yoe + yoe div 4 - yoe div 100)
+  let mp = (5 * doy + 2) div 153
+  let d = doy - (153 * mp + 2) div 5 + 1
+  let m = mp + (if mp < 10: 3 else: -9)
+  return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
+
+proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
+  ## Returns the day of the year.
+  ## Equivalent with ``initDateTime(day, month, year).yearday``.
+  assertValidDate monthday, month, year
+  const daysUntilMonth:     array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+  const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
+
+  if isLeapYear(year):
+    result = daysUntilMonthLeap[month] + monthday - 1
+  else:
+    result = daysUntilMonth[month] + monthday - 1
+
+proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
+  ## Returns the day of the week enum from day, month and year.
+  ## Equivalent with ``initDateTime(day, month, year).weekday``.
+  assertValidDate monthday, month, year
+  # 1970-01-01 is a Thursday, we adjust to the previous Monday
+  let days = toEpochday(monthday, month, year) - 3
+  let weeks = (if days >= 0: days else: days - 6) div 7
+  let wd = days - weeks * 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.
+  result = if wd == 0: dSun else: WeekDay(wd - 1)
+
+# Forward declarations
+proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
+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.}
-  ## computes the difference of two calendar times. Result is in seconds.
+    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.} =
-  ## returns true iff ``a < b``, that is iff a happened before b.
-  when defined(js):
-    result = TimeBase(a) < TimeBase(b)
-  else:
-    result = a - b < 0
+    rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true iff ``a < b``, that is iff a happened before b.
 
 proc `<=` * (a, b: Time): bool {.
-  rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}=
-  ## returns true iff ``a <= b``.
-  when defined(js):
-    result = TimeBase(a) <= TimeBase(b)
-  else:
-    result = a - b <= 0
+    rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true iff ``a <= b``.
 
 proc `==`*(a, b: Time): bool {.
-  rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} =
-  ## returns true if ``a == b``, that is if both times represent the same value
-  when defined(js):
-    result = TimeBase(a) == TimeBase(b)
+    rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect, borrow.}
+  ## Returns true if ``a == b``, that is if both times represent the same point in time.
+
+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
+  # The code above ignores the UTC offset of `timeInfo`,
+  # so we need to compensate for that here.
+  result.inc dt.utcOffset
+
+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 hour = rem div secondsInHour
+  rem = rem - hour * secondsInHour
+  let minute = rem div secondsInMin
+  rem = rem - minute * secondsInMin
+  let second = rem
+
+  let (d, m, y) = fromEpochday(epochday)
+
+  DateTime(
+    year: y,
+    month: m,
+    monthday: d,
+    hour: hour,
+    minute: minute,
+    second: second,
+    weekday: getDayOfWeek(d, m, y),
+    yearday: getDayOfYear(d, m, y),
+    isDst: zt.isDst,
+    timezone: zone,
+    utcOffset: zt.utcOffset
+  )
+
+proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
+  ## Break down ``time`` into a ``DateTime`` using ``zone`` as the timezone.
+  let zoneInfo = zone.zoneInfoFromUtc(time)
+  result = initDateTime(zoneInfo, zone)
+
+proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
+  ## Convert ``dt`` into a ``DateTime`` using ``zone`` as the timezone.
+  dt.toTime.inZone(zone)
+
+proc `$`*(zone: Timezone): string =
+  ## Returns the name of the timezone.
+  zone.name
+
+proc `==`*(zone1, zone2: Timezone): bool =
+  ## Two ``Timezone``'s are considered equal if their name is equal.
+  zone1.name == zone2.name
+
+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
+
+when defined(JS):
+    type JsDate = object
+    proc newDate(year, month, date, hours, minutes, seconds, milliseconds: int): JsDate {.tags: [], raises: [], importc: "new Date".}
+    proc newDate(): JsDate {.importc: "new Date".}
+    proc newDate(value: float): JsDate {.importc: "new Date".}
+    proc getTimezoneOffset(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getTime(js: JsDate): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
+    proc getDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getUTCDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
+    proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
+
+    proc localZoneInfoFromUtc(time: Time): ZonedTime =
+      let jsDate = newDate(time.float * 1000)
+      let offset = jsDate.getTimezoneOffset() * secondsInMin
+      result.adjTime = Time(time.int64 - offset)
+      result.utcOffset = offset
+      result.isDst = false
+
+    proc localZoneInfoFromTz(adjTime: Time): ZonedTime =
+      let utcDate = newDate(adjTime.float * 1000)
+      let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
+        utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
+
+      # This is as dumb as it looks - JS doesn't support years in the range 0-99 in the constructor
+      # because they are assumed to be 19xx...
+      # Because JS doesn't support timezone history, it doesn't really matter in practice.
+      if utcDate.getUTCFullYear() in 0 .. 99:
+        localDate.setFullYear(utcDate.getUTCFullYear())
+
+      result.adjTime = adjTime
+      result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
+      result.isDst = false
+
+else:
+  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
+      defined(macosx):
+    type
+      StructTm {.importc: "struct tm".} = object
+        second {.importc: "tm_sec".},
+          minute {.importc: "tm_min".},
+          hour {.importc: "tm_hour".},
+          monthday {.importc: "tm_mday".},
+          month {.importc: "tm_mon".},
+          year {.importc: "tm_year".},
+          weekday {.importc: "tm_wday".},
+          yearday {.importc: "tm_yday".},
+          isdst {.importc: "tm_isdst".}: cint
+        gmtoff {.importc: "tm_gmtoff".}: clong
   else:
-    result = a - b == 0
+    type
+      StructTm {.importc: "struct tm".} = object
+        second {.importc: "tm_sec".},
+          minute {.importc: "tm_min".},
+          hour {.importc: "tm_hour".},
+          monthday {.importc: "tm_mday".},
+          month {.importc: "tm_mon".},
+          year {.importc: "tm_year".},
+          weekday {.importc: "tm_wday".},
+          yearday {.importc: "tm_yday".},
+          isdst {.importc: "tm_isdst".}: cint
+        when defined(linux) and defined(amd64):
+          gmtoff {.importc: "tm_gmtoff".}: clong
+          zone {.importc: "tm_zone".}: cstring
+  type
+    StructTmPtr = ptr StructTm
+
+  proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
+
+  proc toAdjTime(tm: StructTm): Time =
+    let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
+    result = Time(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 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
+
+  proc localZoneInfoFromTz(adjTime: Time): ZonedTime  =
+    var adjTimei64 = adjTime.int64
+    let past = adjTimei64 - secondsInDay
+    var tm = getStructTm(past)
+    let pastOffset = past - tm.toAdjTime.int64
+
+    let future = adjTimei64 + secondsInDay
+    tm = getStructTm(future)
+    let futureOffset = future - tm.toAdjTime.int64
+
+    var utcOffset: int
+    if pastOffset == futureOffset:
+        utcOffset = pastOffset.int
+    else:
+      if pastOffset > futureOffset:
+        adjTimei64 -= secondsInHour
+
+      adjTimei64 += pastOffset
+      utcOffset = (adjTimei64 - getStructTm(adjTimei64).toAdjTime.int64).int
+
+    # 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
+
+proc utcZoneInfoFromUtc(time: Time): ZonedTime =
+  result.adjTime = time
+  result.utcOffset = 0
+  result.isDst = false
+
+proc utcZoneInfoFromTz(adjTime: Time): ZonedTime =
+  utcZoneInfoFromUtc(adjTime) # adjTime == time since we are in UTC
+
+proc utc*(): TimeZone =
+  ## Get the ``Timezone`` implementation for the UTC timezone.
+  ##
+  ## .. code-block:: nim
+  ##  doAssert now().utc.timezone == utc()
+  ##  doAssert utc().name == "Etc/UTC"
+  Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC")
 
-proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.}
-  ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
+proc local*(): TimeZone =
+  ## Get the ``Timezone`` implementation for the local timezone.
+  ##
+  ## .. code-block:: nim
+  ##  doAssert now().timezone == local()
+  ##  doAssert local().name == "LOCAL"
+  Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL")
+
+proc utc*(dt: DateTime): DateTime =
+  ## Shorthand for ``dt.inZone(utc())``.
+  dt.inZone(utc())
+
+proc local*(dt: DateTime): DateTime =
+  ## Shorthand for ``dt.inZone(local())``.
+  dt.inZone(local())
+
+proc utc*(t: Time): DateTime =
+  ## Shorthand for ``t.inZone(utc())``.  
+  t.inZone(utc())
+
+proc local*(t: Time): DateTime =
+  ## Shorthand for ``t.inZone(local())``.  
+  t.inZone(local())
 
-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.
+proc getTime*(): Time {.tags: [TimeEffect], benign.}
+  ## Gets the current time as a ``Time`` with second resolution. Use epochTime for higher
+  ## resolution.
+
+proc now*(): DateTime {.tags: [TimeEffect], benign.} =
+  ## Get the current time as a  ``DateTime`` in the local timezone.
+  ##
+  ## Shorthand for ``getTime().local``.
+  getTime().local
 
 proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
                    years: int = 0): TimeInterval =
-  ## creates a new ``TimeInterval``.
+  ## Creates a new ``TimeInterval``.
   ##
   ## You can also use the convenience procedures called ``milliseconds``,
   ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
@@ -269,46 +523,33 @@ proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
   ## .. code-block:: nim
   ##
   ##     let day = initInterval(hours=24)
-  ##     let tomorrow = getTime() + day
-  ##     echo(tomorrow)
-  var carryO = 0
-  result.milliseconds = `mod`(milliseconds, 1000)
-  carryO = `div`(milliseconds, 1000)
-  result.seconds = `mod`(carryO + seconds, 60)
-  carryO = `div`(carryO + seconds, 60)
-  result.minutes = `mod`(carryO + minutes, 60)
-  carryO = `div`(carryO + minutes, 60)
-  result.hours = `mod`(carryO + hours, 24)
-  carryO = `div`(carryO + hours, 24)
-  result.days = carryO + days
-
-  result.months = `mod`(months, 12)
-  carryO = `div`(months, 12)
-  result.years = carryO + years
+  ##     let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
+  ##     doAssert $(dt + day) == "2000-01-02T12-00-00+00:00"
+  result.milliseconds = milliseconds
+  result.seconds = seconds
+  result.minutes = minutes
+  result.hours = hours
+  result.days = days
+  result.months = months
+  result.years = years
 
 proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Adds two ``TimeInterval`` objects together.
-  var carryO = 0
-  result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
-  carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
-  result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
-  carryO = `div`(carryO + ti1.seconds + ti2.seconds, 60)
-  result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
-  carryO = `div`(carryO + ti1.minutes + ti2.minutes, 60)
-  result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
-  carryO = `div`(carryO + ti1.hours + ti2.hours, 24)
-  result.days = carryO + ti1.days + ti2.days
-
-  result.months = `mod`(ti1.months + ti2.months, 12)
-  carryO = `div`(ti1.months + ti2.months, 12)
-  result.years = carryO + ti1.years + ti2.years
+  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.months = ti1.months + ti2.months
+  result.years = ti1.years + ti2.years
 
 proc `-`*(ti: TimeInterval): TimeInterval =
   ## Reverses a time interval
+  ##
   ## .. code-block:: nim
   ##
   ##     let day = -initInterval(hours=24)
-  ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: 0, days: -1, months: 0, years: 0)
+  ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: -24, days: 0, months: 0, years: 0)
   result = TimeInterval(
     milliseconds: -ti.milliseconds,
     seconds: -ti.seconds,
@@ -325,123 +566,100 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Time components are compared one-by-one, see output:
   ##
   ## .. code-block:: nim
-  ##     let a = fromSeconds(1_000_000_000)
-  ##     let b = fromSeconds(1_500_000_000)
+  ##     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: -2, months: -2, years: 16)
+  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
   result = ti1 + (-ti2)
 
-proc isLeapYear*(year: int): bool =
-  ## returns true if ``year`` is a leap year
-
-  if year mod 400 == 0:
-    return true
-  elif year mod 100 == 0:
-    return false
-  elif year mod 4 == 0:
-    return true
-  else:
-    return false
-
-proc getDaysInMonth*(month: Month, year: int): int =
-  ## Get the number of days in a ``month`` of a ``year``
+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.
 
-  # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
-  case month
-  of mFeb: result = if isLeapYear(year): 29 else: 28
-  of mApr, mJun, mSep, mNov: result = 30
-  else: result = 31
-
-proc getDaysInYear*(year: int): int =
-  ## Get the number of days in a ``year``
-  result = 365 + (if isLeapYear(year): 1 else: 0)
-
-proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
-  ## Calculates how many seconds the interval is worth by adding up
-  ## all the fields
-
-  var anew = a
+  var anew = dt
   var newinterv = interval
-  result = 0
 
   newinterv.months += interval.years * 12
   var curMonth = anew.month
-  if newinterv.months < 0:   # subtracting
+  # Subtracting
+  if newinterv.months < 0:
     for mth in countDown(-1 * newinterv.months, 1):
-      result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
       if curMonth == mJan:
         curMonth = mDec
         anew.year.dec()
       else:
         curMonth.dec()
-  else:  # adding
+      result.adjDiff -= getDaysInMonth(curMonth, anew.year) * secondsInDay      
+  # Adding
+  else:
     for mth in 1 .. newinterv.months:
-      result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
+      result.adjDiff += getDaysInMonth(curMonth, anew.year) * secondsInDay
       if curMonth == mDec:
         curMonth = mJan
         anew.year.inc()
       else:
         curMonth.inc()
-  result += float(newinterv.days * 24 * 60 * 60)
-  result += float(newinterv.hours * 60 * 60)
-  result += float(newinterv.minutes * 60)
-  result += float(newinterv.seconds)
-  result += newinterv.milliseconds / 1000
-
-proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## adds ``interval`` time from TimeInfo ``a``.
+  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`.
   ##
-  ## **Note:** This has been only briefly tested and it may not be
-  ## very accurate.
-  let t = toSeconds(toTime(a))
-  let secs = toSeconds(a, interval)
-  if a.timezone == 0:
-    result = getGMTime(fromSeconds(t + secs))
-  else:
-    result = getLocalTime(fromSeconds(t + secs))
-
-proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
-  ## subtracts ``interval`` time from TimeInfo ``a``.
-  ##
-  ## **Note:** This has been only briefly tested, it is inaccurate especially
-  ## when you subtract so much that you reach the Julian calendar.
-  let
-    t = toSeconds(toTime(a))
-    secs = toSeconds(a, -interval)
-  if a.timezone == 0:
-    result = getGMTime(fromSeconds(t + secs))
+  ## .. 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 = getLocalTime(fromSeconds(t + secs))
-
-proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
+    result = initDateTime(dt.timezone.zoneInfoFromUtc(Time(dt.toTime.int64 + absDiff)), dt.timezone)
 
-proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
-  ## An alias for a misspelled field in ``TimeInterval``.
-  ##
-  ## **Warning:** This should not be used! It will be removed in the next
-  ## version.
-  t.milliseconds = milliseconds
+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 getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## gets the current date as a string of the format ``YYYY-MM-DD``.
-  var ti = getLocalTime(getTime())
-  result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
+  ## Gets the current date as a string of the format ``YYYY-MM-DD``.
+  var ti = now()
+  result = $ti.year & '-' & intToStr(ord(ti.month), 2) &
     '-' & intToStr(ti.monthday, 2)
 
 proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## gets the current clock time as a string of the format ``HH:MM:SS``.
-  var ti = getLocalTime(getTime())
+  ## Gets the current clock time as a string of the format ``HH:MM:SS``.
+  var ti = now()
   result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
     ':' & intToStr(ti.second, 2)
 
 proc `$`*(day: WeekDay): string =
-  ## stingify operator for ``WeekDay``.
+  ## Stringify operator for ``WeekDay``.
   const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
      "Thursday", "Friday", "Saturday", "Sunday"]
   return lookup[day]
 
 proc `$`*(m: Month): string =
-  ## stingify operator for ``Month``.
+  ## Stringify operator for ``Month``.
   const lookup: array[Month, string] = ["January", "February", "March",
       "April", "May", "June", "July", "August", "September", "October",
       "November", "December"]
@@ -450,74 +668,68 @@ proc `$`*(m: Month): string =
 proc milliseconds*(ms: int): TimeInterval {.inline.} =
   ## TimeInterval of `ms` milliseconds
   ##
-  ## Note: not all time functions have millisecond resolution
-  initInterval(`mod`(ms,1000), `div`(ms,1000))
+  ## Note: not all time procedures have millisecond resolution
+  initInterval(milliseconds = ms)
 
 proc seconds*(s: int): TimeInterval {.inline.} =
   ## TimeInterval of `s` seconds
   ##
   ## ``echo getTime() + 5.second``
-  initInterval(0,`mod`(s,60), `div`(s,60))
+  initInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
   ## TimeInterval of `m` minutes
   ##
   ## ``echo getTime() + 5.minutes``
-  initInterval(0,0,`mod`(m,60), `div`(m,60))
+  initInterval(minutes = m)
 
 proc hours*(h: int): TimeInterval {.inline.} =
   ## TimeInterval of `h` hours
   ##
   ## ``echo getTime() + 2.hours``
-  initInterval(0,0,0,`mod`(h,24),`div`(h,24))
+  initInterval(hours = h)
 
 proc days*(d: int): TimeInterval {.inline.} =
   ## TimeInterval of `d` days
   ##
   ## ``echo getTime() + 2.days``
-  initInterval(0,0,0,0,d)
+  initInterval(days = d)
 
 proc months*(m: int): TimeInterval {.inline.} =
   ## TimeInterval of `m` months
   ##
   ## ``echo getTime() + 2.months``
-  initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
+  initInterval(months = m)
 
 proc years*(y: int): TimeInterval {.inline.} =
   ## TimeInterval of `y` years
   ##
   ## ``echo getTime() + 2.years``
-  initInterval(0,0,0,0,0,0,y)
+  initInterval(years = y)
 
-proc `+=`*(t: var Time, ti: TimeInterval) =
-  ## modifies `t` by adding the interval `ti`
-  t = toTime(getLocalTime(t) + ti)
+proc `+=`*(time: var Time, interval: TimeInterval) =
+  ## Modifies `time` by adding `interval`.
+  time = toTime(time.local + interval)
 
-proc `+`*(t: Time, ti: TimeInterval): Time =
-  ## adds the interval `ti` to Time `t`
-  ## by converting to localTime, adding the interval, and converting back
+proc `+`*(time: Time, interval: TimeInterval): Time =
+  ## Adds `interval` to `time`
+  ## by converting to a ``DateTime`` in the local timezone,
+  ## adding the interval, and converting back to ``Time``.
   ##
   ## ``echo getTime() + 1.day``
-  result = toTime(getLocalTime(t) + ti)
+  result = toTime(time.local + interval)
 
-proc `-=`*(t: var Time, ti: TimeInterval) =
-  ## modifies `t` by subtracting the interval `ti`
-  t = toTime(getLocalTime(t) - ti)
+proc `-=`*(time: var Time, interval: TimeInterval) =
+  ## Modifies `time` by subtracting `interval`.
+  time = toTime(time.local - interval)
 
-proc `-`*(t: Time, ti: TimeInterval): Time =
-  ## subtracts the interval `ti` from Time `t`
+proc `-`*(time: Time, interval: TimeInterval): Time =
+  ## Subtracts `interval` from Time `time`.
   ##
   ## ``echo getTime() - 1.day``
-  result = toTime(getLocalTime(t) - ti)
+  result = toTime(time.local - interval)
 
-const
-  secondsInMin = 60
-  secondsInHour = 60*60
-  secondsInDay = 60*60*24
-  minutesInHour = 60
-  epochStartYear = 1970
-
-proc formatToken(info: TimeInfo, token: string, buf: var string) =
+proc formatToken(dt: DateTime, token: string, buf: var string) =
   ## Helper of the format proc to parse individual tokens.
   ##
   ## Pass the found token in the user input string, and the buffer where the
@@ -525,96 +737,96 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
   ## formatting tokens require modifying the previous characters.
   case token
   of "d":
-    buf.add($info.monthday)
+    buf.add($dt.monthday)
   of "dd":
-    if info.monthday < 10:
+    if dt.monthday < 10:
       buf.add("0")
-    buf.add($info.monthday)
+    buf.add($dt.monthday)
   of "ddd":
-    buf.add(($info.weekday)[0 .. 2])
+    buf.add(($dt.weekday)[0 .. 2])
   of "dddd":
-    buf.add($info.weekday)
+    buf.add($dt.weekday)
   of "h":
-    buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
+    buf.add($(if dt.hour > 12: dt.hour - 12 else: dt.hour))
   of "hh":
-    let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
+    let amerHour = if dt.hour > 12: dt.hour - 12 else: dt.hour
     if amerHour < 10:
       buf.add('0')
     buf.add($amerHour)
   of "H":
-    buf.add($info.hour)
+    buf.add($dt.hour)
   of "HH":
-    if info.hour < 10:
+    if dt.hour < 10:
       buf.add('0')
-    buf.add($info.hour)
+    buf.add($dt.hour)
   of "m":
-    buf.add($info.minute)
+    buf.add($dt.minute)
   of "mm":
-    if info.minute < 10:
+    if dt.minute < 10:
       buf.add('0')
-    buf.add($info.minute)
+    buf.add($dt.minute)
   of "M":
-    buf.add($(int(info.month)+1))
+    buf.add($ord(dt.month))
   of "MM":
-    if info.month < mOct:
+    if dt.month < mOct:
       buf.add('0')
-    buf.add($(int(info.month)+1))
+    buf.add($ord(dt.month))
   of "MMM":
-    buf.add(($info.month)[0..2])
+    buf.add(($dt.month)[0..2])
   of "MMMM":
-    buf.add($info.month)
+    buf.add($dt.month)
   of "s":
-    buf.add($info.second)
+    buf.add($dt.second)
   of "ss":
-    if info.second < 10:
+    if dt.second < 10:
       buf.add('0')
-    buf.add($info.second)
+    buf.add($dt.second)
   of "t":
-    if info.hour >= 12:
+    if dt.hour >= 12:
       buf.add('P')
     else: buf.add('A')
   of "tt":
-    if info.hour >= 12:
+    if dt.hour >= 12:
       buf.add("PM")
     else: buf.add("AM")
   of "y":
-    var fr = ($info.year).len()-1
+    var fr = ($dt.year).len()-1
     if fr < 0: fr = 0
-    buf.add(($info.year)[fr .. ($info.year).len()-1])
+    buf.add(($dt.year)[fr .. ($dt.year).len()-1])
   of "yy":
-    var fr = ($info.year).len()-2
+    var fr = ($dt.year).len()-2
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
     buf.add(fyear)
   of "yyy":
-    var fr = ($info.year).len()-3
+    var fr = ($dt.year).len()-3
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
     buf.add(fyear)
   of "yyyy":
-    var fr = ($info.year).len()-4
+    var fr = ($dt.year).len()-4
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
     buf.add(fyear)
   of "yyyyy":
-    var fr = ($info.year).len()-5
+    var fr = ($dt.year).len()-5
     if fr < 0: fr = 0
-    var fyear = ($info.year)[fr .. ($info.year).len()-1]
+    var fyear = ($dt.year)[fr .. ($dt.year).len()-1]
     if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
     buf.add(fyear)
   of "z":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
     if nonDstTz <= 0: buf.add('+')
     else: buf.add('-')
     buf.add($hours)
   of "zz":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
     if nonDstTz <= 0: buf.add('+')
     else: buf.add('-')
@@ -622,7 +834,7 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
     buf.add($hours)
   of "zzz":
     let
-      nonDstTz = info.timezone - int(info.isDst) * secondsInHour
+      nonDstTz = dt.utcOffset
       hours = abs(nonDstTz) div secondsInHour
       minutes = (abs(nonDstTz) div secondsInMin) mod minutesInHour
     if nonDstTz <= 0: buf.add('+')
@@ -638,8 +850,8 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
     raise newException(ValueError, "Invalid format string: " & token)
 
 
-proc format*(info: TimeInfo, f: string): string =
-  ## This function formats `info` as specified by `f`. The following format
+proc format*(dt: DateTime, f: string): string {.tags: [].}=
+  ## This procedure formats `dt` as specified by `f`. The following format
   ## specifiers are available:
   ##
   ## ==========  =================================================================================  ================================================
@@ -683,7 +895,7 @@ proc format*(info: TimeInfo, f: string): string =
   while true:
     case f[i]
     of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
-      formatToken(info, currentF, result)
+      formatToken(dt, currentF, result)
 
       currentF = ""
       if f[i] == '\0': break
@@ -700,187 +912,187 @@ proc format*(info: TimeInfo, f: string): string =
       if currentF.len < 1 or currentF[high(currentF)] == f[i]:
         currentF.add(f[i])
       else:
-        formatToken(info, currentF, result)
+        formatToken(dt, currentF, result)
         dec(i) # Move position back to re-process the character separately.
         currentF = ""
 
     inc(i)
 
-proc `$`*(timeInfo: TimeInfo): string {.tags: [], raises: [], benign.} =
-  ## converts a `TimeInfo` object to a string representation.
+proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
+  ## Converts a `DateTime` object to a string representation.
   ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
   try:
-    result = format(timeInfo, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this
+    result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz") # todo: optimize this
   except ValueError: assert false # cannot happen because format string is valid
 
-proc `$`*(time: Time): string {.tags: [TimeEffect], raises: [], benign.} =
+proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
   ## converts a `Time` value to a string representation. It will use the local
   ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
-  $getLocalTime(time)
+  $time.local
 
 {.pop.}
 
-proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
+proc parseToken(dt: var DateTime; token, value: string; j: var int) =
   ## Helper of the parse proc to parse individual tokens.
   var sv: int
   case token
   of "d":
     var pd = parseInt(value[j..j+1], sv)
-    info.monthday = sv
+    dt.monthday = sv
     j += pd
   of "dd":
-    info.monthday = value[j..j+1].parseInt()
+    dt.monthday = value[j..j+1].parseInt()
     j += 2
   of "ddd":
     case value[j..j+2].toLowerAscii()
-    of "sun": info.weekday = dSun
-    of "mon": info.weekday = dMon
-    of "tue": info.weekday = dTue
-    of "wed": info.weekday = dWed
-    of "thu": info.weekday = dThu
-    of "fri": info.weekday = dFri
-    of "sat": info.weekday = dSat
+    of "sun": dt.weekday = dSun
+    of "mon": dt.weekday = dMon
+    of "tue": dt.weekday = dTue
+    of "wed": dt.weekday = dWed
+    of "thu": dt.weekday = dThu
+    of "fri": dt.weekday = dFri
+    of "sat": dt.weekday = dSat
     else:
       raise newException(ValueError,
         "Couldn't parse day of week (ddd), got: " & value[j..j+2])
     j += 3
   of "dddd":
     if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
-      info.weekday = dSun
+      dt.weekday = dSun
       j += 6
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("monday") == 0:
-      info.weekday = dMon
+      dt.weekday = dMon
       j += 6
     elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("tuesday") == 0:
-      info.weekday = dTue
+      dt.weekday = dTue
       j += 7
     elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("wednesday") == 0:
-      info.weekday = dWed
+      dt.weekday = dWed
       j += 9
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("thursday") == 0:
-      info.weekday = dThu
+      dt.weekday = dThu
       j += 8
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("friday") == 0:
-      info.weekday = dFri
+      dt.weekday = dFri
       j += 6
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("saturday") == 0:
-      info.weekday = dSat
+      dt.weekday = dSat
       j += 8
     else:
       raise newException(ValueError,
         "Couldn't parse day of week (dddd), got: " & value)
   of "h", "H":
     var pd = parseInt(value[j..j+1], sv)
-    info.hour = sv
+    dt.hour = sv
     j += pd
   of "hh", "HH":
-    info.hour = value[j..j+1].parseInt()
+    dt.hour = value[j..j+1].parseInt()
     j += 2
   of "m":
     var pd = parseInt(value[j..j+1], sv)
-    info.minute = sv
+    dt.minute = sv
     j += pd
   of "mm":
-    info.minute = value[j..j+1].parseInt()
+    dt.minute = value[j..j+1].parseInt()
     j += 2
   of "M":
     var pd = parseInt(value[j..j+1], sv)
-    info.month = Month(sv-1)
+    dt.month = sv.Month
     j += pd
   of "MM":
     var month = value[j..j+1].parseInt()
     j += 2
-    info.month = Month(month-1)
+    dt.month = month.Month
   of "MMM":
     case value[j..j+2].toLowerAscii():
-    of "jan": info.month =  mJan
-    of "feb": info.month =  mFeb
-    of "mar": info.month =  mMar
-    of "apr": info.month =  mApr
-    of "may": info.month =  mMay
-    of "jun": info.month =  mJun
-    of "jul": info.month =  mJul
-    of "aug": info.month =  mAug
-    of "sep": info.month =  mSep
-    of "oct": info.month =  mOct
-    of "nov": info.month =  mNov
-    of "dec": info.month =  mDec
+    of "jan": dt.month =  mJan
+    of "feb": dt.month =  mFeb
+    of "mar": dt.month =  mMar
+    of "apr": dt.month =  mApr
+    of "may": dt.month =  mMay
+    of "jun": dt.month =  mJun
+    of "jul": dt.month =  mJul
+    of "aug": dt.month =  mAug
+    of "sep": dt.month =  mSep
+    of "oct": dt.month =  mOct
+    of "nov": dt.month =  mNov
+    of "dec": dt.month =  mDec
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMM), got: " & value)
     j += 3
   of "MMMM":
     if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
-      info.month =  mJan
+      dt.month =  mJan
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
-      info.month =  mFeb
+      dt.month =  mFeb
       j += 8
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
-      info.month =  mMar
+      dt.month =  mMar
       j += 5
     elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
-      info.month =  mApr
+      dt.month =  mApr
       j += 5
     elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
-      info.month =  mMay
+      dt.month =  mMay
       j += 3
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
-      info.month =  mJun
+      dt.month =  mJun
       j += 4
     elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
-      info.month =  mJul
+      dt.month =  mJul
       j += 4
     elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
-      info.month =  mAug
+      dt.month =  mAug
       j += 6
     elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
-      info.month =  mSep
+      dt.month =  mSep
       j += 9
     elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
-      info.month =  mOct
+      dt.month =  mOct
       j += 7
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
-      info.month =  mNov
+      dt.month =  mNov
       j += 8
     elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
-      info.month =  mDec
+      dt.month =  mDec
       j += 8
     else:
       raise newException(ValueError,
         "Couldn't parse month (MMMM), got: " & value)
   of "s":
     var pd = parseInt(value[j..j+1], sv)
-    info.second = sv
+    dt.second = sv
     j += pd
   of "ss":
-    info.second = value[j..j+1].parseInt()
+    dt.second = value[j..j+1].parseInt()
     j += 2
   of "t":
-    if value[j] == 'P' and info.hour > 0 and info.hour < 12:
-      info.hour += 12
+    if value[j] == 'P' and dt.hour > 0 and dt.hour < 12:
+      dt.hour += 12
     j += 1
   of "tt":
-    if value[j..j+1] == "PM" and info.hour > 0 and info.hour < 12:
-      info.hour += 12
+    if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12:
+      dt.hour += 12
     j += 2
   of "yy":
     # Assumes current century
     var year = value[j..j+1].parseInt()
-    var thisCen = getLocalTime(getTime()).year div 100
-    info.year = thisCen*100 + year
+    var thisCen = now().year div 100
+    dt.year = thisCen*100 + year
     j += 2
   of "yyyy":
-    info.year = value[j..j+3].parseInt()
+    dt.year = value[j..j+3].parseInt()
     j += 4
   of "z":
-    info.isDST = false
+    dt.isDst = false
     if value[j] == '+':
-      info.timezone = 0 - parseInt($value[j+1]) * secondsInHour
+      dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
     elif value[j] == '-':
-      info.timezone = parseInt($value[j+1]) * secondsInHour
+      dt.utcOffset = parseInt($value[j+1]) * secondsInHour
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
@@ -888,13 +1100,13 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
         "Couldn't parse timezone offset (z), got: " & value[j])
     j += 2
   of "zz":
-    info.isDST = false
+    dt.isDst = false
     if value[j] == '+':
-      info.timezone = 0 - value[j+1..j+2].parseInt() * secondsInHour
+      dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
     elif value[j] == '-':
-      info.timezone = value[j+1..j+2].parseInt() * secondsInHour
+      dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
@@ -902,31 +1114,33 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
         "Couldn't parse timezone offset (zz), got: " & value[j])
     j += 3
   of "zzz":
-    info.isDST = false
+    dt.isDst = false
     var factor = 0
     if value[j] == '+': factor = -1
     elif value[j] == '-': factor = 1
     elif value[j] == 'Z':
-      info.timezone = 0
+      dt.utcOffset = 0
       j += 1
       return
     else:
       raise newException(ValueError,
         "Couldn't parse timezone offset (zzz), got: " & value[j])
-    info.timezone = factor * value[j+1..j+2].parseInt() * secondsInHour
+    dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
     j += 4
-    info.timezone += factor * value[j..j+1].parseInt() * 60
+    dt.utcOffset += factor * value[j..j+1].parseInt() * 60
     j += 2
   else:
     # Ignore the token and move forward in the value string by the same length
     j += token.len
 
-proc parse*(value, layout: string): TimeInfo =
-  ## This function parses a date/time string using the standard format
-  ## identifiers as listed below. The function defaults information not provided
-  ## in the format string from the running program (timezone, month, year, etc).
-  ## Daylight saving time is only set if no timezone is given and the given date
-  ## lies within the DST period of the current locale.
+proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
+  ## This procedure parses a date/time string using the standard format
+  ## identifiers as listed below. The procedure defaults information not provided
+  ## in the format string from the running program (month, year, etc).
+  ##
+  ## The return value will always be in the `zone` timezone. If no UTC offset was
+  ## parsed, then the input will be assumed to be specified in the `zone` timezone
+  ## already, so no timezone conversion will be done in that case.
   ##
   ## ==========  =================================================================================  ================================================
   ## Specifier   Description                                                                        Example
@@ -965,17 +1179,17 @@ proc parse*(value, layout: string): TimeInfo =
   var j = 0 # pointer for value string
   var token = ""
   # Assumes current day of month, month and year, but time is reset to 00:00:00. Weekday will be reset after parsing.
-  var info = getLocalTime(getTime())
-  info.hour = 0
-  info.minute = 0
-  info.second = 0
-  info.isDST = true # using this is flag for checking whether a timezone has \
+  var dt = now()
+  dt.hour = 0
+  dt.minute = 0
+  dt.second = 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:
     case layout[i]
     of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
       if token.len > 0:
-        parseToken(info, token, value, j)
+        parseToken(dt, token, value, j)
       # Reset token
       token = ""
       # Break if at end of line
@@ -997,26 +1211,15 @@ proc parse*(value, layout: string): TimeInfo =
         token.add(layout[i])
         inc(i)
       else:
-        parseToken(info, token, value, j)
+        parseToken(dt, token, value, j)
         token = ""
 
-  if info.isDST:
-    # means that no timezone has been parsed. In this case, we need to check
-    # whether the date is within DST of the local time.
-    let tmp = getLocalTime(toTime(info))
-    # correctly set isDST so that the following step works on the correct time
-    info.isDST = tmp.isDST
-
-  # Correct weekday and yearday; transform timestamp to local time.
-  # There currently is no way of returning this with the original (parsed)
-  # timezone while also setting weekday and yearday (we are depending on stdlib
-  # to provide this calculation).
-  return getLocalTime(toTime(info))
-
-# Leap year calculations are adapted from:
-# http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years
-# The dayOfTheWeek procs are adapated from:
-# http://stason.org/TULARC/society/calendars/2-5-What-day-of-the-week-was-2-August-1953.html
+  if dt.isDst:
+    # No timezone parsed - assume timezone is `zone`
+    result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
+  else:
+    # Otherwise convert to `zone`
+    result = dt.toTime.inZone(zone)
 
 proc countLeapYears*(yearSpan: int): int =
   ## Returns the number of leap years spanned by a given number of years.
@@ -1042,75 +1245,7 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
   result.years = days div 365
   result.days = days mod 365
 
-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 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
-  result = d.WeekDay
-
-proc timeToTimeInfo*(t: Time): TimeInfo {.deprecated.} =
-  ## Converts a Time to TimeInfo.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``getLocalTime`` or ``getGMTime`` instead.
-  let
-    secs = t.toSeconds().int
-    daysSinceEpoch = secs div secondsInDay
-    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
-    daySeconds = secs 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 {.deprecated.} =
-  ## Converts a Time to a TimeInterval.
-  ##
-  ## **Warning:** This procedure is deprecated since version 0.14.0.
-  ## Use ``toTimeInterval`` instead.
-  # Milliseconds not available from Time
-  var tInfo = t.getLocalTime()
-  initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
-
-proc toTimeInterval*(t: Time): TimeInterval =
+proc toTimeInterval*(time: Time): TimeInterval =
   ## Converts a Time to a TimeInterval.
   ##
   ## To be used when diffing times.
@@ -1121,10 +1256,25 @@ proc toTimeInterval*(t: 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: -2, months: -2, years: 16)
+  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: 5, months: -2, years: 16)
   # Milliseconds not available from Time
-  var tInfo = t.getLocalTime()
-  initInterval(0, tInfo.second, tInfo.minute, tInfo.hour, tInfo.weekday.ord, tInfo.month.ord, tInfo.year)
+  var dt = time.local
+  initInterval(0, dt.second, dt.minute, dt.hour, dt.monthday, dt.month.ord - 1, dt.year)
+
+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.
+  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
+  )
+  result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
 
 when not defined(JS):
   proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
@@ -1145,160 +1295,39 @@ when not defined(JS):
     ##   doWork()
     ##   echo "CPU time [s] ", cpuTime() - t0
 
-when not defined(JS):
-  # C wrapper:
-  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
-      defined(macosx):
-    type
-      StructTM {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        gmtoff {.importc: "tm_gmtoff".}: clong
-  else:
-    type
-      StructTM {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        when defined(linux) and defined(amd64):
-          gmtoff {.importc: "tm_gmtoff".}: clong
-          zone {.importc: "tm_zone".}: cstring
+when defined(JS):
+  proc getTime(): Time =
+    (newDate().getTime() div 1000).Time
+
+  proc epochTime*(): float {.tags: [TimeEffect].} =
+    newDate().getTime() / 1000
+
+else:
   type
-    TimeInfoPtr = ptr StructTM
     Clock {.importc: "clock_t".} = distinct int
 
-  when not defined(windows):
-    # This is not ANSI C, but common enough
-    proc timegm(t: StructTM): Time {.
-      importc: "timegm", header: "<time.h>", tags: [].}
-
-  proc localtime(timer: ptr Time): TimeInfoPtr {.
-    importc: "localtime", header: "<time.h>", tags: [].}
-  proc gmtime(timer: ptr Time): TimeInfoPtr {.
-    importc: "gmtime", header: "<time.h>", tags: [].}
-  proc timec(timer: ptr Time): Time {.
+  proc timec(timer: ptr CTime): CTime {.
     importc: "time", header: "<time.h>", tags: [].}
-  proc mktime(t: StructTM): Time {.
-    importc: "mktime", header: "<time.h>", tags: [].}
+
   proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
-  proc difftime(a, b: Time): float {.importc: "difftime", header: "<time.h>",
-    tags: [].}
 
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
 
-  # our own procs on top of that:
-  proc tmToTimeInfo(tm: StructTM, local: bool): TimeInfo =
-    const
-      weekDays: array[0..6, WeekDay] = [
-        dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-    TimeInfo(second: int(tm.second),
-      minute: int(tm.minute),
-      hour: int(tm.hour),
-      monthday: int(tm.monthday),
-      month: Month(tm.month),
-      year: tm.year + 1900'i32,
-      weekday: weekDays[int(tm.weekday)],
-      yearday: int(tm.yearday),
-      isDST: tm.isdst > 0,
-      timezone: if local: getTimezone() else: 0
-    )
-
-
-  proc timeInfoToTM(t: TimeInfo): StructTM =
-    const
-      weekDays: array[WeekDay, int8] = [1'i8,2'i8,3'i8,4'i8,5'i8,6'i8,0'i8]
-    result.second = t.second
-    result.minute = t.minute
-    result.hour = t.hour
-    result.monthday = t.monthday
-    result.month = cint(t.month)
-    result.year = cint(t.year - 1900)
-    result.weekday = weekDays[t.weekday]
-    result.yearday = t.yearday
-    result.isdst = if t.isDST: 1 else: 0
-
-  when not defined(useNimRtl):
-    proc `-` (a, b: Time): int64 =
-      return toBiggestInt(difftime(a, b))
-
-  proc getStartMilsecs(): int =
-    #echo "clocks per sec: ", clocksPerSec, "clock: ", int(getClock())
-    #return getClock() div (clocksPerSec div 1000)
-    when defined(macosx):
-      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
-    else:
-      result = int(getClock()) div (clocksPerSec div 1000)
-    when false:
-      var a: Timeval
-      posix_gettimeofday(a)
-      result = a.tv_sec * 1000'i64 + a.tv_usec div 1000'i64
-      #echo "result: ", result
-
-  proc getTime(): Time = return timec(nil)
-  proc getLocalTime(t: Time): TimeInfo =
-    var a = t
-    let lt = localtime(addr(a))
-    assert(not lt.isNil)
-    result = tmToTimeInfo(lt[], true)
-    # copying is needed anyway to provide reentrancity; thus
-    # the conversion is not expensive
-
-  proc getGMTime(t: Time): TimeInfo =
-    var a = t
-    result = tmToTimeInfo(gmtime(addr(a))[], false)
-    # copying is needed anyway to provide reentrancity; thus
-    # the conversion is not expensive
-
-  proc toTime(timeInfo: TimeInfo): Time =
-    var cTimeInfo = timeInfo # for C++ we have to make a copy
-    # because the header of mktime is broken in my version of libc
-
-    result = mktime(timeInfoToTM(cTimeInfo))
-    # mktime is defined to interpret the input as local time. As timeInfoToTM
-    # does ignore the timezone, we need to adjust this here.
-    result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
-
-  proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
+  proc getTime(): Time =
+    timec(nil).Time
 
   const
     epochDiff = 116444736000000000'i64
     rateDiff = 10000000'i64 # 100 nsecs
 
-  proc unixTimeToWinTime*(t: Time): int64 =
+  proc unixTimeToWinTime*(time: CTime): int64 =
     ## converts a UNIX `Time` (``time_t``) to a Windows file time
-    result = int64(t) * rateDiff + epochDiff
+    result = int64(time) * rateDiff + epochDiff
 
-  proc winTimeToUnixTime*(t: int64): Time =
+  proc winTimeToUnixTime*(time: int64): CTime =
     ## converts a Windows time to a UNIX `Time` (``time_t``)
-    result = Time((t - epochDiff) div rateDiff)
-
-  proc getTimezone(): int =
-    when defined(freebsd) or defined(netbsd) or defined(openbsd):
-      var a = timec(nil)
-      let lt = localtime(addr(a))
-      # BSD stores in `gmtoff` offset east of UTC in seconds,
-      # but posix systems using west of UTC in seconds
-      return -(lt.gmtoff)
-    else:
-      return timezone
-
-  proc fromSeconds(since1970: float): Time = Time(since1970)
-
-  proc toSeconds(time: Time): float = float(time)
+    result = CTime((time - epochDiff) div rateDiff)
 
   when not defined(useNimRtl):
     proc epochTime(): float =
@@ -1319,83 +1348,125 @@ when not defined(JS):
     proc cpuTime(): float =
       result = toFloat(int(getClock())) / toFloat(clocksPerSec)
 
-elif defined(JS):
-  proc newDate(): Time {.importc: "new Date".}
-  proc internGetTime(): Time {.importc: "new Date", tags: [].}
+# Deprecated procs
 
-  proc newDate(value: float): Time {.importc: "new Date".}
-  proc newDate(value: cstring): Time {.importc: "new Date".}
-  proc getTime(): Time =
-    # Warning: This is something different in JS.
-    return newDate()
+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.
+  Time(since1970)
 
-  const
-    weekDays: array[0..6, WeekDay] = [
-      dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-
-  proc getLocalTime(t: Time): TimeInfo =
-    result.second = t.getSeconds()
-    result.minute = t.getMinutes()
-    result.hour = t.getHours()
-    result.monthday = t.getDate()
-    result.month = Month(t.getMonth())
-    result.year = t.getFullYear()
-    result.weekday = weekDays[t.getDay()]
-    result.timezone = getTimezone()
-
-    result.yearday = result.monthday - 1
-    for month in mJan..<result.month:
-      result.yearday += getDaysInMonth(month, result.year)
-
-  proc getGMTime(t: Time): TimeInfo =
-    result.second = t.getUTCSeconds()
-    result.minute = t.getUTCMinutes()
-    result.hour = t.getUTCHours()
-    result.monthday = t.getUTCDate()
-    result.month = Month(t.getUTCMonth())
-    result.year = t.getUTCFullYear()
-    result.weekday = weekDays[t.getUTCDay()]
-
-    result.yearday = result.monthday - 1
-    for month in mJan..<result.month:
-      result.yearday += getDaysInMonth(month, result.year)
-
-  proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
-
-  proc toTime*(timeInfo: TimeInfo): Time = newDate($timeInfo)
-
-  proc `-` (a, b: Time): int64 =
-    return a.getTime() - b.getTime()
+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.
+  Time(since1970)
 
-  var
-    startMilsecs = getTime()
+proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
+  ## Returns the time in seconds since the unix epoch.
+  float(time)
 
-  proc getStartMilsecs(): int =
-    ## get the milliseconds from the start of the program
-    return int(getTime() - startMilsecs)
+proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+  ## Converts the calendar time `time` to broken-time representation,
+  ## expressed relative to the user's specified time zone.
+  time.local
+
+proc getGMTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+  ## Converts the calendar time `time` to broken-down time representation,
+  ## expressed in Coordinated Universal Time (UTC).
+  time.utc
+
+proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} =
+  ## Returns the offset of the local (non-DST) timezone in seconds west of UTC.
+  when defined(JS):
+    return newDate().getTimezoneOffset() * 60
+  elif defined(freebsd) or defined(netbsd) or defined(openbsd):
+    var a = timec(nil)
+    let lt = localtime(addr(a))
+    # BSD stores in `gmtoff` offset east of UTC in seconds,
+    # but posix systems using west of UTC in seconds
+    return -(lt.gmtoff)
+  else:
+    return timezone
+
+proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
+  ## Converts a broken-down time structure to calendar time representation.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``toTime`` instead.
+  dt.toTime  
+
+when defined(JS):
+  var startMilsecs = 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)
+else:
+  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
+    when defined(macosx):
+      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
+    else:
+      result = int(getClock()) div (clocksPerSec div 1000)
 
-  proc fromSeconds(since1970: float): Time = result = newDate(since1970 * 1000)
+proc miliseconds*(t: TimeInterval): int {.deprecated.} =
+  t.milliseconds
 
-  proc toSeconds(time: Time): float = result = time.getTime() / 1000
+proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
+  ## Converts a Time to a TimeInterval.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``toTimeInterval`` instead.
+  # Milliseconds not available from Time
+  t.toTimeInterval()
 
-  proc getTimezone(): int = result = newDate().getTimezoneOffset() * 60
+proc timeToTimeInfo*(t: Time): DateTime {.deprecated.} =
+  ## Converts a Time to DateTime.
+  ##
+  ## **Warning:** This procedure is deprecated since version 0.14.0.
+  ## Use ``inZone`` instead.
+  const epochStartYear = 1970
 
-  proc epochTime*(): float {.tags: [TimeEffect].} = newDate().toSeconds()
+  let
+    secs = t.toSeconds().int
+    daysSinceEpoch = secs div secondsInDay
+    (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch)
+    daySeconds = secs mod secondsInDay
 
+    y = yearsSinceEpoch + epochStartYear
 
-when isMainModule:
-  # this is testing non-exported function
   var
-    t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
-    t4L = getLocalTime(fromSeconds(876124714))
-  assert toSeconds(t4, initInterval(seconds=0)) == 0.0
-  assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
-  assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
-  assert toSeconds(t4L, initInterval(minutes=1)) == toSeconds(t4, initInterval(minutes=1))
-  assert toSeconds(t4L, initInterval(hours=1)) == toSeconds(t4, initInterval(hours=1))
-  assert toSeconds(t4L, initInterval(days=1)) == toSeconds(t4, initInterval(days=1))
-  assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
-  assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
-
-  # Further tests are in tests/stdlib/ttime.nim
-  # koch test c stdlib
+    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, y).Weekday
+    h = daySeconds div secondsInHour + 1
+    mi = (daySeconds mod secondsInHour) div secondsInMin
+    s = daySeconds mod secondsInMin
+  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.} =
+  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.
+  # 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
\ No newline at end of file