The Date module provides a Format
class for date and time formatting and parsing that
uses an identical API to Intl.DateTimeFormat:
var d = new Date(), opts = {weekday: "long", year: "numeric", month: "long", day: "numeric"}, fmt = new UsefulJS.Date.Format("fr", opts), dateStr = fmt.format(d); // "dimanche 3 novembre 2013"
The values provided by Intl.DateTimeFormat are strictly one way: there is no way to read them back. UsefulJS.Date.Format objects can read back strings that they produce and turn them back into Date objects:
fmt.parse("dimanche 3 novembre 2013"); // Sun Nov 03 2013 00:00:00 GMT+0000 (GMT)
Constructor for the class.
new UsefulJS.Date.Format([requestedLocales[, options]])
requestedLocales
String or Arrayoptions
ObjectThe requestedLocales
parameter is specified either as a single BCP-47 locale tag ("en" or
"en-GB") or an array of such values indicating the preferred locale and one or more fallbacks. For example,
["quz-PE", "es-PE", "es"] would result in the "es-PE" locale being selected for date and time formatting
since Quechua is not a supported language whereas Spanish is and there is a locale object for
Spanish (Peru).
requestedLocales
parameter is optional. You can set
UsefulJS.Locale.current
to control the default locale.Together with selecting the locale itself, you may specifiy Unicode extensions to control the numbering system used and the calendar used to measure dates. "-u-" is used to specify an extension and then "nu-<N>" for the numbering system and "ca-<C>" for the calendar. For example, to do date formatting in Hindi using Devangaric digits and the Indian national calendar, you would specify "hi-u-nu-deva-ca-indian". Latin ("latn") digits are the default for most locales together with the Gregorian ("gregory") calendar.
You may specify the following values for the numbering system: "arab", "arabext", "bali", "beng", "deva", "fullwide", "gujr", "guru", "hanidec", "khmr", "knda", "laoo", "latn", "limb", "mlym", "mong", "mymr", "orya", "tamldec", "telu", "thai" and "tibt". Of these "arab" is the default numbering system used in dates for most Arabic locales apart from Tunisia, Algeria and Morocco, "arabext" is the default for "fa" (Iran) and "beng" is the default for "bn-BD" (Bangladesh). "deva" would be valid for "hi" (Hindi), "thai" for "th" (Thailand) and "hanidec"/"fullwide" for Chinese and Japanese locales.
The internationalization API allows for fourteen calendars: "buddhist", "chinese", "coptic", "ethioaa", "ethiopic", "gregory", "hebrew", "indian", "islamic", "islamicc", "iso8601", "japanese", "persian" and "roc". Of these, the following calendars are supported by UsefulJS:
The Buddhist calendar is measured from 543 BCE. Year 0 is formatted as "0" and years before the era are formatted as negative values. Otherwise it is the same as the Gregorian calendar (i.e. the year begins on January 1st and leap years are the same). It is the default calendar for the "th" (Thai) locale. The era name ("BE") is only localized for Thai.
The Gregorian calendar introduced in 1582 CE corrected the excess leap years of the Julian calendar which by that time was eleven days out of step with the equinoxes. Years are measured from 1 CE, Years prior to this are formatted as abs(n) + 1, so that 0 becomes "1 BCE". This is the default calendar for most locales and the months and era names, "CE" and "BCE", are localized for all locales.
The Indian national calendar has had official status in India since 1957 CE (or 1879 Saka Era). The year starts on March 22nd relative to the Gregorian (21st in leap years). The first month has thirty days (thirty one in leap years) and is followed by five months of thirty one days and six of thirty. Years are measured from 78 CE which is formatted as "0 Saka Era". Years prior to this are negative values. Leap years are synchronized with Gregorian leap years. The month names and era name are only localized for Hindi.
ISO 8601 is a standard for formatting dates and times to make them more amenable to machine processing: lexicographical order is the same as chronological order and fixed width fields and invariant field separators means that the date value can be read unambiguously from its textual representation. Selection of this calendar imposes a number of restrictions: the date separator is always '-', ignoring the value for the locale, and the time separator is always ':'. Field values are padded with zeroes to make them the correct width, overriding values specified in the options hash. Likewise the numbering system is always "latn" no matter the default for the locale or the system requested through the "-u-" extension. 1 BCE is formatted as "+0000" and years prior to this are negative values. Era names are not used. Time is always in 24-hour format and the first day of the week is Monday.
The Japanese calendar reckons years according to the reign of the Emperor with the era named after him. An era ends when an emperor dies and a new one begins. This means that a single year can span two eras: 7th Jan 1989 was in the year 64 Shōwa while the next day was 1 Heisei. Eras are as follows:
Name | Kanji | Start | End |
---|---|---|---|
Meiji | 明治 | 8 Sep 1868 | 30 Jul 1912 |
Taishō | 大正 | 31 Jul 1912 | 25 Dec 1926 |
Shōwa | 昭和 | 26 Dec 1926 | 7 Jan 1989 |
Heisei | 平成 | 8 Jan 1989 | - |
This calendar was only established in its modern form during the Meiji era, so this implementation does not use eras prior to that. Dates in 1868 prior 8 Sep are formatted as "-1 Meiji". Years prior to 1868 become "-2 Meiji" and so on. This is not correct usage so use the Gregorian calendar for dates before 1868. When formatting dates using this calendar, the era name is always included. Besides the year numbering this calendar is identical to the Gregorian.
The Persian or Solar Hijri calendar is the official calendar of Iran. Years are measured from 622 CE which is formatted as "0 AP". It is an observational calendar with the year beginning on the day on which the vernal equinox occurs at or after solar noon at the longitude of Tehran. The year consists of six months of thirty one days, five of thirty and one of twenty nine (thirty in leap years). Since the calendar is observational, it is completely accurate: a leap year occurs when the period between equinoxes is 366 days. It is the default calendar in the "fa" (Iran) locale
The Republic of China (R.O.C.) calendar measures years from the foundation of the republic, 1911 CE. The era name is always included in date strings. Otherwise, it behaves identically to the Gregorian calendar, including the fomatting of years <= 0. The era names are only localized for the "zh-Hant-TW" (Taiwan) locale. Use of this calendar in the mainland Chinese locale might be something of a faux pas!
The options hash passed to the constructor may contain combinations of various named values that control the formatting of date and time values. The options and their permissible values are detailed below:
Option name | Values | Notes |
---|---|---|
weekday | long, short, narrow | "narrow" is treated the same as "short". Use the cal method if you need abbreviated
weekday names for a calendar widget. |
year | numeric, 2-digit | "numeric" results in an unpadded value unless you are using the "iso8601" calendar which pads year values to four digits. |
month | long, short, narrow, numeric, 2-digit | "narrow" is treated the same as "short". |
day | numeric, 2-digit | - |
hour | numeric, 2-digit | - |
minute | numeric, 2-digit | - |
second | numeric, 2-digit | - |
era | long, short, narrow | All values are treated as "short". This results in values of the form "AD". Era names in date strings are always output when using the "roc" and "japanese" calendars. |
timeZoneName | long, short | Both values are treated as "long", which looks like "GMT+XXXX". When parsing a string, the resulting Date object will be in UTC. |
timeZone | "UTC", any | The "UTC" timezone results in strings being formatted using UTC values rather than local time values. Parsed dates will be in UTC as well. Other values are ignored. |
hour12 | true, false | Whether time values are output in 12-hour (AM/PM) format. |
Any values outside the permissible range will result in RangeError exceptions as per-spec.
The options parameter to the constructor is optional; if not supplied the effect will be the same as
{ year : "numeric", month : "numeric", day : "numeric" }
.
The internationalization API allows for an additional two parameters: localeMatcher and formatMatcher. These are ignored - the algorithms used are "lookup" and "best fit" respectively.
Every locale has a defined value for the hour12 option. For example, 12-hour time is the default in the "en" (US) locale, but not in the "ru" (Russia) one. Before overriding the locale default, note that 12-hour time is not applicable in many locales and the values for "AM" and "PM" are not localized. Setting hour12 to false may be more appropriate as 24-hour time is comprehensible everywhere.
Any option combination will result in something but that something will only be sensible when the combination is sensible. The following date combinations are sensible:
As are the following time combinations:
A sensible date combination may be combined with a sensible time combination to format dates and times together. The timeZoneName option is only used when outputting times and the era option is only used when outputting dates. They are orthogonal to the other options: the era code is added to the date format string after the format string has been determined using a locale-aware pattern. The format code for the timezone is added to the time format string in a similar manner.
The options hash is resolved internally into a format string and you can pass format strings diectly to the
format
and parse
methods to avoid the hassle of constructing new Format objects.
These format strings use familiar strftime format codes with three additions. See strftime for more
information.
Code | Purpose | Example |
---|---|---|
%a | Abbreviated weekday name | Thu |
%A | Full weekday name | Thursday |
%b | Abbreviated month name | Aug |
%B | Full month name | August |
%c | "Preferred" date and time representation for the locale; tends to be quite Unix-y | Thu Aug 23 14:55:02 2001 |
%C | The century number: full year / 100 | 20 |
%d | The day of the month, 0-padded | 03 or 23 |
%D | Fixed date; equivalent to "%m/%d/%y"; waste of a code | 08/23/01 |
%e | The day of the month, unpadded | 3 or 23 |
%E | Used to create an alternate representation for certain codes. This does nothing - specify a calendar extension in the locale value instead. | - |
%F | ISO-8601 date format; equivalent to %Y-%m-%d | 2001-08-23 |
%g | Same as %y, except: if %V would evaluate to week 53 of the previous year, the previous year is output. So not at all confusing | - |
%G | As %g except a four-digit year is emitted | - |
%h | Exactly the same as %b; another waste of a code | - |
%H | The hour using the 24-hour clock, 0-padded | 00 or 23 |
%I | The hour using the 12-hour clock, 0-padded | 01 or 12 |
%j | The day of the year (0 - 366) | - |
%k | The hour using the 24-hour clock, unpadded | 0 or 23 |
%l | The hour using the 12-hour clock, unpadded | 1 or 12 |
%m | The month as a decimal number with a leading zero | 08 or 12 |
%M | Minutes as a two-digit number | 55 |
%n | Newline character, \n | - |
%N | The month as an unpadded decimal number; UsefulJS extension | 8 or 12 |
%O | Used as a modifier on numeric fields, values are output in locale digits rather than 0-9. This modifier will be applied to numeric fields automatically if the numbering system is something other than "latn". | %OY: ٢٠١٣ |
%p | The meridiem code in uppercase; irrelevant in many locales while others have more than two possible values | AM or PM |
%P | The meridiem code in lowercase; why %p and %P aren't the other way round is beyond me | am or pm |
%r | Time in am/pm notation, formatted for the locale; identical to %X in locales that do not use the 12-hour clock | 01:30:45 PM |
%R | Equivalent to %H:%M; not adjusted for the locale | 14:55 |
%s | The number of seconds since the epoch | - |
%S | Seconds as a two-digit number | 02 |
%t | Tab character, \t | - |
%T | Equivalent to %H:%M:%S; not adjusted for the locale | 14:55:02 |
%u | The day of the week with Monday as 1 and Sunday as 7 | 4 |
%U | The week number of the year (00-53), where the first Sunday of January is the first day of week 1; days prior to this are in week 0 | - |
%V | The ISO 8601 week number of the year (01-53), where week 1 is the first week that has four days in the year; dates before this are considered to lie in week 53 of the PREVIOUS year | - |
%w | The day of the week with Sunday as 0 and Saturday as 6 | 4 |
%W | The week number of the year (00-53), where the first Monday of January is the first day of week 1; days prior to this are in week 0 | - |
%x | The "preferred" date representation for the locale; usually has leading zero on the date value which makes it suitable for fixed layouts | 23/08/2001 |
%X | The "preferred" time representation for the locale; usually 24-hour clock with leading zero on the hour value which makes it suitable for fixed layouts | 14:55:02 |
%y | Two-digit year | 01 |
%Y | Four-digit year | 2001 |
%z | The hour and minute offset from UTC | +0100 |
%Z | The timezone code; not available on every system and even then it needs to be extracted from the toString representation; not recommended | BST |
%% | A literal '%' character | - |
%/ | Fractional seconds (ms) from 000-999; UsefulJS extension | - |
%! | The era name. UsefulJS extension. | AD |
Codes that produce unpadded values - %e, %k and %l - are space-padded according to the POSIX spec. However, space-padding is not terribly useful when outputting values for the web. The "numeric" month option requires the month value to be unpadded; the %N code has been added to make this possible.
The %Z code will not result in any output on some systems and this, depending on the format string, may cause the date string to be unparseable. Therefore, its use is not recommended. If you are formatting dates in UTC, the timezone code is always set to "UTC".
The %U, %W and %V codes produce week number values relative to Jan 1st which is incorrect for the indian and persian calendars. The %g, %G or %V codes are intended for ISO-8601 date strings.
Formats a Date object using either the internal format string or a specified format string.
format(date[, fmtStr])
date
DatefmtStr
StringReturns: String. The formatted date string
Throws: TypeError: input is not a Date object; Error: unrecognized format code.
Many of the available format codes are never present in the internally generated format string, but you can use them by supplying your own format string. Any character data in the format string that isn't a format code appears in the output string; this allows you to insert punctuation and sensible whitespace (and anything else you want for that matter).
// Create a format object for the default locale that uses the default format string var fmt1 = new UsefulJS.Date.Format(); // An object for outputting ISO-8601 strings var fmt_iso = new UsefulJS.Date.Format("en-u-ca-iso8601"); // An object for outputting dates in Japanese imperial format var fnt_jp = new UsefulJS.Date.Format("ja-u-nu-fullwide-ca-japanese", { year : "numeric", month : "short", day : "numeric" }); // Output some strings var d = new Date(); var dateStr_us = fmt1.format(d); // 11/17/2013, assuming "en" is the current locale var isoDateStr = fmt_iso.format(d); // 2013-11-17 // Our application needs ISO week values var isoWeekStr = fmt_iso.format(d, "%G-%V-%u"); // 2013-46-7 // ...And, for some reason, Japanese values var impDateStr = fmt_jp.format(d); // 平成25年11月17日
Parses a string produced by format
back into a Date object.
parse(dateStr[, fmtStr])
dateStr
StringfmtStr
StringReturns: Date. The parsed Date object.
Throws: TypeError if given something that isn't a non-empty string to parse; Error if an unrecognized format code is encountered; Error if the date string contains values that cannot be matched against the format string (such as non-numeric data for a numeric field); RangeError if any values are outside the valid range (such as "%j" having a value greater than 366); Error if the format string is not entirely processed (on the assumption that the input is lacking required information); it is not an error if the input string is not entirely consumed.
The parse method follows strptime rules: case is ignored, whitespace is insignificant (but not other character data), and field widths, if applicable, are ignored. Two digit years are disambiguated by the following rule: if, after calendar correction, the value is < 30, the year is 20xx, otherwise it is 19xx.
// Assuming the same objects as above, all will produce 17 Nov, 2013 var d1 = fmt1.parse("11/17/2013"), d2 = fmt_iso.parse("2013-11-17"), d3 = fmt_iso.parse("2013-46-7", "%G-%V-%u"), d4 = fmt_jp.parse("平成25年11月17日");
format
and parse
methods are bound to
their instances. What this means is that you can use a method pointer where a function pointer is expected
(e.g., setTimeout or Array iteration methods) and everything just works.Creates a calendar for the given month and year
cal([dLocal[, firstDay]])
dLocal
Object (optional)firstDay
Number (optional)Returns: Array. Calendar data for the month.
Returns a two dimensional array suitable for use in a calendar widget or similar. Format of the data is
similar to the Unix `cal` command: the first row contains seven abbreviated day names followed by four to
six rows of month days. Empty entries are represented by non-breaking spaces, \u00a0
.
// Generate calendar data for a year for the current locale var fmt = new UsefulJS.Date.Format(null, { year : "numeric", month : "long" }), cal = fmt.getCalendar(), // This year in calendar time y = cal.today().yy; cal.months().forEach(function(n, idx) { // Calendar time to internal time var dLocal = { yy : y, mm : idx, dd : 1 }, dInternal = UsefulJS.clone(dLocal); cal.fromCalendarDate(dInternal); // Generate a calendar header var d = new Date(dInternal.yy, dInternal.mm, dInternal.dd), headerStr = fmt.format(d), // Month data monthData = fmt.cal(dLocal); // Populate the calendar widget ... });
Gets the calendar object used for formatting and parsing dates.
getCalendar()
See above for possible usage. See below for methods of calendar objects.
Gets an Object showing what the requested options resolved to. The locale
property is useful
in that it gives the actual locale being used (unlike supportedLocalesOf
). See the
Intl.DateTimeFormat documentation for more information.
resolvedOptions()
Part of the internationalization API and not terribly useful. Takes an array of locale codes and returns the locales that are "supported" in the sense that they do not resolve to the default locale. For example, "ru-UK" (Ukrainian Russian) would return as "supported", even though the locale that is actually supported is "ru", i.e. Russian Russian. See the Intl documentation for more information.
supportedLocalesOf(locales[, options])
The getCalendar
method of the Format object returns an object that exposes the following
methods:
Returns true when a given year in a given era is a leap year.
isLeapYear(year, eraNo)
The gregory calendar has two eras, numbered 0 and 1, signifying CE and BCE respectively. The roc calendar is similar. The japanese calendar has (so far) four eras. The other calendars have just the one.
Returns true when a given date in a given month is the extra day in a leap year.
isLeapDay(month, date)
For the gregory calendar and others similar to it, the combination 1,29 will return true. For the indian calendar, this will be 0,31 while the persian calendar uses 11,30.
Returns an array of the day counts for the months in the year.
months(year, eraNo)
See above for the significance of the era number. If the year is a leap year, the return value will have the correct number of days in the relevant month.
Turns a Date object into broken-down time representing the date in the calendar (which may well be quite unlike the internal date).
dateToCalendarDate(date, utc)
Returns an Object containing the calendar representation of the date in broken down time. Properties are: yy (year), mm (month), dd (date), wd (week day), en (era number), utc (whether the date is UTC) and dateObj (the input Date object). This method is used to pre-process dates for formatting. The utc parameter determines whether UTC methods (such as getUTCFullYear) are used in preference to non-UTC methods (such as getFullYear).
Turns the broken-down calendar date into values in internal date representation.
fromCalendarDate(dLocal)
The input object has the same properties as above. The method does not return a value but instead modifies its argument in-place. It is used to post-process parsed date strings.
The UsefulJS.Date.Format API is 100% compatible with Intl.DateTimeFormat (and one of the module fixes can
add Intl.DateTimeFormat to the global namespace), but it does not follow that the date strings will be
identical which means that there is no guarantee that you can take a date string created using a native Intl
implementation and parse it using UsefulJS.Date.Format. The reason for this is twofold. The generation of
the format string is algorithmic and I am not privy to the algorithm used by, say, Chrome and that algorithm
will not be the same as that used by Internet Explorer 11. The other is that even given the same format
string (assuming other implementations use strftime internally), we still may not produce the same
date string. What constitutes the "correct" format for a given locale is a question without a definitive
answer. I honestly have no idea how to correctly format date and time values in, say, Vietnamese so rely on
another source for this information, assuming that they have 1) employed a local expert and 2) done their
homework (in the case of the Microsoft style guides I used both are reasonable assumptions). A different
implementation may have used a different source giving different guidance, resulting in different month
abbreviations or commas where I have none or no comma where I add a comma, etc. The parse
method is downright persnickety as to what it will accept - the input has to match the format string
exactly. So here is my suggestion: if your application needs to parse date and time values, use the
UsefulJS namespace. Otherwise use the Intl one (the module fix will ensure that this works in all
browsers).
The Date implementation in Firefox has a method, toLocaleFormat
, not present in other
implementations. I'm guessing that this is a remnant of a proposed standard that never quite got off the
ground. It accepts a strftime format string and gives you back the date and time formatted accordingly.
While it gives you some control over the output, unlike toLocaleString
, it has the same problem
as other toLocale* methods: the locale is baked in at compile time so that you cannot use it to generate
localized date strings, unless the baked-in locale coincides with that of your application. In addition,
there is no facility to obtain sensible format strings for different locales. Therefore its use, even when
available, is not recommended.
The Julian day value is a time measurement used by astronomers and is the number of days (including fractional part) since the beginning of the Julian Period (fixed at noon, 1st January 4713 BCE according to the Julian calendar). Note that the day begins at noon, not midnight. The Julian day value for the Unix epoch is 2,440,587.5.
Converts a Date object to Julian day value
toJulianDay(d)
d
DateReturns Number: the Julian day value.
Throws TypeError: if d
is not a Date object.
var gEpoch = Date.parse("1582-10-15T00:00:00Z"), // Start of the Gregorian calendar jd = UsefulJS.Date.toJulianDay(new Date(gEpoch)); // 2299160.5
Converts a Julian day value to a Date object.
fromJulianDay(jd)
jd
NumberReturns Date: a native Date object.
Throws TypeError: if jd
is not a finite number.
var jd = 2299160.5, gEpoch = UsefulJS.Date.fromJulianDay(jd); // "Fri Oct 15 1582 01:00:00 GMT+0100 (BST)"
Besides the Format class, UsefulJS.Date has a number of additional properties and methods.
Adds a date entry to UsefulJS.featureSupport:
Whether a timezone code can be extracted from a Date object's toString representation.
The timezone code extracted from a Date object's toString representation.
Format strings for standardized date and time formats: iso8601 (ISO date-time string), iso8601_ext (ISO date-time string plus milliseconds), isoweek (year number, week number and day-of-the-week number), isoordinal (year and day-of-the-year) and rfc2822 (as used in the Date header in emails).
Turns format options into a format string
getFormatString(opts, resolved)
This is actually the internal function used to generate a default format string when constructing a Format object. It is exposed as a public method on the assumption that you might want to play with the format strings rather than having the process remain completely opaque. As such, the API is not the most intuitive - it is designed to be useful to me rather than to you! See the usage for details on how to call this.
// Get an internal format string var fmtOpts = { weekday : "long", year : "numeric", month : "long", day : "numeric", era : "short", hour : "numeric", minute : "2-digit", second : "2-digit", timeZoneName : "long", hour12 : true }; // What will this produce in the Finnish locale? var dateOpts = UsefulJS.Locale.dateOptions("fi", fmtOpts); // Get the format string, ignore resolved options vat fmtStr = UsefulJS.Date.getFormatString(dateOpts, {}); // "%Ana %e. %Bta %Y %! %l.%M.%S %p UTC%z"
Given broken-down time, returns milliseconds since the epoch, UTC.
Date.UTC(year,month[,date[,hrs[,min[,sec[,ms]]]]])
This static method should be identical to Date.UTC
. However, at least one
implementation (Konqueror) has a bug where the local time difference is not taken into account so that the
value returned is milliseconds since the epoch, local time, not UTC. The Date module detects this bug
and creates an alternate UTC method that subtracts the local time difference from the value returned by
Date.UTC
. This is necessary to parse date strings correctly in UTC. However, if this bug is
present other Date UTC methods (such as getUTCHours) must be considered untrustworthy:
if (UsefulJS.Date.UTC !== Date.UTC) { // UTC is broken }
The fixes for the Date module are in the _date namespace of the fix options.
Adds a now
static method to the Date class when Date.now
is not implemented
natively. This fix is applied by default.
Adds a toISOString
method to Date.prototype when this is not implemented natively. The
locale option is "en-u-ca-iso8601" and the format string used is formats.iso8601_ext
. This fix
is applied by default.
Adds a toRFC822String
method to Date.prototype. The locale option is "en" and the format
string used is formats.rfc2822
. More accurately, the resulting date-and-time string is RFC2822
compliant since RFC822 mandated a 2-digit year. As a library extension, this fix must be explicitly
enabled.
toISOString
and
toRFC822String
should be parseable by Date.parse
.Makes the UsefulJS.Date.Format class available through the Intl.DateTimeFormat namespace. This allows for completely portable, locale-aware date and time formatting that runs in pretty much any browser, using the native object when available. This fix is applied by default.