See "About This JavaScript Site" in JavaScript Index and Introduction.
See "General Date/Time Introduction" in JavaScript Date and Time Introduction.
My JavaScript RegExps & Validation deals with data validation in general, and indicates an efficient way of coding it.
For output, one must select in detail between a wide range of possible specific formats. But in input, one should be more general, since it is the value and not the layout which is important. The day of the week is not usually given; and, if permitted, is frequently ignored.
For the manual input of dates and times, processing speed is of secondary importance; it may be useful to show what has actually been entered, normalised to a local / standard form. In the processing of pre-prepared dates and times, format flexibility is generally unnecessary.
Note that string reformatting operations may be of use both with input and with output; they are found mainly in 9: Output Formatting.
The year should be 4-digit, but may be 2-digit. There are three basic styles, implemented Date and Time 4 : Validation :-
Techniques used for the above can be adapted for the input of dates in other notations - year-day, ISO or HMRC year-week-day, etc.; often the layout will be better known.
For input validation, see in 4: Validation.
Unless stated otherwise, any of these routines may map years 00-99 to 1900-1999.
If possible, use either "yyyy/mm/dd" or "yyyy Mon dd", which are unambiguous and easily read.
Note, however, that browsers vary and new versions have changes. To be truly safe, avoid the built-in direct conversions between Date Object and String by using intermediate Numbers.
Note that the only alphabetical time-offset indicators accepted by all browsers are GMT & UTC. Instead, use the numeric equivalent.
The following should read a UK-order numeric date string S into a Date object, with or without leading zeroes and with any separator, in any browser, assuming 20xx unless specified :-
Another reliable way to convert valid date strings of known, numeric, format to Date Objects should be along the lines of
A = St.split(/\D+/) ; D = new Date(+A[i], A[j]-1, +A[k])
where ijk is a permutation of 012.
For UK numeric date format string, separated or not, 2- or 4- digit year, to Numbers D M Y :-
The following should read a valid dd-Mon-yy string S into an ISO 8601 string and a Date object, in any browser, assuming 20xx :-
The matching shows extraction of a 1-character day string from "01".."09", not needed here.
According to a letter in the Christmas 2008 PCW, p.121, a basic military date/time format is as "241030ZJUN08", meaning 2008-06-24T10:30Z. The letter, here Z, gives the offset from GMT, in hours. The following should read a valid string of that form S into a Date object, in any browser, assuming year 20xx. Output is also shown.
Note : I believe that the military use the reverse of what my Time Parsing shows for JavaScript; see in my Time Zones. If necessary, change the sign of Ofs, twice.
MS IE 4 & 6 yield "1903-01-02"; US field order is assumed and the previous century is given.
The interpretation of a ##/##/#### date out-of-range for the preferred system can vary.
The third row shows your system's preference. Of rows 2 & 1, one will be compatible with that, and the other will be interpreted either as 2003-01-22 (non-preferred order), or as 2004-09-02 (overrrange accepted); and row 0 will probably give 2004-09-22 (overrange forced) - but behaviour is not guaranteed.
It seems likely that new Date(St) will always correctly interpret as a date a string of the form "YYYY/MM/DD" (but not "YYYY-MM-DD"), provided that YYYY represents a number greater than 99.
MS IE 4, 6 & 7, Firefox 2 & 3, Opera 9, and Safari 3.1 all give "1234-05-06", as is proper.
There is a date validator in 4: Validation.
Maybe not in Safari 1.3.2 ... - see in Date and Time Troubles.
Time input processing is less often needed; it is similar, but simpler. Use the 24-hour clock, and think about the upper limit to be allowed. Be aware of the possible significance of Time Zone and Summer Time.
There is a time validator in 4: Validation.
Take care that a date string such as "2000-09-17" or "17/09/2000" is indeed interpreted as a string, and not as an arithmetic expression (leading in those cases to 1974 or 0.00094).
When defining a date in code, remember that the field order in a string is locale-dependent, and that new Date(Y, M-1, D) and new Date("YYYY Mon DD") are much safer than new Date("DD/MM/YY"), which should never be used on the Web. But new Date("YYYY/MM/DD") seems safe, though new Date("YYYY-MM-DD") is at best unreliable.
To input a string in GMT/UTC with new Date(S), add " GMT" or " UTC" to it; or " Z".
The following should be safe in any reasonable system.
See also for XML, which is older text; and in 8: Enhancing the Object.
ISO 8601 Calendar Date formats are based on yyyy-mm-ddThh:mm:ss±oh:om, Ordinal Dates start yyyy-ddd and Week Dates start yyyy-Www-d instead; T W are literals. The Offset from GMT is + in the east and - in the west.
Similar RegExps can be used for Ordinal Date and for Week Date. It seems likely that no legitimate string will satisfy more than one of the three RegExps.
This uses /\D+/ to be flexible about separators.
Variations can be matched by a RegExp with optional fields. Consider for full Calendar Date
N.B. The code shows a conversion algorithm. It does not validate the format completely, and does not validate the field values at all; it will need to be adapted to its circumstances of use.
If the year is omitted, the code uses (19)77; other defaults should be added if they may be needed.
If the offset from GMT is omitted, then local date is implied; however, one must consider whether it is one's own local or the local of the data source. For non-own local, append offset fields.
function ISO_DT_to_JS_Date(ISO_DT) { // for new Date() argument return ISO_DT.replace(/^(....).(..).(.{11}).*$/, "$1/$2/$3") } // Takes YYYY?MM?DD hh:mm:ss ignoring what follows // For UTC, consider replacing $3 with $3Z or $3 UTC .
For Ordinal Date, treat yyyy-ddd as YYYY Jan DDD and watch out for possible interpretation of, say, 044 as Octal.
For Week Date, process $1 $2 $3 as in Week Number Calculation, $4..$9 as above, and combine. Maybe convert YWD to a daycount DC from YMD, and use Y,M,D+DC.
The 3rd Edition was finalised in 2000, and is routinely implemented. There will be no 4th Edition.
It was said that the 5th Edition would have partial support for input and output more-or-less like ISO 8601. That turns out somewhat exaggerated. The 5th Edition defines I/O support for a single UTC format suitable for use in JSON.
Some current browsers have some support.
BROWSERS | ||||||
---|---|---|---|---|---|---|
new Date(String) | IE8 | FF3 | Op9 | Sf4 | Cr2 | THIS ONE |
OK | OK | OK | OK | OK | ||
NaN | NaN | OK | NaN | OK | ||
NaN | NaN | OK | NaN | OK | ||
NaN | NaN | 2008-01-02 | NaN | NaN | ||
wrong date | NaN | NaN | NaN | NaN | ||
The first table row verifies correct testing of a safe date. |
See also for ISO, which is newer text.
I have read that XML returns the format 2002-06-17T09:25:43.4670000+01:00 , which could mean (probably) 08:25 UTC, or 09:25 / 10:25 UTC. I've seen it described as "regular .NET format".
The following draft methods, which can be tested by pasting into eval (take and use a copy), assume that the format is exact except that the length of the fractional seconds may vary. Here k is constant, probably +1.
With, for example,
var In = "2002-06-17T09:25:43.4670000+01:00"
use A
var D = In.replace(/^(\d{4})-(\d{2})-(\d{2})T([0-9:]*)([.0-9]*)(.)(.*)$/, "$1/$2/$3 $4 GMT") // D = "2002/06/17 09:25:43 GMT" D = Date.parse(D) + 1000*RegExp.$5 // add ms var k = +1 // +1 or 0 or -1 D -= k * Date.parse("1970/01/01 "+RegExp.$7+" GMT") * (RegExp.$6+"1") // TZ D = new Date(D) // Mon, 2002-06-17 08:25:43 UTC
or B (seemed incorrect in MSIE 4)
var D = In.replace( /^(\d{4})-(\d\d)-(\d\d)T([0-9:]*)([.0-9]*)(.)(\d\d):(\d\d)$/, "$1/$2/$3 $4 $6$7$8") // D = "2002/06/17 09:25:43 +0100" D = Date.parse(D) // ?? D += 1000*RegExp.$5 // add ms D = new Date(D) // Mon, 2002-06-17 07:25:43 UTC
or C (as below; seemed correct in MSIE 4)
They include no validation, which should be added for commercial use. They need thorough testing, especially away from the UK Time Zone. They can be compacted.
They did not all give me the same result in MSIE 4; Date.parse("... +0100") /* no GMT */ gave me an unexpected result (Offset).
I have read :-
However, xml extensions have been developed that are being used by many bloggers; and they are known as the "Dublin Core".
Here is a Dublin Core date
<dc:date>2004-05-25T01:37:59-08:00</dc:date>
and its defining xml schema.
That follows ISO 8601, and must represent about 38 minutes past one in the morning, local time, 8 hours away from GMT.
HTML 5 includes new input control types date datetime datetime-local time week month.
This tests <input type=date> and others, which are browser-dependent. They accept and give strings in ISO 8601 YYYY-MM-DD format. So far, only Opera seems interesting and semi-usable.
Browser | Known | Seen | Works | My Result |
---|---|---|---|---|
MSIE 8 | Y | Line | N | Copy |
Firefox 3.0 | Y | Line | N | Copy |
Opera 9.6 | Y | Drop Cal /Scroll | Y * | Derived |
Safari 4.0 | Y | Line | N | Copy |
Chrome 3.0 | Y | Line | N | Copy |
* Except week & month, which give no onChange.
Known draft HTML5 attributes, not necessarily all implemented, are type min max value.
See Datetime types by Olav Junker Kjær, and HTML 5: The Markup Language, a W3c Editor's Draft; also my Calendar Weeks, Week Number Calculation.
Note that it is not really necessary to determine whether a year is Leap, since the length of a month can be obtained without it. As usual, code involving 86400000 or the equivalent is liable to fail at certain times and places.
The UTC day can vary in length by a Leap Second, but JavaScript should know nothing of that.
The civil day can vary in length by an hour, because of Summer Time, which JavaScript understands. See in Date and Time Introduction, 2 : Demonstrations, 5 : Date and Time Elsewhere.
This calculates the difference in 24-hour days between civil dates a week apart, necessarily spanning each EU clock transition :-
Note that new Date(D) where D is a Date Object is not reliable for years before A.D. 100. If D is known to be a Date Object, use +D which will also often be quicker.
Function LastOfMonth is by "rh" using CGjrs from below; see also in Date and Day Count.
Routines without Date Objects are much faster.
Opera Summer Time refers to related errors for years outside about 1968-2038.
February :- Y%4 != 0 ? 28 : Y%100 !=0 ? 29 : Y%400 != 0 ? 28 : 29 28 + (y%4==0) ^ (y%100==0) ^ (y%400==0) 28 + (!(y%4)) ^ (!(y%100)) ^ (!(y%400)) 28 | !(y%4)^!(y%100)^!(y%400) Other Months :- m = 3..13 30 + ((3*m+1)%5 < 3) m = 1,3-12 30 | (m>>3^m) All Months :- m = 0..11
Some of those I have yet to test.
For information on thirty-day months sometimes used in accounting, see in Date Miscellany II.
The EU version, but not the US, can be transformed into a straight difference of 30-day/month daycounts.
Check whether those are the genuine applicable rules.
These are intended for current Gregorian. For Proleptic Gregorian before AD 100, consider adding to y a multiple of 400, such as 4e4.
Note how the routines using UTC are much faster than their non-UTC equivalents, and that routines without Date Objects are much faster than those with; except that Bsxt is amusingly inefficient.
Function LeapUgMm fails in Firefox 2 (3 is OK), TryLY showing ? - Date.UTC(Y, M, D) dislikes negative D.
Or 365 + Number(Leap(y)) for that year.
Note that the difference in time between the beginnings of consecutive instances of YYYY-MM-DD for given MM-DD is usually exactly 365 or 366 days, but can often be greater or less by one hour (ignoring Leap Seconds, as JavaScript should). It can also be 1461 days, or even 2921 days, both probably exact.
The difference for some MM-DD HH-MM can be more complex.