4

我在 XML 文件的字段中有一个日期值,格式如下:

<Date value="4/1/2013 5:13:41 PM"/>

我想把它转换成标准的 XSD 格式:

2013-04-01T17:13:41.000Z

如何在我的 XSL 转换中做到这一点?我可以同时使用 1.0 和 2.0 样式表版本。

4

6 回答 6

5

所以...我很无聊,以前没有玩过xsl:analyze-string。这是一个基于正则表达式的解决方案:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">

  <xsl:template match="/item/date">
    <xsl:analyze-string select="@value" regex="([0-9]+)/([0-9]+)/([0-9]+) ([0-9]+):([0-9]+):([0-9]+) (PM|AM)">

      <xsl:matching-substring>
        <xsl:variable name="month" select="number(regex-group(1))"/>
        <xsl:variable name="day" select="number(regex-group(2))"/>
        <xsl:variable name="year" select="number(regex-group(3))"/>
        <xsl:variable name="hours">
          <xsl:choose>
            <xsl:when test="regex-group(7) = 'PM'">
              <xsl:value-of select="12 + number(regex-group(4))"/>
            </xsl:when>
            <xsl:otherwise><xsl:value-of select="number(regex-group(4))"/></xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:variable name="minutes" select="number(regex-group(5))"/>
        <xsl:variable name="seconds" select="number(regex-group(6))"/>
        <xsl:variable name="dateTime" select="xs:dateTime( concat($year, '-', format-number($month, '00'), '-', format-number($day, '00'), 'T', format-number($hours, '00'), ':', format-number($minutes, '00'), ':', format-number($seconds, '00'), 'Z')  )" />

        <reformattedDate>
          <xsl:value-of select="$dateTime"/>
        </reformattedDate>
      </xsl:matching-substring>
    </xsl:analyze-string>
  </xsl:template>
</xsl:stylesheet>

我针对这样的测试 xml 文件运行了这个:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<item>
    <date value="4/1/2013 5:13:41 PM"/>
</item>

输出是这样的:

<?xml version="1.0" encoding="UTF-8"?>
    <reformattedDate xmlns:xs="http://www.w3.org/2001/XMLSchema">2013-04-01T17:13:41Z</reformattedDate>

如果您想更精确地格式化输出,就像已经推荐的那样,您可以使用format-date 函数

于 2013-06-13T10:19:41.817 回答
4

这是一个 XSLT 1.0 版本,使用substring-beforesubstring-after.

感谢adhocgeekXML 输入。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/item">
    <xsl:copy>
      <xsl:apply-templates select="date"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="date">
    <xsl:copy>

      <xsl:variable name="date" select="substring-before(@value, ' ')"/>
      <xsl:variable name="M" select="substring-before($date, '/')"/>
      <xsl:variable name="D-Y" select="substring-after($date, '/')"/>
      <xsl:variable name="D" select="substring-before($D-Y, '/')"/>
      <xsl:variable name="Y" select="substring-after($D-Y, '/')"/>

      <xsl:variable name="time-ampm" select="substring-after(@value, ' ')"/>
      <xsl:variable name="time" select="substring-before($time-ampm, ' ')"/>
      <xsl:variable name="ampm" select="substring-after($time-ampm, ' ')"/>
      <xsl:variable name="h" select="substring-before($time, ':')"/>
      <xsl:variable name="m-s" select="substring-after($time, ':')"/>
      <xsl:variable name="m" select="substring-before($m-s, ':')"/>
      <xsl:variable name="s" select="substring-after($m-s, ':')"/>

      <xsl:variable name="hh">
        <xsl:choose>
          <xsl:when test="$ampm = 'PM'">
            <xsl:value-of select="format-number($h + 12, '00')"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="format-number($h, '00')"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>          

      <xsl:value-of select="concat($Y, '-', $M, '-', $D, 'T', $hh, ':', $m, ':', $s)"/>

    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XML

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<item>
  <date value="4/1/2013 5:13:41 PM"/>
</item>

输出

<?xml version="1.0" encoding="utf-8"?>
<item>
   <date>2013-4-1T05:13:41</date>
</item>
于 2013-06-13T12:29:04.857 回答
3

看看ESXLT。许多 XSL 实现已经支持开箱即用的功能,并且它们提供了备用,以防您的不支持它。

如果你有完全兼容的实现,你可以这样做:

<?xml ...
  xmlns.date="http://exslt.org/dates-and-times"
  ...

<xsl:variable name="xsdate" select="date:parse-date(Date/@value, 'M/d/yyyy h:mm:ss a')" />

<xsl:value-of select="$xsdate" />

我作弊并date:parse直接使用了结果。人们经常使用该date:format功能将日期格式化为特定格式。

老实说,我一直在努力让 EXSLT 的“后备”功能在 Apache Xalan 中为我工作。(在 Xalan Java 中使用非核心 EXSLT 日期函数

IMO最好首先将日期xs:dateTime格式化,然后再转换为某种本地化格式。您可以根据date:format需要使用该函数来格式化日期,并且该date:format函数在 XSLT 处理器中得到更广泛的支持。

于 2014-02-28T14:52:14.220 回答
1

使用 XSLT 2.0,您可以xs:dateTime通过使用字符串函数提取组件来构造一个值Date/@value,然后您可以根据需要使用以下格式进行格式化:http format-dateTime: //www.w3.org/TR/xslt20/#format-datePM提取诸如年、月、日之类的日期组件应该很简单,显然是因为您需要更多的工作来关注AM12 小时制时钟。

于 2013-06-13T09:03:50.593 回答
0

@adhockgeek 的回答对我来说几乎是完美的。如果当前值为 12,我只需要添加一个条件,以便它不会将 12 添加到小时。即,下午 12:15 != 上午 24:15。

我从他的代码中创建了一个模板,可以在转换中重复调用:

<xsl:template name="FormatDate">
    <xsl:param name="dt"/>
    <xsl:analyze-string select="$dt" regex="([0-9]+)/([0-9]+)/([0-9]+) ([0-9]+):([0-9]+):([0-9]+) (PM|AM)">
        <xsl:matching-substring>
            <xsl:variable name="month" select="number(regex-group(1))"/>
            <xsl:variable name="day" select="number(regex-group(2))"/>
            <xsl:variable name="year" select="number(regex-group(3))"/>
            <xsl:variable name="hours">
                <xsl:choose>
                    <xsl:when test="regex-group(7) = 'PM' and regex-group(4) != '12'">
                        <xsl:value-of select="12 + number(regex-group(4))"/>
                    </xsl:when>
                    <xsl:otherwise><xsl:value-of select="number(regex-group(4))"/></xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <xsl:variable name="minutes" select="number(regex-group(5))"/>
            <xsl:variable name="seconds" select="number(regex-group(6))"/>
            <xsl:variable name="dateTime" select="xs:dateTime(concat($year, '-', format-number($month, '00'), '-', format-number($day, '00'), 'T', format-number($hours, '00'), ':', format-number($minutes, '00'), ':', format-number($seconds, '00')))" />
            <xsl:value-of select="$dateTime"/>
        </xsl:matching-substring>
    </xsl:analyze-string>
</xsl:template>
于 2014-05-05T14:36:51.583 回答
0

具有 substring-before 和 substring-after 的代码可以工作,但应添加更多检查: 12AM => 00 和 12PM => 12 如下所示:

 <xsl:variable name="hh">
    <xsl:choose>
      <xsl:when test="$ampm = 'AM' and $h='12'">00</xsl:when>
      <xsl:when test="$ampm = 'PM' and $h!='12'">
        <xsl:value-of select="format-number($h + 12, '00')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="format-number($h, '00')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
于 2014-11-05T18:15:29.517 回答