![]() |
Table of Contents |
![]() |
Recipe 4.10. Formatting Dates and TimesProblemYou want to format dates and times based on a format string. SolutionThese templates reuse many of the templates already presented in this chapter. The format-date-time uses a format string where %x is a formatting directive (see later) and all other text is output literally. The default format is the ISO date-time format for Gregorian dates: <xsl:template name="ckbk:format-date-time"> <xsl:param name="year"/> <xsl:param name="month"/> <xsl:param name="day"/> <xsl:param name="hour"/> <xsl:param name="minute"/> <xsl:param name="second"/> <xsl:param name="time-zone"/> <xsl:param name="format" select="'%Y-%m-%dT%H:%M:%S%z'"/> <xsl:choose> <xsl:when test="contains($format, '%')"> <xsl:value-of select="substring-before($format, '%')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$format"/> </xsl:otherwise> </xsl:choose> <xsl:variable name="code" select="substring(substring-after($format, '%'), 1, 1)"/> <xsl:choose> <!-- Abbreviated weekday name --> <xsl:when test="$code='a'"> <xsl:variable name="day-of-the-week"> <xsl:call-template name="ckbk:calculate-day-of-the-week"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="ckbk:get-day-of-the-week-abbreviation"> <xsl:with-param name="day-of-the-week" select="$day-of-the-week"/> </xsl:call-template> </xsl:when> <!-- Full weekday name --> <xsl:when test="$code='A'"> <xsl:variable name="day-of-the-week"> <xsl:call-template name="ckbk:calculate-day-of-the-week"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> </xsl:call-template> </xsl:variable> <xsl:call-template name="ckbk:get-day-of-the-week-name"> <xsl:with-param name="day-of-the-week" select="$day-of-the-week"/> </xsl:call-template> </xsl:when> <!-- Abbreviated month name --> <xsl:when test="$code='b'"> <xsl:call-template name="ckbk:get-month-abbreviation"> <xsl:with-param name="month" select="$month"/> </xsl:call-template> </xsl:when> <!-- Full month name --> <xsl:when test="$code='B'"> <xsl:call-template name="ckbk:get-month-name"> <xsl:with-param name="month" select="$month"/> </xsl:call-template> </xsl:when> <!-- Date and time representation appropriate for locale --> <xsl:when test="$code='c'"> <xsl:text>[not implemented]</xsl:text> </xsl:when> <!-- Day of month as decimal number (01 - 31) --> <xsl:when test="$code='d'"> <xsl:value-of select="format-number($day,'00')"/> </xsl:when> <!-- Hour in 24-hour format (00 - 23) --> <xsl:when test="$code='H'"> <xsl:value-of select="format-number($hour,'00')"/> </xsl:when> <!-- Hour in 12-hour format (01 - 12) --> <xsl:when test="$code='I'"> <xsl:choose> <xsl:when test="$hour = 0">12</xsl:when> <xsl:when test="$hour < 13"> <xsl:value-of select="format-number($hour,'00')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="format-number($hour - 12,'00')"/> </xsl:otherwise> </xsl:choose> </xsl:when> <!-- Day of year as decimal number (001 - 366) --> <xsl:when test="$code='j'"> <xsl:variable name="diff"> <xsl:call-template name="ckbk:date-difference"> <xsl:with-param name="from-year" select="$year"/> <xsl:with-param name="from-month" select="1"/> <xsl:with-param name="form-day" select="1"/> <xsl:with-param name="to-year" select="$year"/> <xsl:with-param name="to-month" select="$month"/> <xsl:with-param name="to-day" select="$day"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="format-number($diff + 1, '000')"/> </xsl:when> <!-- Month as decimal number (01 - 12) --> <xsl:when test="$code='m'"> <xsl:value-of select="format-number($month,'00')"/> </xsl:when> <!-- Minute as decimal number (00 - 59) --> <xsl:when test="$code='M'"> <xsl:value-of select="format-number($minute,'00')"/> </xsl:when> <!-- Current locale's A.M./P.M. indicator for 12-hour clock --> <xsl:when test="$code='p'"> <xsl:choose> <xsl:when test="$hour < 12">AM</xsl:when> <xsl:otherwise>PM</xsl:otherwise> </xsl:choose> </xsl:when> <!-- Second as decimal number (00 - 59) --> <xsl:when test="$code='S'"> <xsl:value-of select="format-number($second,'00')"/> </xsl:when> <!-- Week of year as decimal number, with Sunday as first day of week (00 - 53) --> <xsl:when test="$code='U'"> <!-- add 1 to day --> <xsl:call-template name="ckbk:calculate-week-number"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day + 1"/> </xsl:call-template> </xsl:when> <!-- Weekday as decimal number (0 - 6; Sunday is 0) --> <xsl:when test="$code='w'"> <xsl:call-template name="ckbk:calculate-day-of-the-week"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> </xsl:call-template> </xsl:when> <!-- Week of year as decimal number, with Monday as first day of week (00 - 53) --> <xsl:when test="$code='W'"> <xsl:call-template name="ckbk:calculate-week-number"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> </xsl:call-template> </xsl:when> <!-- Date representation for current locale --> <xsl:when test="$code='x'"> <xsl:text>[not implemented]</xsl:text> </xsl:when> <!-- Time representation for current locale --> <xsl:when test="$code='X'"> <xsl:text>[not implemented]</xsl:text> </xsl:when> <!-- Year without century, as decimal number (00 - 99) --> <xsl:when test="$code='y'"> <xsl:value-of select="format-number($year mod 100,'00')"/> </xsl:when> <!-- Year with century, as decimal number --> <xsl:when test="$code='Y'"> <xsl:value-of select="format-number($year,'0000')"/> </xsl:when> <!-- Time-zone name or abbreviation; --> <!-- no characters if time zone is unknown --> <xsl:when test="$code='z'"> <xsl:value-of select="$time-zone"/> </xsl:when> <!-- Percent sign --> <xsl:when test="$code='%'"> <xsl:text>%</xsl:text> </xsl:when> </xsl:choose> <xsl:variable name="remainder" select="substring(substring-after($format, '%'), 2)"/> <xsl:if test="$remainder"> <xsl:call-template name="ckbk:format-date-time"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> <xsl:with-param name="hour" select="$hour"/> <xsl:with-param name="minute" select="$minute"/> <xsl:with-param name="second" select="$second"/> <xsl:with-param name="time-zone" select="$time-zone"/> <xsl:with-param name="format" select="$remainder"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="ckbk:format-julian-day"> <xsl:param name="julian-day"/> <xsl:param name="format" select="'%Y-%m-%d'"/> <xsl:variable name="a" select="$julian-day + 32044"/> <xsl:variable name="b" select="floor((4 * $a + 3) div 146097)"/> <xsl:variable name="c" select="$a - floor(($b * 146097) div 4)"/> <xsl:variable name="d" select="floor((4 * $c + 3) div 1461)"/> <xsl:variable name="e" select="$c - floor((1461 * $d) div 4)"/> <xsl:variable name="m" select="floor((5 * $e + 2) div 153)"/> <xsl:variable name="day" select="$e - floor((153 * $m + 2) div 5) + 1"/> <xsl:variable name="month" select="$m + 3 - 12 * floor($m div 10)"/> <xsl:variable name="year" select="$b * 100 + $d - 4800 + floor($m div 10)"/> <xsl:call-template name="ckbk:format-date-time"> <xsl:with-param name="year" select="$year"/> <xsl:with-param name="month" select="$month"/> <xsl:with-param name="day" select="$day"/> <xsl:with-param name="format" select="$format"/> </xsl:call-template> </xsl:template> XSLT 2.0Use the XSLT 2.0 date formatting functions. In the following fragment, I assume you have to deal with a date as a string in the form YYYYMMDD, which is often more common in practice than the ISO date format required by format-date: xsl:param name="date" as="xs:string"/> <invoiceDate><xsl:value-of select="format-date( xs:date(concat(substring($date,1,4), '-', substring($date,5,2), '-', substring($date,7,2))), '[MNn] [D01], [Y0001]')"/></invoiceDate> This would output the following for $date = '20050811': <invoiceDate>August 11, 2005</invoiceDate> DiscussionXSLT 1.0This example was made possible by all the date work done in the prior examples. The options requiring locale are not implemented, but could be implemented using extension functions (see Chapter 12). XSLT 2.0I provide an excerpt from the W3C specification for your convenience. Three functions are provided to represent dates and times as a string, using the conventions of a selected calendar and locale. Each has two variants: format-dateTime($value as xs:dateTime?, $picture as xs:string, $date-format-name as xs:string) as xs:string? format-dateTime($value as xs:dateTime?, $picture as xs:string) as xs:string? format-date($value as xs:date?, $picture as xs:string, $date-format-name as xs:string) as xs:string? format-date($value as xs:date?, $picture as xs:string) as xs:string? format-time($value as xs:time?, $picture as xs:string, $date-format-name as xs:string) as xs:string? format-time($value as xs:time?, $picture as xs:string) as xs:string? The format-dateTime, format-date, and format-time functions format $value as a string using the picture string specified by the $picture argument and the date-format named by the $date-format-name argument, or the default date-format, if there is no $date-format-name argument. The result of the function is the formatted string representation of the supplied dateTime, date, or time value. The three functions, format-dateTime, format-date, and format-time, and are referred to collectively as the date formatting functions. It is a dynamic error if the name specified as the $date-format-name argument is not a valid QName, or if its prefix has not been declared in an in-scope namespace declaration, or if the stylesheet does not contain a declaration of a date-format with a matching expanded-QName. The processor must either signal the error, or must recover by ignoring the $date-format-name argument. If the processor is able to detect the error statically (for example, when the argument is supplied as a string literal), then the processor may optionally signal this as a static error. If $value is the empty sequence, the empty sequence is returned. The date-format declaration<!-- Category: declaration --> <xsl:date-format name = qname language = nmtoken calendar = qname /> The xsl:date-format element declares a date-format, which provides information used by the date formatting functions. If there is a name attribute, then the element declares a named date format; otherwise, it declares the default date format. The value of the name attribute is a QName. It is a static error to declare either the default date-format or a date-format with a given name more than once (even with different import precedence), unless it is declared every time with the same value for all attributes (taking into account any default values). If a stylesheet does not contain a declaration of the default date-format, a declaration equivalent to an xsl:date-format element with no attributes is implied. The language attribute specifies the language to be used for the result string of the format-date function. The effective value of the attribute must be a value that would be valid for the xml:lang attribute. If the language attribute is omitted, then the default is implementation-defined. The language is used to select the appropriate language-dependent forms of:
The set of languages that are supported is implementation-defined. The calendar attribute specifies that the dateTime, date, or time supplied in the $value argument must be converted to a value in that calendar and then converted to a string using the conventions of that calendar. A calendar value must be a valid QName. If the QName does not have a prefix, then it identifies a calendar with the designator specified below. If the QName has a prefix, then the QName is expanded into an expanded-QName; the expanded-QName identifies the calendar; the behavior in this case is not specified by this document. If the calendar attribute is omitted, a locale-specific value is used. It is a static error if an implementation does not support the language specified in the language attribute, or the calendar specified in the calendar attribute, or the combination of the two. The processor must either signal the error, or must recover by using a locale-specific value of the two attributes instead of the values specified. If a different calendar is used from that requested, the name of this calendar must be included in the result string.
At least one of the preceding calendars must be supported. It is implementation-defined which calendars are supported. The ISO 8601 calendar, which is included in the previous list and designated ISO, is essentially the same as the Gregorian calendar designated AD, but it prescribes the use of particular numbering conventions as defined in ISO 8601, rather than allowing these to be localized on a per-language basis. Specifically, in the ISO calendar, the days of the week are numbered from 1 (Monday) to 7 (Sunday), and week 1 in any calendar year is the week (from Monday to Sunday) that includes the first Thursday of that year. The numeric values of the components year, month, day, hour, minutes, and seconds are the same in this calendar as the values used in the lexical representation of the date and time as defined in XML Schema. The ISO calendar is intended primarily for applications that need to produce dates and times in formats to be read by other software, rather than by human users.
The picture stringThe picture consists of a sequence of variable markers and literal substrings. A substring enclosed in square brackets is interpreted as a variable marker; substrings not enclosed in square brackets are taken as literal substrings. The literal substrings are optional and if present are rendered unchanged, including any whitespace. If an opening or closing square bracket is required within a literal sub-string, it must be doubled. The variable markers are replaced in the result by strings representing aspects of the date and/or time to be formatted. These are described in detail next. A variable marker consists of a component specifier followed optionally by one or two presentation modifiers and/or optionally by a length modifier. Whitespace within a variable marker is ignored. The component specifier indicates the component of the date or time that is required, and takes the following values:
It is a dynamic error if a component specifier within the picture refers to components that are not available in the given $value, or which are not supported in the chosen calendar. This is a recoverable error . The processor may signal the error, or may recover by ignoring the offending component specifiers. The first presentation modifier indicates the style in which the value of a component is to be represented, and takes the following values:
Any character that has a decimal digit value of 1 (as specified in the Unicode character property database) generates a decimal representation of the number using the appropriate set of Unicode digits. Any other character may be used to indicate a numbering sequence that starts with that character, if the implementation supports such a numbering sequence. If the implementation does not support the use of the requested presentation modifier, it must use the default presentation modifier for that component. If the first presentation modifier is present, then it may optionally be followed by a second presentation modifier as follows:
Whether or not a presentation modifier is included, a width modifier may be supplied. This indicates the number of characters or digits to be included in the representation of the value. The width modifier takes the form: min-width ("-" max-width)? where min-width is either an unsigned integer indicating the minimum number of characters to be output, or * indicating that there is no explicit minumum, and max-width is either an unsigned integer indicating the maximum number of characters to be output, or * indicating that there is no explicit maximum. If max-width is omitted, then * is assumed. Both integers, if present, must be greater than zero. If there is no width modifier, then the output uses as many characters as are required to represent the value of the component without truncation and without padding: this is referred to below as the full representation of the value. If the full representation of the value exceeds the specified maximum width, then the processor should attempt to use an alternative shorter representation that fits within the maximum width. Where the presentation modifier is n or N, this is done by abbreviating the name, using either conventional abbreviations if available, or crude right-truncation if not. For example, setting max-width to 4 indicates that four-letter abbreviations should be used, though it would be acceptable to use a three-letter abbreviation if this is in conventional use. (For example, "Tuesday" might be abbreviated to "Tues", and "Friday" to "Fri".) In the case of the year component, setting max-width requests omission of high-order digits from the year; for example, if max-width is set to 2, then the year 2003 will be output as 03. If no mechanism is available for fitting the value within the specified maximum width (for example, when roman numerals are used), then the value should be output in its full representation. If the full representation of the value is shorter than the specified minimum width, then the processor should pad the value to the specified width. For decimal representations of numbers, this should be done by prepending zero digits from the appropriate set of digit characters. In other cases, it should be done by prepending spaces. The choice of the names and abbreviations used in any given language is implementation-defined. For example, one implementation might abbreviate July as Jul while another uses Jly. In German, one implementation might represent Saturday as Samstag while another uses Sonnabend. Implementations may provide mechanisms allowing users to control such choices. The following examples show a selection of dates and times and the way they might be formatted. These examples assume the use of the Gregorian calendar, and assume that the name of xsl:date-format declarations in the stylesheet is the same as the value of their language attribute. (For example, <date-format name="sv" language="sv"/>.)
The following examples use calendars other than the Gregorian calendar: <!-- Example: Thai --> <xsl:date-format name="modern_Thai" language="th" calendar="BE"/> format-date($d, "[D๑] [Mn] [Y๑]", "modern_Thai") <!-- Result: |
![]() |
Table of Contents |
![]() |