トップ > Tech > XSLT > XSLTで日付計算

XSLTで日付計算

このページでは 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)) &lt;= 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)) &lt;= 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>

  1. <xsl:param> で定義されるパラメーター ymd には呼び出し元から yyyy-mm-dd 形式の日付文字列を渡す(3行目)
  2. <xsl:variable> で y, m, d をそれぞれ ymd から部分文字列で切り出し、数値に変換(4, 14, 24行目)
  3. m <= 2 のときは m = m + 12, y = y - 1 にする(6, 16行目)
  4. <xsl:value-of> で計算した結果を書き出す(25行目)

計算に用いる演算子について、加減・乗算は通常の +-* で問題ないが、除算は 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>

  1. ymd2mjd に日付文字列を渡し、MJD を算出し、結果を CURRENT_MJD にセットする(2〜6行目)
  2. その CURRENT_MJD から 1 引いた値を mjd2ymd に渡すと結果の日付文字列が得られる(9〜13行目)
(2011/04/06 18:13:03)
関連ページ
11794
プロフィール

Kenz Yamada(山田研二)。1984年生。大阪。ちょっとずつ好きなプログラム作ってます。 好きなものはカメラと旅行。ガジェットや身の回り、ちょっとこだわります。 詳しくは Web mixi で。

Bookmark and Share