src/chrono

Search:
Group by:
Source   Edit  

Chrono a Timestamps, Calendars, and Timezones library for nim.

Parse timestamps:

var ts = parseTs(
  "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
  "1988-02-09T03:34:12Z"
)

Format timestamps:

echo formatTs(
  ts,
  "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
)

Manipulate Calendars:

var cal = Calendar(year: 2013, month: 12, day: 31, hour: 59, minute: 59, second: 59)
cal.addSeconds(20)
cal.subMinutes(15)
cal.addDays(40)
cal.subMonths(120)

Use timezones:

echo formatTs(
    ts,
    "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
    tzName = "America/Los_Angeles"
)

Format Specification

SpecifierDescriptionExample
{year}Year in as many digits as needed. Can be negative.12012/9/3 -> 12012
{year/2}Two digit year, 0-30 represents 2000-2030 while 30-99 is 1930 to 1999.2012/9/3 -> 12
{year/4}Four digits of the year. Years 0 - 9999.2012/9/3 -> 2012
{month}Month in digits 1-122012/9/3 -> 9
{month/2}Month in two digits 01-122012/9/3 -> 09
{month/n}Full name of monthSeptember -> September
{month/n/3}Three letter name of monthSeptember -> Sep
{day}Day in digits 1-312012/9/3 -> 3
{day/2}Day in two digits 01-312012/9/3 -> 03
{hour}Hour in digits 0-2309:08:07 -> 9
{hour/2}Hour in two digits 00-2309:08:07 -> 09
{hour/ap}Hour as 12-hour am/pm as digits 1-1213:08:07 -> 1
{hour/2/ap}Hour as 12-hour am/pm as two digits 01-1213:08:07 -> 01
{am/pm}Based on hour outputs "am" or "pm"13:08:07 -> pm
{minute}Minute in digits 0-5909:08:07 -> 8
{minute/2}Minute in two digits 00-5909:08:07 -> 08
{second}Second in digits 0-5909:08:07 -> 7
{second/2}Second in two digits 00-5909:08:07 -> 07
{secondFraction}Seconds fraction value.09:08:07.123456789123 -> 0.123456789123
{secondFraction/2}Seconds fraction 2 digit value.09:08:07.12 -> 0.12
{secondFraction/7}Seconds fraction 7 digit value.09:08:07.1234567 -> 0.1234567
{weekday}Full name of weekdaySaturday -> Saturday
{weekday/3}Three letter of name of weekdaySaturday -> Sat
{weekday/2}Two letter of name of weekdaySaturday -> Sa
{tzName}Timezone name (can't be parsed)America/Los_Angeles
{dstName}Daylight savings name or standard name (can't be parsed)PDT
{offsetDir}Which direction is the offset going+ or -
{offsetHour/2}Offset hour 00-1207
{offsetMinute/2}Offset minute 00-5800
{offsetSecond/2}Offset second 00-5800

Any string that is not in {} considered to not be part of the format and is just inserted. "{year/4} and {month/2} and {day/2}" -> "1988 and 02 and 09"

Common Formats

American Date: 01/12/2020 "{month/2}/{day/2}/{year/4}"

Europian Date: 01.12.2020 "{year/4}.{month/2}.{day/2}"

American Time: 8:12pm "{hour/2/ap}:{minute/2}"

Europian Time: 20:12 "{hour/2}:{minute/2}"

ISO with fractional seconds: 2020-01-12T02:00:33.387824Z "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second}.{secondFraction/7}Z" parseIso/formatIso are faster but fractional seconds.

chrono/calendars

Calendars are more involved they support more features but come with complexity and are mutable.

I do not recommend storing Calendar objects in files or databases. Store Timestamp instead.

Most useful thing about calendars is that you can add years, months, days, hours, minutes or seconds to them.

var cal = Calendar(year: 2013, month: 12, day: 31, hour: 59, minute: 59, second: 59)
cal.add(Second, 20)
cal.sub(Minute, 15)
cal.add(Day, 40)
cal.sub(Month, 120)
cal.toStartOf(Day)
cal.toEndOf(Month)

It supports same format specification as Timestamp:

echo cal.formatCalendar("{month/2}/{day/2}/{year/4} {hour/2}:{minute/2}:{second/2}")

If you need extra features that calendars provide I recommending creating a Calendar with tsToCalendar doing your work and converting back with calendarToTs.

ts = cal.ts
cal = ts.calendar

chrono/timestamps

If you are going to just parse or format dates. I recommend using just the import chrono/timestamps module. It it imports the Timestamp that is enough for most cases involved with times. I always recommend storing dates as a float64 number of seconds since 1970. This is exactly what Timestamp is. When you need to parse it or display it use parseTs or formatTs.

var ts = parseTs(
  "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
  "1988-02-09T03:34:12Z"
)

echo ts

echo formatTs(
  ts,
  "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
)

If you need to parse ISO dates which is a very common format you find all over the internet. You can even use faster optimized versions here:

echo parseIsoTs("2017-11-08T08:01:43Z")
echo Timestamp(1510128103.0).formatIso()

chrono/timezones

Timezones can be complicated. But if you treat them as a presentation level issue sort of like language it becomes easier. Never store anything as non-UTC. If you need to store timezone info store it as a string plus a Timestamp. When you need to display or parse it use the tzName attribute.

var ts = parseTs(
    "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
   "1988-02-09T03:34:12Z",
    tzName = "America/Los_Angeles"
)

echo ts

echo formatTs(
    ts,
    "{year/4}-{month/2}-{day/2}T{hour/2}:{minute/2}:{second/2}Z",
    tzName = "America/Los_Angeles"
)

Timezone and daylight savings can and do change unpredictably remember to keep this library up to date.

When you import the library it also statically includes compressed daylight savings table in the binary which is about 0.7MB. It does not use OS's timezone functions. This way, you are always guaranteed to get the same result on all platforms.

Many confuse proper time zone names like "America/Los_Angeles" with 3-4 letter time zone abbreviations like "PST" or "PDT". Time zone abbreviations cannot describe a time zone fully, unless you know what country you are in and its a common one. It is always recommended to use full timezone names for parsing and storage and only display time zone abbreviations and never parse them.

Types

Calendar = object
  year*: int
  month*: int
  day*: int
  hour*: int
  minute*: int
  second*: int
  secondFraction*: float64
  tzOffset*: float64
  tzName*: string
  dstName*: string
Source   Edit  
DstChange = object
  tzId*: int16
  name*: string
  start*: float64
  offset*: int32
Day Light Savings time transition Source   Edit  
TimeScale = enum
  Unknown, Second, Minute, Hour, Day, Week, Month, Quarter, Year
Source   Edit  
Timestamp = distinct float64
Always seconds since 1970 UTC. Source   Edit  
TimeZone = object
  id*: int16
  name*: string
Time Zone information Source   Edit  

Vars

dstChanges: seq[DstChange]
List of all DST changes Source   Edit  
tzs: seq[TimeZone]
List of all timezones Source   Edit  

Procs

proc `$`(a: Calendar): string {....raises: [], tags: [], forbids: [].}
Display a Calendar as a ISO 8601 string. Source   Edit  
proc `$`(a: Timestamp): string {....raises: [], tags: [], forbids: [].}
Display a timestamps as a float64. Source   Edit  
proc `<`(a, b: Calendar): bool {....raises: [], tags: [], forbids: [].}
Source   Edit  
proc `<`(a, b: Timestamp): bool {....raises: [], tags: [], forbids: [].}
Compare timestamps. Source   Edit  
proc `<=`(a, b: Calendar): bool {....raises: [], tags: [], forbids: [].}
Source   Edit  
proc `<=`(a, b: Timestamp): bool {....raises: [], tags: [], forbids: [].}
Compare timestamps. Source   Edit  
proc `==`(a, b: Calendar): bool {....raises: [], tags: [], forbids: [].}
Source   Edit  
proc `==`(a, b: Timestamp): bool {....raises: [], tags: [], forbids: [].}
Compare timestamps. Source   Edit  
proc `>`(a, b: Calendar): bool {....raises: [], tags: [], forbids: [].}
Source   Edit  
proc `>`(a, b: Timestamp): bool {....raises: [], tags: [], forbids: [].}
Compare timestamps. Source   Edit  
proc `>=`(a, b: Calendar): bool {....raises: [], tags: [], forbids: [].}
Source   Edit  
proc `>=`(a, b: Timestamp): bool {....raises: [], tags: [], forbids: [].}
Compare timestamps. Source   Edit  
proc add(cal: var Calendar; timeScale: TimeScale; number: float) {....raises: [],
    tags: [], forbids: [].}
Source   Edit  
proc add(cal: var Calendar; timeScale: TimeScale; number: int) {....raises: [],
    tags: [], forbids: [].}
Add a Day, Hour, Year... to calendar. Source   Edit  
proc add(ts: Timestamp; timeScale: TimeScale; number: int): Timestamp {.
    ...raises: [], tags: [], forbids: [].}
Add Seconds, Minutes, Hours, Days ... to Timestamp. Source   Edit  
proc applyTimezone(cal: var Calendar; tzName: string) {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
take a calendar and apply a timezone to it this does not changes timestamp of the calendar but does change the hour:minute Source   Edit  
proc calendar(ts: Timestamp): Calendar {....raises: [], tags: [], forbids: [].}
Converts a Timestamp to a Calendar. Source   Edit  
proc calendar(ts: Timestamp; tzName: string): Calendar {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
Convert Timestamp to calendar with a timezone. Source   Edit  
proc calendar(ts: Timestamp; tzOffset: float64): Calendar {....raises: [],
    tags: [], forbids: [].}
Converts a Timestamp to a Calendar with a tz offset. Does not deal with DST. Source   Edit  
proc clearTimezone(cal: var Calendar) {....raises: [], tags: [], forbids: [].}
Removes timezone form calendar Source   Edit  
proc compare(a, b: Calendar): int {....raises: [], tags: [], forbids: [].}
Compare two calendars. Source   Edit  
proc copy(cal: Calendar): Calendar {....raises: [], tags: [], forbids: [].}
Copies the calendar Source   Edit  
proc daysInMonth(cal: Calendar): int {....raises: [], tags: [], forbids: [].}
Get number of days in a calendar month. Source   Edit  
proc findTimeZone(tzId: int): TimeZone {....raises: [], tags: [], forbids: [].}
Finds timezone by its id (slow). Source   Edit  
proc findTimeZone(tzName: string): TimeZone {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
Finds timezone by its name Source   Edit  
proc format(cal: Calendar; format: string): string {....raises: [ValueError],
    tags: [], forbids: [].}
Formats calendars to a string based on the format specification. Source   Edit  
proc format(ts: Timestamp; fmt: string): string {....raises: [ValueError],
    tags: [], forbids: [].}
Format a Timestamp using the format string. Source   Edit  
proc format(ts: Timestamp; fmt: string; tzName: string): string {.
    ...raises: [Exception, ValueError], tags: [RootEffect], forbids: [].}
Format a Timestamp with timezone using the format string. Source   Edit  
proc formatIso(cal: Calendar): string {....raises: [], tags: [], forbids: [].}
Fastest way to convert Calendar to an ISO 8601 string representation. Use this instead of the format function when dealing with ISO format. Warning does minimal checking for speed. Make sure your calendar is valid. Note: Does not support fractional seconds. Source   Edit  
proc formatIso(ts: Timestamp): string {....raises: [], tags: [], forbids: [].}
Fastest way to convert Timestamp to an ISO 8601 string representation. Use this instead of the format function when dealing with ISO format. Source   Edit  
proc formatIso(ts: Timestamp; tzName: string): string {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
Fastest way to convert Timestamp to an ISO 8601 string representation. Use this instead of the format function when dealing with ISO format. Source   Edit  
proc formatIso(ts: Timestamp; tzOffset: float64): string {....raises: [], tags: [],
    forbids: [].}
Fastest way to convert Timestamp to an ISO 8601 string representation. Use this instead of the format function when dealing with ISO format. Source   Edit  
proc leapYear(cal: Calendar): bool {....raises: [], tags: [], forbids: [].}
Is the calendar in a leap year Source   Edit  
proc loadTzData(tzData: string) {....raises: [IOError, OSError, JsonParsingError,
    ValueError, KeyError, JsonKindError], tags: [ReadIOEffect, WriteIOEffect,
    RootEffect], forbids: [].}

Loads timezone information from tzdata.json file contents. To statically include time zones in your program:

  loadTzData(staticRead("your-path/tzdata.json"))

Source   Edit  
proc normalize(cal: var Calendar) {....raises: [], tags: [], forbids: [].}
Fixes any issues with calendar such as extra hours, extra days, and negative months. Source   Edit  
proc normalizeTimezone(cal: var Calendar) {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
After shifting around the calendar, its DST might need to be updated. Source   Edit  
proc parseCalendar(format: string; value: string): Calendar {.
    ...raises: [ValueError], tags: [], forbids: [].}
Parses calendars from a string based on the format specification. Note that not all valid formats can be parsed, things such as weekdays or am/pm stuff without hours. Source   Edit  
proc parseCalendar(formats: seq[string]; value: string): Calendar {.
    ...raises: [ValueError], tags: [], forbids: [].}
Parses calendars from a seq of strings based on the format specification. Returns first format that parses or rases ValueError. Source   Edit  
proc parseIsoCalendar(iso: string): Calendar {....raises: [ValueError], tags: [],
    forbids: [].}
Fastest way to convert an ISO 8601 string representation to a Calendar. Use this instead of the parseTimestamp function when dealing with ISO format. Note: Does not support fractional seconds. Source   Edit  
proc parseIsoTs(iso: string): Timestamp {....raises: [ValueError], tags: [],
    forbids: [].}
Fastest way to convert an ISO 8601 string representation to a Timestamp. Use this instead of the parseTimestamp function when dealing with ISO format. Source   Edit  
proc parseTimeScale(timeScale: string): TimeScale {....raises: [], tags: [],
    forbids: [].}
Turns a time scale string like "second" to the enum Second. Source   Edit  
proc parseTs(fmt: string; value: string): Timestamp {....raises: [ValueError],
    tags: [], forbids: [].}
Parse time using the Chrono format string into a Timestamp. Source   Edit  
proc parseTs(fmt: string; value: string; tzName: string): Timestamp {.
    ...raises: [ValueError, Exception], tags: [RootEffect], forbids: [].}
Parse time using the Chrono format string with timezone nto a Timestamp. Source   Edit  
proc shiftTimezone(cal: var Calendar; tzName: string) {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
take a calendar and moves it into a timezone this does changes timestamp of the calendar but does not change the hour:minute Source   Edit  
proc sub(cal: var Calendar; timeScale: TimeScale; number: float) {....raises: [],
    tags: [], forbids: [].}
Subtract a Day, Hour, Year... to calendar. Source   Edit  
proc sub(cal: var Calendar; timeScale: TimeScale; number: int) {....raises: [],
    tags: [], forbids: [].}
Subtract a Day, Hour, Year... to calendar. Source   Edit  
proc sub(ts: Timestamp; timeScale: TimeScale; number: int): Timestamp {.
    ...raises: [], tags: [], forbids: [].}
Subtract Seconds, Minutes, Hours, Days ... to Timestamp. Source   Edit  
proc toEndOf(cal: var Calendar; timeScale: TimeScale) {....raises: [], tags: [],
    forbids: [].}
Move the calendar to an end of a time scale. Source   Edit  
proc toEndOf(ts: Timestamp; timeScale: TimeScale): Timestamp {....raises: [],
    tags: [], forbids: [].}
Move the time stamp to an end of a time scale. Source   Edit  
proc toStartOf(cal: var Calendar; timeScale: TimeScale) {....raises: [], tags: [],
    forbids: [].}
Move the time stamp to a start of a time scale. Source   Edit  
proc toStartOf(ts: Timestamp; timeScale: TimeScale): Timestamp {....raises: [],
    tags: [], forbids: [].}
Move the time stamp to a start of a time scale. Source   Edit  
proc ts(cal: Calendar): Timestamp {....raises: [], tags: [], forbids: [].}
Converts Calendar to a Timestamp. Source   Edit  
proc valid(tz: TimeZone): bool {....raises: [], tags: [], forbids: [].}
Returns true if timezone is valid Source   Edit  
proc weekday(cal: Calendar): int {....raises: [], tags: [], forbids: [].}
Get number of a weekday 0..6. Monday being 0 Source   Edit  

Iterators

iterator findDstChanges(tz: TimeZone): DstChange {....raises: [Exception],
    tags: [RootEffect], forbids: [].}
Finds timezone dst changes by timezone. Source   Edit  
iterator findTimeZoneFromDstName(dstName: string): TimeZone {....raises: [],
    tags: [], forbids: [].}
Finds timezones by its dst name (slow). Source   Edit