このページでは XSLT で日付計算を行い、指定した日付の前後の日付を割り出す方法を説明する。
まず、XSLT(特に version 1.0)には日付演算用の関数や機能は用意されていない。そのため、基本的には自分で作る必要がある。
今回は日付文字列を一旦「修正ユリウス日」に変換し、演算したあと再度日付の文字列に戻すという実装にした。
ユリウス通日やその変換については下記のページが詳しい。
俗に言う「西暦」は「グレゴリオ暦」である。
細かいことは参照ページに譲るが、ユリウス日というのはようするにユリウス暦の紀元前4713年1月1日正午からの日数らしい。2455000.x ぐらいの値になる。小数点以下は正午からの時間である。
修正ユリウス暦はケタが多すぎるという理由で、この値から 2400000.5 を引いた値が「修正ユリウス日」である。そのため、今回も扱いやすい桁数の「修正ユリウス日(以下、MJD)」を用いた。
MJD は Wikipedia にもあるように次に示すフリーゲルの公式を用いて算出できる。
floor(365.25 * y) + floor(y / 400) - floor(y / 100) + floor(30.59 * (m - 2)) + d - 678912
ここで、 y, m, d はそれぞれグレゴリオ暦の 年, 月, 日 である。
ただし、1月と2月に関しては事前に12を足して、13月と14月にし、年を1減じておく必要がある。
一度エクセルに下記のような数式を入れて確認してみるとよい。
= FLOOR(365.25 * A1 ,1) + FLOOR(A1 / 400 ,1) - FLOOR(A1 / 100 ,1) + FLOOR(30.59 * (B1 - 2) ,1) + C1 - 678912
この数式をA2 セルあたりに設定し、A1 セルに年、B1 セルに月、C1 セルに日を入力すれば計算される。 (月が1月と2月の場合は13と14にし、年を1引いておくことを忘れずに)
これを XSLT のテンプレートで実装する。テンプレート名は ymd2mjd とした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
<!-- 西暦の yyyy-mm-dd から修正ユリウス通日(MJD) を求める -->
<xsl:template name="ymd2mjd">
<xsl:param name="ymd"/>
<xsl:variable name="y">
<xsl:choose>
<xsl:when test="number(substring($ymd,6,2)) <= 2">
<xsl:value-of select="number(substring($ymd,1,4)) - 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(substring($ymd,1,4))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="m">
<xsl:choose>
<xsl:when test="number(substring($ymd,6,2)) <= 2">
<xsl:value-of select="number(substring($ymd,6,2)) + 12"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="number(substring($ymd,6,2))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="d" select="number(substring($ymd,9,2))"/>
<xsl:value-of select="floor(365.25 * $y) + floor($y div 400) - floor($y div 100) + floor(30.59 * ($m - 2)) + $d - 678912"/>
</xsl:template>
計算に用いる演算子について、加減・乗算は通常の +-* で問題ないが、除算は div になるので注意。 これでテンプレート実行結果として、55000 のような整数の MJD が得られる。
また、前後の日付の MJD はこの数値に -1, +1 することで得られる。あるいは、2 つの日付の差(日数)を求めたい場合は、このテンプレートで求めた 2 つの MJD の差を求めればよい。
今回は前後の日付を求め、その日付文字列を得ることが目的である。そのためには、MJD から 年, 月, 日 をそれぞれ求める必要がある。
このアルゴリズムについてはややこしいので割愛するが、Delphi で実装されている先人 のコードを利用させていただいた。
テンプレートを下記に示す。テンプレート名は逆の mjd2ymd とした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<!-- 修正ユリウス通日(MJD) から西暦の yyyy-mm-dd からに変換 -->
<xsl:template name="mjd2ymd">
<xsl:param name="mjd"/>
<xsl:variable name="y1" select="floor(($mjd - 15078.2) div 365.25 )"/>
<xsl:variable name="m1" select="floor(($mjd - 14956.1 - floor($y1 * 365.25)) div 30.6001)"/>
<xsl:variable name="d1" select="$mjd - 14956 - floor($y1 * 365.25) - floor($m1 * 30.6001)"/>
<xsl:variable name="k">
<xsl:choose>
<xsl:when test="$m1 = 14 or $m1 = 15">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="y" select="$y1 + $k + 1900"/>
<xsl:variable name="m2" select="$m1 - 1 - $k * 12"/>
<xsl:variable name="m3" select="concat('00',string($m2))"/>
<xsl:variable name="m" select="substring($m3,string-length($m3)-1,2)"/>
<xsl:variable name="d2" select="concat('00',string($d1))"/>
<xsl:variable name="d" select="substring($d2,string-length($d2)-1,2)"/>
<xsl:value-of select="concat($y,'-',$m,'-',$d)"/>
</xsl:template>
パラメーター mjd には、前章で求めた修正ユリウス日(MJD)を渡す(3行目)。
XSLT の変数は上書きできないため、計算過程は逐一別の変数を使っている。後ろに数字がついている変数が一時的な変数である。
16〜20行目の m2→m3→m、d1→d2→d という処理は月と日の 0 埋め(ゼロ埋め)をするためである。 先頭に 00 をつけたあと、最後尾から 2 文字を切り出している。
作成したテンプレートを用いて前日の日付を求める XSLT を示す。
1 2 3 4 5 6 7 8 9 10 11 12 13
<!-- 修正ユリウス通日(MJD) の算出 -->
<xsl:variable name="CURRENT_MJD">
<xsl:call-template name="ymd2mjd">
<xsl:with-param name="ymd">2011-04-06</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<!-- 前日の日付算出 -->
<xsl:variable name="PREV_DATE">
<xsl:call-template name="mjd2ymd">
<xsl:with-param name="mjd" select="$CURRENT_MJD - 1"/>
</xsl:call-template>
</xsl:variable>
Kenz Yamada(山田研二)。1984年生。大阪。ちょっとずつ好きなプログラム作ってます。
好きなものはカメラと旅行。ガジェットや身の回り、ちょっとこだわります。
詳しくは Web mixi で。