我找不到这个问题的现有答案,所以我把它发布在这里。
请注意,输入日期时间:
- 可能有也可能没有小数秒
- 可能已经处于UTC/GMT
这使过程变得复杂。
示例输入:
<data>
<datetime>2020-06-01T04:45:15+05:30</datetime>
<datetime>2020-06-01T04:45:15.123+05:30</datetime>
<datetime>2020-05-31T19:45:15-08:00</datetime>
<datetime>2020-05-31T19:45:15.123-08:00</datetime>
<datetime>2020-05-31T19:45:15Z</datetime>
<datetime>2020-05-31T19:45:15.123Z</datetime>
</data>
预期输出:
<data>
<datetime>2020-05-31T23:15:15Z</datetime>
<datetime>2020-05-31T23:15:15.123Z</datetime>
<datetime>2020-06-01T03:45:15Z</datetime>
<datetime>2020-06-01T03:45:15.123Z</datetime>
<datetime>2020-05-31T19:45:15Z</datetime>
<datetime>2020-05-31T19:45:15.123Z</datetime>
</data>
以下解决方案首先测试给定的日期时间是否已为UTC/GMT。如果是,则按原样传递给输出。否则,给定日期的各个组件时间:
- 年
- 月份
- 天
- 小时
- 分钟
- 秒
- 偏移小时
- 偏移分钟
- 偏移符号
被提取。
接下来,本地日期时间被转换为自公元前4714年11月24日午夜(儒略日数字历元(以来经过的总秒数。与UTC/GMT的偏移量也转换为秒,并从总数中减去。
然后将结果转换回格里高利日期和时间,并格式化为ISO 8601日期时间。
XSLT 1.0
<xsl:template name="dateTime-to-UTC">
<xsl:param name="dateTime"/>
<xsl:choose>
<xsl:when test="contains($dateTime, 'Z')">
<xsl:value-of select="$dateTime"/>
</xsl:when>
<xsl:otherwise>
<!-- extract components -->
<xsl:variable name="date" select="substring-before($dateTime, 'T')" />
<xsl:variable name="time" select="substring-after($dateTime, 'T')" />
<!-- date components -->
<xsl:variable name="year" select="substring($date, 1, 4)" />
<xsl:variable name="month" select="substring($date, 6, 2)" />
<xsl:variable name="day" select="substring($date, 9, 2)" />
<!-- time components -->
<xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)" />
<xsl:variable name="hour" select="substring($local-time, 1, 2)" />
<xsl:variable name="minute" select="substring($local-time, 4, 2)" />
<xsl:variable name="second" select="substring($local-time, 7)" />
<!-- offset components -->
<xsl:variable name="offset" select="substring-after($time, $local-time)"/>
<xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
<xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
<xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />
<!-- convert to seconds -->
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year + 4800 - $a"/>
<xsl:variable name="m" select="$month + 12*$a - 3"/>
<xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
<xsl:variable name="total-seconds" select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
<!-- convert to date -->
<xsl:variable name="new-jd" select="floor($total-seconds div 86400)"/>
<xsl:variable name="new-hour" select="floor($total-seconds mod 86400 div 3600)"/>
<xsl:variable name="new-minute" select="floor($total-seconds mod 3600 div 60)"/>
<xsl:variable name="new-second" select="$total-seconds mod 60"/>
<xsl:variable name="f" select="$new-jd + 1401 + floor((floor((4 * $new-jd + 274277) div 146097) * 3) div 4) - 38"/>
<xsl:variable name="e" select="4*$f + 3"/>
<xsl:variable name="g" select="floor(($e mod 1461) div 4)"/>
<xsl:variable name="h" select="5*$g + 2"/>
<xsl:variable name="D" select="floor(($h mod 153) div 5 ) + 1"/>
<xsl:variable name="M" select="(floor($h div 153) + 2) mod 12 + 1"/>
<xsl:variable name="Y" select="floor($e div 1461) - 4716 + floor((14 - $M) div 12)"/>
<!-- output -->
<xsl:value-of select="$Y"/>
<xsl:value-of select="format-number($M, '-00')"/>
<xsl:value-of select="format-number($D, '-00')"/>
<xsl:text>T</xsl:text>
<xsl:value-of select="format-number($new-hour, '00')"/>
<xsl:value-of select="format-number($new-minute, ':00')"/>
<xsl:value-of select="format-number($new-second, ':00.###')"/>
<xsl:text>Z</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
演示:https://xsltfiddle.liberty-development.net/aiyne6
另请参阅:https://stackoverflow.com/a/46196920/3016153