如何2013-08-13T17:57:55Z
使用 XSLT 1.0 将 ISO 8601 日期/时间文本转换为“自纪元以来的毫秒数”?更具体地说,是 Google Chrome 的 XSLT 版本。
2 回答
扩展朱利安日期的算法是我的老经理教我的(嗨,乔治!),他又从ACM 的收集算法中得到它(尽管它似乎没有包含在CALGO 的在线版本中,我也没有能够找到算法编号、发布日期或作者 [但请参阅下面的后记]),以下模板计算,对于时区 Z 中的给定 ISO 8601 时间戳,自儒略日开始以来的毫秒数0,不包括闰秒。儒略日 0 开始于儒略历公元前 4713 年 1 月 1 日中午;那是公历公元前 4714 年 11 月 24 日的中午。
检查输入的完整性、调整以使用不同的纪元、扩展以处理 Z 以外的时区以及扩展以处理普通时代之前的闰秒和日期,这些都留给读者作为练习。(或者您可以调用 Javascript,但其中的乐趣在哪里?)
<xsl:template name="ts2i">
<!--* timestamp to integer: convert an ISO 8601 time stamp
* to the number of milliseconds since an epoch.
* Our epoch is 1 January 4713 BCE (Julian!),
* which is Julian day 0.
* To use 1970-01-01T00:00:00Z as the epoch,
* subtract 210866760000000 ms.
*-->
<xsl:param name="ts"/>
<!--* checking the timestamp for bad data is left as an
* exercise of the reader. Our contract is simpler:
* give me a correct timestamp in time zone Z, for a
* date on or after 0001-01-01, and I'll
* give you a correct answer (modulo overflow).
*-->
<!--* yyyy-mm-ddThh:mm:ss.sss...Z
* ....|....|....|....|... |
* 1 5 10 15 20 n
*-->
<!--* Parse out c, y, m, d, hh, mm, ss (for century,
* years in current century, months since February,
* days in current month, hours, minutes, seconds).
* the y and m values are adjusted to make the
* year begin 1 March (so leap day is always the last
* day of the year).
*-->
<xsl:variable name="y0" select="substring($ts,1,4)"/>
<xsl:variable name="m0" select="substring($ts,6,2)"/>
<xsl:variable name="d" select="substring($ts,9,2)"/>
<xsl:variable name="y1">
<xsl:choose>
<xsl:when test="$m0 < 3"><xsl:value-of select="$y0 - 1"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$y0"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="m" select="($m0 + 9) mod 12"/>
<xsl:variable name="c" select="floor($y1 div 100)"/>
<xsl:variable name="y" select="($y1 mod 100)"/>
<xsl:variable name="hh" select="substring($ts,12,2)"/>
<xsl:variable name="mm" select="substring($ts,15,2)"/>
<xsl:variable name="s0" select="substring($ts,18)"/>
<xsl:variable name="ss">
<xsl:choose>
<xsl:when test="contains($s0,'Z')">
<xsl:value-of select="substring-before($s0,'Z')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$s0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--* H holds the offset in days between Julian day 0
* and the beginning of the common era.
* J holds the offset in ms between midnight and
* noon, when Julian day 0 actually began.
*-->
<xsl:variable name="H" select="1721119"/>
<xsl:variable name="J" select="43200000"/>
<!--* Calculate the Julian day that begins on the
* given date. There are 146097 days in each
* 400-year period (including 25 leap days),
* 1461 in each 4-year period, and there are
* (($m * 153) + 2) div 5 days in $m months
* elapsed since 1 March.
* This is straight from the Collected Algorithms.
*-->
<xsl:variable name="j" select="floor(($c * 146097) div 4)
+ floor(($y * 1461) div 4)
+ floor((($m * 153) + 2) div 5)
+ $d + $H"/>
<!--* Calculate the milliseconds since the beginning
* of Julian day 0. This is my extension, and
* it could have an off-by-one error.
*-->
<xsl:value-of select="$j * 86400000
+ $hh * 3600000
+ $mm * 60000
+ $ss * 1000
- $J"/>
</xsl:template>
当给定输入值“2013-08-13T17:57:55Z”时,给定的模板返回数值2.12243176675e+14,即212243176675000。
正如其中一条评论所指出的,我在写这篇文章时担心溢出,但是 XSLT 1.0 数字是 IEEE 双精度数,因此(我相信)尾数为 52 位。所以在接下来的几千年里,溢出不太可能成为问题;在 138001 年 4 月 15 日左右之前,您的毫秒数不会出现舍入错误。
后记:在ACM 数字图书馆进行的一项小研究发现,我认为这一定是我的经理 George Yanos 学习此算法的来源:Robert G. Tantzen,“算法 199:日历日期和儒略日数之间的转换”,通讯ACM 6.8 (Aug 1963): 444
唯一的方法是调用 Javascript 代码。