Mit dem hebräischen Kalender arbeiten

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen mit Daten im hebräischen System arbeiten.

Lösung

Sie müssen sich einige grundlegende Hilfsmittel schaffen, um effektiv mit dem hebräischen Kalender zu arbeiten. Hebräische Jahre haben in einem normalen Jahr 12 und in einem Schaltjahr 13 Monate. Schaltjahre treten im 3., 6., 8., 11., 14., 17. und 19. Jahr des metonischen Zyklus auf (siehe den Abschnitt »Diskussion«). Eine knappe Methode zum Feststellen dieser Bestimmung wird durch die Gleichung 7y + 1 mod 19 < 7 angegeben. Daraus können Sie leicht eine Funktion entwickeln, mit der sich der letzte Monat eines hebräischen Jahres ermitteln lässt:

<xsl:template name="ckbk:last-month-of-hebrew-year">
  <xsl:param name="year"/>
  <xsl:choose>
    <xsl:when test="(7 * $year + 1) mod 19 &lt; 7">
      <xsl:value-of select="13"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="12"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Als Voraussetzung zum Ermitteln der Anzahl der Tage in einem bestimmten Monat oder Jahr müssen Sie die komplexen Regeln einbinden, die bestimmen, wann das hebräische neue Jahr beginnt. Eine ausführliche Erklärung finden Sie bei Dershowitz und Reingold:

<!-- Anzahl der Tage, die vom Sonntag vor dem Start des hebräischen Kalenders bis zum durchschnittlichen Zusammentreffen von Tischri des hebräischen Jahres verstrichen sind. -->
<xsl:template name="ckbk:hebrew-calendar-elapsed-days">
  <xsl:param name="year"/>
  <xsl:variable name="hebrew-leap-year" select="(7 * $year + 1) mod 19 &lt; 7"/>
  <xsl:variable name="hebrew-leap-year-last-year" select="(7 * ($year - 1) + 1) mod 19 &lt; 7"/>
  <xsl:variable name="months-elapsed" select="235 * floor(($year −1) div 19) + 12 * (($year −1) mod 19) + floor((7 * (($year - 1) mod 19) + 1) div 19)"/>
  <xsl:variable name="parts-elapsed" select="13753 * $months-elapsed + 5604"/>
  <xsl:variable name="day" select="1 + 29 * $months-elapsed + floor($parts-elapsed div 25920)"/>
  <xsl:variable name="parts" select="$parts-elapsed mod 25920"/>
  <xsl:variable name="alternative-day">
    <xsl:choose>
      <xsl:when test="$parts &gt;= 19440">
        <xsl:value-of select="$day + 1"/>
      </xsl:when>
      <xsl:when test="$day mod 7 = 2 and $parts &gt;= 9924 and not($hebrew-leap-year)">
        <xsl:value-of select="$day + 1"/>
      </xsl:when>
      <xsl:when test="$day mod 7 = 1 and $parts &gt;= 16789 and $hebrew-leap-year-last-year">
        <xsl:value-of select="$day + 1"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$day"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$alternative-day mod 7 = 0">
      <xsl:value-of select="$alternative-day + 1"/>
    </xsl:when>
    <xsl:when test="$alternative-day mod 7 =3">
      <xsl:value-of select="$alternative-day + 1"/>
    </xsl:when>
    <xsl:when test="$alternative-day mod 7 = 5">
      <xsl:value-of select="$alternative-day + 1"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$alternative-day"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Die Anzahl der Tage in einem hebräischen Jahr wird als die Differenz zwischen den verstrichenen Tagen in aufeinander folgenden Jahren berechnet:

<xsl:template name="ckbk:days-in-hebrew-year">
  <xsl:param name="year"/>
  <xsl:variable name="e1">
    <xsl:call-template name="ckbk:hebrew-calendar-elapsed-days">
      <xsl:with-param name="year" select="$year + 1"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="e2">
    <xsl:call-template name="ckbk:hebrew-calendar-elapsed-days">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:value-of select="$e1 - $e2"/>
</xsl:template>

Cheschwan und Kislew sind der achte und der neunte Monat des hebräischen Jahres. Die Anzahlen ihrer Tage können variieren. Sie müssen wissen, wann Cheschwan lang und wann Kislew kurz ist, sodass Sie zwei Prädikate erzeugen:

<xsl:template name="ckbk:long-heshvan">
  <xsl:param name="year"/>
  <xsl:variable name="days">
    <xsl:call-template name="ckbk:days-in-hebrew-year">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:if select="$days mod 10 = 5">
    <xsl:value-of select="true( )"/>
  </xsl:if>
</xsl:template>
<xsl:template name="ckbk:short-kislev">
  <xsl:param name="year"/>
  <xsl:variable name="days">
    <xsl:call-template name="ckbk:days-in-hebrew-year">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:if select="$days mod 10 = 3">
    <xsl:value-of select="true( )"/>
  </xsl:if>
</xsl:template>

Achtung!
Falls Sie Prädikats-Templates in XSLT 1.0 schreiben, sollten Sie sie so abfassen, dass sie true( ) (oder alternativ 'true') für wahr, aber '' (null string) für falsch zurückgeben. Das Problem ist hier, dass Templates Bäume zurückliefern und dass jeder Baum, selbst einer, dessen einziger Knoten false( ) oder '' enthält, zu true ausgewertet wird. Der Vorteil eines Baums, der '' enthält, besteht darin, dass er tatsächlich als Boolescher Ausdruck ausgewertet werden kann, indem man die string( )-Konvertierung verwendet. Dies ist eine der seltsamsten Tatsachen von XSLT. In XSLT 2.0 können Sie stattdessen Funktionen benutzen, da diese Boolesche Werte zurückliefern können.

Die meisten Mechanismen sind nun am Start, sodass Sie jetzt mit Standard-Datums-Uhrzeit-Funktionen loslegen können, die in den anderen Rezepten zur Verfügung gestellt wurden. Die erste Standardfunktion liefert Ihnen für einen bestimmten hebräischen Monat in einem bestimmten hebräischen Jahr den letzten Tag in diesem Monat:

<xsl:template name="ckbk:last-day-of-hebrew-month">
  <xsl:param name="month"/>
  <xsl:param name="year"/>
  <xsl:variable name="hebrew-leap-year" select="(7 * $year + 1) mod 19 &lt; 7"/>
  <xsl:variable name="long-heshvan">
    <xsl:call-template name="ckbk:long-heshvan">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="short-kislev">
    <xsl:call-template name="ckbk:short-kislev">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$month=12 and $hebrew-leap-year">
      <xsl:value-of select="30"/>
    </xsl:when>
    <xsl:when test="$month=8 and string($long-heshvan)">
      <xsl:value-of select="30"/>
    </xsl:when>
    <xsl:when test="$month=9 and string($short-kislev)">
      <xsl:value-of select="29"/>
    </xsl:when>
    <xsl:when test="$month=13">
      <xsl:value-of select="29"/>
    </xsl:when>
    <xsl:when test="$month mod 2 = 0">
      <xsl:value-of select="29"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="30"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Dieses rekursive Hilfsprogramm erlaubt es Ihnen, für ein bestimmtes Jahr die letzten Tage in einem Bereich hebräischer Monate zu summieren. Es wird verwendet, wenn ein hebräisches Datum in ein absolutes Jahr umgewandelt werden soll:

<xsl:template name="ckbk:sum-last-day-in-hebrew-months">
  <xsl:param name="year"/>
  <xsl:param name="from-month"/>
  <xsl:param name="to-month"/>
  <xsl:param name="accum" select="0"/>
  <xsl:choose>
    <xsl:when test="$from-month &lt;= $to-month">
      <xsl:call-template name="ckbk:sum-last-day-in-hebrew-months">
        <xsl:with-param name="year" select="$year"/>
        <xsl:with-param name="from-month" select="$from-month+1"/>
        <xsl:with-param name="to-month" select="$to-month"/>
        <xsl:with-param name="accum">
          <xsl:variable name="temp">
            <xsl:call-template name="ckbk:last-day-of-hebrew-month">
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$from-month"/>
            </xsl:call-template>
          </xsl:variable>
          <xsl:value-of select="$temp + $accum"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$accum"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
<xsl:template name="ckbk:hebrew-date-to-absolute-day">
  <xsl:param name="year"/>
  <xsl:param name="month"/>
  <xsl:param name="day"/>
  <xsl:variable name="prior-months-days">
    <xsl:choose>
      <xsl:when test="7 &gt; $month"> <!-- before Tishri -->
        <xsl:variable name="last-month-of-year">
          <xsl:call-template name="ckbk:last-month-of-hebrew-year">
            <xsl:with-param name="year" select="$year"/>
          </xsl:call-template>
        </xsl:variable>
        <!-- Addiere Tage vor und nach Nissan -->
        <xsl:variable name="days-before-nisan">
          <xsl:call-template name="ckbk:sum-last-day-in-hebrew-months">
            <xsl:with-param name="year" select="$year"/>
            <xsl:with-param name="from-month" select="7"/>
            <xsl:with-param name="to-month" select="$last-month-of-year"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:call-template name="ckbk:sum-last-day-in-hebrew-months">
          <xsl:with-param name="year" select="$year"/>
          <xsl:with-param name="from-month" select="1"/>
          <xsl:with-param name="to-month" select="$month - 1"/>
          <xsl:with-param name="accum" select="$days-before-nisan"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <!-- Tage in früheren Monaten dieses Jahres-->
        <xsl:call-template name="ckbk:sum-last-day-in-hebrew-months">
          <xsl:with-param name="year" select="$year"/>
          <xsl:with-param name="from-month" select="7"/>
          <xsl:with-param name="to-month" select="$month - 1"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="days-in-prior-years">
    <xsl:call-template name="ckbk:hebrew-calendar-elapsed-days">
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <!-- 1373429 Tage vor dem absoluten Tag 1 -->
  <xsl:value-of select="$day + $prior-months-days + $days-in-prior-years - 1373429"/>
</xsl:template>

Bevor Sie absolute-day-to-hebrew-date implementieren können, brauchen Sie zwei weitere rekursive Summierungshilfsprogramme, die dabei helfen, aus Schätzungen desselben Jahres und Monats nach dem tatsächlichen Jahr und Monat zu suchen, die einem absoluten Tag entsprechen:

<xsl:template name="ckbk:fixup-hebrew-year">
  <xsl:param name="start-year"/>
  <xsl:param name="abs-day"/>
  <xsl:param name="accum" select="0"/>
  <xsl:variable name="next">
    <xsl:call-template name="ckbk:hebrew-date-to-absolute-day">
      <xsl:with-param name="month" select="7"/>
      <xsl:with-param name="day" select="1"/>
      <xsl:with-param name="year" select="$start-year + 1"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$abs-day &gt;= $next">
      <xsl:call-template name="ckbk:fixup-hebrew-year">
        <xsl:with-param name="start-year" select="$start-year+1"/>
        <xsl:with-param name="abs-day" select="$abs-day"/>
        <xsl:with-param name="accum" select="$accum + 1"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$accum"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
<xsl:template name="ckbk:fixup-hebrew-month">
  <xsl:param name="year"/>
  <xsl:param name="start-month"/>
  <xsl:param name="abs-day"/>
  <xsl:param name="accum" select="0"/>
  <xsl:variable name="next">
    <xsl:call-template name="ckbk:hebrew-date-to-absolute-day">
      <xsl:with-param name="month" select="$start-month"/>
      <xsl:with-param name="day">
        <xsl:call-template name="ckbk:last-day-of-hebrew-month">
          <xsl:with-param name="month" select="$start-month"/>
          <xsl:with-param name="year" select="$year"/>
        </xsl:call-template>
      </xsl:with-param>
      <xsl:with-param name="year" select="$year"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$abs-day &gt; $next">
      <xsl:call-template name="ckbk:fixup-hebrew-month">
        <xsl:with-param name="year" select="$year"/>
        <xsl:with-param name="start-month" select="$start-month + 1"/>
        <xsl:with-param name="abs-day" select="$abs-day"/>
        <xsl:with-param name="accum" select="$accum + 1"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$accum"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
<xsl:template name="ckbk:absolute-day-to-hebrew-date">
  <xsl:param name="abs-day"/>
  <xsl:variable name="year">
    <xsl:variable name="approx" select="floor(($abs-day + 1373429) div 366)"/>
    <xsl:variable name="fixup">
      <xsl:call-template name="ckbk:fixup-hebrew-year">
        <xsl:with-param name="start-year" select="$approx"/>
        <xsl:with-param name="abs-day" select="$abs-day"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$approx + $fixup"/>
  </xsl:variable>
  <xsl:variable name="month">
    <xsl:variable name="first-day-of-year">
      <xsl:call-template name="ckbk:hebrew-date-to-absolute-day">
        <xsl:with-param name="month" select="1"/>
        <xsl:with-param name="day" select="1"/>
        <xsl:with-param name="year" select="$year"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="approx">
      <xsl:choose>
        <xsl:when test="$abs-day &lt; $first-day-of-year">
          <xsl:value-of select="7"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="1"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="fixup">
      <xsl:call-template name="ckbk:fixup-hebrew-month">
        <xsl:with-param name="year" select="$year"/>
        <xsl:with-param name="start-month" select="$approx"/>
        <xsl:with-param name="abs-day" select="$abs-day"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$approx + $fixup"/>
  </xsl:variable>
  <xsl:variable name="day">
    <xsl:variable name="days-to-first-of-month">
      <xsl:call-template name="ckbk:hebrew-date-to-absolute-day">
        <xsl:with-param name="month" select="$month"/>
        <xsl:with-param name="day" select="1"/>
        <xsl:with-param name="year" select="$year"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:value-of select="$abs-day - ($days-to-first-of-month - 1)"/>
  </xsl:variable>
  <xsl:value-of select="concat($year,'-',$month,'-',$day)"/>
</xsl:template>

Anmerkung:
Im vorherigen Beispiel schaffen Sie drei eindeutige, aber ähnliche rekursive Templates, die die Summe einer Funktion berechnen. Es ist sehr wünschenswert, ein generisches Hilfsprogramm zu schreiben, das eine beliebige Funktion summieren kann. Leser, die mit Lisp vertraut sind, könnten annehmen, dass der ursprüngliche Lisp-Code, von dem dieses XSLT abgeleitet wurde, genau für diesen Zweck ein Lisp-Makro besaß. Rezepte für vertikale XSLT-Anwendungen demonstriert, wie generische Programmierung in XSLT eingesetzt und wie daraus folgend dieses Rezept deutlich vereinfacht werden kann.

Diskussion

Der hebräische Kalender ist der komplexeste Kalender, den wir in diesem Kapitel behandeln. Daher ist der hebräische date-time-Code entsprechend kompliziert. Der hebräische Kalender ist umständlich, weil seine Monate streng lunar sind, er aber festlegt, dass Passah immer im Frühling stattfinden muss. Während andere Kalender eine feste Anzahl von Monaten besitzen, hat der hebräische Kalender während eines normalen Jahres 12 Monate und während eines Schaltjahres 13 Monate.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de