Timestamp from datetime using XSLT
In life, it so happens that despite all the love for differentiating data and presentation, the day comes when it becomes necessary to transfer part of the logic to the XSLT template.
In my case, nothing criminal was foreseen on the horizon: it was necessary to calculate the time between two events in a hierarchical XML log. The date and time was stored in a format partially compatible with RFC 3339 .
This compatibility was ensured by the correct date
was decided to assemble the templates in extension with the same namespace as exslt, so that the type of container is
The following listing is a template for converting date-time to millisecond timetamp: I won’t go into details of calculations, I’ll just talk about its capabilities. @param Accepts the only parameter to which the formatted string is passed. The presence and number of spaces inside the line does not matter - they are all broadcast. Date separators can be any single characters except space. The generalized pattern for the parameter is as follows: - year - month - day - date identifier - hours - minutes - fractional part of a second, may contain an arbitrary number of digits (including none) - UTC timezone identifier
@output Returns the number of milliseconds since the beginning of the Unix era 1970-01-01T00: 00: 00Z.
This listing is the inverse conversion from a number to an RFC formatted string. @param Like the previous template, it takes a single parameter. - The number of milliseconds elapsed since 1970-01-01T00: 00: 00Z. If the date is earlier than the beginning of 1970, then the timestamp must be negative. @output A line of the form I checked the patterns, both with negative years and with positive ones - the result is similar to the truth. Tests and source can be picked up at rapidshare . If I don’t like rapid, I’ll post it somewhere else.
In my case, nothing criminal was foreseen on the horizon: it was necessary to calculate the time between two events in a hierarchical XML log. The date and time was stored in a format partially compatible with RFC 3339 .
This compatibility was ensured by the correct date
yyyy-MM-dd
and time notation hh:mm:ss.SS
, but the following deviations from the standard took place:- Date and time were separated by a space, not a letter
T
; - The number of digits representing milliseconds could vary from “niode” to “many, many”;
- The time zone was not indicated at all.
date:difference
, but I had to abandon it. The fact is that the difference was required to be obtained with an accuracy of milliseconds, and this algorithm returned a valid xsd:duration
(ISO 8601), which does not contain milliseconds. In addition, parsing someone else's output, although formalized, is not very grateful. Thus, digging a bit in exslt, I decided to write the parser myself, in the hope that I could do it quickly ... It was decided to assemble the templates in extension with the same namespace as exslt, so that the type of container is
appropriate: The extension namespace declared with the help of the extension , will be used by templates, and XML namespace will be used only once to declare the following container:- xmlns:date="date"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- extension-element-prefixes="date">
-
extension-element-prefixes
date:*
-
31 -
28 -
31 -
30 -
31 -
30 -
31 -
31 -
30 -
31 -
30 -
31
For convenience, getting the number of days by number or month name, we introduce a variable:
Now you can write XPath like - the total number of days in a leap year for the fifth month inclusive.
In the process of parsing a date-time string, we often had to check the numerical values for coincidence with and, in the case of a positive comparison, replace them with . That would spawn a lot that cluttered the code a lot . Therefore, I started using this look:
But this code also does not look neat. After some deliberation, an option was chosen with autoformat fractional numbers:
Interestingly, in Opera 10.53, the function
select="document('')//date:month"/>
sum($date:month/*[$i>=position()])+($i>2)
$i
NaN
0
translate
translate($expression,'NaN',0)
format-number
not able to work with three arguments and generates unknown error, which does not allow the use of named formats of numbers decimal-format
like this: That is, here is such an XPath drop template:- NaN="0">
format-number($expression,0,'date:NaN')
Date-time to timestamp
The following listing is a template for converting date-time to millisecond timetamp: I won’t go into details of calculations, I’ll just talk about its capabilities. @param Accepts the only parameter to which the formatted string is passed. The presence and number of spaces inside the line does not matter - they are all broadcast. Date separators can be any single characters except space. The generalized pattern for the parameter is as follows: - year - month - day - date identifier - hours - minutes - fractional part of a second, may contain an arbitrary number of digits (including none) - UTC timezone identifier
-
-
- select="
- normalize-space(
- translate($date-time,'TZ ',''))"/>
-
- select="
- translate(
- substring($compact,1,
- 4+(starts-with($compact,'+') or
- starts-with($compact,'-'))),
- '+','')"/>
-
- select="substring-after($compact,$year)"/>
-
- select="substring($date,7)"/>
-
- select="format-number(substring($date,2,2)-1,0)"/>
-
-
- select="
- concat(
- substring-after($time,'+'),
- substring-after($time,'-'))"/>
-
-
-
$date-time
yyyy
MM
dd
T
hh
mm
S
Z
[]
- the contents of the brackets may or may not be present; ()
- the contents of the brackets must be present; |
- or @output Returns the number of milliseconds since the beginning of the Unix era 1970-01-01T00: 00: 00Z.
Timestamp to date-time
This listing is the inverse conversion from a number to an RFC formatted string. @param Like the previous template, it takes a single parameter. - The number of milliseconds elapsed since 1970-01-01T00: 00: 00Z. If the date is earlier than the beginning of 1970, then the timestamp must be negative. @output A line of the form I checked the patterns, both with negative years and with positive ones - the result is similar to the truth. Tests and source can be picked up at rapidshare . If I don’t like rapid, I’ll post it somewhere else.
-
-
-
- select="$timestamp div (24*3600000)"/>
-
- select="
- $timestamp div 1000
- -floor($days)*24*3600"/>
-
- select="
- 1970+floor(
- format-number($days div 365.24,'0.#'))"/>
-
- select="
- 719528-$year*365
- -floor($year div 4)
- +floor($year div 100)
- -floor($year div 400)
- +floor($days)"/>
-
- select="
- count($date:month
- /*[$year-offset>=sum(preceding-sibling::*)][last()]
- /preceding-sibling::*)"/>
-
- select="floor($time div 3600)"/>
-
- select="floor($time div 60-$hours*60)"/>
-
- select="floor($time -$hours*3600-$min*60)"/>
-
-
$timestamp
[-|+]yyyy-MM-ddThh:mm:ss.SSSZ