times.nim 93 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2017 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ##[
  10. This module contains routines and types for dealing with time using a proleptic Gregorian calendar.
  11. It's also available for the `JavaScript target <backends.html#the-javascript-target>`_.
  12. Although the types use nanosecond time resolution, the underlying resolution used by ``getTime()``
  13. depends on the platform and backend (JS is limited to millisecond precision).
  14. Examples:
  15. .. code-block:: nim
  16. import times, os
  17. let time = cpuTime()
  18. sleep(100) # replace this with something to be timed
  19. echo "Time taken: ",cpuTime() - time
  20. echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
  21. echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
  22. echo "cpuTime() float value: ", cpuTime()
  23. echo "An hour from now : ", now() + 1.hours
  24. echo "An hour from (UTC) now: ", getTime().utc + initDuration(hours = 1)
  25. Parsing and Formatting Dates
  26. ----------------------------
  27. The ``DateTime`` type can be parsed and formatted using the different
  28. ``parse`` and ``format`` procedures.
  29. .. code-block:: nim
  30. let dt = parse("2000-01-01", "yyyy-MM-dd")
  31. echo dt.format("yyyy-MM-dd")
  32. The different format patterns that are supported are documented below.
  33. ============= ================================================================================= ================================================
  34. Pattern Description Example
  35. ============= ================================================================================= ================================================
  36. ``d`` Numeric value representing the day of the month, | ``1/04/2012 -> 1``
  37. it will be either one or two digits long. | ``21/04/2012 -> 21``
  38. ``dd`` Same as above, but is always two digits. | ``1/04/2012 -> 01``
  39. | ``21/04/2012 -> 21``
  40. ``ddd`` Three letter string which indicates the day of the week. | ``Saturday -> Sat``
  41. | ``Monday -> Mon``
  42. ``dddd`` Full string for the day of the week. | ``Saturday -> Saturday``
  43. | ``Monday -> Monday``
  44. ``h`` The hours in one digit if possible. Ranging from 1-12. | ``5pm -> 5``
  45. | ``2am -> 2``
  46. ``hh`` The hours in two digits always. If the hour is one digit 0 is prepended. | ``5pm -> 05``
  47. | ``11am -> 11``
  48. ``H`` The hours in one digit if possible, ranging from 0-23. | ``5pm -> 17``
  49. | ``2am -> 2``
  50. ``HH`` The hours in two digits always. 0 is prepended if the hour is one digit. | ``5pm -> 17``
  51. | ``2am -> 02``
  52. ``m`` The minutes in 1 digit if possible. | ``5:30 -> 30``
  53. | ``2:01 -> 1``
  54. ``mm`` Same as above but always 2 digits, 0 is prepended if the minute is one digit. | ``5:30 -> 30``
  55. | ``2:01 -> 01``
  56. ``M`` The month in one digit if possible. | ``September -> 9``
  57. | ``December -> 12``
  58. ``MM`` The month in two digits always. 0 is prepended. | ``September -> 09``
  59. | ``December -> 12``
  60. ``MMM`` Abbreviated three-letter form of the month. | ``September -> Sep``
  61. | ``December -> Dec``
  62. ``MMMM`` Full month string, properly capitalized. | ``September -> September``
  63. ``s`` Seconds as one digit if possible. | ``00:00:06 -> 6``
  64. ``ss`` Same as above but always two digits. 0 is prepended. | ``00:00:06 -> 06``
  65. ``t`` ``A`` when time is in the AM. ``P`` when time is in the PM. | ``5pm -> P``
  66. | ``2am -> A``
  67. ``tt`` Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. | ``5pm -> PM``
  68. | ``2am -> AM``
  69. ``yy`` The last two digits of the year. When parsing, the current century is assumed. | ``2012 AD -> 12``
  70. ``yyyy`` The year, padded to atleast four digits. | ``2012 AD -> 2012``
  71. Is always positive, even when the year is BC. | ``24 AD -> 0024``
  72. When the year is more than four digits, '+' is prepended. | ``24 BC -> 00024``
  73. | ``12345 AD -> +12345``
  74. ``YYYY`` The year without any padding. | ``2012 AD -> 2012``
  75. Is always positive, even when the year is BC. | ``24 AD -> 24``
  76. | ``24 BC -> 24``
  77. | ``12345 AD -> 12345``
  78. ``uuuu`` The year, padded to atleast four digits. Will be negative when the year is BC. | ``2012 AD -> 2012``
  79. When the year is more than four digits, '+' is prepended unless the year is BC. | ``24 AD -> 0024``
  80. | ``24 BC -> -0023``
  81. | ``12345 AD -> +12345``
  82. ``UUUU`` The year without any padding. Will be negative when the year is BC. | ``2012 AD -> 2012``
  83. | ``24 AD -> 24``
  84. | ``24 BC -> -23``
  85. | ``12345 AD -> 12345``
  86. ``z`` Displays the timezone offset from UTC. | ``GMT+7 -> +7``
  87. | ``GMT-5 -> -5``
  88. ``zz`` Same as above but with leading 0. | ``GMT+7 -> +07``
  89. | ``GMT-5 -> -05``
  90. ``zzz`` Same as above but with ``:mm`` where *mm* represents minutes. | ``GMT+7 -> +07:00``
  91. | ``GMT-5 -> -05:00``
  92. ``zzzz`` Same as above but with ``:ss`` where *ss* represents seconds. | ``GMT+7 -> +07:00:00``
  93. | ``GMT-5 -> -05:00:00``
  94. ``g`` Era: AD or BC | ``300 AD -> AD``
  95. | ``300 BC -> BC``
  96. ``fff`` Milliseconds display | ``1000000 nanoseconds -> 1``
  97. ``ffffff`` Microseconds display | ``1000000 nanoseconds -> 1000``
  98. ``fffffffff`` Nanoseconds display | ``1000000 nanoseconds -> 1000000``
  99. ============= ================================================================================= ================================================
  100. Other strings can be inserted by putting them in ``''``. For example
  101. ``hh'->'mm`` will give ``01->56``. The following characters can be
  102. inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
  103. ``,``. A literal ``'`` can be specified with ``''``.
  104. However you don't need to necessarily separate format patterns, a
  105. unambiguous format string like ``yyyyMMddhhmmss`` is valid too (although
  106. only for years in the range 1..9999).
  107. ]##
  108. {.push debugger:off.} # the user does not want to trace a part
  109. # of the standard library!
  110. import
  111. strutils, parseutils, algorithm, math, options, strformat
  112. include "system/inclrtl"
  113. # This is really bad, but overflow checks are broken badly for
  114. # ints on the JS backend. See #6752.
  115. when defined(JS):
  116. {.push overflowChecks: off.}
  117. proc `*`(a, b: int64): int64 =
  118. system.`* `(a, b)
  119. proc `*`(a, b: int): int =
  120. system.`* `(a, b)
  121. proc `+`(a, b: int64): int64 =
  122. system.`+ `(a, b)
  123. proc `+`(a, b: int): int =
  124. system.`+ `(a, b)
  125. proc `-`(a, b: int64): int64 =
  126. system.`- `(a, b)
  127. proc `-`(a, b: int): int =
  128. system.`- `(a, b)
  129. proc inc(a: var int, b: int) =
  130. system.inc(a, b)
  131. proc inc(a: var int64, b: int) =
  132. system.inc(a, b)
  133. {.pop.}
  134. when defined(posix):
  135. import posix
  136. type CTime = posix.Time
  137. var CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
  138. proc gettimeofday(tp: var Timeval, unused: pointer = nil) {.
  139. importc: "gettimeofday", header: "<sys/time.h>".}
  140. when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
  141. var timezone {.importc, header: "<time.h>".}: int
  142. tzset()
  143. elif defined(windows):
  144. import winlean
  145. when defined(i386) and defined(gcc):
  146. type CTime {.importc: "time_t", header: "<time.h>".} = distinct int32
  147. else:
  148. # newest version of Visual C++ defines time_t to be of 64 bits
  149. type CTime {.importc: "time_t", header: "<time.h>".} = distinct int64
  150. # visual c's c runtime exposes these under a different name
  151. var timezone {.importc: "_timezone", header: "<time.h>".}: int
  152. type
  153. Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
  154. ## the month number in the range ``[1..12]``.
  155. mJan = (1, "January")
  156. mFeb = "February"
  157. mMar = "March"
  158. mApr = "April"
  159. mMay = "May"
  160. mJun = "June"
  161. mJul = "July"
  162. mAug = "August"
  163. mSep = "September"
  164. mOct = "October"
  165. mNov = "November"
  166. mDec = "December"
  167. WeekDay* = enum ## Represents a weekday.
  168. dMon = "Monday"
  169. dTue = "Tuesday"
  170. dWed = "Wednesday"
  171. dThu = "Thursday"
  172. dFri = "Friday"
  173. dSat = "Saturday"
  174. dSun = "Sunday"
  175. MonthdayRange* = range[1..31]
  176. HourRange* = range[0..23]
  177. MinuteRange* = range[0..59]
  178. SecondRange* = range[0..60]
  179. YeardayRange* = range[0..365]
  180. NanosecondRange* = range[0..999_999_999]
  181. Time* = object ## Represents a point in time.
  182. seconds: int64
  183. nanosecond: NanosecondRange
  184. DateTime* = object of RootObj ## Represents a time in different parts.
  185. ## Although this type can represent leap
  186. ## seconds, they are generally not supported
  187. ## in this module. They are not ignored,
  188. ## but the ``DateTime``'s returned by
  189. ## procedures in this module will never have
  190. ## a leap second.
  191. nanosecond*: NanosecondRange ## The number of nanoseconds after the second,
  192. ## in the range 0 to 999_999_999.
  193. second*: SecondRange ## The number of seconds after the minute,
  194. ## normally in the range 0 to 59, but can
  195. ## be up to 60 to allow for a leap second.
  196. minute*: MinuteRange ## The number of minutes after the hour,
  197. ## in the range 0 to 59.
  198. hour*: HourRange ## The number of hours past midnight,
  199. ## in the range 0 to 23.
  200. monthday*: MonthdayRange ## The day of the month, in the range 1 to 31.
  201. month*: Month ## The current month.
  202. year*: int ## The current year, using astronomical year numbering
  203. ## (meaning that before year 1 is year 0, then year -1 and so on).
  204. weekday*: WeekDay ## The current day of the week.
  205. yearday*: YeardayRange ## The number of days since January 1,
  206. ## in the range 0 to 365.
  207. isDst*: bool ## Determines whether DST is in effect.
  208. ## Always false for the JavaScript backend.
  209. timezone*: Timezone ## The timezone represented as an implementation of ``Timezone``.
  210. utcOffset*: int ## The offset in seconds west of UTC, including any offset due to DST.
  211. ## Note that the sign of this number is the opposite
  212. ## of the one in a formatted offset string like ``+01:00``
  213. ## (which would be parsed into the UTC offset ``-3600``).
  214. TimeInterval* = object ## Represents a non-fixed duration of time. Can be used to add and subtract
  215. ## non-fixed time units from a ``DateTime`` or ``Time``.
  216. ## ``TimeInterval`` doesn't represent a fixed duration of time,
  217. ## since the duration of some units depend on the context (e.g a year
  218. ## can be either 365 or 366 days long). The non-fixed time units are years,
  219. ## months and days.
  220. nanoseconds*: int ## The number of nanoseconds
  221. microseconds*: int ## The number of microseconds
  222. milliseconds*: int ## The number of milliseconds
  223. seconds*: int ## The number of seconds
  224. minutes*: int ## The number of minutes
  225. hours*: int ## The number of hours
  226. days*: int ## The number of days
  227. weeks*: int ## The number of weeks
  228. months*: int ## The number of months
  229. years*: int ## The number of years
  230. Duration* = object ## Represents a fixed duration of time.
  231. ## Uses the same time resolution as ``Time``.
  232. ## This type should be prefered over ``TimeInterval`` unless
  233. ## non-static time units is needed.
  234. seconds: int64
  235. nanosecond: NanosecondRange
  236. TimeUnit* = enum ## Different units of time.
  237. Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years
  238. FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
  239. ## These are the units that can be represented by a ``Duration``.
  240. Timezone* = ref object ## \
  241. ## Timezone interface for supporting ``DateTime``'s of arbritary
  242. ## timezones. The ``times`` module only supplies implementations for the
  243. ## systems local time and UTC.
  244. zonedTimeFromTimeImpl: proc (x: Time): ZonedTime
  245. {.tags: [], raises: [], benign.}
  246. zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime
  247. {.tags: [], raises: [], benign.}
  248. name: string
  249. ZonedTime* = object ## Represents a point in time with an associated
  250. ## UTC offset and DST flag. This type is only used for
  251. ## implementing timezones.
  252. time*: Time ## The point in time being represented.
  253. utcOffset*: int ## The offset in seconds west of UTC,
  254. ## including any offset due to DST.
  255. isDst*: bool ## Determines whether DST is in effect.
  256. DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
  257. TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts
  258. TimesMutableTypes = DateTime | Time | Duration | TimeInterval
  259. {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
  260. TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
  261. const
  262. secondsInMin = 60
  263. secondsInHour = 60*60
  264. secondsInDay = 60*60*24
  265. minutesInHour = 60
  266. rateDiff = 10000000'i64 # 100 nsecs
  267. # The number of hectonanoseconds between 1601/01/01 (windows epoch)
  268. # and 1970/01/01 (unix epoch).
  269. epochDiff = 116444736000000000'i64
  270. const unitWeights: array[FixedTimeUnit, int64] = [
  271. 1'i64,
  272. 1000,
  273. 1_000_000,
  274. 1e9.int64,
  275. secondsInMin * 1e9.int64,
  276. secondsInHour * 1e9.int64,
  277. secondsInDay * 1e9.int64,
  278. 7 * secondsInDay * 1e9.int64,
  279. ]
  280. proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} =
  281. ## Convert a quantity of some duration unit to another duration unit.
  282. runnableExamples:
  283. doAssert convert(Days, Hours, 2) == 48
  284. doAssert convert(Days, Weeks, 13) == 1 # Truncated
  285. doAssert convert(Seconds, Milliseconds, -1) == -1000
  286. if unitFrom < unitTo:
  287. (quantity div (unitWeights[unitTo] div unitWeights[unitFrom])).T
  288. else:
  289. ((unitWeights[unitFrom] div unitWeights[unitTo]) * quantity).T
  290. proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
  291. ## Normalize a (seconds, nanoseconds) pair and return it as either
  292. ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a
  293. ## positive nanosecond part in the range ``NanosecondRange``.
  294. result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds)
  295. var nanosecond = nanoseconds mod convert(Seconds, Nanoseconds, 1)
  296. if nanosecond < 0:
  297. nanosecond += convert(Seconds, Nanoseconds, 1)
  298. result.seconds -= 1
  299. result.nanosecond = nanosecond.int
  300. # Forward declarations
  301. proc utcTzInfo(time: Time): ZonedTime {.tags: [], raises: [], benign .}
  302. proc localZonedTimeFromTime(time: Time): ZonedTime {.tags: [], raises: [], benign .}
  303. proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
  304. proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
  305. {.tags: [], raises: [], benign noSideEffect.}
  306. proc initDuration*(nanoseconds, microseconds, milliseconds,
  307. seconds, minutes, hours, days, weeks: int64 = 0): Duration
  308. {.tags: [], raises: [], benign noSideEffect.}
  309. proc nanosecond*(time: Time): NanosecondRange =
  310. ## Get the fractional part of a ``Time`` as the number
  311. ## of nanoseconds of the second.
  312. time.nanosecond
  313. proc weeks*(dur: Duration): int64 {.inline.} =
  314. ## Number of whole weeks represented by the duration.
  315. convert(Seconds, Weeks, dur.seconds)
  316. proc days*(dur: Duration): int64 {.inline.} =
  317. ## Number of whole days represented by the duration.
  318. convert(Seconds, Days, dur.seconds)
  319. proc minutes*(dur: Duration): int64 {.inline.} =
  320. ## Number of whole minutes represented by the duration.
  321. convert(Seconds, Minutes, dur.seconds)
  322. proc hours*(dur: Duration): int64 {.inline.} =
  323. ## Number of whole hours represented by the duration.
  324. convert(Seconds, Hours, dur.seconds)
  325. proc seconds*(dur: Duration): int64 {.inline.} =
  326. ## Number of whole seconds represented by the duration.
  327. dur.seconds
  328. proc milliseconds*(dur: Duration): int {.inline.} =
  329. ## Number of whole milliseconds represented by the **fractional**
  330. ## part of the duration.
  331. runnableExamples:
  332. let dur = initDuration(seconds = 1, milliseconds = 1)
  333. doAssert dur.milliseconds == 1
  334. convert(Nanoseconds, Milliseconds, dur.nanosecond)
  335. proc microseconds*(dur: Duration): int {.inline.} =
  336. ## Number of whole microseconds represented by the **fractional**
  337. ## part of the duration.
  338. runnableExamples:
  339. let dur = initDuration(seconds = 1, microseconds = 1)
  340. doAssert dur.microseconds == 1
  341. convert(Nanoseconds, Microseconds, dur.nanosecond)
  342. proc nanoseconds*(dur: Duration): int {.inline.} =
  343. ## Number of whole nanoseconds represented by the **fractional**
  344. ## part of the duration.
  345. runnableExamples:
  346. let dur = initDuration(seconds = 1, nanoseconds = 1)
  347. doAssert dur.nanoseconds == 1
  348. dur.nanosecond
  349. proc fractional*(dur: Duration): Duration {.inline.} =
  350. ## The fractional part of duration, as a duration.
  351. runnableExamples:
  352. let dur = initDuration(seconds = 1, nanoseconds = 5)
  353. doAssert dur.fractional == initDuration(nanoseconds = 5)
  354. initDuration(nanoseconds = dur.nanosecond)
  355. proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} =
  356. ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``.
  357. runnableExamples:
  358. doAssert $fromUnix(0).utc == "1970-01-01T00:00:00Z"
  359. initTime(unix, 0)
  360. proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
  361. ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``).
  362. runnableExamples:
  363. doAssert fromUnix(0).toUnix() == 0
  364. t.seconds
  365. proc fromWinTime*(win: int64): Time =
  366. ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
  367. ## to a ``Time``.
  368. const hnsecsPerSec = convert(Seconds, Nanoseconds, 1) div 100
  369. let nanos = floorMod(win, hnsecsPerSec) * 100
  370. let seconds = floorDiv(win - epochDiff, hnsecsPerSec)
  371. result = initTime(seconds, nanos)
  372. proc toWinTime*(t: Time): int64 =
  373. ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
  374. result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
  375. proc isLeapYear*(year: int): bool =
  376. ## Returns true if ``year`` is a leap year.
  377. year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
  378. proc getDaysInMonth*(month: Month, year: int): int =
  379. ## Get the number of days in a ``month`` of a ``year``.
  380. # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
  381. case month
  382. of mFeb: result = if isLeapYear(year): 29 else: 28
  383. of mApr, mJun, mSep, mNov: result = 30
  384. else: result = 31
  385. proc getDaysInYear*(year: int): int =
  386. ## Get the number of days in a ``year``
  387. result = 365 + (if isLeapYear(year): 1 else: 0)
  388. proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
  389. assert monthday <= getDaysInMonth(month, year),
  390. $year & "-" & intToStr(ord(month), 2) & "-" & $monthday & " is not a valid date"
  391. proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
  392. ## Get the epoch day from a year/month/day date.
  393. ## The epoch day is the number of days since 1970/01/01 (it might be negative).
  394. assertValidDate monthday, month, year
  395. # Based on http://howardhinnant.github.io/date_algorithms.html
  396. var (y, m, d) = (year, ord(month), monthday.int)
  397. if m <= 2:
  398. y.dec
  399. let era = (if y >= 0: y else: y-399) div 400
  400. let yoe = y - era * 400
  401. let doy = (153 * (m + (if m > 2: -3 else: 9)) + 2) div 5 + d-1
  402. let doe = yoe * 365 + yoe div 4 - yoe div 100 + doy
  403. return era * 146097 + doe - 719468
  404. proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month, year: int] =
  405. ## Get the year/month/day date from a epoch day.
  406. ## The epoch day is the number of days since 1970/01/01 (it might be negative).
  407. # Based on http://howardhinnant.github.io/date_algorithms.html
  408. var z = epochday
  409. z.inc 719468
  410. let era = (if z >= 0: z else: z - 146096) div 146097
  411. let doe = z - era * 146097
  412. let yoe = (doe - doe div 1460 + doe div 36524 - doe div 146096) div 365
  413. let y = yoe + era * 400;
  414. let doy = doe - (365 * yoe + yoe div 4 - yoe div 100)
  415. let mp = (5 * doy + 2) div 153
  416. let d = doy - (153 * mp + 2) div 5 + 1
  417. let m = mp + (if mp < 10: 3 else: -9)
  418. return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
  419. proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
  420. ## Returns the day of the year.
  421. ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``.
  422. assertValidDate monthday, month, year
  423. const daysUntilMonth: array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
  424. const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
  425. if isLeapYear(year):
  426. result = daysUntilMonthLeap[month] + monthday - 1
  427. else:
  428. result = daysUntilMonth[month] + monthday - 1
  429. proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
  430. ## Returns the day of the week enum from day, month and year.
  431. ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``.
  432. assertValidDate monthday, month, year
  433. # 1970-01-01 is a Thursday, we adjust to the previous Monday
  434. let days = toEpochday(monthday, month, year) - 3
  435. let weeks = floorDiv(days, 7)
  436. let wd = days - weeks * 7
  437. # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
  438. # so we must correct for the WeekDay type.
  439. result = if wd == 0: dSun else: WeekDay(wd - 1)
  440. {. pragma: operator, rtl, noSideEffect, benign .}
  441. template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
  442. normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond)
  443. template addImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
  444. normalize[T](a.seconds + b.seconds, a.nanosecond + b.nanosecond)
  445. template ltImpl(a: Duration|Time, b: Duration|Time): bool =
  446. a.seconds < b.seconds or (
  447. a.seconds == b.seconds and a.nanosecond < b.nanosecond)
  448. template lqImpl(a: Duration|Time, b: Duration|Time): bool =
  449. a.seconds < b.seconds or (
  450. a.seconds == b.seconds and a.nanosecond <= b.nanosecond)
  451. template eqImpl(a: Duration|Time, b: Duration|Time): bool =
  452. a.seconds == b.seconds and a.nanosecond == b.nanosecond
  453. proc initDuration*(nanoseconds, microseconds, milliseconds,
  454. seconds, minutes, hours, days, weeks: int64 = 0): Duration =
  455. runnableExamples:
  456. let dur = initDuration(seconds = 1, milliseconds = 1)
  457. doAssert dur.milliseconds == 1
  458. doAssert dur.seconds == 1
  459. let seconds = convert(Weeks, Seconds, weeks) +
  460. convert(Days, Seconds, days) +
  461. convert(Minutes, Seconds, minutes) +
  462. convert(Hours, Seconds, hours) +
  463. convert(Seconds, Seconds, seconds) +
  464. convert(Milliseconds, Seconds, milliseconds) +
  465. convert(Microseconds, Seconds, microseconds) +
  466. convert(Nanoseconds, Seconds, nanoseconds)
  467. let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
  468. convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
  469. nanoseconds mod 1_000_000_000).int
  470. # Nanoseconds might be negative so we must normalize.
  471. result = normalize[Duration](seconds, nanoseconds)
  472. const DurationZero* = initDuration() ## \
  473. ## Zero value for durations. Useful for comparisons.
  474. ##
  475. ## .. code-block:: nim
  476. ##
  477. ## doAssert initDuration(seconds = 1) > DurationZero
  478. ## doAssert initDuration(seconds = 0) == DurationZero
  479. proc toParts*(dur: Duration): DurationParts =
  480. ## Converts a duration into an array consisting of fixed time units.
  481. ##
  482. ## Each value in the array gives information about a specific unit of
  483. ## time, for example ``result[Days]`` gives a count of days.
  484. ##
  485. ## This procedure is useful for converting ``Duration`` values to strings.
  486. runnableExamples:
  487. var dp = toParts(initDuration(weeks=2, days=1))
  488. doAssert dp[Days] == 1
  489. doAssert dp[Weeks] == 2
  490. dp = toParts(initDuration(days = -1))
  491. doAssert dp[Days] == -1
  492. var remS = dur.seconds
  493. var remNs = dur.nanosecond.int
  494. # Ensure the same sign for seconds and nanoseconds
  495. if remS < 0 and remNs != 0:
  496. remNs -= convert(Seconds, Nanoseconds, 1)
  497. remS.inc 1
  498. for unit in countdown(Weeks, Seconds):
  499. let quantity = convert(Seconds, unit, remS)
  500. remS = remS mod convert(unit, Seconds, 1)
  501. result[unit] = quantity
  502. for unit in countdown(Milliseconds, Nanoseconds):
  503. let quantity = convert(Nanoseconds, unit, remNs)
  504. remNs = remNs mod convert(unit, Nanoseconds, 1)
  505. result[unit] = quantity
  506. proc stringifyUnit(value: int | int64, unit: TimeUnit): string =
  507. ## Stringify time unit with it's name, lowercased
  508. let strUnit = $unit
  509. result = ""
  510. result.add($value)
  511. result.add(" ")
  512. if abs(value) != 1:
  513. result.add(strUnit.toLowerAscii())
  514. else:
  515. result.add(strUnit[0..^2].toLowerAscii())
  516. proc humanizeParts(parts: seq[string]): string =
  517. ## Make date string parts human-readable
  518. result = ""
  519. if parts.len == 0:
  520. result.add "0 nanoseconds"
  521. elif parts.len == 1:
  522. result = parts[0]
  523. elif parts.len == 2:
  524. result = parts[0] & " and " & parts[1]
  525. else:
  526. for i in 0..high(parts)-1:
  527. result.add parts[i] & ", "
  528. result.add "and " & parts[high(parts)]
  529. proc `$`*(dur: Duration): string =
  530. ## Human friendly string representation of ``Duration``.
  531. runnableExamples:
  532. doAssert $initDuration(seconds = 2) == "2 seconds"
  533. doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
  534. doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
  535. doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
  536. var parts = newSeq[string]()
  537. var numParts = toParts(dur)
  538. for unit in countdown(Weeks, Nanoseconds):
  539. let quantity = numParts[unit]
  540. if quantity != 0.int64:
  541. parts.add(stringifyUnit(quantity, unit))
  542. result = humanizeParts(parts)
  543. proc `+`*(a, b: Duration): Duration {.operator.} =
  544. ## Add two durations together.
  545. runnableExamples:
  546. doAssert initDuration(seconds = 1) + initDuration(days = 1) ==
  547. initDuration(seconds = 1, days = 1)
  548. addImpl[Duration](a, b)
  549. proc `-`*(a, b: Duration): Duration {.operator.} =
  550. ## Subtract a duration from another.
  551. runnableExamples:
  552. doAssert initDuration(seconds = 1, days = 1) - initDuration(seconds = 1) ==
  553. initDuration(days = 1)
  554. subImpl[Duration](a, b)
  555. proc `-`*(a: Duration): Duration {.operator.} =
  556. ## Reverse a duration.
  557. runnableExamples:
  558. doAssert -initDuration(seconds = 1) == initDuration(seconds = -1)
  559. normalize[Duration](-a.seconds, -a.nanosecond)
  560. proc `<`*(a, b: Duration): bool {.operator.} =
  561. ## Note that a duration can be negative,
  562. ## so even if ``a < b`` is true ``a`` might
  563. ## represent a larger absolute duration.
  564. ## Use ``abs(a) < abs(b)`` to compare the absolute
  565. ## duration.
  566. runnableExamples:
  567. doAssert initDuration(seconds = 1) < initDuration(seconds = 2)
  568. doAssert initDuration(seconds = -2) < initDuration(seconds = 1)
  569. ltImpl(a, b)
  570. proc `<=`*(a, b: Duration): bool {.operator.} =
  571. lqImpl(a, b)
  572. proc `==`*(a, b: Duration): bool {.operator.} =
  573. eqImpl(a, b)
  574. proc `*`*(a: int64, b: Duration): Duration {.operator} =
  575. ## Multiply a duration by some scalar.
  576. runnableExamples:
  577. doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
  578. normalize[Duration](a * b.seconds, a * b.nanosecond)
  579. proc `*`*(a: Duration, b: int64): Duration {.operator} =
  580. ## Multiply a duration by some scalar.
  581. runnableExamples:
  582. doAssert initDuration(seconds = 1) * 5 == initDuration(seconds = 5)
  583. b * a
  584. proc `div`*(a: Duration, b: int64): Duration {.operator} =
  585. ## Integer division for durations.
  586. runnableExamples:
  587. doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500)
  588. doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1)
  589. let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b)
  590. normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b)
  591. proc initTime*(unix: int64, nanosecond: NanosecondRange): Time =
  592. ## Create a ``Time`` from a unix timestamp and a nanosecond part.
  593. result.seconds = unix
  594. result.nanosecond = nanosecond
  595. proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
  596. ## Computes the duration between two points in time.
  597. subImpl[Duration](a, b)
  598. proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} =
  599. ## Add a duration of time to a ``Time``.
  600. runnableExamples:
  601. doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1)
  602. addImpl[Time](a, b)
  603. proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} =
  604. ## Subtracts a duration of time from a ``Time``.
  605. runnableExamples:
  606. doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1)
  607. subImpl[Time](a, b)
  608. proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
  609. ## Returns true iff ``a < b``, that is iff a happened before b.
  610. ltImpl(a, b)
  611. proc `<=` * (a, b: Time): bool {.operator, extern: "ntLeTime".} =
  612. ## Returns true iff ``a <= b``.
  613. lqImpl(a, b)
  614. proc `==`*(a, b: Time): bool {.operator, extern: "ntEqTime".} =
  615. ## Returns true if ``a == b``, that is if both times represent the same point in time.
  616. eqImpl(a, b)
  617. proc high*(typ: typedesc[Time]): Time =
  618. initTime(high(int64), high(NanosecondRange))
  619. proc low*(typ: typedesc[Time]): Time =
  620. initTime(low(int64), 0)
  621. proc high*(typ: typedesc[Duration]): Duration =
  622. ## Get the longest representable duration.
  623. initDuration(seconds = high(int64), nanoseconds = high(NanosecondRange))
  624. proc low*(typ: typedesc[Duration]): Duration =
  625. ## Get the longest representable duration of negative direction.
  626. initDuration(seconds = low(int64))
  627. proc abs*(a: Duration): Duration =
  628. runnableExamples:
  629. doAssert initDuration(milliseconds = -1500).abs ==
  630. initDuration(milliseconds = 1500)
  631. initDuration(seconds = abs(a.seconds), nanoseconds = -a.nanosecond)
  632. proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
  633. ## Converts a broken-down time structure to
  634. ## calendar time representation.
  635. let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
  636. var seconds = epochDay * secondsInDay
  637. seconds.inc dt.hour * secondsInHour
  638. seconds.inc dt.minute * 60
  639. seconds.inc dt.second
  640. seconds.inc dt.utcOffset
  641. result = initTime(seconds, dt.nanosecond)
  642. proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
  643. ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone.
  644. let adjTime = zt.time - initDuration(seconds = zt.utcOffset)
  645. let s = adjTime.seconds
  646. let epochday = floorDiv(s, secondsInDay)
  647. var rem = s - epochday * secondsInDay
  648. let hour = rem div secondsInHour
  649. rem = rem - hour * secondsInHour
  650. let minute = rem div secondsInMin
  651. rem = rem - minute * secondsInMin
  652. let second = rem
  653. let (d, m, y) = fromEpochday(epochday)
  654. DateTime(
  655. year: y,
  656. month: m,
  657. monthday: d,
  658. hour: hour,
  659. minute: minute,
  660. second: second,
  661. nanosecond: zt.time.nanosecond,
  662. weekday: getDayOfWeek(d, m, y),
  663. yearday: getDayOfYear(d, m, y),
  664. isDst: zt.isDst,
  665. timezone: zone,
  666. utcOffset: zt.utcOffset
  667. )
  668. proc newTimezone*(
  669. name: string,
  670. zonedTimeFromTimeImpl: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.},
  671. zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
  672. ): Timezone =
  673. ## Create a new ``Timezone``.
  674. ##
  675. ## ``zonedTimeFromTimeImpl`` and ``zonedTimeFromAdjTimeImpl`` is used
  676. ## as the underlying implementations for ``zonedTimeFromTime`` and
  677. ## ``zonedTimeFromAdjTime``.
  678. ##
  679. ## If possible, the name parameter should match the name used in the
  680. ## tz database. If the timezone doesn't exist in the tz database, or if the
  681. ## timezone name is unknown, then any string that describes the timezone
  682. ## unambiguously can be used. Note that the timezones name is used for
  683. ## checking equality!
  684. runnableExamples:
  685. proc utcTzInfo(time: Time): ZonedTime =
  686. ZonedTime(utcOffset: 0, isDst: false, time: time)
  687. let utc = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
  688. Timezone(
  689. name: name,
  690. zonedTimeFromTimeImpl: zonedTimeFromTimeImpl,
  691. zonedTimeFromAdjTimeImpl: zonedTimeFromAdjTimeImpl
  692. )
  693. proc name*(zone: Timezone): string =
  694. ## The name of the timezone.
  695. ##
  696. ## If possible, the name will be the name used in the tz database.
  697. ## If the timezone doesn't exist in the tz database, or if the timezone
  698. ## name is unknown, then any string that describes the timezone
  699. ## unambiguously might be used. For example, the string "LOCAL" is used
  700. ## for the systems local timezone.
  701. ##
  702. ## See also: https://en.wikipedia.org/wiki/Tz_database
  703. zone.name
  704. proc zonedTimeFromTime*(zone: Timezone, time: Time): ZonedTime =
  705. ## Returns the ``ZonedTime`` for some point in time.
  706. zone.zonedTimeFromTimeImpl(time)
  707. proc zonedTimeFromAdjTime*(zone: TimeZone, adjTime: Time): ZonedTime =
  708. ## Returns the ``ZonedTime`` for some local time.
  709. ##
  710. ## Note that the ``Time`` argument does not represent a point in time, it
  711. ## represent a local time! E.g if ``adjTime`` is ``fromUnix(0)``, it should be
  712. ## interpreted as 1970-01-01T00:00:00 in the ``zone`` timezone, not in UTC.
  713. zone.zonedTimeFromAdjTimeImpl(adjTime)
  714. proc `$`*(zone: Timezone): string =
  715. ## Returns the name of the timezone.
  716. zone.name
  717. proc `==`*(zone1, zone2: Timezone): bool =
  718. ## Two ``Timezone``'s are considered equal if their name is equal.
  719. runnableExamples:
  720. doAssert local() == local()
  721. doAssert local() != utc()
  722. zone1.name == zone2.name
  723. proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
  724. ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone.
  725. result = initDateTime(zone.zonedTimeFromTime(time), zone)
  726. proc inZone*(dt: DateTime, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
  727. ## Returns a ``DateTime`` representing the same point in time as ``dt`` but
  728. ## using ``zone`` as the timezone.
  729. dt.toTime.inZone(zone)
  730. proc toAdjTime(dt: DateTime): Time =
  731. let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
  732. var seconds = epochDay * secondsInDay
  733. seconds.inc dt.hour * secondsInHour
  734. seconds.inc dt.minute * secondsInMin
  735. seconds.inc dt.second
  736. result = initTime(seconds, dt.nanosecond)
  737. when defined(JS):
  738. type JsDate = object
  739. proc newDate(year, month, date, hours, minutes, seconds, milliseconds: int): JsDate {.tags: [], raises: [], importc: "new Date".}
  740. proc newDate(): JsDate {.importc: "new Date".}
  741. proc newDate(value: float): JsDate {.importc: "new Date".}
  742. proc getTimezoneOffset(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  743. proc getDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  744. proc getFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  745. proc getHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  746. proc getMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  747. proc getMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  748. proc getMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  749. proc getSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  750. proc getTime(js: JsDate): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
  751. proc getDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  752. proc getUTCDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  753. proc getUTCFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  754. proc getUTCHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  755. proc getUTCMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  756. proc getUTCMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  757. proc getUTCMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  758. proc getUTCSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  759. proc getUTCDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  760. proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
  761. proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
  762. proc localZonedTimeFromTime(time: Time): ZonedTime =
  763. let jsDate = newDate(time.seconds.float * 1000)
  764. let offset = jsDate.getTimezoneOffset() * secondsInMin
  765. result.time = time
  766. result.utcOffset = offset
  767. result.isDst = false
  768. proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
  769. let utcDate = newDate(adjTime.seconds.float * 1000)
  770. let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
  771. utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
  772. # This is as dumb as it looks - JS doesn't support years in the range 0-99 in the constructor
  773. # because they are assumed to be 19xx...
  774. # Because JS doesn't support timezone history, it doesn't really matter in practice.
  775. if utcDate.getUTCFullYear() in 0 .. 99:
  776. localDate.setFullYear(utcDate.getUTCFullYear())
  777. result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
  778. result.time = adjTime + initDuration(seconds = result.utcOffset)
  779. result.isDst = false
  780. else:
  781. when defined(freebsd) or defined(netbsd) or defined(openbsd) or
  782. defined(macosx):
  783. type
  784. StructTm {.importc: "struct tm".} = object
  785. second {.importc: "tm_sec".},
  786. minute {.importc: "tm_min".},
  787. hour {.importc: "tm_hour".},
  788. monthday {.importc: "tm_mday".},
  789. month {.importc: "tm_mon".},
  790. year {.importc: "tm_year".},
  791. weekday {.importc: "tm_wday".},
  792. yearday {.importc: "tm_yday".},
  793. isdst {.importc: "tm_isdst".}: cint
  794. gmtoff {.importc: "tm_gmtoff".}: clong
  795. else:
  796. type
  797. StructTm {.importc: "struct tm".} = object
  798. second {.importc: "tm_sec".},
  799. minute {.importc: "tm_min".},
  800. hour {.importc: "tm_hour".},
  801. monthday {.importc: "tm_mday".},
  802. month {.importc: "tm_mon".},
  803. year {.importc: "tm_year".},
  804. weekday {.importc: "tm_wday".},
  805. yearday {.importc: "tm_yday".},
  806. isdst {.importc: "tm_isdst".}: cint
  807. when defined(linux) and defined(amd64) or defined(haiku):
  808. gmtoff {.importc: "tm_gmtoff".}: clong
  809. zone {.importc: "tm_zone".}: cstring
  810. type
  811. StructTmPtr = ptr StructTm
  812. proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
  813. proc toAdjUnix(tm: StructTm): int64 =
  814. let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
  815. result = epochDay * secondsInDay
  816. result.inc tm.hour * secondsInHour
  817. result.inc tm.minute * 60
  818. result.inc tm.second
  819. proc getLocalOffsetAndDst(unix: int64): tuple[offset: int, dst: bool] =
  820. # Windows can't handle unix < 0, so we fall back to unix = 0.
  821. # FIXME: This should be improved by falling back to the WinAPI instead.
  822. when defined(windows):
  823. if unix < 0:
  824. var a = 0.CTime
  825. let tmPtr = localtime(addr(a))
  826. if not tmPtr.isNil:
  827. let tm = tmPtr[]
  828. return ((0 - tm.toAdjUnix).int, false)
  829. return (0, false)
  830. # In case of a 32-bit time_t, we fallback to the closest available
  831. # timezone information.
  832. var a = clamp(unix, low(CTime), high(CTime)).CTime
  833. let tmPtr = localtime(addr(a))
  834. if not tmPtr.isNil:
  835. let tm = tmPtr[]
  836. return ((a.int64 - tm.toAdjUnix).int, tm.isdst > 0)
  837. return (0, false)
  838. proc localZonedTimeFromTime(time: Time): ZonedTime =
  839. let (offset, dst) = getLocalOffsetAndDst(time.seconds)
  840. result.time = time
  841. result.utcOffset = offset
  842. result.isDst = dst
  843. proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
  844. var adjUnix = adjTime.seconds
  845. let past = adjUnix - secondsInDay
  846. let (pastOffset, _) = getLocalOffsetAndDst(past)
  847. let future = adjUnix + secondsInDay
  848. let (futureOffset, _) = getLocalOffsetAndDst(future)
  849. var utcOffset: int
  850. if pastOffset == futureOffset:
  851. utcOffset = pastOffset.int
  852. else:
  853. if pastOffset > futureOffset:
  854. adjUnix -= secondsInHour
  855. adjUnix += pastOffset
  856. utcOffset = getLocalOffsetAndDst(adjUnix).offset
  857. # This extra roundtrip is needed to normalize any impossible datetimes
  858. # as a result of offset changes (normally due to dst)
  859. let utcUnix = adjTime.seconds + utcOffset
  860. let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix)
  861. result.time = initTime(utcUnix, adjTime.nanosecond)
  862. result.utcOffset = finalOffset
  863. result.isDst = dst
  864. proc utcTzInfo(time: Time): ZonedTime =
  865. ZonedTime(utcOffset: 0, isDst: false, time: time)
  866. var utcInstance {.threadvar.}: Timezone
  867. var localInstance {.threadvar.}: Timezone
  868. proc utc*(): TimeZone =
  869. ## Get the ``Timezone`` implementation for the UTC timezone.
  870. runnableExamples:
  871. doAssert now().utc.timezone == utc()
  872. doAssert utc().name == "Etc/UTC"
  873. if utcInstance.isNil:
  874. utcInstance = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
  875. result = utcInstance
  876. proc local*(): TimeZone =
  877. ## Get the ``Timezone`` implementation for the local timezone.
  878. runnableExamples:
  879. doAssert now().timezone == local()
  880. doAssert local().name == "LOCAL"
  881. if localInstance.isNil:
  882. localInstance = newTimezone("LOCAL", localZonedTimeFromTime,
  883. localZonedTimeFromAdjTime)
  884. result = localInstance
  885. proc utc*(dt: DateTime): DateTime =
  886. ## Shorthand for ``dt.inZone(utc())``.
  887. dt.inZone(utc())
  888. proc local*(dt: DateTime): DateTime =
  889. ## Shorthand for ``dt.inZone(local())``.
  890. dt.inZone(local())
  891. proc utc*(t: Time): DateTime =
  892. ## Shorthand for ``t.inZone(utc())``.
  893. t.inZone(utc())
  894. proc local*(t: Time): DateTime =
  895. ## Shorthand for ``t.inZone(local())``.
  896. t.inZone(local())
  897. proc getTime*(): Time {.tags: [TimeEffect], benign.} =
  898. ## Gets the current time as a ``Time`` with nanosecond resolution.
  899. when defined(JS):
  900. let millis = newDate().getTime()
  901. let seconds = convert(Milliseconds, Seconds, millis)
  902. let nanos = convert(Milliseconds, Nanoseconds,
  903. millis mod convert(Seconds, Milliseconds, 1).int)
  904. result = initTime(seconds, nanos)
  905. # I'm not entirely certain if freebsd needs to use `gettimeofday`.
  906. elif defined(macosx) or defined(freebsd):
  907. var a: Timeval
  908. gettimeofday(a)
  909. result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int))
  910. elif defined(posix):
  911. var ts: Timespec
  912. discard clock_gettime(CLOCK_REALTIME, ts)
  913. result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
  914. elif defined(windows):
  915. var f: FILETIME
  916. getSystemTimeAsFileTime(f)
  917. result = fromWinTime(rdFileTime(f))
  918. proc now*(): DateTime {.tags: [TimeEffect], benign.} =
  919. ## Get the current time as a ``DateTime`` in the local timezone.
  920. ##
  921. ## Shorthand for ``getTime().local``.
  922. getTime().local
  923. proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
  924. seconds, minutes, hours,
  925. days, weeks, months, years: int = 0): TimeInterval =
  926. ## Creates a new ``TimeInterval``.
  927. ##
  928. ## You can also use the convenience procedures called ``milliseconds``,
  929. ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
  930. ##
  931. runnableExamples:
  932. let day = initTimeInterval(hours=24)
  933. let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
  934. doAssert $(dt + day) == "2000-01-02T12:00:00Z"
  935. result.nanoseconds = nanoseconds
  936. result.microseconds = microseconds
  937. result.milliseconds = milliseconds
  938. result.seconds = seconds
  939. result.minutes = minutes
  940. result.hours = hours
  941. result.days = days
  942. result.weeks = weeks
  943. result.months = months
  944. result.years = years
  945. proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
  946. ## Adds two ``TimeInterval`` objects together.
  947. result.nanoseconds = ti1.nanoseconds + ti2.nanoseconds
  948. result.microseconds = ti1.microseconds + ti2.microseconds
  949. result.milliseconds = ti1.milliseconds + ti2.milliseconds
  950. result.seconds = ti1.seconds + ti2.seconds
  951. result.minutes = ti1.minutes + ti2.minutes
  952. result.hours = ti1.hours + ti2.hours
  953. result.days = ti1.days + ti2.days
  954. result.weeks = ti1.weeks + ti2.weeks
  955. result.months = ti1.months + ti2.months
  956. result.years = ti1.years + ti2.years
  957. proc `-`*(ti: TimeInterval): TimeInterval =
  958. ## Reverses a time interval
  959. runnableExamples:
  960. let day = -initTimeInterval(hours=24)
  961. doAssert day.hours == -24
  962. result = TimeInterval(
  963. nanoseconds: -ti.nanoseconds,
  964. microseconds: -ti.microseconds,
  965. milliseconds: -ti.milliseconds,
  966. seconds: -ti.seconds,
  967. minutes: -ti.minutes,
  968. hours: -ti.hours,
  969. days: -ti.days,
  970. weeks: -ti.weeks,
  971. months: -ti.months,
  972. years: -ti.years
  973. )
  974. proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
  975. ## Subtracts TimeInterval ``ti1`` from ``ti2``.
  976. ##
  977. ## Time components are subtracted one-by-one, see output:
  978. runnableExamples:
  979. let ti1 = initTimeInterval(hours=24)
  980. let ti2 = initTimeInterval(hours=4)
  981. doAssert (ti1 - ti2) == initTimeInterval(hours=20)
  982. result = ti1 + (-ti2)
  983. proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
  984. ## Gets the current date as a string of the format ``YYYY-MM-DD``.
  985. var ti = now()
  986. result = $ti.year & '-' & intToStr(ord(ti.month), 2) &
  987. '-' & intToStr(ti.monthday, 2)
  988. proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
  989. ## Gets the current clock time as a string of the format ``HH:MM:SS``.
  990. var ti = now()
  991. result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
  992. ':' & intToStr(ti.second, 2)
  993. proc toParts* (ti: TimeInterval): TimeIntervalParts =
  994. ## Converts a `TimeInterval` into an array consisting of its time units,
  995. ## starting with nanoseconds and ending with years
  996. ##
  997. ## This procedure is useful for converting ``TimeInterval`` values to strings.
  998. ## E.g. then you need to implement custom interval printing
  999. runnableExamples:
  1000. var tp = toParts(initTimeInterval(years=1, nanoseconds=123))
  1001. doAssert tp[Years] == 1
  1002. doAssert tp[Nanoseconds] == 123
  1003. var index = 0
  1004. for name, value in fieldPairs(ti):
  1005. result[index.TimeUnit()] = value
  1006. index += 1
  1007. proc `$`*(ti: TimeInterval): string =
  1008. ## Get string representation of `TimeInterval`
  1009. runnableExamples:
  1010. doAssert $initTimeInterval(years=1, nanoseconds=123) == "1 year and 123 nanoseconds"
  1011. doAssert $initTimeInterval() == "0 nanoseconds"
  1012. var parts: seq[string] = @[]
  1013. var tiParts = toParts(ti)
  1014. for unit in countdown(Years, Nanoseconds):
  1015. if tiParts[unit] != 0:
  1016. parts.add(stringifyUnit(tiParts[unit], unit))
  1017. result = humanizeParts(parts)
  1018. proc nanoseconds*(nanos: int): TimeInterval {.inline.} =
  1019. ## TimeInterval of ``nanos`` nanoseconds.
  1020. initTimeInterval(nanoseconds = nanos)
  1021. proc microseconds*(micros: int): TimeInterval {.inline.} =
  1022. ## TimeInterval of ``micros`` microseconds.
  1023. initTimeInterval(microseconds = micros)
  1024. proc milliseconds*(ms: int): TimeInterval {.inline.} =
  1025. ## TimeInterval of ``ms`` milliseconds.
  1026. initTimeInterval(milliseconds = ms)
  1027. proc seconds*(s: int): TimeInterval {.inline.} =
  1028. ## TimeInterval of ``s`` seconds.
  1029. ##
  1030. ## ``echo getTime() + 5.second``
  1031. initTimeInterval(seconds = s)
  1032. proc minutes*(m: int): TimeInterval {.inline.} =
  1033. ## TimeInterval of ``m`` minutes.
  1034. ##
  1035. ## ``echo getTime() + 5.minutes``
  1036. initTimeInterval(minutes = m)
  1037. proc hours*(h: int): TimeInterval {.inline.} =
  1038. ## TimeInterval of ``h`` hours.
  1039. ##
  1040. ## ``echo getTime() + 2.hours``
  1041. initTimeInterval(hours = h)
  1042. proc days*(d: int): TimeInterval {.inline.} =
  1043. ## TimeInterval of ``d`` days.
  1044. ##
  1045. ## ``echo getTime() + 2.days``
  1046. initTimeInterval(days = d)
  1047. proc weeks*(w: int): TimeInterval {.inline.} =
  1048. ## TimeInterval of ``w`` weeks.
  1049. ##
  1050. ## ``echo getTime() + 2.weeks``
  1051. initTimeInterval(weeks = w)
  1052. proc months*(m: int): TimeInterval {.inline.} =
  1053. ## TimeInterval of ``m`` months.
  1054. ##
  1055. ## ``echo getTime() + 2.months``
  1056. initTimeInterval(months = m)
  1057. proc years*(y: int): TimeInterval {.inline.} =
  1058. ## TimeInterval of ``y`` years.
  1059. ##
  1060. ## ``echo getTime() + 2.years``
  1061. initTimeInterval(years = y)
  1062. proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDur: Duration] =
  1063. ## Evaluates how many nanoseconds the interval is worth
  1064. ## in the context of ``dt``.
  1065. ## The result in split into an adjusted diff and an absolute diff.
  1066. var months = interval.years * 12 + interval.months
  1067. var curYear = dt.year
  1068. var curMonth = dt.month
  1069. # Subtracting
  1070. if months < 0:
  1071. for mth in countDown(-1 * months, 1):
  1072. if curMonth == mJan:
  1073. curMonth = mDec
  1074. curYear.dec
  1075. else:
  1076. curMonth.dec()
  1077. let days = getDaysInMonth(curMonth, curYear)
  1078. result.adjDur = result.adjDur - initDuration(days = days)
  1079. # Adding
  1080. else:
  1081. for mth in 1 .. months:
  1082. let days = getDaysInMonth(curMonth, curYear)
  1083. result.adjDur = result.adjDur + initDuration(days = days)
  1084. if curMonth == mDec:
  1085. curMonth = mJan
  1086. curYear.inc
  1087. else:
  1088. curMonth.inc()
  1089. result.adjDur = result.adjDur + initDuration(
  1090. days = interval.days,
  1091. weeks = interval.weeks)
  1092. result.absDur = initDuration(
  1093. nanoseconds = interval.nanoseconds,
  1094. microseconds = interval.microseconds,
  1095. milliseconds = interval.milliseconds,
  1096. seconds = interval.seconds,
  1097. minutes = interval.minutes,
  1098. hours = interval.hours)
  1099. proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
  1100. hour: HourRange, minute: MinuteRange, second: SecondRange,
  1101. nanosecond: NanosecondRange, zone: Timezone = local()): DateTime =
  1102. ## Create a new ``DateTime`` in the specified timezone.
  1103. runnableExamples:
  1104. let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc())
  1105. doAssert $dt1 == "2017-03-30T00:00:00Z"
  1106. assertValidDate monthday, month, year
  1107. let dt = DateTime(
  1108. monthday: monthday,
  1109. year: year,
  1110. month: month,
  1111. hour: hour,
  1112. minute: minute,
  1113. second: second,
  1114. nanosecond: nanosecond
  1115. )
  1116. result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone)
  1117. proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
  1118. hour: HourRange, minute: MinuteRange, second: SecondRange,
  1119. zone: Timezone = local()): DateTime =
  1120. ## Create a new ``DateTime`` in the specified timezone.
  1121. runnableExamples:
  1122. let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1123. doAssert $dt1 == "2017-03-30T00:00:00Z"
  1124. initDateTime(monthday, month, year, hour, minute, second, 0, zone)
  1125. proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
  1126. ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
  1127. ## in the order of their size, i.e first the ``years`` component, then the ``months``
  1128. ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
  1129. ##
  1130. ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
  1131. ## month doesn't have enough days it, the month will be incremented and the monthday will be
  1132. ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
  1133. ## which will overflow and result in `1 December`.
  1134. ##
  1135. runnableExamples:
  1136. let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1137. doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z"
  1138. # This is correct and happens due to monthday overflow.
  1139. doAssert $(dt - 1.months) == "2017-03-02T00:00:00Z"
  1140. let (adjDur, absDur) = evaluateInterval(dt, interval)
  1141. if adjDur != DurationZero:
  1142. var zt = dt.timezone.zonedTimeFromAdjTime(dt.toAdjTime + adjDur)
  1143. if absDur != DurationZero:
  1144. zt = dt.timezone.zonedTimeFromTime(zt.time + absDur)
  1145. result = initDateTime(zt, dt.timezone)
  1146. else:
  1147. result = initDateTime(zt, dt.timezone)
  1148. else:
  1149. var zt = dt.timezone.zonedTimeFromTime(dt.toTime + absDur)
  1150. result = initDateTime(zt, dt.timezone)
  1151. proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
  1152. ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
  1153. ## in the order of their size, i.e first the ``years`` component, then the ``months``
  1154. ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
  1155. runnableExamples:
  1156. let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1157. doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z"
  1158. dt + (-interval)
  1159. proc `+`*(dt: DateTime, dur: Duration): DateTime =
  1160. runnableExamples:
  1161. let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1162. let dur = initDuration(hours = 5)
  1163. doAssert $(dt + dur) == "2017-03-30T05:00:00Z"
  1164. (dt.toTime + dur).inZone(dt.timezone)
  1165. proc `-`*(dt: DateTime, dur: Duration): DateTime =
  1166. runnableExamples:
  1167. let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1168. let dur = initDuration(days = 5)
  1169. doAssert $(dt - dur) == "2017-03-25T00:00:00Z"
  1170. (dt.toTime - dur).inZone(dt.timezone)
  1171. proc `-`*(dt1, dt2: DateTime): Duration =
  1172. ## Compute the duration between ``dt1`` and ``dt2``.
  1173. runnableExamples:
  1174. let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
  1175. let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc())
  1176. doAssert dt1 - dt2 == initDuration(days = 5)
  1177. dt1.toTime - dt2.toTime
  1178. proc `<`*(a, b: DateTime): bool =
  1179. ## Returns true iff ``a < b``, that is iff a happened before b.
  1180. return a.toTime < b.toTime
  1181. proc `<=` * (a, b: DateTime): bool =
  1182. ## Returns true iff ``a <= b``.
  1183. return a.toTime <= b.toTime
  1184. proc `==`*(a, b: DateTime): bool =
  1185. ## Returns true if ``a == b``, that is if both dates represent the same point in time.
  1186. return a.toTime == b.toTime
  1187. proc isStaticInterval(interval: TimeInterval): bool =
  1188. interval.years == 0 and interval.months == 0 and
  1189. interval.days == 0 and interval.weeks == 0
  1190. proc evaluateStaticInterval(interval: TimeInterval): Duration =
  1191. assert interval.isStaticInterval
  1192. initDuration(nanoseconds = interval.nanoseconds,
  1193. microseconds = interval.microseconds,
  1194. milliseconds = interval.milliseconds,
  1195. seconds = interval.seconds,
  1196. minutes = interval.minutes,
  1197. hours = interval.hours)
  1198. proc between*(startDt, endDt: DateTime): TimeInterval =
  1199. ## Evaluate difference between two dates in ``TimeInterval`` format, so, it
  1200. ## will be relative.
  1201. ##
  1202. ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in
  1203. ## different ``TimeZone's``.
  1204. ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC.
  1205. runnableExamples:
  1206. var a = initDateTime(year = 2018, month = Month(3), monthday = 25,
  1207. hour = 0, minute = 59, second = 59, nanosecond = 1,
  1208. zone = utc()).local
  1209. var b = initDateTime(year = 2018, month = Month(3), monthday = 25,
  1210. hour = 1, minute = 1, second = 1, nanosecond = 0,
  1211. zone = utc()).local
  1212. doAssert between(a, b) == initTimeInterval(
  1213. nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1)
  1214. a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc())
  1215. b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz")
  1216. doAssert between(a, b) == initTimeInterval(hours=1, days=2)
  1217. ## Though, here correct answer should be 1 day 25 hours (cause this day in
  1218. ## this tz is actually 26 hours). That's why operating different TZ is
  1219. ## discouraged
  1220. var startDt = startDt.utc()
  1221. var endDt = endDt.utc()
  1222. if endDt == startDt:
  1223. return initTimeInterval()
  1224. elif endDt < startDt:
  1225. return -between(endDt, startDt)
  1226. var coeffs: array[FixedTimeUnit, int64] = unitWeights
  1227. var timeParts: array[FixedTimeUnit, int]
  1228. for unit in Nanoseconds..Weeks:
  1229. timeParts[unit] = 0
  1230. for unit in Seconds..Days:
  1231. coeffs[unit] = coeffs[unit] div unitWeights[Seconds]
  1232. var startTimepart = initTime(
  1233. nanosecond = startDt.nanosecond,
  1234. unix = startDt.hour * coeffs[Hours] + startDt.minute * coeffs[Minutes] +
  1235. startDt.second
  1236. )
  1237. var endTimepart = initTime(
  1238. nanosecond = endDt.nanosecond,
  1239. unix = endDt.hour * coeffs[Hours] + endDt.minute * coeffs[Minutes] +
  1240. endDt.second
  1241. )
  1242. # We wand timeParts for Seconds..Hours be positive, so we'll borrow one day
  1243. if endTimepart < startTimepart:
  1244. timeParts[Days] = -1
  1245. let diffTime = endTimepart - startTimepart
  1246. timeParts[Seconds] = diffTime.seconds.int()
  1247. #Nanoseconds - preliminary count
  1248. timeParts[Nanoseconds] = diffTime.nanoseconds
  1249. for unit in countdown(Milliseconds, Microseconds):
  1250. timeParts[unit] += timeParts[Nanoseconds] div coeffs[unit].int()
  1251. timeParts[Nanoseconds] -= timeParts[unit] * coeffs[unit].int()
  1252. #Counting Seconds .. Hours - final, Days - preliminary
  1253. for unit in countdown(Days, Minutes):
  1254. timeParts[unit] += timeParts[Seconds] div coeffs[unit].int()
  1255. # Here is accounted the borrowed day
  1256. timeParts[Seconds] -= timeParts[unit] * coeffs[unit].int()
  1257. # Set Nanoseconds .. Hours in result
  1258. result.nanoseconds = timeParts[Nanoseconds]
  1259. result.microseconds = timeParts[Microseconds]
  1260. result.milliseconds = timeParts[Milliseconds]
  1261. result.seconds = timeParts[Seconds]
  1262. result.minutes = timeParts[Minutes]
  1263. result.hours = timeParts[Hours]
  1264. #Days
  1265. if endDt.monthday.int + timeParts[Days] < startDt.monthday.int():
  1266. if endDt.month > 1.Month:
  1267. endDt.month -= 1.Month
  1268. else:
  1269. endDt.month = 12.Month
  1270. endDt.year -= 1
  1271. timeParts[Days] += endDt.monthday.int() + getDaysInMonth(
  1272. endDt.month, endDt.year) - startDt.monthday.int()
  1273. else:
  1274. timeParts[Days] += endDt.monthday.int() -
  1275. startDt.monthday.int()
  1276. result.days = timeParts[Days]
  1277. #Months
  1278. if endDt.month < startDt.month:
  1279. result.months = endDt.month.int() + 12 - startDt.month.int()
  1280. endDt.year -= 1
  1281. else:
  1282. result.months = endDt.month.int() -
  1283. startDt.month.int()
  1284. # Years
  1285. result.years = endDt.year - startDt.year
  1286. proc `+`*(time: Time, interval: TimeInterval): Time =
  1287. ## Adds `interval` to `time`.
  1288. ## If `interval` contains any years, months, weeks or days the operation
  1289. ## is performed in the local timezone.
  1290. runnableExamples:
  1291. let tm = fromUnix(0)
  1292. doAssert tm + 5.seconds == fromUnix(5)
  1293. if interval.isStaticInterval:
  1294. time + evaluateStaticInterval(interval)
  1295. else:
  1296. toTime(time.local + interval)
  1297. proc `-`*(time: Time, interval: TimeInterval): Time =
  1298. ## Subtracts `interval` from Time `time`.
  1299. ## If `interval` contains any years, months, weeks or days the operation
  1300. ## is performed in the local timezone.
  1301. runnableExamples:
  1302. let tm = fromUnix(5)
  1303. doAssert tm - 5.seconds == fromUnix(0)
  1304. if interval.isStaticInterval:
  1305. time - evaluateStaticInterval(interval)
  1306. else:
  1307. toTime(time.local - interval)
  1308. proc `+=`*[T, U: TimesMutableTypes](a: var T, b: U) =
  1309. ## Modify ``a`` in place by adding ``b``.
  1310. runnableExamples:
  1311. var tm = fromUnix(0)
  1312. tm += initDuration(seconds = 1)
  1313. doAssert tm == fromUnix(1)
  1314. a = a + b
  1315. proc `-=`*[T, U: TimesMutableTypes](a: var T, b: U) =
  1316. ## Modify ``a`` in place by subtracting ``b``.
  1317. runnableExamples:
  1318. var tm = fromUnix(5)
  1319. tm -= initDuration(seconds = 5)
  1320. doAssert tm == fromUnix(0)
  1321. a = a - b
  1322. proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) =
  1323. # Mutable type is often multiplied by number
  1324. runnableExamples:
  1325. var dur = initDuration(seconds = 1)
  1326. dur *= 5
  1327. doAssert dur == initDuration(seconds = 5)
  1328. a = a * b
  1329. #
  1330. # Parse & format implementation
  1331. #
  1332. type
  1333. AmPm = enum
  1334. apUnknown, apAm, apPm
  1335. Era = enum
  1336. eraUnknown, eraAd, eraBc
  1337. ParsedTime = object
  1338. amPm: AmPm
  1339. era: Era
  1340. year: Option[int]
  1341. month: Option[int]
  1342. monthday: Option[int]
  1343. utcOffset: Option[int]
  1344. # '0' as default for these work fine
  1345. # so no need for `Option`.
  1346. hour: int
  1347. minute: int
  1348. second: int
  1349. nanosecond: int
  1350. FormatTokenKind = enum
  1351. tkPattern, tkLiteral
  1352. FormatPattern {.pure.} = enum
  1353. d, dd, ddd, dddd
  1354. h, hh, H, HH
  1355. m, mm, M, MM, MMM, MMMM
  1356. s, ss
  1357. fff, ffffff, fffffffff
  1358. t, tt
  1359. y, yy, yyy, yyyy, yyyyy
  1360. YYYY
  1361. uuuu
  1362. UUUU
  1363. z, zz, zzz, zzzz
  1364. g
  1365. # This is a special value used to mark literal format values.
  1366. # See the doc comment for ``TimeFormat.patterns``.
  1367. Lit
  1368. TimeFormat* = object ## Represents a format for parsing and printing
  1369. ## time types.
  1370. patterns: seq[byte] ## \
  1371. ## Contains the patterns encoded as bytes.
  1372. ## Literal values are encoded in a special way.
  1373. ## They start with ``Lit.byte``, then the length of the literal, then the
  1374. ## raw char values of the literal. For example, the literal `foo` would
  1375. ## be encoded as ``@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]``.
  1376. formatStr: string
  1377. const FormatLiterals = { ' ', '-', '/', ':', '(', ')', '[', ']', ',' }
  1378. proc `$`*(f: TimeFormat): string =
  1379. ## Returns the format string that was used to construct ``f``.
  1380. runnableExamples:
  1381. let f = initTimeFormat("yyyy-MM-dd")
  1382. doAssert $f == "yyyy-MM-dd"
  1383. f.formatStr
  1384. proc raiseParseException(f: TimeFormat, input: string, msg: string) =
  1385. raise newException(ValueError,
  1386. &"Failed to parse '{input}' with format '{f}'. {msg}")
  1387. iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] =
  1388. var i = 0
  1389. var currToken = ""
  1390. template yieldCurrToken() =
  1391. if currToken.len != 0:
  1392. yield (tkPattern, currToken)
  1393. currToken = ""
  1394. while i < f.len:
  1395. case f[i]
  1396. of '\'':
  1397. yieldCurrToken()
  1398. if i.succ < f.len and f[i.succ] == '\'':
  1399. yield (tkLiteral, "'")
  1400. i.inc 2
  1401. else:
  1402. var token = ""
  1403. inc(i) # Skip '
  1404. while i < f.len and f[i] != '\'':
  1405. token.add f[i]
  1406. i.inc
  1407. if i > f.high:
  1408. raise newException(ValueError,
  1409. &"Unclosed ' in time format string. " &
  1410. "For a literal ', use ''.")
  1411. i.inc
  1412. yield (tkLiteral, token)
  1413. of FormatLiterals:
  1414. yieldCurrToken()
  1415. yield (tkLiteral, $f[i])
  1416. i.inc
  1417. else:
  1418. # Check if the letter being added matches previous accumulated buffer.
  1419. if currToken.len == 0 or currToken[0] == f[i]:
  1420. currToken.add(f[i])
  1421. i.inc
  1422. else:
  1423. yield (tkPattern, currToken)
  1424. currToken = $f[i]
  1425. i.inc
  1426. yieldCurrToken()
  1427. proc stringToPattern(str: string): FormatPattern =
  1428. case str
  1429. of "d": result = d
  1430. of "dd": result = dd
  1431. of "ddd": result = ddd
  1432. of "dddd": result = dddd
  1433. of "h": result = h
  1434. of "hh": result = hh
  1435. of "H": result = H
  1436. of "HH": result = HH
  1437. of "m": result = m
  1438. of "mm": result = mm
  1439. of "M": result = M
  1440. of "MM": result = MM
  1441. of "MMM": result = MMM
  1442. of "MMMM": result = MMMM
  1443. of "s": result = s
  1444. of "ss": result = ss
  1445. of "fff": result = fff
  1446. of "ffffff": result = ffffff
  1447. of "fffffffff": result = fffffffff
  1448. of "t": result = t
  1449. of "tt": result = tt
  1450. of "y": result = y
  1451. of "yy": result = yy
  1452. of "yyy": result = yyy
  1453. of "yyyy": result = yyyy
  1454. of "yyyyy": result = yyyyy
  1455. of "YYYY": result = YYYY
  1456. of "uuuu": result = uuuu
  1457. of "UUUU": result = UUUU
  1458. of "z": result = z
  1459. of "zz": result = zz
  1460. of "zzz": result = zzz
  1461. of "zzzz": result = zzzz
  1462. of "g": result = g
  1463. else: raise newException(ValueError, &"'{str}' is not a valid pattern")
  1464. proc initTimeFormat*(format: string): TimeFormat =
  1465. ## Construct a new time format for parsing & formatting time types.
  1466. ##
  1467. ## See `Parsing and formatting dates`_ for documentation of the
  1468. ## ``format`` argument.
  1469. runnableExamples:
  1470. let f = initTimeFormat("yyyy-MM-dd")
  1471. doAssert "2000-01-01" == "2000-01-01".parse(f).format(f)
  1472. result.formatStr = format
  1473. result.patterns = @[]
  1474. for kind, token in format.tokens:
  1475. case kind
  1476. of tkLiteral:
  1477. case token
  1478. else:
  1479. result.patterns.add(FormatPattern.Lit.byte)
  1480. if token.len > 255:
  1481. raise newException(ValueError,
  1482. "Format literal is to long:" & token)
  1483. result.patterns.add(token.len.byte)
  1484. for c in token:
  1485. result.patterns.add(c.byte)
  1486. of tkPattern:
  1487. result.patterns.add(stringToPattern(token).byte)
  1488. proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) =
  1489. template yearOfEra(dt: DateTime): int =
  1490. if dt.year <= 0: abs(dt.year) + 1 else: dt.year
  1491. case pattern
  1492. of d:
  1493. result.add $dt.monthday
  1494. of dd:
  1495. result.add dt.monthday.intToStr(2)
  1496. of ddd:
  1497. result.add ($dt.weekday)[0..2]
  1498. of dddd:
  1499. result.add $dt.weekday
  1500. of h:
  1501. result.add(
  1502. if dt.hour == 0: "12"
  1503. elif dt.hour > 12: $(dt.hour - 12)
  1504. else: $dt.hour
  1505. )
  1506. of hh:
  1507. result.add(
  1508. if dt.hour == 0: "12"
  1509. elif dt.hour > 12: (dt.hour - 12).intToStr(2)
  1510. else: dt.hour.intToStr(2)
  1511. )
  1512. of H:
  1513. result.add $dt.hour
  1514. of HH:
  1515. result.add dt.hour.intToStr(2)
  1516. of m:
  1517. result.add $dt.minute
  1518. of mm:
  1519. result.add dt.minute.intToStr(2)
  1520. of M:
  1521. result.add $ord(dt.month)
  1522. of MM:
  1523. result.add ord(dt.month).intToStr(2)
  1524. of MMM:
  1525. result.add ($dt.month)[0..2]
  1526. of MMMM:
  1527. result.add $dt.month
  1528. of s:
  1529. result.add $dt.second
  1530. of ss:
  1531. result.add dt.second.intToStr(2)
  1532. of fff:
  1533. result.add(intToStr(convert(Nanoseconds, Milliseconds, dt.nanosecond), 3))
  1534. of ffffff:
  1535. result.add(intToStr(convert(Nanoseconds, Microseconds, dt.nanosecond), 6))
  1536. of fffffffff:
  1537. result.add(intToStr(dt.nanosecond, 9))
  1538. of t:
  1539. result.add if dt.hour >= 12: "P" else: "A"
  1540. of tt:
  1541. result.add if dt.hour >= 12: "PM" else: "AM"
  1542. of y: # Deprecated
  1543. result.add $(dt.yearOfEra mod 10)
  1544. of yy:
  1545. result.add (dt.yearOfEra mod 100).intToStr(2)
  1546. of yyy: # Deprecated
  1547. result.add (dt.yearOfEra mod 1000).intToStr(3)
  1548. of yyyy:
  1549. let year = dt.yearOfEra
  1550. if year < 10000:
  1551. result.add year.intToStr(4)
  1552. else:
  1553. result.add '+' & $year
  1554. of yyyyy: # Deprecated
  1555. result.add (dt.yearOfEra mod 100_000).intToStr(5)
  1556. of YYYY:
  1557. if dt.year < 1:
  1558. result.add $(abs(dt.year) + 1)
  1559. else:
  1560. result.add $dt.year
  1561. of uuuu:
  1562. let year = dt.year
  1563. if year < 10000 or year < 0:
  1564. result.add year.intToStr(4)
  1565. else:
  1566. result.add '+' & $year
  1567. of UUUU:
  1568. result.add $dt.year
  1569. of z, zz, zzz, zzzz:
  1570. if dt.timezone.name == "Etc/UTC":
  1571. result.add 'Z'
  1572. else:
  1573. result.add if -dt.utcOffset >= 0: '+' else: '-'
  1574. let absOffset = abs(dt.utcOffset)
  1575. case pattern:
  1576. of z:
  1577. result.add $(absOffset div 3600)
  1578. of zz:
  1579. result.add (absOffset div 3600).intToStr(2)
  1580. of zzz:
  1581. let h = (absOffset div 3600).intToStr(2)
  1582. let m = ((absOffset div 60) mod 60).intToStr(2)
  1583. result.add h & ":" & m
  1584. of zzzz:
  1585. let absOffset = abs(dt.utcOffset)
  1586. let h = (absOffset div 3600).intToStr(2)
  1587. let m = ((absOffset div 60) mod 60).intToStr(2)
  1588. let s = (absOffset mod 60).intToStr(2)
  1589. result.add h & ":" & m & ":" & s
  1590. else: assert false
  1591. of g:
  1592. result.add if dt.year < 1: "BC" else: "AD"
  1593. of Lit: assert false # Can't happen
  1594. proc parsePattern(input: string, pattern: FormatPattern, i: var int,
  1595. parsed: var ParsedTime): bool =
  1596. template takeInt(allowedWidth: Slice[int]): int =
  1597. var sv: int
  1598. let max = i + allowedWidth.b - 1
  1599. var pd =
  1600. if max > input.high:
  1601. parseInt(input, sv, i)
  1602. else:
  1603. parseInt(input[i..max], sv)
  1604. if pd notin allowedWidth:
  1605. return false
  1606. i.inc pd
  1607. sv
  1608. template contains[T](t: typedesc[T], i: int): bool =
  1609. i in low(t)..high(t)
  1610. result = true
  1611. case pattern
  1612. of d:
  1613. parsed.monthday = some(takeInt(1..2))
  1614. result = parsed.monthday.get() in MonthdayRange
  1615. of dd:
  1616. parsed.monthday = some(takeInt(2..2))
  1617. result = parsed.monthday.get() in MonthdayRange
  1618. of ddd:
  1619. result = input.substr(i, i+2).toLowerAscii() in [
  1620. "sun", "mon", "tue", "wed", "thu", "fri", "sat"]
  1621. if result:
  1622. i.inc 3
  1623. of dddd:
  1624. if input.substr(i, i+5).cmpIgnoreCase("sunday") == 0:
  1625. i.inc 6
  1626. elif input.substr(i, i+5).cmpIgnoreCase("monday") == 0:
  1627. i.inc 6
  1628. elif input.substr(i, i+6).cmpIgnoreCase("tuesday") == 0:
  1629. i.inc 7
  1630. elif input.substr(i, i+8).cmpIgnoreCase("wednesday") == 0:
  1631. i.inc 9
  1632. elif input.substr(i, i+7).cmpIgnoreCase("thursday") == 0:
  1633. i.inc 8
  1634. elif input.substr(i, i+5).cmpIgnoreCase("friday") == 0:
  1635. i.inc 6
  1636. elif input.substr(i, i+7).cmpIgnoreCase("saturday") == 0:
  1637. i.inc 8
  1638. else:
  1639. result = false
  1640. of h, H:
  1641. parsed.hour = takeInt(1..2)
  1642. result = parsed.hour in HourRange
  1643. of hh, HH:
  1644. parsed.hour = takeInt(2..2)
  1645. result = parsed.hour in HourRange
  1646. of m:
  1647. parsed.minute = takeInt(1..2)
  1648. result = parsed.hour in MinuteRange
  1649. of mm:
  1650. parsed.minute = takeInt(2..2)
  1651. result = parsed.hour in MinuteRange
  1652. of M:
  1653. let month = takeInt(1..2)
  1654. result = month in 1..12
  1655. parsed.month = some(month)
  1656. of MM:
  1657. let month = takeInt(2..2)
  1658. result = month in 1..12
  1659. parsed.month = some(month)
  1660. of MMM:
  1661. case input.substr(i, i+2).toLowerAscii()
  1662. of "jan": parsed.month = some(1)
  1663. of "feb": parsed.month = some(2)
  1664. of "mar": parsed.month = some(3)
  1665. of "apr": parsed.month = some(4)
  1666. of "may": parsed.month = some(5)
  1667. of "jun": parsed.month = some(6)
  1668. of "jul": parsed.month = some(7)
  1669. of "aug": parsed.month = some(8)
  1670. of "sep": parsed.month = some(9)
  1671. of "oct": parsed.month = some(10)
  1672. of "nov": parsed.month = some(11)
  1673. of "dec": parsed.month = some(12)
  1674. else:
  1675. result = false
  1676. if result:
  1677. i.inc 3
  1678. of MMMM:
  1679. if input.substr(i, i+6).cmpIgnoreCase("january") == 0:
  1680. parsed.month = some(1)
  1681. i.inc 7
  1682. elif input.substr(i, i+7).cmpIgnoreCase("february") == 0:
  1683. parsed.month = some(2)
  1684. i.inc 8
  1685. elif input.substr(i, i+4).cmpIgnoreCase("march") == 0:
  1686. parsed.month = some(3)
  1687. i.inc 5
  1688. elif input.substr(i, i+4).cmpIgnoreCase("april") == 0:
  1689. parsed.month = some(4)
  1690. i.inc 5
  1691. elif input.substr(i, i+2).cmpIgnoreCase("may") == 0:
  1692. parsed.month = some(5)
  1693. i.inc 3
  1694. elif input.substr(i, i+3).cmpIgnoreCase("june") == 0:
  1695. parsed.month = some(6)
  1696. i.inc 4
  1697. elif input.substr(i, i+3).cmpIgnoreCase("july") == 0:
  1698. parsed.month = some(7)
  1699. i.inc 4
  1700. elif input.substr(i, i+5).cmpIgnoreCase("august") == 0:
  1701. parsed.month = some(8)
  1702. i.inc 6
  1703. elif input.substr(i, i+8).cmpIgnoreCase("september") == 0:
  1704. parsed.month = some(9)
  1705. i.inc 9
  1706. elif input.substr(i, i+6).cmpIgnoreCase("october") == 0:
  1707. parsed.month = some(10)
  1708. i.inc 7
  1709. elif input.substr(i, i+7).cmpIgnoreCase("november") == 0:
  1710. parsed.month = some(11)
  1711. i.inc 8
  1712. elif input.substr(i, i+7).cmpIgnoreCase("december") == 0:
  1713. parsed.month = some(12)
  1714. i.inc 8
  1715. else:
  1716. result = false
  1717. of s:
  1718. parsed.second = takeInt(1..2)
  1719. of ss:
  1720. parsed.second = takeInt(2..2)
  1721. of fff, ffffff, fffffffff:
  1722. let len = ($pattern).len
  1723. let v = takeInt(len..len)
  1724. parsed.nanosecond = v * 10^(9 - len)
  1725. result = parsed.nanosecond in NanosecondRange
  1726. of t:
  1727. case input[i]:
  1728. of 'P':
  1729. parsed.amPm = apPm
  1730. of 'A':
  1731. parsed.amPm = apAm
  1732. else:
  1733. result = false
  1734. i.inc 1
  1735. of tt:
  1736. if input.substr(i, i+1).cmpIgnoreCase("AM") == 0:
  1737. parsed.amPm = apAM
  1738. i.inc 2
  1739. elif input.substr(i, i+1).cmpIgnoreCase("PM") == 0:
  1740. parsed.amPm = apPm
  1741. i.inc 2
  1742. else:
  1743. result = false
  1744. of yy:
  1745. # Assumes current century
  1746. var year = takeInt(2..2)
  1747. var thisCen = now().year div 100
  1748. parsed.year = some(thisCen*100 + year)
  1749. result = year > 0
  1750. of yyyy:
  1751. let year =
  1752. if input[i] in { '+', '-' }:
  1753. takeInt(4..high(int))
  1754. else:
  1755. takeInt(4..4)
  1756. result = year > 0
  1757. parsed.year = some(year)
  1758. of YYYY:
  1759. let year = takeInt(1..high(int))
  1760. parsed.year = some(year)
  1761. result = year > 0
  1762. of uuuu:
  1763. let year =
  1764. if input[i] in { '+', '-' }:
  1765. takeInt(4..high(int))
  1766. else:
  1767. takeInt(4..4)
  1768. parsed.year = some(year)
  1769. of UUUU:
  1770. parsed.year = some(takeInt(1..high(int)))
  1771. of z, zz, zzz, zzzz:
  1772. case input[i]
  1773. of '+', '-':
  1774. let sign = if input[i] == '-': 1 else: -1
  1775. i.inc
  1776. var offset = 0
  1777. case pattern
  1778. of z:
  1779. offset = takeInt(1..2) * -3600
  1780. of zz:
  1781. offset = takeInt(2..2) * -3600
  1782. of zzz:
  1783. offset.inc takeInt(2..2) * 3600
  1784. if input[i] != ':':
  1785. return false
  1786. i.inc
  1787. offset.inc takeInt(2..2) * 60
  1788. of zzzz:
  1789. offset.inc takeInt(2..2) * 3600
  1790. if input[i] != ':':
  1791. return false
  1792. i.inc
  1793. offset.inc takeInt(2..2) * 60
  1794. if input[i] != ':':
  1795. return false
  1796. i.inc
  1797. offset.inc takeInt(2..2)
  1798. else: assert false
  1799. parsed.utcOffset = some(offset * sign)
  1800. of 'Z':
  1801. parsed.utcOffset = some(0)
  1802. i.inc
  1803. else:
  1804. result = false
  1805. of g:
  1806. if input.substr(i, i+1).cmpIgnoreCase("BC") == 0:
  1807. parsed.era = eraBc
  1808. i.inc 2
  1809. elif input.substr(i, i+1).cmpIgnoreCase("AD") == 0:
  1810. parsed.era = eraAd
  1811. i.inc 2
  1812. else:
  1813. result = false
  1814. of y, yyy, yyyyy:
  1815. raise newException(ValueError,
  1816. &"The pattern '{pattern}' is only valid for formatting")
  1817. of Lit: assert false # Can't happen
  1818. proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
  1819. input: string): DateTime =
  1820. var month = mJan
  1821. var year: int
  1822. var monthday: int
  1823. # `now()` is an expensive call, so we avoid it when possible
  1824. (year, month, monthday) =
  1825. if p.year.isNone or p.month.isNone or p.monthday.isNone:
  1826. let n = now()
  1827. (p.year.get(n.year),
  1828. p.month.get(n.month.int).Month,
  1829. p.monthday.get(n.monthday))
  1830. else:
  1831. (p.year.get(), p.month.get().Month, p.monthday.get())
  1832. year =
  1833. case p.era
  1834. of eraUnknown:
  1835. year
  1836. of eraBc:
  1837. if year < 1:
  1838. raiseParseException(f, input,
  1839. "Expected year to be positive " &
  1840. "(use 'UUUU' or 'uuuu' for negative years).")
  1841. -year + 1
  1842. of eraAd:
  1843. if year < 1:
  1844. raiseParseException(f, input,
  1845. "Expected year to be positive " &
  1846. "(use 'UUUU' or 'uuuu' for negative years).")
  1847. year
  1848. let hour =
  1849. case p.amPm
  1850. of apUnknown:
  1851. p.hour
  1852. of apAm:
  1853. if p.hour notin 1..12:
  1854. raiseParseException(f, input,
  1855. "AM/PM time must be in the interval 1..12")
  1856. if p.hour == 12: 0 else: p.hour
  1857. of apPm:
  1858. if p.hour notin 1..12:
  1859. raiseParseException(f, input,
  1860. "AM/PM time must be in the interval 1..12")
  1861. if p.hour == 12: p.hour else: p.hour + 12
  1862. let minute = p.minute
  1863. let second = p.second
  1864. let nanosecond = p.nanosecond
  1865. if monthday > getDaysInMonth(month, year):
  1866. raiseParseException(f, input,
  1867. $year & "-" & ord(month).intToStr(2) &
  1868. "-" & $monthday & " is not a valid date")
  1869. result = DateTime(
  1870. year: year, month: month, monthday: monthday,
  1871. hour: hour, minute: minute, second: second, nanosecond: nanosecond
  1872. )
  1873. if p.utcOffset.isNone:
  1874. # No timezone parsed - assume timezone is `zone`
  1875. result = initDateTime(zone.zonedTimeFromAdjTime(result.toAdjTime), zone)
  1876. else:
  1877. # Otherwise convert to `zone`
  1878. result.utcOffset = p.utcOffset.get()
  1879. result = result.toTime.inZone(zone)
  1880. proc format*(dt: DateTime, f: TimeFormat): string {.raises: [].} =
  1881. ## Format ``dt`` using the format specified by ``f``.
  1882. runnableExamples:
  1883. let f = initTimeFormat("yyyy-MM-dd")
  1884. let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
  1885. doAssert "2000-01-01" == dt.format(f)
  1886. var idx = 0
  1887. while idx <= f.patterns.high:
  1888. case f.patterns[idx].FormatPattern
  1889. of Lit:
  1890. idx.inc
  1891. let len = f.patterns[idx]
  1892. for i in 1'u8..len:
  1893. idx.inc
  1894. result.add f.patterns[idx].char
  1895. idx.inc
  1896. else:
  1897. formatPattern(dt, f.patterns[idx].FormatPattern, result = result)
  1898. idx.inc
  1899. proc format*(dt: DateTime, f: string): string =
  1900. ## Shorthand for constructing a ``TimeFormat`` and using it to format ``dt``.
  1901. ##
  1902. ## See `Parsing and formatting dates`_ for documentation of the
  1903. ## ``format`` argument.
  1904. runnableExamples:
  1905. let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
  1906. doAssert "2000-01-01" == format(dt, "yyyy-MM-dd")
  1907. let dtFormat = initTimeFormat(f)
  1908. result = dt.format(dtFormat)
  1909. proc format*(dt: DateTime, f: static[string]): string {.raises: [].} =
  1910. ## Overload that validates ``format`` at compile time.
  1911. const f2 = initTimeFormat(f)
  1912. result = dt.format(f2)
  1913. proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} =
  1914. ## Shorthand for constructing a ``TimeFormat`` and using it to format
  1915. ## ``time``. Will use the timezone specified by ``zone``.
  1916. ##
  1917. ## See `Parsing and formatting dates`_ for documentation of the
  1918. ## ``f`` argument.
  1919. runnableExamples:
  1920. var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
  1921. var tm = dt.toTime()
  1922. doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00"
  1923. time.inZone(zone).format(f)
  1924. proc format*(time: Time, f: static[string],
  1925. zone: Timezone = local()): string {.tags: [].} =
  1926. ## Overload that validates ``f`` at compile time.
  1927. const f2 = initTimeFormat(f)
  1928. result = time.inZone(zone).format(f2)
  1929. proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime =
  1930. ## Parses ``input`` as a ``DateTime`` using the format specified by ``f``.
  1931. ## If no UTC offset was parsed, then ``input`` is assumed to be specified in
  1932. ## the ``zone`` timezone. If a UTC offset was parsed, the result will be
  1933. ## converted to the ``zone`` timezone.
  1934. runnableExamples:
  1935. let f = initTimeFormat("yyyy-MM-dd")
  1936. let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
  1937. doAssert dt == "2000-01-01".parse(f, utc())
  1938. var inpIdx = 0 # Input index
  1939. var patIdx = 0 # Pattern index
  1940. var parsed: ParsedTime
  1941. while inpIdx <= input.high and patIdx <= f.patterns.high:
  1942. let pattern = f.patterns[patIdx].FormatPattern
  1943. case pattern
  1944. of Lit:
  1945. patIdx.inc
  1946. let len = f.patterns[patIdx]
  1947. patIdx.inc
  1948. for _ in 1'u8..len:
  1949. if input[inpIdx] != f.patterns[patIdx].char:
  1950. raiseParseException(f, input,
  1951. "Unexpected character: " & input[inpIdx])
  1952. inpIdx.inc
  1953. patIdx.inc
  1954. else:
  1955. if not parsePattern(input, pattern, inpIdx, parsed):
  1956. raiseParseException(f, input, &"Failed on pattern '{pattern}'")
  1957. patIdx.inc
  1958. if inpIdx <= input.high:
  1959. raiseParseException(f, input,
  1960. "Parsing ended but there was still input remaining")
  1961. if patIdx <= f.patterns.high:
  1962. raiseParseException(f, input,
  1963. "Parsing ended but there was still patterns remaining")
  1964. result = toDateTime(parsed, zone, f, input)
  1965. proc parse*(input, f: string, tz: Timezone = local()): DateTime =
  1966. ## Shorthand for constructing a ``TimeFormat`` and using it to parse
  1967. ## ``input`` as a ``DateTime``.
  1968. ##
  1969. ## See `Parsing and formatting dates`_ for documentation of the
  1970. ## ``f`` argument.
  1971. runnableExamples:
  1972. let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
  1973. doAssert dt == parse("2000-01-01", "yyyy-MM-dd", utc())
  1974. let dtFormat = initTimeFormat(f)
  1975. result = input.parse(dtFormat, tz)
  1976. proc parse*(input: string, f: static[string], zone: Timezone = local()): DateTime =
  1977. ## Overload that validates ``f`` at compile time.
  1978. const f2 = initTimeFormat(f)
  1979. result = input.parse(f2, zone)
  1980. proc parseTime*(input, f: string, zone: Timezone): Time =
  1981. ## Shorthand for constructing a ``TimeFormat`` and using it to parse
  1982. ## ``input`` as a ``DateTime``, then converting it a ``Time``.
  1983. ##
  1984. ## See `Parsing and formatting dates`_ for documentation of the
  1985. ## ``format`` argument.
  1986. runnableExamples:
  1987. let tStr = "1970-01-01T00:00:00+00:00"
  1988. doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", utc()) == fromUnix(0)
  1989. parse(input, f, zone).toTime()
  1990. proc parseTime*(input: string, f: static[string], zone: Timezone): Time =
  1991. ## Overload that validates ``format`` at compile time.
  1992. const f2 = initTimeFormat(f)
  1993. result = input.parse(f2, zone).toTime()
  1994. #
  1995. # End of parse & format implementation
  1996. #
  1997. proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
  1998. ## Converts a `DateTime` object to a string representation.
  1999. ## It uses the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
  2000. runnableExamples:
  2001. let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
  2002. doAssert $dt == "2000-01-01T12:00:00Z"
  2003. result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz")
  2004. proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
  2005. ## converts a `Time` value to a string representation. It will use the local
  2006. ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
  2007. runnableExamples:
  2008. let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
  2009. let tm = dt.toTime()
  2010. doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz")
  2011. $time.local
  2012. {.pop.}
  2013. proc countLeapYears*(yearSpan: int): int =
  2014. ## Returns the number of leap years spanned by a given number of years.
  2015. ##
  2016. ## **Note:** For leap years, start date is assumed to be 1 AD.
  2017. ## counts the number of leap years up to January 1st of a given year.
  2018. ## Keep in mind that if specified year is a leap year, the leap day
  2019. ## has not happened before January 1st of that year.
  2020. (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400
  2021. proc countDays*(yearSpan: int): int =
  2022. ## Returns the number of days spanned by a given number of years.
  2023. (yearSpan - 1) * 365 + countLeapYears(yearSpan)
  2024. proc countYears*(daySpan: int): int =
  2025. ## Returns the number of years spanned by a given number of days.
  2026. ((daySpan - countLeapYears(daySpan div 365)) div 365)
  2027. proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
  2028. ## Returns the number of years spanned by a given number of days and the
  2029. ## remainder as days.
  2030. let days = daySpan - countLeapYears(daySpan div 365)
  2031. result.years = days div 365
  2032. result.days = days mod 365
  2033. proc toTimeInterval*(time: Time): TimeInterval =
  2034. ## Converts a Time to a TimeInterval.
  2035. ##
  2036. ## To be used when diffing times. Consider using `between` instead.
  2037. runnableExamples:
  2038. let a = fromUnix(10)
  2039. let b = fromUnix(1_500_000_000)
  2040. let ti = b.toTimeInterval() - a.toTimeInterval()
  2041. doAssert a + ti == b
  2042. var dt = time.local
  2043. initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour,
  2044. dt.monthday, 0, dt.month.ord - 1, dt.year)
  2045. when not defined(JS):
  2046. type
  2047. Clock {.importc: "clock_t".} = distinct int
  2048. proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
  2049. var
  2050. clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
  2051. when not defined(useNimRtl):
  2052. proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
  2053. ## gets time spent that the CPU spent to run the current process in
  2054. ## seconds. This may be more useful for benchmarking than ``epochTime``.
  2055. ## However, it may measure the real time instead (depending on the OS).
  2056. ## The value of the result has no meaning.
  2057. ## To generate useful timing values, take the difference between
  2058. ## the results of two ``cpuTime`` calls:
  2059. runnableExamples:
  2060. var t0 = cpuTime()
  2061. # some useless work here (calculate fibonacci)
  2062. var fib = @[0, 1, 1]
  2063. for i in 1..10:
  2064. fib.add(fib[^1] + fib[^2])
  2065. echo "CPU time [s] ", cpuTime() - t0
  2066. echo "Fib is [s] ", fib
  2067. result = toFloat(int(getClock())) / toFloat(clocksPerSec)
  2068. proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} =
  2069. ## gets time after the UNIX epoch (1970) in seconds. It is a float
  2070. ## because sub-second resolution is likely to be supported (depending
  2071. ## on the hardware/OS).
  2072. ##
  2073. ## ``getTime`` should generally be prefered over this proc.
  2074. when defined(posix):
  2075. var a: Timeval
  2076. gettimeofday(a)
  2077. result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001
  2078. elif defined(windows):
  2079. var f: winlean.FILETIME
  2080. getSystemTimeAsFileTime(f)
  2081. var i64 = rdFileTime(f) - epochDiff
  2082. var secs = i64 div rateDiff
  2083. var subsecs = i64 mod rateDiff
  2084. result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
  2085. else:
  2086. {.error: "unknown OS".}
  2087. when defined(JS):
  2088. proc epochTime*(): float {.tags: [TimeEffect].} =
  2089. newDate().getTime() / 1000
  2090. # Deprecated procs
  2091. when not defined(JS):
  2092. proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
  2093. ## Converts a UNIX `Time` (``time_t``) to a Windows file time
  2094. ##
  2095. ## **Deprecated:** use ``toWinTime`` instead.
  2096. result = int64(time) * rateDiff + epochDiff
  2097. proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
  2098. ## Converts a Windows time to a UNIX `Time` (``time_t``)
  2099. ##
  2100. ## **Deprecated:** use ``fromWinTime`` instead.
  2101. result = CTime((time - epochDiff) div rateDiff)
  2102. proc initInterval*(seconds, minutes, hours, days, months,
  2103. years: int = 0): TimeInterval {.deprecated.} =
  2104. ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.
  2105. initTimeInterval(0, 0, 0, seconds, minutes, hours, days, 0, months, years)
  2106. proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign, deprecated.} =
  2107. ## Takes a float which contains the number of seconds since the unix epoch and
  2108. ## returns a time object.
  2109. ##
  2110. ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
  2111. let nanos = ((since1970 - since1970.int64.float) * convert(Seconds, Nanoseconds, 1).float).int
  2112. initTime(since1970.int64, nanos)
  2113. proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprecated.} =
  2114. ## Takes an int which contains the number of seconds since the unix epoch and
  2115. ## returns a time object.
  2116. ##
  2117. ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
  2118. fromUnix(since1970)
  2119. proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
  2120. ## Returns the time in seconds since the unix epoch.
  2121. ##
  2122. ## **Deprecated since v0.18.0:** use ``toUnix`` instead
  2123. time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
  2124. proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
  2125. ## Converts the calendar time `time` to broken-time representation,
  2126. ## expressed relative to the user's specified time zone.
  2127. ##
  2128. ## **Deprecated since v0.18.0:** use ``local`` instead
  2129. time.local
  2130. proc getGMTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
  2131. ## Converts the calendar time `time` to broken-down time representation,
  2132. ## expressed in Coordinated Universal Time (UTC).
  2133. ##
  2134. ## **Deprecated since v0.18.0:** use ``utc`` instead
  2135. time.utc
  2136. proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} =
  2137. ## Returns the offset of the local (non-DST) timezone in seconds west of UTC.
  2138. ##
  2139. ## **Deprecated since v0.18.0:** use ``now().utcOffset`` to get the current
  2140. ## utc offset (including DST).
  2141. when defined(JS):
  2142. return newDate().getTimezoneOffset() * 60
  2143. elif defined(freebsd) or defined(netbsd) or defined(openbsd):
  2144. var a: CTime
  2145. discard time(a)
  2146. let lt = localtime(addr(a))
  2147. # BSD stores in `gmtoff` offset east of UTC in seconds,
  2148. # but posix systems using west of UTC in seconds
  2149. return -(lt.gmtoff)
  2150. else:
  2151. return timezone
  2152. proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
  2153. ## Converts a broken-down time structure to calendar time representation.
  2154. ##
  2155. ## **Deprecated since v0.14.0:** use ``toTime`` instead.
  2156. dt.toTime
  2157. when defined(JS):
  2158. var start = getTime()
  2159. proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
  2160. let dur = getTime() - start
  2161. result = (convert(Seconds, Milliseconds, dur.seconds) +
  2162. convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
  2163. else:
  2164. proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
  2165. ## get the milliseconds from the start of the program.
  2166. ##
  2167. ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
  2168. when defined(macosx):
  2169. result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
  2170. else:
  2171. result = int(getClock()) div (clocksPerSec div 1000)
  2172. proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
  2173. ## Converts a Time to a TimeInterval.
  2174. ##
  2175. ## **Deprecated since v0.14.0:** use ``toTimeInterval`` instead.
  2176. # Milliseconds not available from Time
  2177. t.toTimeInterval()
  2178. proc getDayOfWeek*(day, month, year: int): WeekDay {.tags: [], raises: [], benign, deprecated.} =
  2179. ## **Deprecated since v0.18.0:** use
  2180. ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
  2181. getDayOfWeek(day, month.Month, year)
  2182. proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
  2183. ## Returns the day of the week enum from day, month and year,
  2184. ## according to the Julian calendar.
  2185. ## **Deprecated since v0.18.0**
  2186. # Day & month start from one.
  2187. let
  2188. a = (14 - month) div 12
  2189. y = year - a
  2190. m = month + (12*a) - 2
  2191. d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
  2192. result = d.WeekDay
  2193. proc adjTime*(zt: ZonedTime): Time
  2194. {.deprecated: "Use zt.time instead".} =
  2195. ## **Deprecated since v0.19.0:** use the ``time`` field instead.
  2196. zt.time - initDuration(seconds = zt.utcOffset)
  2197. proc `adjTime=`*(zt: var ZonedTime, adjTime: Time)
  2198. {.deprecated: "Use zt.time instead".} =
  2199. ## **Deprecated since v0.19.0:** use the ``time`` field instead.
  2200. zt.time = adjTime + initDuration(seconds = zt.utcOffset)
  2201. proc zoneInfoFromUtc*(zone: Timezone, time: Time): ZonedTime
  2202. {.deprecated: "Use zonedTimeFromTime instead".} =
  2203. ## **Deprecated since v0.19.0:** use ``zonedTimeFromTime`` instead.
  2204. zone.zonedTimeFromTime(time)
  2205. proc zoneInfoFromTz*(zone: Timezone, adjTime: Time): ZonedTime
  2206. {.deprecated: "Use zonedTimeFromAdjTime instead".} =
  2207. ## **Deprecated since v0.19.0:** use the ``zonedTimeFromAdjTime`` instead.
  2208. zone.zonedTimeFromAdjTime(adjTime)