For the following solution I used this XML file:
<root>
<news>
<data alias="date">2008-10-20</data>
</news>
<news>
<data alias="date">2009-11-25</data>
</news>
<news>
<data alias="date">2009-11-20</data>
</news>
<news>
<data alias="date">2009-03-20</data>
</news>
<news>
<data alias="date">2008-01-20</data>
</news>
</root>
and this XSLT 1.0 transformation:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cfg="http://tempuri.org/config"
exclude-result-prefixes="cfg"
>
<xsl:output method="xml" encoding="utf-8" />
<!-- index news by their "yyyy" value (first 4 chars) -->
<xsl:key
name="kNewsByY"
match="news"
use="substring(data[@alias= date ], 1, 4)"
/>
<!-- index news by their "yyyy-mm" value (first 7 chars) -->
<xsl:key
name="kNewsByYM"
match="news"
use="substring(data[@alias= date ], 1, 7)"
/>
<!-- translation table (month number to name) -->
<config xmlns="http://tempuri.org/config">
<months>
<month id="01" name="Jan" />
<month id="02" name="Feb" />
<month id="03" name="Mar" />
<month id="04" name="Apr" />
<month id="05" name="May" />
<month id="06" name="Jun" />
<month id="07" name="Jul" />
<month id="08" name="Aug" />
<month id="09" name="Sep" />
<month id="10" name="Oct" />
<month id="11" name="Nov" />
<month id="12" name="Dec" />
</months>
</config>
<xsl:template match="root">
<xsl:copy>
<!-- group news by "yyyy" -->
<xsl:apply-templates mode="year" select="
news[
generate-id()
=
generate-id(key( kNewsByY , substring(data[@alias= date ], 1, 4))[1])
]
">
<xsl:sort select="data[@alias= date ]" order="descending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<!-- year groups will be enclosed in a <year> element -->
<xsl:template match="news" mode="year">
<xsl:variable name="y" select="substring(data[@alias= date ], 1, 4)" />
<year num="{$y}">
<!-- group this year s news by "yyyy-mm" -->
<xsl:apply-templates mode="month" select="
key( kNewsByY , $y)[
generate-id()
=
generate-id(key( kNewsByYM , substring(data[@alias= date ], 1, 7))[1])
]
">
<xsl:sort select="data[@alias= date ]" order="descending" />
</xsl:apply-templates>
</year>
</xsl:template>
<!-- month groups will be enclosed in a <month> element -->
<xsl:template match="news" mode="month">
<xsl:variable name="ym" select="substring(data[@alias= date ], 1, 7)" />
<xsl:variable name="m" select="substring-after($ym, - )" />
<!-- select the label of the current month from the config -->
<xsl:variable name="label" select="document( )/*/cfg:config/cfg:months/cfg:month[@id = $m]/@name" />
<month num="{$m}" label="{$label}">
<!-- process news of the current "yyyy-mm" group -->
<xsl:apply-templates select="key( kNewsByYM , $ym)">
<xsl:sort select="data[@alias= date ]" order="descending" />
</xsl:apply-templates>
</month>
</xsl:template>
<!-- for the sake of this example, news elements will just be copied -->
<xsl:template match="news">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
When the transformation is applied, the following output is produced:
<root>
<year num="2009">
<month num="11" label="Nov">
<news>
<data alias="date">2009-11-25</data>
</news>
<news>
<data alias="date">2009-11-20</data>
</news>
</month>
<month num="03" label="Mar">
<news>
<data alias="date">2009-03-20</data>
</news>
</month>
</year>
<year num="2008">
<month num="10" label="Oct">
<news>
<data alias="date">2008-10-20</data>
</news>
</month>
<month num="01" label="Jan">
<news>
<data alias="date">2008-01-20</data>
</news>
</month>
</year>
</root>
It has the right structure already, you can adapt actual appearance to your own needs.
The solution is a two-phase Muenchian grouping approach. In the first phase, news items are grouped by year, in the second phase by year-month.
Please refer to my explanation of <xsl:key>
and key()
over here. You don t need to read the other question, though it is a similar problem. Just read the lower part of my answer.