Dates and times

The manakai project, 15 July 2024

Latest version
https://manakai.github.io/spec-datetime/
Version history
https://github.com/manakai/spec-datetime/commits/gh-pages

Abstract

This document defines date and time formats and algorithms.

Table of contents

  1. 1 Terminology
  2. 2 Numbers of days
  3. 3 Eras
    1. 3.1 AD
    2. 3.2 Eras
    3. 3.3 Era systems
  4. 4 Calendars
    1. 4.1 Gregorian calendar
    2. 4.2 Julian calendar
    3. 4.3 Kyuureki
  5. 5 File format
  6. 6 Data file
  7. 7 Times
  8. 8 Test data
  9. Author

1 Terminology

This specification depends on the Infra Standard.

The term ASCII digit is defined by the Infra Standard.

The term utf-8 is defined by the Encoding Standard.

2 Numbers of days

The JD day number of a day is a Julian Day number representing the start of the day in UTC.

Unlike the Julian Day Number (JDN), the JD day number is not an integer.

3 Eras

3.1 AD

An AD year is an integer.

Note that the year just before year 1 is year 0, not year −1.

A string is a year string representing an AD year year, if it consists of the following components in the given order:

  1. If year is less than zero (0):
    1. A U+002D HYPHEN-MINUS character (-)
    2. Four or more ASCII digits representing −year in decimal
  2. Otherwise, four or more ASCII digits representing year in decimal

To parse a year string string, the implementation MUST run these steps:

  1. Let input be a copy of string.
  2. Let sign be 1.
  3. If the first character in input is a U+002B PLUS SIGN character (+), remove it from input.
  4. Otherwise, if the first character in input is a U+002D HYPHEN-MINUS character (-), remove it from input and set sign to −1.
  5. If input is not a string of one or more ASCII digits, return failure and abort these steps.
  6. Let uyear be the result of interpreting input as a decimal integer.
  7. Let year be sign × uyear.

A string is a YMD string of a tuple (year, month, day) if it consists of the following components in the given order:

  1. A year string representing year
  2. A U+002D HYPHEN-MINUS character (-)
  3. Two ASCII digits representing month in decimal
  4. A U+002D HYPHEN-MINUS character (-)
  5. Two ASCII digits representing day in decimal

To parse a YMD string string, the implementation MUST run these steps:

  1. Let input be a copy of string.
  2. Let sign be 1.
  3. If the first character in input is a U+002B PLUS SIGN character (+), remove it from input.
  4. Otherwise, if the first character in input is a U+002D HYPHEN-MINUS character (-), remove it from input and set sign to −1.
  5. If string is not a string of one or more ASCII digits, followed by a U+002D HYPHEN-MINUS character (-), followed by one or more ASCII digits, followed by a U+002D HYPHEN-MINUS character (-), followed by one or more ASCII digits, return (failure, failure, failure) and abort these steps.
  6. Let uyear be the result of interpreting the first sequence of ASCII digits in string as a decimal integer.
  7. Let month be the result of interpreting the second sequence of ASCII digits in string as a decimal integer.
  8. Let day be the result of interpreting the last sequence of ASCII digits in string as a decimal integer.
  9. Return (sign × uyear, month, day).

3.2 Eras

The notation era.prop, where era is an era, represents that the value of the name/value pair whose name is prop of the era description (a JSON object contained as a value of the eras value of the topmost object) in the calendar-era-defs data file.

The era key of an era era is era.key. An era key is a short string which uniquely identifies an era.

To convert from era era and year era year into an AD year, the implementation MUST run these steps:

  1. If era.offset is null, return null.
  2. Otherwise, return era.offset + era year.

If the mapping from era to AD is not known, these steps return null.

To convert an AD year year for era era into a year era year in era, the implementation MUST run these steps:

  1. If era.offset is null, return null.
  2. Otherwise, return yearera.offset.

If the mapping from AD to era is not known, these steps return null.

A string is an era year string representing a year year in era era, if it consists of the following components in the given order:

  1. The era key of era
  2. If era year is less than zero (0):
    1. A U+002D HYPHEN-MINUS character (-)
    2. One or more ASCII digits representing −era year in decimal
  3. Otherwise, one or more ASCII digits representing era year in decimal
... where era year is year converted for era.

A string is an era YMD string of a tuple (year, month, day) with era era if it consists of the following components in the given order:

  1. An era year string representing year in era
  2. A U+002D HYPHEN-MINUS character (-)
  3. Two ASCII digits representing month in decimal
  4. A U+002D HYPHEN-MINUS character (-)
  5. Two ASCII digits representing day in decimal

An era name context is a mapping from strings to era keys, which is used to interpret era names in human-readable texts.

Era name contexts are defined in the calendar-era-defs data file as values of the name_to_key JSON object in the topmost object.

To get an era from name name in era name context context, the implementation must run these steps:

  1. If name is not a name in context, return null and abort these steps.
  2. Let key be the value of a name/value pair whose name is name in context.
  3. Return the era whose era key is key.

An era name context is required to interpret strings as eras as there are different eras sharing the same name.

3.3 Era systems

An era system is a set of rules to choose an era which is appropriate for a day.

An era system has an ordered sequence of time points. A time point is a tuple of type, value, and era. The type is one of Julian Day and year. If the type is Julian Day, the value is the JD day number of a day. If the type is year, the value is an AD year.

To get the era and era year of a calendar day day using an era system eras, the implementation MUST run these steps:

  1. Let jd be the JD day number of day.
  2. Let year be the AD year of day.
  3. Let era be null.
  4. Let i be zero (0).
  5. Loop: If i is equal to the number of time points in eras, go to the step labeled as Era found.
  6. Set point to the ith time point in eras (zero-indexed).
  7. If the type of point is Julian Day and jd is greater than or equal to the value of point, set era to the era of point and go to the step labeled as Era found.
  8. Otherwise, if the type of point is year and year is greater than or equal to the value of point, set era to the era of point and go to the step labeled as Era found.
  9. Otherwise, increment i and go to the step labeled as Loop.
  10. Era found: If era is null, return nothing and abort these steps.
  11. Let era year be year converted for era.
  12. If era year is null, return nothing and abort these steps.
  13. Return a tuple of era era and year era year.

These steps return a pair of an era and a year in the era, which can be used to represent day. If no era is defined by eras for day, it returns nothing.

4 Calendars

For the purpose of this specification, a calendar day is a Gregorian calendar day, Julian calendar day, or Kyuureki day.

4.1 Gregorian calendar

For the purpose of this specification, the Gregorian calendar refers to the proleptic Gregorian calendar.

A Gregorian calendar day represents a day in the Gregorian calendar. It is identified by a tuple of an AD year, a month number, and a day number. The year number is an integer. The month number is an integer in the range [1, 12]. The day number is an integer in the range [1, 31].

Not all months have the days 29, 30, and 31.

A string is a Gregorian date string of a Gregorian calendar day (year, month, day) if it is a YMD string of the tuple (year, month, day).

If it represents a valid day and year is in the range [1, 9999], it is also a valid date string.

A string is a Gregorian era date string of a Gregorian calendar day (year, month, day) with era era if it is an era YMD string of the tuple (year, month, day) in era.

To get a Gregorian date from a year string string, the implementation must run these steps:

  1. Let year be the result of applying the steps to parse a year string to string.
  2. If year is failure, return failure and abort these steps.
  3. Otherwise, return Gregorian calendar day (year, 1, 1).

To get a Gregorian date from a YMD string string, the implementation must run these steps:

  1. Let year, month, and day be the result of applying the steps to parse a YMD string to string.
  2. If year is failure, return failure and abort these steps.
  3. Otherwise, return Gregorian calendar day (year, month, day).

4.2 Julian calendar

For the purpose of this specification, the Julian calendar refers to the proleptic Julian calendar.

A Julian calendar day represents a day in the Julian calendar. It is identified by a tuple of an AD year, a month number, and a day number. The year number is an integer. The month number is an integer in the range [1, 12]. The day number is an integer in the range [1, 31].

Not all months have the days 29, 30, and 31.

Julian calendar day is different from Julian Day.

A string is a Julian date string of a Julian calendar day (year, month, day) if it is a YMD string of the tuple (year, month, day).

A string is a Julian era date string of a Julian calendar day (year, month, day) with era era if it is an era YMD string of the tuple (year, month, day) in era.

4.3 Kyuureki

The Kyuureki (旧暦) is the calendar system historically used in Japan. It is a variant of Chinese calendar systems.

It is normatively defined by 太陽暦 (Government of Japan, 1873-1879), (Government of Japan, 1880-1909), 日本百科大辭典 (keyword "太陰暦", 平山清次, 1912), 神宮暦 (神宮司廳, 1947-), 日本暦日原典 (内田正男, 1992), and 日本書紀暦日原典 (内田正男, 1993).

See also SuikaWiki:旧暦.

A Kyuureki day represents a day in the Kyuureki. It is identified by a tuple of an AD year, a month number, a boolean flag, and a day number. The year number is an integer. The month number is an integer in the range [1, 12], where the value 1 represents the 正月. If the boolean flag is true, the month is a leap month. Otherwise, it is not a leap month. The day number is an integer in the range [1, 30], where the value 1 represents the 朔日 of the month.

Not all months have the day 30.

A string is a Kyuureki date string of a Kyuureki day (year, month, is leap month, day) if it consists of the following components in the given order:

  1. A year string representing year
  2. A U+002D HYPHEN-MINUS character (-)
  3. Two ASCII digits representing month in decimal
  4. If is leap month is true, a U+0027 APOSTROPHE character (')
  5. A U+002D HYPHEN-MINUS character (-)
  6. Two ASCII digits representing day in decimal

A string is a Kyuureki era date string of a Kyuureki day (year, month, is leap month, day) with era era if it consists of the following components in the given order:

  1. An era year string representing year in era
  2. A U+002D HYPHEN-MINUS character (-)
  3. Two ASCII digits representing month in decimal
  4. If is leap month is true, a U+0027 APOSTROPHE character (')
  5. A U+002D HYPHEN-MINUS character (-)
  6. Two ASCII digits representing day in decimal

5 File format

A date mapping file is a file consist of zero of lines. A line consists of two slots separated by a 0x09 byte, followed by a 0x0A byte. A line represents an entry in the mapping, where the first slot represents the source of the entry and the second slot represents the destination of the entry. The values in the slots are encoded by utf-8.

6 Data file

The calendar-era-defs data file (documentation) is normatively referenced from this specification.

7 Times

To resolve a day-of-time time-of-day daytime relative to a time with time-zone offset reference time, run these steps:

  1. Let time2 be a new time with time-zone offset with reference time's year, reference time's month, reference time's day, daytime's hour, daytime's minute, daytime's second, and reference time's time-zone offset.
  2. Let time1 be time2 - 24 × 60 × 60 seconds.
  3. Let time3 be time2 + 24 × 60 × 60 seconds.
  4. Let delta1 be |reference time - time1|.
  5. Let delta2 be |reference time - time2|.
  6. Let delta3 be |reference time - time3|.
  7. Let minimum delta be the minimum value of delta1, delta2, and delta3.
  8. If delta1 is minimum delta, return time1.
  9. Otherwise, if delta2 is minimum delta, return time2.
  10. Otherwise, if delta3 is minimum delta, return time3.

These steps return the time represented by daytime that is nearest to reference time.

Need to define time with time-zone offset, time-of-day, and time computations.

8 Test data

This section is non-normative.

There are tests for some parts of this specification.

Author

This document is written by Wakaba <wakaba@suikawiki.org>.

This document is developed as part of the manakai project.

Per CC0, to the extent possible under law, the author has waived all copyright and related or neighboring rights to this work.