如何用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)
- ABC1的
方案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 < $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 < $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 < 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
相关产品推荐
相关产品推荐

