Effectively the JSF DateTime Converter is a JavaScript implementation of the Java™ SimpleDateFormat class.
The DateTime Converter converts a string (often the value of a input field) to a JavaScript date object and vice versa. The conversion is controlled by a Java DateFormat pattern that describes the format of the date (e.g., order of the date components, each component's format, the separators, etc.).
The format used by a DateConverter is "locale-dependent". The format defines exactly the order of date components (e.g., whether the components are displayed in month-day-year order or year-month-day order). It also defines exactly which characters are used as separators between the components (e.g., that / characters are used to separate all components or that a space is used between the first two components and comma-space is used between the second two components. All "words" used in a date/time are taken from the localized string file which has been included in (or emitted into) the page. All conversions within the page will use the same set of words (e.g., all conversions will be in French). You cannot currently have some conversions use one language while other conversions use a different language.
To as great a degree as is practical, the DateConverter converts the dates/strings using the same algorithm used by the Java DateFormat class so that client-side and server-side conversions are the same.
The converter is used by other client side componentry such as the JSFBehaviorValidate, DateTimeAssist (keyboard assist for date/time) and the client-side data caching. It can also be used directly a programmer who needs to "cast" a string containing a formatted date into a JavaScript Date object.
hX_5.addConverter("id", new hX_5.DateTimeConverter(attributes)); where
id |
The ID of the HTML tag to which the component is attached. |
Attribute name |
Description |
||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
format |
The Java datetime pattern to use when converting this value. See below for details. |
||||||||||
ICU4J |
If present (and not false), asterisk and plus will be parsed as pattern characters and secondary grouping symbols will be parsed in the pattern. |
||||||||||
base-2digit-span |
When parsing a two-digit year, this many years before the current date are treated as "last century". For example, if this year is 2003 and the span is 30 then years between "73" and "99" will be treated as "1973 through 1999". Note that the Microsoft® norm for this value is 30 while the Java norm is 80. |
||||||||||
first-day-of-week |
If present, specifies which day of the week
(0 meaning Sunday, 1 meaning Monday, ...) is used when calculating "weeks".
Note: This
should always be provided (even if ICU4J is not set so that "w" and "Y" can
be correctly calculated.
|
||||||||||
digits |
If provided, the representation of the single character that represents a valid zero digit in the Unicode character set OTHER THAN the Western "0" character. This character and the following 9 characters in the Unicode character set will be output as "digits" when a value is formatted and will be parsed as digits when a value is converted from a string. This allows support for non-Western digits such as proper Arabic and/or Indic-Arabic digits. The value is the Unicode code point of this character expressed in decimal. For example, an Arabic zero is HEX 660, so 1632 should be the value of this attribute in this case. |
||||||||||
epoch |
Which calendar/epoch system to use. Can be specified even
if ICU4J is not set. The following systems are supported:
|
||||||||||
strict |
If 0 (liberal), all "literals" are ignored while parsing a value, mis-ordered components will be rearranged, any missing component is supplied using the current datetime, misspellings and incomplete spellings are allowed (to the extent that they can be evaluated), "E" and other non-unique components are ignored. If 1 (strict), all "literals" are ignored while parsing a value, any missing component is supplied using the current datetime, misspellings and incomplete spellings are allowed (to the extent that they can be evaluated), "E" and other non-unique components are ignored. For example if the pattern is MMMM dd, yyyy a user can enter "Feb/02/04" and it will be parsed as February 02, 2004. If 2 (very strict), an exact match is required except long month names can be "shortened" to the minimum number of unique characters and "E" and other non-unique components are ignored. Literals must match exactly. |
For more information on the Java Decimal Format number pattern, refer to http://java.sun.com/j2se/1.4.2/docs/api/java/text/DateFormat.html (when ICU4J is not enabled) and at http://icu.sourceforge.net/apiref/icu4j/ (when ICU4J is enabled). Synopsizing the syntax, a pattern consists of a sequence of formatting characters where each character describes what is allowed at that position in a date/time. The basic formatting characters are:
GGGG |
Epoch symbol as a full string, e.g., "Heisei" or "Anno Domini". Note that Java Util 1.4x implementation of GGGG does not work correctly -- it displays AD instead of Anno Domini. |
GGG, GG, G |
Epoch symbol as a short string, e.g., "A.D" or "Hei" |
yyyy |
The year as a four digit string, e.g., 1993 with up to 3 leading zeroes. |
yyy |
If not Japanese, same as yy. If Japanese, the year as a three digit string with up to 2 leading zeroes. |
yy |
If not Japanese, the year as two digit string, e.g., 93 vs 1993. Century calculation rules apply on input (see above, base-2digit-span). If Japanese, the year as a string with up to one leading zeroes (e.g., "Heisei 02"). |
y |
If not Japanese, same as yy. If Japanese, the year with no leading zeroes. |
IYYYY, YYY, YY, Y |
Same as "y" but for use with the "w" pattern character. Year is adjusted so that it's correct for the first/last week of the year (e.g., Jan 1, 2005 when displayed using "w YYYY" is '53 2004'. |
MMMM |
Month as a full string, e.g., "January". |
MMM |
Month as an abbreviated string, e.g., "Jan" (note that in Java, this is almost always a three character abbreviation). |
MM |
Month as a number including a leading zero if needed, e.g., "09". |
M |
Month as a number excluding a leading zero, e.g., "9". |
EEEE |
Day of Week as full string, e.g., "Wednesday". |
EEE, EE, E |
Day of Week as abbreviated string, e.g., "Wed" (note that in Java, this is usually a three character abbreviation). |
Ie |
Day of week, expressed as a number. Value is 1-based and localized. E.g., if first-day-of-week is Tuesday, then a Wednesday will be "2". |
dd |
Day of month as a number including a leading zero if needed, e.g., "04". |
d |
Day of month as a number excluding a leading zero, e.g., "4". |
DDD |
Day in year (as a number), includes up to two leading zeroes if needed, e.g., 002. |
DD |
Day in year (as a number), includes up to one leading zero if needed, e.g., 020. |
D |
Day in year (as a number), excludes leading zeroes e.g., 189. |
F |
Day of week in month (as a number), e.g., 2 (meaning second Wednesday in the month). NOTE: F is ignored when parsing a string. |
Ig, gg ... gggggggg |
... gggggggg Julian date, that is, day since January 1, 4713 BC (0-based value). |
ww |
Week in year (as a number) including leading zero if needed, e.g., 05. |
w |
Week in year (as a number) excluding leading zeroes, e.g., 51. |
W |
Week in month (as a number), e.g., 2. NOTE: w and W are ignored when converting a string to a date. ALSO, when converting from a date to a string, Java's rules are used. In Java, Week 1 of the year ends on the first Saturday of the year (so may be less than 7 days long) and subsequent weeks are computed starting with the following Sunday. |
hh |
Hour in 12 hour clock format with leading zero if needed, e.g., 06. |
h |
Hour in 12 hour clock format excluding leading zero, e.g., 6. |
HH |
Hour in 24 hour clock format with leading zero if needed, e.g., 07 and 17. |
H |
Hour in 24 hour clock format with excluding leading zero, e.g., 7 and 17. |
kk |
As HH but 1 based (that is 01-24). |
k |
As H but 1 based (that is 1-24). |
KK |
As hh but 1 based (that is 01-12). |
K |
As h but 1 based (that is 1-12). |
mm |
Minutes with leading zero if needed, e.g., 08. |
m |
Minutes excluding leading zero, e.g., 8. |
ss |
Seconds with leading zero if needed, e.g., 09. |
s |
Seconds excluding leading zero, e.g., 9. |
SSS |
Milliseconds, all three digits shown. |
SS |
Milliseconds, first two digits shown. |
S |
Milliseconds, first digit shown. |
a |
AM/PM marker (expands to size required by locale). |
IA, AA ... AAAAAAAA |
Milliseconds in day. |
z |
Time zone (expands to size required by locale). The general format for a time zone is GMT+nn:nn (where nn:nn is the offset from GMT). For specific languages (e.g., English), "common form" time zones may be used instead (e.g., EST, PST). |
Iv |
Time zone without "daylight savings" indicator. NOT SUPPORTED as the client will always display the time using the zone on the client which is often in daylight savings time. |
IZ |
Time zone expressed in RFC 822 format. This is +/-nnnn (where nn:nn is the offset from GMT). |
zzzz |
"Long form" of the time zone. NOT SUPPORTED as we can't parse this. |
o |
Ordinal day of month. NOT SUPPORTED as we can't localize this. |
Iu |
ICU4J extended year. NOT SUPPORTED as it appears meaningless for the years supported by hxclient. |
' |
Enclose literal text that would conflict with a symbol in single quotes. |
'' |
Use two single quotes to specify a literal character that is the single quote |
A format is constructed by assembling the above characters in a string (each of the above may appear at most once in the pattern). Any character encountered in the format string that is not one of the above formatting characters is treated as a "delimiter" (literal text). For example, in the format string "MM/dd/yyyy" the slashes are treated as literals. Care must be taken that literal text does not inadvertently include formatting characters (some of which are obscure and many people may not know they are formatting characters). Use single quotes around any text that might conflict with a format string character. For example, "hh o'' clock" will not return "12 o'clock", rather, it will return "12 12th' cl12thck" as "o" is a format character. Java may throw an error if an alphabetic character is included in a format and that character is not a format character and it is not in quotes, but it does not do this for all alphabetic characters that are not format characters.
Some care must be taken in constructing a pattern that will be used to convert from a string to a date object to make sure sufficient components are provided so that the string can be unambiguously interpreted. For example, if you include a 'z" (time zone) you should include a "date" in the format as time-zone cannot be interpreted without knowing the date (if no date is in the format, today's date is used by default).
It's important to note that turning on ICU4J changes how existing patterns might be interpreted. For example, the pattern "Y: yyyy" applied to the value "Jan 1, 2005" displays as "Y: 2005" when ICU4J is off. It displays as "04: 2005" when ICU4J is on. "Y" is not a pattern character when ICU4J is off so it's considered a literal. It is a pattern character when ICU4J is on.
API call |
Description |
---|---|
number = stringToValue(string) |
Converts a string to a JavaScript number object. Returns null if fails. |
string = valueToString(date) |
Converts a JavaScript date object to a string. Returns null if fails. |
string = lastError() |
If a conversion fails, returns the reason for the failure as a localized string. |
object = setAttribute(attribute) |
Sets an attribute or changes its value (if it was set previously). |
string = getAttribute(attribute-name) |
Retrieves the current value of an attribute. |
There are a lot of issues/limitations in Java's handling of datetime objects and converting to/from them. These same issues/limitations apply to the JavaScript converter. An exhaustive description of all issues is beyond the scope of this documentation. The following list describes some of the more important limitations and issues.
October 15, 1582 is when the Julian calendar was first changed to the Gregorian calendar. In the Julian calendar every 4th year (every year evenly divisible by 4) is a leap year. In the Gregorian calendar, every 4th year is a leap year except if the year is divisible by 100 and not by 400 (thus 1900 is not a leap year but 2000 is a leap year). When the calendars changed from Julian to Gregorian, the "excess leap days" had to be deleted from the calendar (that is, leap days that were added in under the Julian calendar that shouldn't have been under the rules of the Gregorian calendar). Thus, if a country made the change from Julian to Gregorian in 1582, the dates October 5th to October 14th were removed from the calendar (that is, October 15th followed October 4th). The change from Julian to Gregorian was first implemented in the Italy, Portugal, Spain and Poland. Other parts of the world made the change later. For example, Britain and her colonies (e.g., Boston) made the change in 1752. Some parts of the world did not make the change until the 20th century (e.g., Greece in 1924, Turkey in 1927, and most of China in 1949).
Note that the DateTimeConverter does not support the ICU4J Gregorian calendar option to modify the Gregorian change-over date (that is it doesn't support setGregorianChange()), so the changeover always occurs in 1582.
To deal with the Julian to Gregorian shift, Java uses the Julian calendar rules for dates before 1582, removes October 5th through October 14th, and uses the Gregorian rules for dates after October 15th, 1582. Thus October 10, 1582 is not a valid date, February 29, 1300 is a valid date and February 29, 1900 is not a valid date. Further, conversions to/from Julian dates (absolute day number starting at January 1, 4713) follow this same pattern (e.g., January 1, 1000 AD is Julian Date 2086308. Note that the Julian calendar rules are used on dates before 64 BC even though that is the year the Julian calendar came into use (there was no Julian calendar before Julius Caesar just as there was no Gregorian calendar before Pope Gregory XIII).
JavaScript does not deal with dates the same way. JavaScript applies the Gregorian calendar rules to all dates. Thus October 10, 1582 is a valid date. February 29, 1300 is not a valid date and February 29, 1900 is not a valid date. Equally, if JavaScript supported Julian Dates, January 1, 1000 AD is Julian Date 2086303 (a difference of 5 days than above). In other words, dates before October 15, 1582 are handled differently by JavaScript than by Java.
The DateTime Converter uses the same rules as Java as best it can. Thus it computes Julian Dates the same as the Java. It cannot, however, allow February 29 to be entered as date for any year prior to 1582 where the Gregorian calendar wouldn't allow it (JavaScript simply converts these dates to March 1st and there appears to be no way around this). So prior to 1582, in ANY calendar (Gregorian or not), when a date is parsed and the components converted to a JavaScript date, February 29th in some leap years cannot be represented.
For example, in Windows®, a user can specify their preferred format for a "short" and "long" date. This information cannot be read by the browser (or the server) so it is never used to format a date. The only information used to display a date is the format and locale information that is passed to the DateTime converter.
When designing a date/time format, a designer may choose to specify it exactly (provide a format) or they may use a locale-dependent format ("short", "medium", "long"). If they choose a locale-dependent format, the format used is the one defined for all users of that locale (individual user preferences are ignored). Excessive moaning and groaning about this limitation will not change it.
Convert the value of an input field to a JavaScript date, add one to it and store back the result.
// Construct the converter with a format of MMMM dd, yyyy hX.addConverter("AZ1", new hX.DateTimeConverter("format:MMMM dd, yyyy", "strict:2", "base-2digit-span:30")); // Convert the value of an input field from a string formatted using this pattern to a JS date var x = document.getElementById("form1:text1"); var cvt = hX.getConverterById("AZ1"); var d = cvt.stringToValue(x.value); // Check for errors if (d==null) alert ("ERROR: " + cvt.lastError()); // Increment the value and put it back else { d.setDate(d.getDate()+1); x.value = cvt.valueToString(d); }