Главная » XSLT » Еврейский календарь

0

Задача

Требуется работать с датами в еврейской системе летоисчисления.

Решение

Для эффективной работы с еврейским календарем понадобятся несколько вспомогательных шаблонов. В еврейском календаре обычный год состоит из 12 месяцев, а високосный – из 13. Високосными считаются 3, 6, 8, 11, 14, 17 и 19 года метонова цикла (см. ниже раздел «Обсуждение»). Точная формулировка условия високосности такова: 7y + 1 mod 19 < 7. Это позволяет написать шаблон для опре­деления последнего месяца года по еврейскому календарю:

<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>

Для вычисления количества дней в заданном месяце или годе нужно предва­рительно инкапсулировать сложные правила определения начала года в еврейс­ком календаре. Подробные объяснения см. в работе Дершовица и Рейнгольда.

<!– Количество дней, прошедших с воскресенья, предшествующего началу еврейского календаря, до усредненной даты начала месяца Тишрей –>

<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-ellapsed"

select="2 35 * floor(($year -1) div 19) + 12 * (($year -1) mod 19) +

floor((7 * (($year – 1) mod 19) + 1) div 19)"/>

<xsl:variable name="parts-ellapsed"

select="137 5 3 * $months-ellapsed + 5604"/>

<xsl:variable name="day" select="1 + 29 * $months-ellapsed + floor($parts-ellapsed div 25920)"/>

<xsl:variable name="parts" select="$parts-ellapsed mod 25920"/>

<xsl:variable name="alternative-day"> <xsl:choose>

<xsl:when test="$parts >= 19440">

<xsl:value-of select="$day + 1"/> </xsl:when>

<xsl:when test="$day mod 7 = 2 and $parts >= 9924 and not($hebrew-leap-year)"> <xsl:value-of select="$day + 1"/> </xsl:when>

<xsl:when test="$day mod 7 = 1 and $parts >= 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>

Число дней в году вычисляется как разность между количеством дней до на­чала последующего и предыдущего года:

<xsl:template name="ckbk:days-in-hebrew-year"> <xsl:param name="year"/>

<xsl:call-template name="ckbk:hebrew-calendar-ellapsed-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-ellapsed-days">

<xsl:with-param name="year" select="$year"/> </xsl:call-template> </xsl:variable>

<xsl:value-of select="$e1 – $e2"/> </xsl:template>

Хешван и Кислев – восьмой и девятый месяцы еврейского года, число дней в них переменно. Необходимо знать, когда Хешван длинный, а Кислев короткий, поэтому введем два предиката:

<xsl:template name="date:long-heshvan"> <xsl:param name="year"/>

<xsl:variable name="days">

<xsl:call-template name="date:days-in-hebrew-year">

<xsl:with-param name="year" select="$year"/> </xsl:call-template> </xsl:variable>

<xsl:if test="$days mod 10 = 5">

<xsl:value-of select="true()"/> </xsl:if> </xsl:template>

<xsl:template name="date:short-kislev"> <xsl:param name="year"/>

<xsl:variable name="days">

<xsl:call-template name="date:days-in-hebrew-year">

<xsl:with-param name="year" select="$year"/> </xsl:call-template> </xsl:variable>

<xsl:if test="$days mod 10 = 3">

<xsl:value-of select="true()"/> </xsl:if> </xsl:template>

 При кодировании шаблонов-предикатов на XSLT 1.0 нужно, чтобы они воз­вращали true() (или ‘true’)в качестве истинного значения, но ” (null string) – в качестве ложного. Проблема в том, что шаблоны возвращают деревья, а вычисление любого дерева, пусть даже всего из одного узла, который содержит false() или ”, дает true. Но у дерева, содержащего узел ”, есть одно достоинство: оно эффективно вычисляется как булевс­кое значение с помощью преобразования функцией string(). Это одна из многих странностей XSLT. В XSLT 2.0 можно вместо этого просто ис­пользовать функции, возвращающие булевские значения.

Теперь почти все готово к написанию стандартных функций для работы с да­той и временем, которые приводились в других рецептах. Первая из них дает пос­ледний день заданного месяца и года еврейского календаря:

<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="2 9"/> </xsl:when>

<xsl:when test="$month=13"> <xsl:value-of select="2 9"/>

</xsl:when>

<xsl:when test="$month mod 2 = 0">

<xsl:value-of select="2 9"/> </xsl:when> <xsl:otherwise>

<xsl:value-of select="30"/> </xsl:otherwise> </xsl:choose> </xsl:template>

Следующий рекурсивный шаблон суммирует последние дни в заданном диа­пазоне месяцев заданного года. Он используется при преобразовании даты еврей­ского календаря в абсолютную.

<xsl:template name="date: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="date: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="date: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="date: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 > $month"> <!— before Tishri —> <xsl:variable name="last-month-of-year">

<xsl:call-template name="date:last-month-of-hebrew-year">

<xsl:with-param name="year" select="$year"/> </xsl:call-template> </xsl:variable>

<!– Добавить дни до и после месяца Нисан –> <xsl:variable name="days-before-nisan">

<xsl:call-template name="date: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="date: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>

<!– количество дней в предшествующих месяцах этого года —> <xsl:call-template name="date: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="date:hebrew-calendar-ellapsed-days">

<xsl:with-param name="year" select="$year"/> </xsl:call-template> </xsl:variable>

<xsl:value-of select="$day + $prior-months-days + $days-in-prior-years – 1373429"/> </xsl:template>

Прежде чем приступать к реализации шаблона absolute-days-to- hebrew-date, нам понадобятся еще две рекурсивные утилиты суммирования, которые помогут вычислить истинный год и месяц, соответствующие абсолютно­му дню, по их приблизительным значениям:

<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 >= $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: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 > $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="date: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="date: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="date: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="date: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="date: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>

В предыдущем примере мы создали три похожих рекурсивных шаблона для вычисления суммы значений разных функций. Хорошо бы иметь обобщен­ную утилиту для суммирования значений произвольной функции. Читате­ли, знакомые с языком Lisp, наверное, догадались, что в оригинальном коде для этой цели применялся макрос. В главе 13 показано, как можно реали­зовать обобщенное программирование на языке XSLT и тем самым замет­но упростить этот рецепт.

Обсуждение

– самый сложный из рассмотренных в этой главе. Пото­му и код для работы с ним получился таким сложным. Сложность еврейского ка­лендаря обусловлена тем, что месяцы в нем всегда лунные, но пасха (праздник Песах) обязательно должна приходиться на весну. Если в других календарях чис­ло месяцев в году фиксировано, то в еврейском обычный год состоит из 12 меся­цев, а високосный – из 13.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии