FAQ Topic - How do I format a date with javascript? (2009-06-01)

D

Dr J R Stockton

In comp.lang.javascript message <1q2dnfMVHsc8jbbXnZ2dnUVZ_tednZ2d@supern

On second thoughts, omit the AD but not the BC.
Dr J R Stockton, would it be possible to slightly
reduce your nationalist overtones? You constantly complain about
inconsiderate Americans and Germans (and one in particular), but you
yourself use expressions like "literate English readers" (as opposed to
Americans),

I can only refer to literate English readers; I know nothing of any
literate American ones. The German I complain of is just plain nasty.
It's not compulsory there; contrast MH.
and there's rarely a post from you which doesn't suggest
British supremacy in logic and intelligence.

One must recognise the situation as it is. The Americans have money
instead.
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Sat, 6 Jun 2009 00:51:15, Garrett Smith

[...]
I think I see how the variable name positiveDate is unclear. "positive"
in what sense? Gregorian? Epoch? Astronomers? Yeah. positiveDate is no
good.

commonEradate implies exclusion of year 0.

dateInRange?

Don't struggle to encode a description in a name; it can rarely be done
briefly and accurately. Call it ISODateStr.

Renaming of methods and variables is so common in XP and refactoring in
general that many IDEs have the functionality built right in.

"Refactoring" by Martin Fowler contains good explanations of the
importance of renaming, and why one would care about the name of a
variable.

It is important to give meaningful names so that the code might be
easily understandable by others.

A meaningful name can only be created when author understands If a
meaningful name cannot be created, it indicates that the author needs to
think about the problem the code solves and why the code exists.

That is the point to creating a meaningful names.

[...]
The case is too rare; and can be found on testing. The FAQ should not
recommend bloat which will so infrequently be useful.

If the method is tested thoroughly, one will find that in some cases,
something that when the function is called, is not an ISO 8601 format is
returned.

Returning "00-1-10-19" is not an ISO 8601 format.

A common programming idiom is to check the input argument and see if it
is something that can be handled.
Substantially, when executed by the authoring team as opposed to the
ultimate customer - the in-house testing time.

Sorry, you totally lost me.
In Firefox, RegExp-checking the output of padLeft for being 4 digits is
not much slower than the arithmetic test. I see no need to prepend an
empty string.

The functionality that required sign in the first place was changed.
As it stands, it fails for negative years in Firefox. The code just
ceases execution, and the previous result remains displayed. I've never
needed to use throw, I've rarely seen it here, and the intended FAQ
readership cannot be expected to know. It would be better to use
return "RangeError"

Why? "RangeError" is not an ISO 8601 date.
(note - same length), or to return argument.toString(), or to use alert.

argument.toString() would result in a ReferenceError; |argument| is
undefined.

I believe ISO 9075-2 was referenced. I don't have that and it appears to
be not for free. Tell me: Is the range specified allowed for SQL Date?
Can you propose a sentence for the FAQ?

YYYY-MM-DD

[...]

Garrett
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Sun, 7 Jun 2009 23:19:43, Garrett Smith
Dr J R Stockton wrote:

Renaming of methods and variables is so common in XP and refactoring in
general that many IDEs have the functionality built right in.

"Refactoring" by Martin Fowler contains good explanations of the
importance of renaming, and why one would care about the name of a
variable.

Would it not be better to think for yourself? A name should not be an
essay; it need only be sufficient to make it readily recognisable in the
presence of other names in current use, but not misleading. If code is
cluttered with over-long names, they become a distraction from
understanding what is being done in between.


If the method is tested thoroughly, one will find that in some cases,
something that when the function is called, is not an ISO 8601 format is
returned.

Returning "00-1-10-19" is not an ISO 8601 format.

A common programming idiom is to check the input argument and see if it
is something that can be handled.

You omitted ... and to give an appropriate indication of the situation.
Since a well-written, completed program will never go wrong, the initial
appropriate indication should be diagnostic; there should be no cover-
up.


Remember - presenting a Date Object representing a negative year almost
always is because of a previous error.

Sorry, you totally lost me.

But you do not indicate whether you are now found.

The functionality that required sign in the first place was changed.


Why? "RangeError" is not an ISO 8601 date.

Better to return an obviously wrong value than to just stop and leave
the previous value displayed in a form. If your code was complete, then
it was unsatisfactory; if it was incomplete, it should have been
completed.

The lack of any FAQ entry for "throw" rather suggests that the target
audience knows nothing of it; the only instance of the string in
"Version 16, Updated June 04, 2009" is of no help (and that's the only
instance of "TypeError").
argument.toString() would result in a ReferenceError; |argument| is
undefined.

That argument refers to the Date object supplied. Generally, if a
correctly-formatted result cannot be presented, an incorrectly-formatted
one may be acceptable - especially as it's likely to be seen at
development time, when the immediate aim is not to get answers but to
understand the code under test.
I believe ISO 9075-2 was referenced. I don't have that and it appears
to be not for free.

one said:
Tell me: Is the range specified allowed for SQL Date? Can you propose
a sentence for the FAQ?

SQL dates and JSON dates are off-topic for a section dealing with an ISO
date. Non-equivalences will lead to confusion. Fence them off with a
new subsection heading.

Range - don't know; but would be surprised if negative years were
included. The business case for them must be insignificant. For such
data one needs the Roman calendar.



From what I've seen, however, anyone who can write an ISO date will be
able to write an SQL date or a JSON date, given an appropriate formal or
informal specification.
 
J

John G Harris

Not necessarily. If the result produced by formatDate matches the
production for SQL date then it would be a nice coincidence. It may
very well be different, but if the format matches, it might be worth
mentioning.

Maybe I can get a little more insight from John G Harris. John?
<snip>

This is what ISO/IEC 9075-2:2003 (E), the SQL standard, says. The syntax
spec. for date literals is :

Section 5.3 :
<date value> ::= <years value> <minus sign> <months value> <minus sign>
<days value>

and rule 26 :
The years value must be 4 digits, unsigned
Other numbers must be 2 digits, unsigned

This is a human input format. Dates inside a database or displayed in a
query result can be different.

HTH, John
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Mon, 8 Jun 2009 18:00:21, Garrett Smith
'Refactoring' explains that well enough.

FAQ readers should not be expected to understand jargon terms such as
"refactoring".

The error provides indication of the problem.
[...]

Your "throw" provided no indication that any problem existed, and left
the previously displayed value. That is not acceptable.

Can't see much reason for adding a form.

The code was tested in a form.

Testing with

document.writeln("aaa ", formatDate(new Date(-1e14)), " bbb") // ,

Firefox displays no positive indication of detected error, except in the
error console which the average user will not be seeing.

Where code detects an error, and that error cannot be repaired, then the
reader of the page should not be made to believe that the page has
worked as intended and provided the desired result.

Having a human-readable message string is not sufficiently helpful; to
be useful, it must at least be actually human-read.

^^^^^^^^^^^^^^^^^^^
I read:
| The SQL standard is not freely available, but it may be purchased from
| ISO or ANSI.

Good so far; but did you read the rest of that paragraph? It says
A draft of SQL:2008 is freely available as a zip archive,
however.[14] The zip archive contains a number of PDF files that
define the parts of the SQL:2008 specification.
and [14] says
Zip archive of the SQL:2008 draft from Whitemarsh Information
Systems Corporation.
A new downloadable draft is not as authoritative as a printed standard;
but it may well be useful if handled with care.

Not necessarily. If the result produced by formatDate matches the
production for SQL date then it would be a nice coincidence.

I doubt it; I think it could be intentional.

But you've not followed your own precept : a Google for "SQL Date" gives
a useful-looking first result. Clearly that ISO form should be
acceptable to SQL; clearly SQL is unsafe on freeform dates (it will get
05/11/1605 wrong, I suspect).

The second result swiftly leads to : 0001-01-01 through 9999-12-31 (and
subsets thereof).


OTOH, ISTM that SQL frequently needs a date with time offset indicator,
and/or UTC, at least for cross-zone applications. It would be a mistake
to apparently promise more than is really offered.



Since your code of June 5 uses getDate etc., the text should include
"Civil" or "local" as a reminder that this is not a UTC routine. It'd
be far easier if JavaScript used something like my DATE2 object.

Probably the FAQ should not use "internationally" as that is commonly
used to mean "by foreigners"; "everywhere" is shorter and better.

The reference to 15.9.4.2 is not helpful; Date.parse etc. cannot read
any ISO date format unassisted.


Change the question to
"4.1 How do I write and read a date with javascript?",
add (in essence) new Date(S.replace(/-/g, "/")), and observe that
"25/12/2009", which most people (even in the USA) will recognise as
meaning Christmas Day, will be read as January 12th.
 
T

Thomas 'PointedEars' Lahn

Dr said:
Garrett Smith posted:


FAQ readers should not be expected to understand jargon terms such as
"refactoring".

Rubbish. "Refactoring" is not a jargon term, and FAQ readers should be
expected to be software developers or at least people aspiring to be such
one day. Script-kiddie support is next door.


PointedEars
 
G

Garrett Smith

Thomas said:
Rubbish. "Refactoring" is not a jargon term, and FAQ readers should be
expected to be software developers or at least people aspiring to be such
one day. Script-kiddie support is next door.

"Refactoring" was quoted because I meant it in reference to Martin
Fowler's book that is titled "Refactoring" and subtitled "Improving the
Design of Existing Code".

I did not intend to include the term in the FAQ. That would not be a bad
idea, but it was totally not what I was trying to communicate. I wanted
to mention a book that explains reasoning behind choosing names.

Again, I think "Refactoring" is a good book and Fowler's writing style
is light and sometimes funny.

Interesting stuff on the Bliki, too:-
http://martinfowler.com/bliki/DynamicTypeCheck.html

Interesting stuff and reminiscent of the criticism of the method
overloading found in jQuery.

Regarding the inclusion of new terms in the FAQ, I believe a Glossary
would be helpful.

Anyone who wants the proper recognition ought to write a glossary and
get it approved on the newsgroup. I listed a few terms in an earlier
post. Just search the archives for "glossary". It shouldn't be hard to
find at least 30 more terms that deserve defining, starting with the
term "Refactoring".

The "proper recognition" is your name in the Contributors page.

Garrett
 
G

Garrett Smith

John said:
<snip>

This is what ISO/IEC 9075-2:2003 (E), the SQL standard, says. The syntax
spec. for date literals is :

Section 5.3 :
<date value> ::= <years value> <minus sign> <months value> <minus sign>
<days value>

and rule 26 :
The years value must be 4 digits, unsigned
Other numbers must be 2 digits, unsigned

This is a human input format. Dates inside a database or displayed in a
query result can be different.

HTH, John

I think that is useful and worth mentioning in the FAQ. Anyone searching
for the relevant search terms (9075, javascript, sql date) would find it
in this comment:-

/** Formats a Date to YYYY-MM-DD, compatible with both
* ISO 8601 and ISO/IEC 9075-2:2003 (E) (SQL Date).
* @param {Date} dateInRange year 0000 to 9999.
* @throws {Error} if the year is &amp;lt; 0.
*/

What does 9075 say about the "T" separator for time? The entry mentions
that "T" may be omitted.

Is there a link to something about 9075 that could go in the "see also"?

To answer the question on parsing:
======================================================================
An Extended ISO 8601 Date format YYYY-MM-DD can be parsed to a local
Date with the following:-

function parseISO8601( dateString ) {
var parts = dateString.split(/-/),
// 0-based month for Date.
mm = +parts[1] - 1,
date = new Date;
date.setFullYear(parts[0], mm, parts[2]);
return date;
}
======================================================================

Garrett
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected].
/** Get IS0 8601 format YYYY-MM-DD from a Date Object */
function formatDate(date) {
var year = date.getFullYear(), sign = "", yyyy, mm, dd;
if(year < 0) {
sign = "-";
year = -year;
}
yyyy = sign + padLeft(year, 4, "0"),
mm = padLeft(date.getMonth() + 1, 2, "0"),
dd = padLeft(date.getDate(), 2, "0");
return yyyy + "-" + mm + "-" + dd;
}

Since padding to two digits is commonly required, it is worth having a
concise function call for that case. I use LZ(X) for it.

The year, even if negative, can be handled by

year = date.getFullYear()
String(year).replace(/(\d{1,})/,
function (a, b) { while (b.length<4) b = "0" + b ; return b } )

which does the right thing for negative years. It does not add the +
which may be required for non-negative years if the possibility of
negative years has been agreed between communicating parties.

If there is a nice way of parameterising that 4, so that it can also be
2, then the same type of replacement can be applied to minus the date
and minus the true month, thereby generating the separator followed by a
2-digit field.

One could end up with
ZL( date.getFullYear(), 4) +
ZL(-date.getMonth()-1, 2) +
ZL(-date.getDate(), 2)

Parameterising would also be wanted for the case where it has been
agreed that |year| can exceed 9999.

An example of replace(RegExp, Function) in the FAQ could be useful
anyway.
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Mon, 8 Jun 2009 18:00:21, Garrett Smith


The code was tested in a form.

That problem itself is unrelated to forms or dom.

BTW - The section on DOM and FORMS may get a sentence on what it is.

| This section deals with...

(I am time-limited now, but will draft something).
^^^^^^^^^^^^^^^^^^^
I read:
| The SQL standard is not freely available, but it may be purchased from
| ISO or ANSI.

Good so far; but did you read the rest of that paragraph? It says
A draft of SQL:2008 is freely available as a zip archive,
however.[14] The zip archive contains a number of PDF files that
define the parts of the SQL:2008 specification.
and [14] says
Zip archive of the SQL:2008 draft from Whitemarsh Information
Systems Corporation.
A new downloadable draft is not as authoritative as a printed standard;
but it may well be useful if handled with care.

An unofficial draft. I may have looked at that previously, but am not
sure. I remeber reading some sort of draft but found nothing relevant
and forgot about it.

[...]
Since your code of June 5 uses getDate etc., the text should include
"Civil" or "local" as a reminder that this is not a UTC routine. It'd
be far easier if JavaScript used something like my DATE2 object.

| A local Date object where year >= 0 can be formatted to a common
| ISO 8601 format YYYY-MM-DD with:-

Probably the FAQ should not use "internationally" as that is commonly
used to mean "by foreigners"; "everywhere" is shorter and better.

"Everywhere" makes me think of Green Eggs an Ham. (In a house or with a
mouse, etc).

Internationally is a common word and does not mean "foreigners". I'm not
sure what the term "Foreigners" would mean in the FAQ. British are
foreigners to Americans and vice-versa, though both commonly speak English.
The reference to 15.9.4.2 is not helpful; Date.parse etc. cannot read
any ISO date format unassisted.

The FAQ does not state that Date.parse can read it. That is the reason
this entry exists in the first place, and anyone readiing the spec will
see that, and may very well learn something else.

Mentioning the official specification of the Date object is highly relevant.
Change the question to
"4.1 How do I write and read a date with javascript?",
add (in essence) new Date(S.replace(/-/g, "/")), and observe that
"25/12/2009", which most people (even in the USA) will recognise as
meaning Christmas Day, will be read as January 12th.

I added a parse routine in my reply to John G Harris.

Garrett
 
J

John G Harris

What does 9075 say about the "T" separator for time? The entry mentions
that "T" may be omitted.
<snip>

SQL uses the name timestamp for a combined date and time-of-day. The
syntax, again for literals, is :

<unquoted timestamp string> ::= <unquoted date string> <space> <unquoted
time string>

<unquoted date string> ::= <date value> (as given earlier)

<unquoted time string> ::= <time value> [ <time zone interval> ]

<time value> ::= <hours value> <colon> <minutes value> <colon> <seconds
value>

<time zone interval> ::= <sign> <hours value> <colon> <minutes value>

Each hours, minutes, and seconds value is 2 digits, unsigned.

The value of the <time zone interval> must be in the range -12:59 to
+14:00.

Thus the date and time values are separated by a space, and the time and
zone values are separated by a sign character (+ or -).

John
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Wed, 10 Jun 2009 00:41:15, Garrett Smith
I think that is useful and worth mentioning in the FAQ. Anyone
searching for the relevant search terms (9075, javascript, sql date)
would find it in this comment:-

/** Formats a Date to YYYY-MM-DD, compatible with both
* ISO 8601 and ISO/IEC 9075-2:2003 (E) (SQL Date).
********
There are various types of "SQL Date", according to
<http://msdn.microsoft.com/en-us/library/ms186724.aspx>; it would be
well to indicate which is most intended. That one seems to be "date"
(case-dependence?) so "(SQL 'date' type)" seems better.
* @param {Date} dateInRange year 0000 to 9999.
* @throws {Error} if the year is &amp;lt; 0.
*/

Putting that in means that the effect of throw needs to be described.

What does 9075 say about the "T" separator for time? The entry mentions
that "T" may be omitted.

ISO 8601 says "T" can be omitted, but is not thought to mean that; "T"
can be replaced by a space.

An Extended ISO 8601 Date format YYYY-MM-DD can be parsed to a local

To me, "parse" implies a reasonable degree of format checking, such as
(at least) using a moderately rigorous RegExp. Here and in the function
name, "read" would be better.
Date with the following:-

function parseISO8601( dateString ) {
var parts = dateString.split(/-/),
// 0-based month for Date.
mm = +parts[1] - 1,
date = new Date;
date.setFullYear(parts[0], mm, parts[2]);
return date;
}

The + is not needed. The code does not handle negative years. The
function name implies that it will parse all ISO 8601 date strings; at
least, YYYY-MM-DD or Y-M-D should be in comment.

The Date2 object's new Date2(String) also can read week numbering and
ordinal dates.

The code adds to the date supplied the time that it is executed, more or
less.

One should not use new Date[()] unless at least some of the current
date/time is required. Using new Date(0) is considerably faster, and
means that the return value is fully determinate. Here in the UK it
will have a time part of 00:00:00.0 UTC, but setFullYear seems to handle
that as desired even if the input date is Summer. Away from the UK time
zone I think the result will contain a time part equal or opposite to
the Winter Offset.

Use, therefore, setHours(0,0,0,0).

Test in locations on either side of the UK zone and of the equator (any
sufficiently South Americans here?), and, since new Date(0) can give Dec
31, include testing with a February date. In fact, new Date(864e6)
might be better; well away from month-end. And, in test, try 200*864e5;
that could flush an Australian bug.

Substituting to use new Date("YYYY/MM/DD") is much simpler and safer.
Moreover, that can also read as GMT by merely appending " GMT", and can
read with a known Time Offset, if care is taken to substitute only the
separators and not the sign of the offset.


The FAQ should at least mention date validation; you can find details on
my site.
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Wed, 10 Jun 2009 11:54:47, Garrett Smith
That problem itself is unrelated to forms or dom.

It was, and is likely to be, seen in a form, where a location expected
to receive a new result may well hold an old result. If in the middle
of the new calculation the code throws up the towel, the old one
misleadingly remains. Recognised code errors must always be brought to
the attention of the end user, except when the result of the code is not
significant (and the code should be deleted).



| A local Date object where year >= 0 can be formatted to a common
| ISO 8601 format YYYY-MM-DD with:-

No. There is no such thing as a local date object in JavaScript; all
date objects hold UTC. It is the YYYY-MM-DD which is local.



"Everywhere" makes me think of Green Eggs an Ham. (In a house or with a
mouse, etc).

Your problem.
Internationally is a common word and does not mean "foreigners".

Correct. But it is commonly used for that; mainly by Americans, but
today also by the BBC.
I'm not sure what the term "Foreigners" would mean in the FAQ.

That is precisely why "international" should not be used when foreigners
is inappropriate. But it would be entirely reasonable to use
"foreigners" in FAQ section 1, perhaps saying "Remember when writing
that most of your readers here will be foreigners; avoid localisms.".
British are foreigners to Americans and vice-versa, though both
commonly speak English.

That's a matter of opinion - I was never quite sure just what George
Bush Junior spoke.

The term "world-wide" is better and shorter than "international" - and
it includes everywhere, whereas many places are not in nations. The
Isle of Man, for example.

The FAQ does not state that Date.parse can read it.

The implication of including a link is that following the link and
reading what is indicated will be useful, and more useful than could be
written in a like or two of FAQ. Use instead, at the top of 4.1,
something brief like "JavaScript has no specific string formatting.", if
you must; but that's obvious enough from the FAQ section.
Mentioning the official specification of the Date object is highly relevant.

The specification is section 15.9, not 15.9.something.


I added a parse routine in my reply to John G Harris.

When you write date code, you repeat all the traditional mistakes that
were well-known here long before you appeared. A FAQ maintainer must be
an editor; a FAQ maintainer should only try to be an author in respect
of those parts where he has particular expertise.


FAQ Section 4 has few entries.

A common question has been like "Why is my date/time result wrong?".

Common causes include :

* Days are not always 24 hours long; remember Summer Time's effects.
* Months can be short; going past a month-end gives the following month.
* Using Date routines that take years 00-99 as meaning 1900-1999.
* Expecting code of US origin to handle DD/MM/YYYY.
* Using the 12-hour clock.

Common solutions include :

* Rounding to the nearest day.
* Using UTC rather than local time, e.g. for calendars.
* Using get/setFullYear; offsetting +400 years -4800 months.
* Converting to YYYY/MM/DD.
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Wed, 10 Jun 2009 00:41:15, Garrett Smith
<[email protected]> posted:
[...]


Putting that in means that the effect of throw needs to be described.

"throw" is a common flow-of-control term in programming. It could be
listed in the glossary, if such thing is to be created.
What does 9075 say about the "T" separator for time? The entry mentions
that "T" may be omitted.

ISO 8601 says "T" can be omitted, but is not thought to mean that; "T"
can be replaced by a space.

An Extended ISO 8601 Date format YYYY-MM-DD can be parsed to a local

To me, "parse" implies a reasonable degree of format checking, such as
(at least) using a moderately rigorous RegExp. Here and in the function
name, "read" would be better.
Date with the following:-

function parseISO8601( dateString ) {
var parts = dateString.split(/-/),
// 0-based month for Date.
mm = +parts[1] - 1,
date = new Date;
date.setFullYear(parts[0], mm, parts[2]);
return date;
}

The + is not needed. The code does not handle negative years. The
function name implies that it will parse all ISO 8601 date strings; at
least, YYYY-MM-DD or Y-M-D should be in comment.

Right, conversion to number happens with "-".

"3" - 2; // 1.

Fixed that and added a doc comment.

Method parseISO8601 has a choice for handling a cases with
dateStringInRange.

1) return an invalid date or 2) throw an error.

An invalid date keeps the code a tiny bit smaller. It is like returning
null however, the returned object is a Date, so it does not require a
null check.

OTOH, throwing an error would require the caller to either a) perform
pre-parse test, or b) wrap the call in try-catch. I don't like either of
these.

I think an invalid date is the better solution here. The caller can
always check it with:-

if(isNaN(invalidDate));

If I am going to go with that, I can also use regexp.exec(
dateStringInRange ) to avoid error.
The Date2 object's new Date2(String) also can read week numbering and
ordinal dates.

The code adds to the date supplied the time that it is executed, more or
less.

One should not use new Date[()] unless at least some of the current
date/time is required. Using new Date(0) is considerably faster, and
means that the return value is fully determinate. Here in the UK it
will have a time part of 00:00:00.0 UTC, but setFullYear seems to handle
that as desired even if the input date is Summer. Away from the UK time
zone I think the result will contain a time part equal or opposite to
the Winter Offset.

Use, therefore, setHours(0,0,0,0).

OK.

Test in locations on either side of the UK zone and of the equator (any
sufficiently South Americans here?), and, since new Date(0) can give Dec
31, include testing with a February date. In fact, new Date(864e6)
might be better; well away from month-end. And, in test, try 200*864e5;
that could flush an Australian bug.

I don't quite understand your testing methodology. It sounds like it
would require changing the computer's time zone. Did it get that right?

NaN should generate an invalid date in any time zone, so what you wanted
to test would not apply here, right?
Substituting to use new Date("YYYY/MM/DD") is much simpler and safer.
Moreover, that can also read as GMT by merely appending " GMT", and can
read with a known Time Offset, if care is taken to substitute only the
separators and not the sign of the offset.

I can't recommend relying on undocumented implementation extensions.

Not here:
http://bclary.com/2004/11/07/#a-15.9.4.1
or here:
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/parse
or here:
http://msdn.microsoft.com/en-us/library/cd9w2te4(VS.85).aspx

Taking all of the suggestions, the new draft follows:-

================================================================
4.1 How do I write and read a date with javascript?

The ISO 8601 Extended format can be understood internationally, without
ambiguity.

A local Date object where year >= 0 can be formatted to a common ISO
8601 format YYYY-MM-DD with:-

/** Formats a Date to YYYY-MM-DD, compatible with both
* ISO 8601 and ISO/IEC 9075-2:2003 (E) (SQL 'date' type).
* @param {Date} dateInRange year 0000 to 9999.
* @throws {Error} if the year is < 0.
*/
function formatDate(dateInRange) {
var year = dateInRange.getFullYear(),
isInRange = year > 0 && year < 9999,
yyyy, mm, dd;
if(!isInRange ) {
throw Error("year must be 0000-9999");
}
yyyy = padLeft(year, 4, "0");
mm = padLeft(dateInRange.getMonth() + 1, 2, "0");
dd = padLeft(dateInRange.getDate(), 2, "0");
return yyyy + "-" + mm + "-" + dd;
}

/**
* @param {string} input: input value converted to string.
* @param {number} size: desired length of output.
* @param {string} ch: single character to prefix to s.
*/
function padLeft(input, size, ch) {
var s = input + "";
while (s.length < size) {
s = ch + s;
}
return s;
}

Never use a local date/time for a non-local event. Instead, use UTC, as
in ISO 8601 YYYY-MM-DDThh:mm:ssZ ( Z is the only letter suffix).

The T may be omitted where doing so would not cause ambiguity. For an
SQL date, it must be replaced by a single space.

For a local date/time with time offset, to unambiguously indicate a
particular instant, use ISO 8601 format YYYY-MM-DDThh:mm:ss±hh:mm.

An Extended ISO 8601 Date format YYYY-MM-DD can be parsed to a local
Date with the following:-

/**Parses string formatted as YYYY-MM-DD to a Date object.
* If the supplied string does not match the format, an
* invalid Date is returned.
* @param {string} dateStringInRange format YYYY-MM-DD, with year in
* range of 0000-9999, inclusive.
* @return {Date} Native Date object representing the string.
*/
function parseISO8601( dateStringInRange ) {
var date = new Date(NaN),
isoExp, parts;
isoExp = /^\s*([\d]{4})-(0[1-9]|1[0-2])-([0-2][0-9]|(?:3[0|1]))\s*$/;
parts = isoExp.exec(dateStringInRange);

if(parts) {
date.setFullYear(parts[1], parts[2] - 1, parts[3]);
date.setHours(0,0,0,0);
}
return date;
}

See also:
* ECMA-262 Date.prototype, s. 15.9.4.2
* http://en.wikipedia.org/wiki/ISO_8601
* ISO 8601:2004(E)
* http://www.merlyn.demon.co.uk/js-date9.htm
================================================================

The entry is long.

Garrett
 
J

John G Harris

In comp.lang.javascript message <[email protected]
september.org>, Wed, 10 Jun 2009 00:41:15, Garrett Smith

********
There are various types of "SQL Date", according to
<http://msdn.microsoft.com/en-us/library/ms186724.aspx>; it would be
well to indicate which is most intended. That one seems to be "date"
(case-dependence?) so "(SQL 'date' type)" seems better.
<snip>

Those web pages describe Microsoft's SQL Server product. It understands
many date formats. That includes the ANSI SQL Standard format. See
<URL:http://msdn.microsoft.com/en-us/library/ms180878.aspx#StringLiteralDateandTimeFormats>
Microsoft don't refer to the ISO standard because it's well known that
only communists and other enemies of democracy refer to "International"
documents. Only the American national standard will do. (That's more
true than it ought to be.)

Incidentally, ISO 9075-2:2003 has been superseded by ISO 9075-2:2008,
but that costs $450 dollars even as a PDF file.

John
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Wed, 10 Jun 2009 11:54:47, Garrett Smith


It was, and is likely to be, seen in a form, where a location expected
to receive a new result may well hold an old result. If in the middle
of the new calculation the code throws up the towel, the old one
misleadingly remains. Recognised code errors must always be brought to
the attention of the end user, except when the result of the code is not
significant (and the code should be deleted).

Maybe the FAQ needs an entry for Testing.

And error handling.

A quick web search result:
http://en.wikipedia.org/wiki/Exception_handling

| From the point of view of the author of a routine, raising an
| exception is a useful way to signal that a routine could not execute
| normally. For example, when an input argument is invalid

That says it in a nutshell.

Try to understand that the "user" is not calling this code
No. There is no such thing as a local date object in JavaScript; all
date objects hold UTC. It is the YYYY-MM-DD which is local.

I've revised to:-

Your problem.


Correct. But it is commonly used for that; mainly by Americans, but
today also by the BBC.

ISO need not to change their name to FSO.

I've not heard that usage. I saw:-
http://www.thefreedictionary.com/international

| 2. Extending across or transcending national boundaries
That's a matter of opinion - I was never quite sure just what George
Bush Junior spoke.

That's your problem.

The term "world-wide" is better and shorter than "international" - and
it includes everywhere, whereas many places are not in nations. The
Isle of Man, for example.

I disagree based on the dictionary definition.

If anyone from "Isle of Man" feels excluded by "international", I'll
reconsider.
The implication of including a link is that following the link and
reading what is indicated will be useful, and more useful than could be
written in a like or two of FAQ. Use instead, at the top of 4.1,
something brief like "JavaScript has no specific string formatting.", if
you must; but that's obvious enough from the FAQ section.


The specification is section 15.9, not 15.9.something.

Got it.
When you write date code, you repeat all the traditional mistakes that
were well-known here long before you appeared. A FAQ maintainer must be
an editor; a FAQ maintainer should only try to be an author in respect
of those parts where he has particular expertise.

Dates are your area of expertise and your reviews are helpful. Please do
comment on the revised version.
FAQ Section 4 has few entries.

No, it only has one. Are you suggesting more?
A common question has been like "Why is my date/time result wrong?".

Common causes include :

* Days are not always 24 hours long; remember Summer Time's effects.
* Months can be short; going past a month-end gives the following month.
* Using Date routines that take years 00-99 as meaning 1900-1999.
* Expecting code of US origin to handle DD/MM/YYYY.
* Using the 12-hour clock.

Common solutions include :

* Rounding to the nearest day.
* Using UTC rather than local time, e.g. for calendars.
* Using get/setFullYear; offsetting +400 years -4800 months.
* Converting to YYYY/MM/DD.

Why do you advise "offsetting +400 years -4800 months"? What is wrong
with just setFullYear?

Garrett
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Thu, 11 Jun 2009 09:13:27, Garrett Smith
Dr J R Stockton wrote:

"throw" is a common flow-of-control term in programming. It could be
listed in the glossary, if such thing is to be created.

That does not mean that the FAQ reader will know what the effect of
throw Error("year must be 0000-9999");
will be in all browsers and all settings.

IMHO, reserved words in computer languages should not be common words in
common natural languages, since that makes it hard to search for the
reserved word itself. Bring back the KDF9, where IIRC reserved words
all started with "!" and could be abbreviated once unique with "." - the
compiler would understand "!fun." and the editor could auto-expand it to
"!function".

Perhaps SAM or others could help there; if there is a good
JavaScript text - not the standard - on-line in French, it could
be searched for "throw" and one would find only the reserved
word proper, avoiding instances of the verb "jeter".

Using "alert" would be better; one knows what it will do, and it's an
obvious candidate for replacement if unsuited to circumstances.
Consider it as a place-holder for whatever suits the application.

An invalid date keeps the code a tiny bit smaller. It is like returning
null however, the returned object is a Date, so it does not require a
null check.

AFAIK, there is only one "invalid" value that a Date Object
can have, NaN. If it is used, there should be a warning that
new Date(NaN).toString() is browser-dependent.

FWIW, here new Date(NaN) is slower than new Date(0) in FF but not IE.

if(isNaN(invalidDate));
^ ^ Omit; add " ..." or " { ... }".
| Yes; but more readable with a space there

If I am going to go with that, I can also use regexp.exec(
dateStringInRange ) to avoid error.

Don't know whether/why 'exec' is the best method of RegExp or String to
use there. But it may well be.

I don't quite understand your testing methodology. It sounds like it
would require changing the computer's time zone. Did it get that right?

Either that or using consultants elsewhere. We have readers in
positive, zero, and negative northern zones, and at least a positive
southern one. But you don't mean time zone, you mean offset; AFAIK, no-
one here currently has LCT = UTC.

NaN should generate an invalid date in any time zone, so what you
wanted to test would not apply here, right?

Yes, but I've not tested that. But NaN is a valid date in JavaScript,
one which is off-calendar. It should not lightly be called invalid.

I can't recommend relying on undocumented implementation extensions.

To be consistent, you'll have to avoid new Date("1/20/2009") for the
Inauguration.

Anyone relying on the first release of any software is a fool; any
browser not accepting "2009/01/20" correctly on or after the second
release should never be trusted, because five major browsers accept it
(and I've never known them not to).

And you might have to avoid 'throw', since the Standard seems not to
define the actual effect seen (and if it does, it has to define it as
somewhat indeterminate, since browsers behave differently).

For a FAQ, it is only actual browser behaviour that matters, though it
would be wrong to recommend something definitely contrary to the
Standard. Where the Standard is silent, the facts can speak.

Taking all of the suggestions, the new draft follows:-

================================================================
4.1 How do I write and read a date with javascript?

The ISO 8601 Extended format can be understood internationally, without
xxx ^An (there are more than one) (don't use Any[0])
world-wide ---> xxxxxxxxxxxxxx
ambiguity.

A local Date object where year >= 0 can be formatted to a common ISO xxxxx with?
8601 format YYYY-MM-DD with:-

/** Formats a Date to YYYY-MM-DD, compatible with both ^ (local time)
* ISO 8601 and ISO/IEC 9075-2:2003 (E) (SQL 'date' type).
* @param {Date} dateInRange year 0000 to 9999.
* @throws {Error} if the year is < 0.
*/
function formatDate(dateInRange) {
var year = dateInRange.getFullYear(),
isInRange = year > 0 && year < 9999,
yyyy, mm, dd;
if(!isInRange ) {
throw Error("year must be 0000-9999");

You cannot guarantee that to be the only throw of that string in the
page; "formatDate : year must be 0000-9999" could be better.
}
yyyy = padLeft(year, 4, "0");
mm = padLeft(dateInRange.getMonth() + 1, 2, "0");
dd = padLeft(dateInRange.getDate(), 2, "0");
return yyyy + "-" + mm + "-" + dd;
}

I see no need to have both yyyy & year. Entia non sunt multiplicanda
praeter necessitatem.
/**
* @param {string} input: input value converted to string.
* @param {number} size: desired length of output.
* @param {string} ch: single character to prefix to s.
*/
function padLeft(input, size, ch) {
var s = input + "";

or input += "" and consequential changes
while (s.length < size) {
s = ch + s;
}
return s;
}

Never use a local date/time for a non-local event. Instead, use UTC, as
in ISO 8601 YYYY-MM-DDThh:mm:ssZ ( Z is the only letter suffix). ?? postfix ??

The T may be omitted where doing so would not cause ambiguity. For an
SQL date, it must be replaced by a single space.

For a local date/time with time offset, to unambiguously indicate a
particular instant, use ISO 8601 format YYYY-MM-DDThh:mm:ss±hh:mm.

An Extended ISO 8601 Date format YYYY-MM-DD can be parsed to a local
A local date in Extended ISO 8601 format YYYY-MM-DD can be read to a
Date with the following:-
Date Object with the following:-
* @return {Date} Native Date object representing the string.

Can you promise that the Native one has not been superseded?
function parseISO8601( dateStringInRange ) {
var date = new Date(NaN),
isoExp, parts;
isoExp = /^\s*([\d]{4})-(0[1-9]|1[0-2])-([0-2][0-9]|(?:3[0|1]))\s*$/;
parts = isoExp.exec(dateStringInRange);

if(parts) {
date.setFullYear(parts[1], parts[2] - 1, parts[3]);
date.setHours(0,0,0,0);

With new Date(NaN), setFullYear cannot avoid setting the "local time
part"; therefore, setHours may not now be essential.
}
return date;
}

See also:
* ECMA-262 Date.prototype, s. 15.9.4.2 15.9
* http://en.wikipedia.org/wiki/ISO_8601
* ISO 8601:2004(E)
* http://www.merlyn.demon.co.uk/js-date9.htm


The RegExp is not acceptable. It partly but incompletely validates the
date. The date should be either tested for character pattern or not so
tested; and it should either be fully numerically validated or not
numerically validated at all.

Again, the answers are all in the archives[1], and Thomas seems to be
slipping since AFAIR he has not told you that.

Test the pattern for, at the very minimum,
/^\D*\d+\D+\d+\D+\d{1,2} then non-digit or end/.
Apply the three fields as above into new Date,
check the Month of the date object agrees with the second numeric field.
Note : I know that RegExp pattern is more liberal than ISO 8601.
Note : RegExp for non-extended can be given; /^\s+-?\d{4,}-\d\d-\d\d ...
Note : I left the parentheses off, for simplicity.

With the Date2 Object, one piece of code like that should serve both for
local date and UTC date.

[0] Unwise to claim that ISO Week Numbering will be understood in NA.
[1] and on my site.
 
G

Garrett Smith

Dr said:
In comp.lang.javascript message <[email protected]
september.org>, Thu, 11 Jun 2009 09:13:27, Garrett Smith


That does not mean that the FAQ reader will know what the effect of
throw Error("year must be 0000-9999");
will be in all browsers and all settings.

The entry:

"11.1 How do I get my browser to report javascript errors?"

covers the subject of browsers reporting errors.
IMHO, reserved words in computer languages should not be common words in
common natural languages, since that makes it hard to search for the
reserved word itself. Bring back the KDF9, where IIRC reserved words
all started with "!" and could be abbreviated once unique with "." - the
compiler would understand "!fun." and the editor could auto-expand it to
"!function".

Perhaps SAM or others could help there; if there is a good
JavaScript text - not the standard - on-line in French, it could
be searched for "throw" and one would find only the reserved
word proper, avoiding instances of the verb "jeter".

I do not know KDF9, but "throw" is a basic computer science term nowadays.
Using "alert" would be better; one knows what it will do, and it's an
obvious candidate for replacement if unsuited to circumstances.
Consider it as a place-holder for whatever suits the application.

No! The alert method has no place in such code. Calling alert there
would be a bad side effect. It would be awkward for the caller to handle
that gracefully. Consider:-

var realAlert = window.alert;
window.alert = logError;
formatDate( myDate );
window.alert = realAlert;

That is awful.

The problem with |throw| is that it is a more recent addition to the
language. Like |in|, it doesn't work in as many browsers (Mac IE).
AFAIK, there is only one "invalid" value that a Date Object
can have, NaN. If it is used, there should be a warning that
new Date(NaN).toString() is browser-dependent.

The return value of Date.prototype.toString itself is
implementation-dependent and that is stated in the specification which
is linked.
FWIW, here new Date(NaN) is slower than new Date(0) in FF but not IE.


^ ^ Omit; add " ..." or " { ... }".
| Yes; but more readable with a space there

Correct, but I did not intend to add that to the FAQ.
Don't know whether/why 'exec' is the best method of RegExp or String to
use there. But it may well be.



Either that or using consultants elsewhere. We have readers in
positive, zero, and negative northern zones, and at least a positive
southern one. But you don't mean time zone, you mean offset; AFAIK, no-
one here currently has LCT = UTC.



Yes, but I've not tested that. But NaN is a valid date in JavaScript,
one which is off-calendar. It should not lightly be called invalid.

It's like a "Null Object".
To be consistent, you'll have to avoid new Date("1/20/2009") for the
Inauguration.

Anyone relying on the first release of any software is a fool; any
browser not accepting "2009/01/20" correctly on or after the second
release should never be trusted, because five major browsers accept it
(and I've never known them not to).


And you might have to avoid 'throw', since the Standard seems not to
define the actual effect seen (and if it does, it has to define it as
somewhat indeterminate, since browsers behave differently).

The throw statement was a new Edition to ECMA-262. It does not seem to
work in Mac IE, but that does not surprise me much.
For a FAQ, it is only actual browser behaviour that matters, though it
would be wrong to recommend something definitely contrary to the
Standard. Where the Standard is silent, the facts can speak.

I can't see a good reason for favoring a non-standard approach. The
standard way of solving the problem is simple, guaranteed, and clearly
documented.

When sites are developed that way, it puts a greater burden on new
browsers to support nonstandard (and undocumented in this case) behavior
and that behavior can only be discovered by the implementation testing
those sites that use that nonstandard behavior.

Taking all of the suggestions, the new draft follows:-

================================================================
4.1 How do I write and read a date with javascript?

The ISO 8601 Extended format can be understood internationally, without
xxx ^An (there are more than one) (don't use Any[0])
world-wide ---> xxxxxxxxxxxxxx

ISO 8601 Extended formats can be...

or

[Most|Common|Many] 8601 Extended formats can be...

or even

Many common ISO 8601 Extended formats can be..

Though I think I prefer:-
| Common 8601 Extended formats can be...

I do not have a set of which formats are common. I know that YYYY-MM-DD
is common, and is recommended by the w3c[1].

(the next paragraph following that one then describes one such format).

How about that?

You cannot guarantee that to be the only throw of that string in the
page; "formatDate : year must be 0000-9999" could be better.


I see no need to have both yyyy & year. Entia non sunt multiplicanda
praeter necessitatem.

Look at the return statement:-

| return yyyy + "-" + mm + "-" + dd;

The intent of the code and code is doing could not be any clearer.

I think it should stay that way, even at the cost of having an extra
variable.

[...]
?? postfix ??

Don't those two words mean the same thing?
Can you promise that the Native one has not been superseded?

By what, a user-defined one? That would break it.
if(parts) {
date.setFullYear(parts[1], parts[2] - 1, parts[3]);
date.setHours(0,0,0,0);

With new Date(NaN), setFullYear cannot avoid setting the "local time
part"; therefore, setHours may not now be essential.

Good.
}
return date;
}

See also:
* ECMA-262 Date.prototype, s. 15.9.4.2 15.9
* http://en.wikipedia.org/wiki/ISO_8601
* ISO 8601:2004(E)
* http://www.merlyn.demon.co.uk/js-date9.htm


The RegExp is not acceptable. It partly but incompletely validates the
date. The date should be either tested for character pattern or not so
tested; and it should either be fully numerically validated or not
numerically validated at all.

Again, the answers are all in the archives[1], and Thomas seems to be
slipping since AFAIR he has not told you that.

Test the pattern for, at the very minimum,
/^\D*\d+\D+\d+\D+\d{1,2} then non-digit or end/.
Apply the three fields as above into new Date,
check the Month of the date object agrees with the second numeric field.

It occurred to me to validate that by calling the relevant date methods
but I did not attach much significance to it and considered the function
could return an adjusted date (from MakeDay), should the caller pass a
February 29 on a non-leap year, for example.

If validation is the way to go, the parts should be checked in order
from most to least likely to fail. That is the days, then months, then
years. By doing this, failure is reached faster.

If the validation is going to fail, it will have a greater chance of
failing sooner with a stricter regexp (a null match avoids method calls):
/^\s*([\d]{4})-(0[1-9]|1[0-2])-([0-2][0-9]|(?:3[0|1]))\s*$/

However, the simpler pattern posted earlier is easier to read and
perhaps more suitable for the FAQ.

The pattern should ignore leading and trailing whitespace only and not
try and find a date format buried in a string. \s is probably enough
here. I know that implementations are not consistent with each other or
the specification on \s and we have discussed the Mongolian vowel
separator and no-break space (\u00a0).

Revised:-

/**Parses string formatted as YYYY-MM-DD to a Date object.
* If the supplied string does not match the format, an
* invalid Date (value NaN) is returned.
* @param {string} dateStringInRange format YYYY-MM-DD, with year in
* range of 0000-9999, inclusive.
* @return {Date} Native Date object representing the string.
*/
function parseISO8601( dateStringInRange ) {
var date = new Date(NaN),
isoExp, parts;
isoExp = /^\s*([\d]{4})-(\d\d)-(\d\d)\s*$/;
parts = isoExp.exec(dateStringInRange);

if(parts) {
date.setFullYear(parts[1], parts[2] - 1, parts[3], 0, 0, 0, 0);
if(parts[2] != date.getDate()
|| parts[1] != date.getMonth() + 1
|| parts[0] != date.getFullYear() ) {
date.setFullYear(NaN);
}
}
return date;
}
Note : I know that RegExp pattern is more liberal than ISO 8601.

And more liberal than the one I provided.
With the Date2 Object, one piece of code like that should serve both for
local date and UTC date.

It looks like Date2[2] handles parsing and setting of various ISO 8601
formats.

I think is more complicated than the example would need. While you have
some good ideas, I find function names like ZBDoCY to be unmemorable and
confusing.
[0] Unwise to claim that ISO Week Numbering will be understood in NA.

What is NA?

Garrett

[1]http://www.w3.org/QA/Tips/iso-date
[2]http://www.merlyn.demon.co.uk/js-dobj2.htm#DOP
 
J

John G Harris

Don't those two words mean the same thing?
<snip>

Postfix is jargon and awkward to say. Suffix is better.

KDF9 was a postfix (RPN) computer. To add two variables in User Code
(assembly) you would do something like
V0; V1; +; =V2;

John
 
D

Dr J R Stockton

In comp.lang.javascript message <[email protected]
september.org>, Thu, 11 Jun 2009 14:44:38, Garrett Smith
Maybe the FAQ needs an entry for Testing.

Undoubtedly. That's partly why
scant as it is said:
And error handling.

With the exception of feature detection for RegExp level, I can't
immediately recall a case where throw/try-type error handling is both
necessary and possible (except that ISTM that an un-handled throw is in
effect a QUIT statement). If the need for a throw is detected, code can
be used to recover of give up more gracefully.

Try to understand that the "user" is not calling this code

It is the user who is executing the page. A Web page without users is
of no use.


I've revised to:-

| An Extended ISO 8601 local Date format <ICODE>YYYY-MM-DD</ICODE> can
| be parsed to a Date with the following:-

Of course, parsing a format and parsing a date string are not the same
thing.

"A local date string in extended ISO 8601 format <ICODE>YYYY-MM-
DD</ICODE> can be read to a Date Object with the following :-"


ISO need not to change their name to FSO.

That makes no sense. They use International with its proper meaning;
they could have used WW as in WWW, except that I rather suspect ISO is
not actually World-Wide - it does indeed not list the Holy See, or
Monaco, or the Isle of Man, participating. The WWW could have been
called the IW, except for it not being organised by Nations as such, and
IW is dangerously near IWW.

By Farlex, Inc. One sees in it what one expects to find in American
dictionaries.
That's your problem.

I'm not at all sure that it wasn't better that way; no problem.


Dates are your area of expertise and your reviews are helpful. Please
do comment on the revised version.

That reminds me of a Wagner joke, which Google seems not to know.

Why do you advise "offsetting +400 years -4800 months"? What is wrong
with just setFullYear?


It is an alternative. It can be used when a Date Object is not
necessary or getFullYear is not sufficient, for example in

DATE2.UTC = function (a, b, c, d, e, f, g) {
if (Math.abs(a)<100) { a += 400 ; b -= 4800 } // Fix years 0-99
return Date.UTC(a, b||0, c||1, d||0, e||0, f||0, g||0) }

And "solutions include" makes no claim to include all possibilities.
And the FAQ is for code readers as well as code writers; readers may
need to recognise that way of treating such problems.

I see a problem to fix in that code for |a|<100 & b is undefined.


* * *

To my recent post on validation : as well as adding 1900 to years before
100, it will consequently reject 0000-02-29. Fix by changing the
allowable year range.

It might be well just to always add 400 to the year and subtract 4800
months; the loss of 400 years up to & including Sep 12 275760 cannot be
of great immediate importance.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,098
Messages
2,570,625
Members
47,236
Latest member
EverestNero

Latest Threads

Top