You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用XSLT将日期范围拆分为单日数据记录?

XSLT: Split Date Range into Individual Daily Records

Hey there! 要把你的XML里的日期范围拆成单日记录,每条记录的Start和End日期都是同一天,这个需求在XSLT里完全可以实现。下面分两种场景给你方案,优先推荐XSLT 2.0及以上版本的实现,因为原生日期处理会省很多事。


先确认你的输入XML(修正了根节点结构)

首先注意你提供的原始XML里根节点<root>的位置有点问题,正确的结构应该是<root>包裹所有<data>节点,像这样:

<root>
  <data>
    <ID>ABC1</ID>
    <Start_Date>2018-05-01</Start_Date>
    <End_Date>2018-05-10</End_Date>
  </data>
  <data>
    <ID>ABC2</ID>
    <Start_Date>2018-05-10</Start_Date>
    <End_Date>2018-05-12</End_Date>
  </data>
</root>

方案1:XSLT 2.0+(推荐)

XSLT 2.0及以上支持原生的xs:date类型和日期序列生成,代码非常简洁:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xsl:output method="xml" indent="yes"/>

  <!-- 匹配根节点,保留根结构 -->
  <xsl:template match="/root">
    <root>
      <!-- 遍历每个原始data节点 -->
      <xsl:for-each select="data">
        <!-- 把字符串日期转成xs:date类型,方便后续计算 -->
        <xsl:variable name="start-date" select="xs:date(Start_Date)"/>
        <xsl:variable name="end-date" select="xs:date(End_Date)"/>
        
        <!-- 生成从起始到结束的所有日期序列,自动包含首尾日期 -->
        <xsl:for-each select="$start-date to $end-date">
          <data>
            <!-- 复制原始ID -->
            <xsl:copy-of select="../ID"/>
            <!-- 当前日期作为Start和End -->
            <Start_Date><xsl:value-of select="."/></Start_Date>
            <End_Date><xsl:value-of select="."/></End_Date>
          </data>
        </xsl:for-each>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

关键逻辑说明:

  • 日期类型转换:用xs:date()将字符串格式的日期(YYYY-MM-DD)转换成XSLT的日期类型,这样才能进行范围序列的生成。
  • 日期序列生成:$start-date to $end-date会自动生成两个日期之间的所有单日(包括起始和结束日期),刚好对应你需要的拆分数量:
    • ABC1的2018-05-01到2018-05-10会生成10条记录
    • ABC2的2018-05-10到2018-05-12会生成3条记录(如果你的需求是要排除起始日期,只生成2018-05-11和2018-05-12,可以把序列改成$start-date + xs:dayTimeDuration('P1D') to $end-date)

方案2:XSLT 1.0(兼容旧环境)

如果你的运行环境只能用XSLT 1.0(没有原生日期支持),就得手动处理日期的递增和边界情况(比如闰年、每月天数),用递归模板来实现:

<?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="/root">
    <root>
      <xsl:apply-templates select="data"/>
    </root>
  </xsl:template>

  <!-- 处理每个原始data节点,拆分日期参数 -->
  <xsl:template match="data">
    <xsl:variable name="id" select="ID"/>
    <!-- 拆分起始日期的年、月、日 -->
    <xsl:variable name="start-year" select="substring(Start_Date,1,4)"/>
    <xsl:variable name="start-month" select="substring(Start_Date,6,2)"/>
    <xsl:variable name="start-day" select="substring(Start_Date,9,2)"/>
    <!-- 拆分结束日期的年、月、日 -->
    <xsl:variable name="end-year" select="substring(End_Date,1,4)"/>
    <xsl:variable name="end-month" select="substring(End_Date,6,2)"/>
    <xsl:variable name="end-day" select="substring(End_Date,9,2)"/>

    <!-- 调用递归模板生成单日记录 -->
    <xsl:call-template name="generate-daily-records">
      <xsl:with-param name="current-year" select="$start-year"/>
      <xsl:with-param name="current-month" select="$start-month"/>
      <xsl:with-param name="current-day" select="$start-day"/>
      <xsl:with-param name="end-year" select="$end-year"/>
      <xsl:with-param name="end-month" select="$end-month"/>
      <xsl:with-param name="end-day" select="$end-day"/>
      <xsl:with-param name="id" select="$id"/>
    </xsl:call-template>
  </xsl:template>

  <!-- 递归生成每日记录的核心模板 -->
  <xsl:template name="generate-daily-records">
    <xsl:param name="current-year"/>
    <xsl:param name="current-month"/>
    <xsl:param name="current-day"/>
    <xsl:param name="end-year"/>
    <xsl:param name="end-month"/>
    <xsl:param name="end-day"/>
    <xsl:param name="id"/>

    <!-- 格式化当前日期为YYYY-MM-DD -->
    <xsl:variable name="current-date">
      <xsl:value-of select="$current-year"/>-<xsl:value-of select="$current-month"/>-<xsl:value-of select="$current-day"/>
    </xsl:variable>
    <!-- 格式化结束日期为YYYY-MM-DD -->
    <xsl:variable name="end-date">
      <xsl:value-of select="$end-year"/>-<xsl:value-of select="$end-month"/>-<xsl:value-of select="$end-day"/>
    </xsl:variable>

    <!-- 生成当前日期的记录 -->
    <data>
      <ID><xsl:value-of select="$id"/></ID>
      <Start_Date><xsl:value-of select="$current-date"/></Start_Date>
      <End_Date><xsl:value-of select="$current-date"/></End_Date>
    </data>

    <!-- 如果当前日期还没到结束日期,递归生成下一天 -->
    <xsl:if test="$current-date &lt; $end-date">
      <!-- 计算下一天的日期 -->
      <xsl:variable name="next-day">
        <xsl:call-template name="add-one-day">
          <xsl:with-param name="year" select="$current-year"/>
          <xsl:with-param name="month" select="$current-month"/>
          <xsl:with-param name="day" select="$current-day"/>
        </xsl:call-template>
      </xsl:variable>
      <!-- 递归调用生成下一天的记录 -->
      <xsl:call-template name="generate-daily-records">
        <xsl:with-param name="current-year" select="substring($next-day,1,4)"/>
        <xsl:with-param name="current-month" select="substring($next-day,6,2)"/>
        <xsl:with-param name="current-day" select="substring($next-day,9,2)"/>
        <xsl:with-param name="end-year" select="$end-year"/>
        <xsl:with-param name="end-month" select="$end-month"/>
        <xsl:with-param name="end-day" select="$end-day"/>
        <xsl:with-param name="id" select="$id"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <!-- 辅助模板:给指定日期加一天,处理月份和年份的进位 -->
  <xsl:template name="add-one-day">
    <xsl:param name="year"/>
    <xsl:param name="month"/>
    <xsl:param name="day"/>

    <!-- 计算当前月份的天数 -->
    <xsl:variable name="days-in-month">
      <xsl:choose>
        <xsl:when test="$month = 2">
          <!-- 处理闰年 -->
          <xsl:choose>
            <xsl:when test="($year mod 4 = 0 and $year mod 100 != 0) or $year mod 400 = 0">29</xsl:when>
            <xsl:otherwise>28</xsl:otherwise>
          </xsl:choose>
        </xsl:when>
        <xsl:when test="$month = 4 or $month = 6 or $month = 9 or $month = 11">30</xsl:when>
        <xsl:otherwise>31</xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:choose>
      <!-- 不是当月最后一天,直接加1天 -->
      <xsl:when test="$day &lt; $days-in-month">
        <xsl:value-of select="$year"/>-<xsl:value-of select="$month"/>-<xsl:value-of select="format-number($day + 1, '00')"/>
      </xsl:when>
      <!-- 是当月最后一天,月份加1,天设为1 -->
      <xsl:when test="$month &lt; 12">
        <xsl:value-of select="$year"/>-<xsl:value-of select="format-number($month + 1, '00')"/>-01
      </xsl:when>
      <!-- 是12月最后一天,年份加1,月份设为1,天设为1 -->
      <xsl:otherwise>
        <xsl:value-of select="$year + 1"/>-01-01
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

关键逻辑说明:

  • 日期拆分:通过substring()把YYYY-MM-DD格式的字符串拆成年、月、日三个部分,方便后续计算。
  • 递归生成:用generate-daily-records模板递归生成每一天的记录,直到当前日期等于结束日期。
  • 日期递增:add-one-day模板处理日期的进位逻辑,包括每月不同天数、闰年2月的特殊情况,确保日期计算准确。

内容的提问来源于stack exchange,提问作者Nick B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:50:08