summaryrefslogtreecommitdiff
path: root/library/WT/Date
diff options
context:
space:
mode:
Diffstat (limited to 'library/WT/Date')
-rw-r--r--library/WT/Date/Arabic.php84
-rw-r--r--library/WT/Date/Calendar.php615
-rw-r--r--library/WT/Date/French.php190
-rw-r--r--library/WT/Date/Gregorian.php70
-rw-r--r--library/WT/Date/Hebrew.php172
-rw-r--r--library/WT/Date/Hijri.php155
-rw-r--r--library/WT/Date/Jewish.php177
-rw-r--r--library/WT/Date/Julian.php109
-rw-r--r--library/WT/Date/Roman.php50
9 files changed, 1622 insertions, 0 deletions
diff --git a/library/WT/Date/Arabic.php b/library/WT/Date/Arabic.php
new file mode 100644
index 0000000000..11739b2667
--- /dev/null
+++ b/library/WT/Date/Arabic.php
@@ -0,0 +1,84 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Arabic calendar.
+// NOTE - this is the same as the Hijri Calendar, but displays dates in arabic
+// rather than the local language.
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Arabic extends WT_Date_Hijri {
+ static $ARABIC_DAYS=array("الأثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعه", "السبت", "الأحد");
+
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ // Do not translate these - they are supposed to be arabic, whatever language is shown.
+ switch ($n) {
+ case 1: return 'محرّم';
+ case 2: return 'صفر';
+ case 3: return 'ربيع الأول';
+ case 4: return 'ربيع الثانى';
+ case 5: return 'جمادى الأول';
+ case 6: return 'جمادى الثاني';
+ case 7: return 'رجب';
+ case 8: return 'شعبان';
+ case 9: return 'رمضان';
+ case 10: return 'شوّال';
+ case 11: return 'ذو القعدة';
+ case 12: return 'ذو الحجة';
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ // Arabic does not have genitive forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ // Arabic does not have locative forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ // Arabic does not have instrumental forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ // TODO: Do these have short names?
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ function FormatLongWeekday() {
+ return $this->$ARABIC_DAYS[$this->minJD % $this->NUM_DAYS_OF_WEEK()];
+ }
+
+ function FormatShortWeekday() {
+ return $this->$ARABIC_DAYS[$this->minJD % $this->NUM_DAYS_OF_WEEK()];
+ }
+}
diff --git a/library/WT/Date/Calendar.php b/library/WT/Date/Calendar.php
new file mode 100644
index 0000000000..802ae926c0
--- /dev/null
+++ b/library/WT/Date/Calendar.php
@@ -0,0 +1,615 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// WT_Date_Calendar is a base class for classes such as WT_Date_Gregorian, etc.
+//
+// + All supported calendars have non-zero days/months/years.
+// + We store dates as both Y/M/D and Julian Days.
+// + For imprecise dates such as "JAN 2000" we store the start/end julian day.
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Calendar {
+ var $y, $m, $d; // Numeric year/month/day
+ var $minJD, $maxJD; // Julian Day numbers
+
+ function __construct($date) {
+ // Construct from an integer (a julian day number)
+ if (is_numeric($date)) {
+ $this->minJD=$date;
+ $this->maxJD=$date;
+ list($this->y, $this->m, $this->d)=$this->JDtoYMD($date);
+ return;
+ }
+
+ // Construct from an array (of three gedcom-style strings: "1900", "feb", "4")
+ if (is_array($date)) {
+ $this->d=(int)$date[2];
+ if (!is_null($this->MONTH_TO_NUM($date[1]))) {
+ $this->m=$this->MONTH_TO_NUM($date[1]);
+ } else {
+ $this->m=0;
+ $this->d=0;
+ }
+ $this->y=$this->ExtractYear($date[0]);
+ $this->SetJDfromYMD();
+ return;
+ }
+
+ // Construct from an equivalent xxxxDate object
+ if ($this->CALENDAR_ESCAPE()==$date->CALENDAR_ESCAPE()) {
+ // NOTE - can't copy whole object - need to be able to copy Hebrew to Jewish, etc.
+ $this->y=$date->y;
+ $this->m=$date->m;
+ $this->d=$date->d;
+ $this->minJD=$date->minJD;
+ $this->maxJD=$date->maxJD;
+ return;
+ }
+
+ // ...else construct an inequivalent xxxxDate object
+ if ($date->y==0) {
+ // Incomplete date - convert on basis of anniversary in current year
+ $today=$date->TodayYMD();
+ $jd=$date->YMDtoJD($today[0], $date->m, $date->d==0?$today[2]:$date->d);
+ } else {
+ // Complete date
+ $jd=floor(($date->maxJD+$date->minJD)/2);
+ }
+ list($this->y, $this->m, $this->d)=$this->JDtoYMD($jd);
+ // New date has same precision as original date
+ if ($date->y==0) $this->y=0;
+ if ($date->m==0) $this->m=0;
+ if ($date->d==0) $this->d=0;
+ $this->SetJDfromYMD();
+ }
+
+ // Set the object's JD from a potentially incomplete YMD
+ function SetJDfromYMD() {
+ if ($this->y==0) {
+ $this->minJD=0;
+ $this->maxJD=0;
+ } else
+ if ($this->m==0) {
+ $this->minJD=$this->YMDtoJD($this->y, 1, 1);
+ $this->maxJD=$this->YMDtoJD($this->NextYear($this->y), 1, 1)-1;
+ } else {
+ if ($this->d==0) {
+ list($ny,$nm)=$this->NextMonth();
+ $this->minJD=$this->YMDtoJD($this->y, $this->m, 1);
+ $this->maxJD=$this->YMDtoJD($ny, $nm, 1)-1;
+ } else {
+ $this->minJD=$this->YMDtoJD($this->y, $this->m, $this->d);
+ $this->maxJD=$this->minJD;
+ }
+ }
+ }
+
+ // Calendars are defined in terms of the following static functions.
+ // They should redefine them as necessary.
+ static function CALENDAR_ESCAPE() {
+ return '@#DUNKNOWN@';
+ }
+ static function NUM_MONTHS() {
+ return 12;
+ }
+ static function MONTH_TO_NUM($m) {
+ static $months=array(''=>0, 'jan'=>1, 'feb'=>2, 'mar'=>3, 'apr'=>4, 'may'=>5, 'jun'=>6, 'jul'=>7, 'aug'=>8, 'sep'=>9, 'oct'=>10, 'nov'=>11, 'dec'=>12);
+ if (isset($months[$m])) {
+ return $months[$m];
+ } else {
+ return null;
+ }
+ }
+ // We put these in the base class, to save duplicating it in the Julian and Gregorian calendars
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('NOMINATIVE', 'January');
+ case 2: return i18n::translate_c('NOMINATIVE', 'February');
+ case 3: return i18n::translate_c('NOMINATIVE', 'March');
+ case 4: return i18n::translate_c('NOMINATIVE', 'April');
+ case 5: return i18n::translate_c('NOMINATIVE', 'May');
+ case 6: return i18n::translate_c('NOMINATIVE', 'June');
+ case 7: return i18n::translate_c('NOMINATIVE', 'July');
+ case 8: return i18n::translate_c('NOMINATIVE', 'August');
+ case 9: return i18n::translate_c('NOMINATIVE', 'September');
+ case 10: return i18n::translate_c('NOMINATIVE', 'October');
+ case 11: return i18n::translate_c('NOMINATIVE', 'November');
+ case 12: return i18n::translate_c('NOMINATIVE', 'December');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('GENITIVE', 'January');
+ case 2: return i18n::translate_c('GENITIVE', 'February');
+ case 3: return i18n::translate_c('GENITIVE', 'March');
+ case 4: return i18n::translate_c('GENITIVE', 'April');
+ case 5: return i18n::translate_c('GENITIVE', 'May');
+ case 6: return i18n::translate_c('GENITIVE', 'June');
+ case 7: return i18n::translate_c('GENITIVE', 'July');
+ case 8: return i18n::translate_c('GENITIVE', 'August');
+ case 9: return i18n::translate_c('GENITIVE', 'September');
+ case 10: return i18n::translate_c('GENITIVE', 'October');
+ case 11: return i18n::translate_c('GENITIVE', 'November');
+ case 12: return i18n::translate_c('GENITIVE', 'December');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('LOCATIVE', 'January');
+ case 2: return i18n::translate_c('LOCATIVE', 'February');
+ case 3: return i18n::translate_c('LOCATIVE', 'March');
+ case 4: return i18n::translate_c('LOCATIVE', 'April');
+ case 5: return i18n::translate_c('LOCATIVE', 'May');
+ case 6: return i18n::translate_c('LOCATIVE', 'June');
+ case 7: return i18n::translate_c('LOCATIVE', 'July');
+ case 8: return i18n::translate_c('LOCATIVE', 'August');
+ case 9: return i18n::translate_c('LOCATIVE', 'September');
+ case 10: return i18n::translate_c('LOCATIVE', 'October');
+ case 11: return i18n::translate_c('LOCATIVE', 'November');
+ case 12: return i18n::translate_c('LOCATIVE', 'December');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('INSTRUMENTAL', 'January');
+ case 2: return i18n::translate_c('INSTRUMENTAL', 'February');
+ case 3: return i18n::translate_c('INSTRUMENTAL', 'March');
+ case 4: return i18n::translate_c('INSTRUMENTAL', 'April');
+ case 5: return i18n::translate_c('INSTRUMENTAL', 'May');
+ case 6: return i18n::translate_c('INSTRUMENTAL', 'June');
+ case 7: return i18n::translate_c('INSTRUMENTAL', 'July');
+ case 8: return i18n::translate_c('INSTRUMENTAL', 'August');
+ case 9: return i18n::translate_c('INSTRUMENTAL', 'September');
+ case 10: return i18n::translate_c('INSTRUMENTAL', 'October');
+ case 11: return i18n::translate_c('INSTRUMENTAL', 'November');
+ case 12: return i18n::translate_c('INSTRUMENTAL', 'December');
+ default: return '';
+ }
+ }
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('Abbreviation for January', 'Jan');
+ case 2: return i18n::translate_c('Abbreviation for February', 'Feb');
+ case 3: return i18n::translate_c('Abbreviation for March', 'Mar');
+ case 4: return i18n::translate_c('Abbreviation for April', 'Apr');
+ case 5: return i18n::translate_c('Abbreviation for May', 'May');
+ case 6: return i18n::translate_c('Abbreviation for June', 'Jun');
+ case 7: return i18n::translate_c('Abbreviation for July', 'Jul');
+ case 8: return i18n::translate_c('Abbreviation for August', 'Aug');
+ case 9: return i18n::translate_c('Abbreviation for September', 'Sep');
+ case 10: return i18n::translate_c('Abbreviation for October', 'Oct');
+ case 11: return i18n::translate_c('Abbreviation for November', 'Nov');
+ case 12: return i18n::translate_c('Abbreviation for December', 'Dec');
+ default: return '';
+ }
+ }
+ static function NUM_TO_GEDCOM_MONTH($n, $leap_year) {
+ switch ($n) {
+ case 1: return 'JAN';
+ case 2: return 'FEB';
+ case 3: return 'MAR';
+ case 4: return 'APR';
+ case 5: return 'MAY';
+ case 6: return 'JUN';
+ case 7: return 'JUL';
+ case 8: return 'AUG';
+ case 9: return 'SEP';
+ case 10: return 'OCT';
+ case 11: return 'NOV';
+ case 12: return 'DEC';
+ default: return '';
+ }
+ }
+ static function CAL_START_JD() {
+ return 0; // @#DJULIAN@ 01 JAN 4713B.C.
+ }
+ static function CAL_END_JD() {
+ return 99999999;
+ }
+ static function NUM_DAYS_OF_WEEK() {
+ return 7;
+ }
+ static function LONG_DAYS_OF_WEEK($n) {
+ switch ($n) {
+ case 0: return i18n::translate('Monday');
+ case 1: return i18n::translate('Tuesday');
+ case 2: return i18n::translate('Wednesday');
+ case 3: return i18n::translate('Thursday');
+ case 4: return i18n::translate('Friday');
+ case 5: return i18n::translate('Saturday');
+ case 6: return i18n::translate('Sunday');
+ }
+ }
+ static function SHORT_DAYS_OF_WEEK($n) {
+ switch ($n) {
+ case 0: return i18n::translate('Mon');
+ case 1: return i18n::translate('Tue');
+ case 2: return i18n::translate('Wed');
+ case 3: return i18n::translate('Thu');
+ case 4: return i18n::translate('Fri');
+ case 5: return i18n::translate('Sat');
+ case 6: return i18n::translate('Sun');
+ }
+ }
+ static function YMDtoJD($y, $m, $d) {
+ return 0;
+ }
+ static function JDtoYMD($j) {
+ return array(0, 0, 0);
+ }
+ // Most years are 1 more than the previous, but not always (e.g. 1BC->1AD)
+ static function NextYear($y) {
+ return $y+1;
+ }
+ // Calendars that use suffixes, etc. (e.g. 'B.C.') or OS/NS notation should redefine this.
+ function ExtractYear($year) {
+ return (int)$year;
+ }
+ // Leap years may have extra days, extra months, etc.
+ function IsLeapYear() {
+ return false;
+ }
+
+ // Compare two dates - helper function for sorting by date
+ static function Compare($d1, $d2) {
+ if ($d1->maxJD < $d2->minJD)
+ return -1;
+ if ($d2->minJD > $d1->maxJD)
+ return 1;
+ return 0;
+ }
+
+ // How long between an event and a given julian day
+ // Return result as either a number of years or
+ // a gedcom-style age string.
+ // bool $full: true=gedcom style, false=just years
+ // int $jd: date for calculation
+ // TODO: WT_Date_Jewish needs to redefine this to cope with leap months
+ function GetAge($full, $jd, $warn_on_negative=true) {
+ if ($this->y==0 || $jd==0) {
+ return '';
+ }
+ if ($this->minJD < $jd && $this->maxJD > $jd) {
+ return '';
+ }
+ if ($this->minJD==$jd) {
+ return $full?'':'0';
+ }
+ if ($warn_on_negative && $jd<$this->minJD) {
+ return '<img alt="" src="images/warning.gif" />';
+ }
+ list($y,$m,$d)=$this->JDtoYMD($jd);
+ $dy=$y-$this->y;
+ $dm=$m-max($this->m,1);
+ $dd=$d-max($this->d,1);
+ if ($dd<0) {
+ $dd+=$this->DaysInMonth();
+ $dm--;
+ }
+ if ($dm<0) {
+ $dm+=$this->NUM_MONTHS();
+ $dy--;
+ }
+ // Not a full age? Then just the years
+ if (!$full)
+ return $dy;
+ // Age in years?
+ if ($dy>1)
+ return $dy.'y';
+ $dm+=$dy*$this->NUM_MONTHS();
+ // Age in months?
+ if ($dm>1)
+ return $dm.'m';
+ // Age in days?
+ return ($jd-$this->minJD)."d";
+ }
+
+ // Convert a date from one calendar to another.
+ function convert_to_cal($calendar) {
+ switch ($calendar) {
+ case 'gregorian':
+ return new WT_Date_Gregorian($this);
+ case 'julian':
+ return new WT_Date_Julian($this);
+ case 'jewish':
+ if (WT_LOCALE!='he')
+ return new WT_Date_Jewish($this);
+ // no break
+ case 'hebrew':
+ return new WT_Date_Hebrew($this);
+ case 'french':
+ return new WT_Date_French($this);
+ case 'arabic':
+ if (WT_LOCALE!='ar')
+ return new WT_Date_Arabic($this);
+ // no break
+ case 'hijri':
+ return new WT_Date_Hijri($this);
+ default:
+ return $this;
+ }
+ }
+
+ // Is this date within the valid range of the calendar
+ function InValidRange() {
+ return $this->minJD>=$this->CAL_START_JD() && $this->maxJD<=$this->CAL_END_JD();
+ }
+
+ // How many days in the current month
+ function DaysInMonth() {
+ list($ny,$nm)=$this->NextMonth();
+ return $this->YMDtoJD($ny, $nm, 1) - $this->YMDtoJD($this->y, $this->m, 1);
+ }
+
+ // How many days in the current week
+ function DaysInWeek() {
+ return $this->NUM_DAYS_OF_WEEK();
+ }
+
+ // Format a date
+ // $format - format string: the codes are specified in http://php.net/date
+ function Format($format, $qualifier='') {
+ // Don't show exact details for inexact dates
+ if (!$this->d) {
+ // The comma is for US "M D, Y" dates
+ $format=preg_replace('/%[djlDNSwz][,]?/', '', $format);
+ }
+ if (!$this->m) {
+ $format=str_replace(array('%F', '%m', '%M', '%n', '%t'), '', $format);
+ }
+ if (!$this->y) {
+ $format=str_replace(array('%t', '%L', '%G', '%y', '%Y'), '', $format);
+ }
+ // If we've trimmed the format, also trim the punctuation
+ if (!$this->d || !$this->m || !$this->y) {
+ $format=trim($format, ',. ;/-');
+ }
+ if ($this->d && preg_match('/%[djlDNSwz]/', $format)) {
+ // If we have a day-number *and* we are being asked to display it, then genitive
+ $case='GENITIVE';
+ } else {
+ switch ($qualifier) {
+ case '':
+ case 'int':
+ case 'est':
+ case 'cal': $case='NOMINATIVE'; break;
+ case 'to':
+ case 'abt':
+ case 'from': $case='GENITIVE'; break;
+ case 'aft': $case='LOCATIVE'; break;
+ case 'bef':
+ case 'bet':
+ case 'and': $case='INSTRUMENTAL'; break;
+ }
+ }
+ // Build up the formated date, character at a time
+ preg_match_all('/%[^%]/', $format, $matches);
+ foreach ($matches[0] as $match) {
+ switch ($match) {
+ case '%d': $format=str_replace($match, $this->FormatDayZeros(), $format); break;
+ case '%j': $format=str_replace($match, $this->FormatDay(), $format); break;
+ case '%l': $format=str_replace($match, $this->FormatLongWeekday(), $format); break;
+ case '%D': $format=str_replace($match, $this->FormatShortWeekday(), $format); break;
+ case '%N': $format=str_replace($match, $this->FormatISOWeekday(), $format); break;
+ case '%S': $format=str_replace($match, $this->FormatOrdinalSuffix(), $format); break;
+ case '%w': $format=str_replace($match, $this->FormatNumericWeekday(), $format); break;
+ case '%z': $format=str_replace($match, $this->FormatDayOfYear(), $format); break;
+ case '%F': $format=str_replace($match, $this->FormatLongMonth($case), $format); break;
+ case '%m': $format=str_replace($match, $this->FormatMonthZeros(), $format); break;
+ case '%M': $format=str_replace($match, $this->FormatShortMonth(), $format); break;
+ case '%n': $format=str_replace($match, $this->FormatMonth(), $format); break;
+ case '%t': $format=str_replace($match, $this->DaysInMonth(), $format); break;
+ case '%L': $format=str_replace($match, (int)$this->IsLeapYear(), $format); break;
+ case '%Y': $format=str_replace($match, $this->FormatLongYear(), $format); break;
+ case '%y': $format=str_replace($match, $this->FormatShortYear(), $format); break;
+ // These 4 extensions are useful for re-formatting gedcom dates.
+ case '%@': $format=str_replace($match, $this->CALENDAR_ESCAPE(), $format); break;
+ case '%A': $format=str_replace($match, $this->FormatGedcomDay(), $format); break;
+ case '%O': $format=str_replace($match, $this->FormatGedcomMonth(), $format); break;
+ case '%E': $format=str_replace($match, $this->FormatGedcomYear(), $format); break;
+ }
+ }
+ return $format;
+ }
+
+ // Functions to extract bits of the date in various formats. Individual calendars
+ // will want to redefine some of these.
+ function FormatDayZeros() {
+ if ($this->d<10)
+ return '0'.$this->d;
+ else
+ return $this->d;
+ }
+
+ function FormatDay() {
+ return $this->d;
+ }
+
+ function FormatLongWeekday() {
+ return $this->LONG_DAYS_OF_WEEK($this->minJD % $this->NUM_DAYS_OF_WEEK());
+ }
+
+ function FormatShortWeekday() {
+ return $this->SHORT_DAYS_OF_WEEK($this->minJD % $this->NUM_DAYS_OF_WEEK());
+ }
+
+ function FormatISOWeekday() {
+ return $this->minJD % 7 + 1;
+ }
+
+ function FormatOrdinalSuffix() {
+ $func="ordinal_suffix_".WT_LOCALE;
+ if (function_exists($func))
+ return $func($this->d);
+ else
+ return '';
+ }
+
+ function FormatNumericWeekday() {
+ return ($this->minJD + 1) % $this->NUM_DAYS_OF_WEEK();
+ }
+
+ function FormatDayOfYear() {
+ return $this->minJD - $this->YMDtoJD($this->y, 1, 1);
+ }
+
+ function FormatMonth() {
+ return $this->m;
+ }
+
+ function FormatMonthZeros() {
+ if ($this->m > 9)
+ return $this->m;
+ else
+ return '0'.$this->m;
+ }
+
+ function FormatLongMonth($case='NOMINATIVE') {
+ switch ($case) {
+ case 'GENITIVE': return $this->NUM_TO_MONTH_GENITIVE ($this->m, $this->IsLeapYear());
+ case 'NOMINATIVE': return $this->NUM_TO_MONTH_NOMINATIVE ($this->m, $this->IsLeapYear());
+ case 'LOCATIVE': return $this->NUM_TO_MONTH_LOCATIVE ($this->m, $this->IsLeapYear());
+ case 'INSTRUMENTAL': return $this->NUM_TO_MONTH_INSTRUMENTAL($this->m, $this->IsLeapYear());
+ }
+ }
+
+ function FormatShortMonth() {
+ return $this->NUM_TO_SHORT_MONTH($this->m, $this->IsLeapYear());
+ }
+
+ // NOTE Short year is NOT a 2-digit year. It is for calendars such as hebrew
+ // which have a 3-digit form of 4-digit years.
+ function FormatShortYear() {
+ return $this->y;
+ }
+
+ function FormatGedcomDay() {
+ if ($this->d==0)
+ return '';
+ else
+ return sprintf('%02d', $this->d);
+ }
+
+ function FormatGedcomMonth() {
+ return $this->NUM_TO_GEDCOM_MONTH($this->m, $this->IsLeapYear());
+ }
+
+ function FormatGedcomYear() {
+ if ($this->y==0)
+ return '';
+ else
+ return sprintf('%04d', $this->y);
+ }
+
+ function FormatLongYear() {
+ return $this->y;
+ }
+
+ // Calendars with leap-months should redefine this.
+ function NextMonth() {
+ return array(
+ $this->m==$this->NUM_MONTHS() ? $this->NextYear($this->y) : $this->y,
+ ($this->m%$this->NUM_MONTHS())+1
+ );
+ }
+
+ // Convert a decimal number to roman numerals
+ static function NumToRoman($num) {
+ static $lookup=array(1000=>'M', '900'=>'CM', '500'=>'D', 400=>'CD', 100=>'C', 90=>'XC', 50=>'L', 40=>'XL', 10=>'X', 9=>'IX', 5=>'V', 4=>'IV', 1=>'I');
+ if ($num<1) return $num;
+ $roman='';
+ foreach ($lookup as $key=>$value)
+ while ($num>=$key) {
+ $roman.=$value;
+ $num-=$key;
+ }
+ return $roman;
+ }
+
+ // Convert a roman numeral to decimal
+ static function RomanToNum($roman) {
+ static $lookup=array(1000=>'M', '900'=>'CM', '500'=>'D', 400=>'CD', 100=>'C', 90=>'XC', 50=>'L', 40=>'XL', 10=>'X', 9=>'IX', 5=>'V', 4=>'IV', 1=>'I');
+ $num=0;
+ foreach ($lookup as $key=>$value)
+ if (strpos($roman, $value)===0) {
+ $num+=$key;
+ $roman=substr($roman, strlen($value));
+ }
+ return $num;
+ }
+
+ // Get today's date in the current calendar
+ function TodayYMD() {
+ return $this->JDtoYMD(WT_Date_Gregorian::YMDtoJD(date('Y'), date('n'), date('j')));
+ }
+ function Today() {
+ $tmp=clone $this;
+ $ymd=$tmp->TodayYMD();
+ $tmp->y=$ymd[0];
+ $tmp->m=$ymd[1];
+ $tmp->d=$ymd[2];
+ $tmp->SetJDfromYMD();
+ return $tmp;
+ }
+
+ // Create a URL that links this date to the WT calendar
+ function CalendarURL($date_fmt="") {
+ global $DATE_FORMAT;
+ if (empty($date_fmt)) {
+ $date_fmt=$DATE_FORMAT;
+ }
+ $URL='calendar.php?cal='.$this->CALENDAR_ESCAPE();
+ $action="year";
+ if (strpos($date_fmt, "Y")!==false
+ || strpos($date_fmt, "y")!==false) {
+ $URL.='&amp;year='.$this->FormatGedcomYear();
+ }
+ if (strpos($date_fmt, "F")!==false
+ || strpos($date_fmt, "M")!==false
+ || strpos($date_fmt, "m")!==false
+ || strpos($date_fmt, "n")!==false) {
+ $URL.='&amp;month='.$this->FormatGedcomMonth();
+ if ($this->m>0)
+ $action="calendar";
+ }
+ if (strpos($date_fmt, "d")!==false
+ || strpos($date_fmt, "D")!==false
+ || strpos($date_fmt, "j")!==false) {
+ $URL.='&amp;day='.$this->FormatGedcomDay();
+ if ($this->d>0)
+ $action="today";
+ }
+ return $URL.'&amp;action='.$action;
+ }
+}
diff --git a/library/WT/Date/French.php b/library/WT/Date/French.php
new file mode 100644
index 0000000000..9403a91b8e
--- /dev/null
+++ b/library/WT/Date/French.php
@@ -0,0 +1,190 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the French Republican calendar
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_French extends WT_Date_Calendar {
+ static function CALENDAR_ESCAPE() {
+ return '@#DFRENCH R@';
+ }
+
+ static function MONTH_TO_NUM($m) {
+ static $months=array(''=>0, 'vend'=>1, 'brum'=>2, 'frim'=>3, 'nivo'=>4, 'pluv'=>5, 'vent'=>6, 'germ'=>7, 'flor'=>8, 'prai'=>9, 'mess'=>10, 'ther'=>11, 'fruc'=>12, 'comp'=>13);
+ if (isset($months[$m])) {
+ return $months[$m];
+ } else {
+ return null;
+ }
+ }
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('NOMINATIVE', 'Vendémiaire');
+ case 2: return i18n::translate_c('NOMINATIVE', 'Brumaire');
+ case 3: return i18n::translate_c('NOMINATIVE', 'Frimaire');
+ case 4: return i18n::translate_c('NOMINATIVE', 'Nivôse');
+ case 5: return i18n::translate_c('NOMINATIVE', 'Pluviôse');
+ case 6: return i18n::translate_c('NOMINATIVE', 'Ventôse');
+ case 7: return i18n::translate_c('NOMINATIVE', 'Germinal');
+ case 8: return i18n::translate_c('NOMINATIVE', 'Floréal');
+ case 9: return i18n::translate_c('NOMINATIVE', 'Prairial');
+ case 10: return i18n::translate_c('NOMINATIVE', 'Messidor');
+ case 11: return i18n::translate_c('NOMINATIVE', 'Thermidor');
+ case 12: return i18n::translate_c('NOMINATIVE', 'Fructidor');
+ case 13: return i18n::translate_c('NOMINATIVE', 'jours complémentaires');
+ }
+ }
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('GENITIVE', 'Vendémiaire');
+ case 2: return i18n::translate_c('GENITIVE', 'Brumaire');
+ case 3: return i18n::translate_c('GENITIVE', 'Frimaire');
+ case 4: return i18n::translate_c('GENITIVE', 'Nivôse');
+ case 5: return i18n::translate_c('GENITIVE', 'Pluviôse');
+ case 6: return i18n::translate_c('GENITIVE', 'Ventôse');
+ case 7: return i18n::translate_c('GENITIVE', 'Germinal');
+ case 8: return i18n::translate_c('GENITIVE', 'Floréal');
+ case 9: return i18n::translate_c('GENITIVE', 'Prairial');
+ case 10: return i18n::translate_c('GENITIVE', 'Messidor');
+ case 11: return i18n::translate_c('GENITIVE', 'Thermidor');
+ case 12: return i18n::translate_c('GENITIVE', 'Fructidor');
+ case 13: return i18n::translate_c('GENITIVE', 'jours complémentaires');
+ }
+ }
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('LOCATIVE', 'Vendémiaire');
+ case 2: return i18n::translate_c('LOCATIVE', 'Brumaire');
+ case 3: return i18n::translate_c('LOCATIVE', 'Frimaire');
+ case 4: return i18n::translate_c('LOCATIVE', 'Nivôse');
+ case 5: return i18n::translate_c('LOCATIVE', 'Pluviôse');
+ case 6: return i18n::translate_c('LOCATIVE', 'Ventôse');
+ case 7: return i18n::translate_c('LOCATIVE', 'Germinal');
+ case 8: return i18n::translate_c('LOCATIVE', 'Floréal');
+ case 9: return i18n::translate_c('LOCATIVE', 'Prairial');
+ case 10: return i18n::translate_c('LOCATIVE', 'Messidor');
+ case 11: return i18n::translate_c('LOCATIVE', 'Thermidor');
+ case 12: return i18n::translate_c('LOCATIVE', 'Fructidor');
+ case 13: return i18n::translate_c('LOCATIVE', 'jours complémentaires');
+ }
+ }
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('INSTRUMENTAL', 'Vendémiaire');
+ case 2: return i18n::translate_c('INSTRUMENTAL', 'Brumaire');
+ case 3: return i18n::translate_c('INSTRUMENTAL', 'Frimaire');
+ case 4: return i18n::translate_c('INSTRUMENTAL', 'Nivôse');
+ case 5: return i18n::translate_c('INSTRUMENTAL', 'Pluviôse');
+ case 6: return i18n::translate_c('INSTRUMENTAL', 'Ventôse');
+ case 7: return i18n::translate_c('INSTRUMENTAL', 'Germinal');
+ case 8: return i18n::translate_c('INSTRUMENTAL', 'Floréal');
+ case 9: return i18n::translate_c('INSTRUMENTAL', 'Prairial');
+ case 10: return i18n::translate_c('INSTRUMENTAL', 'Messidor');
+ case 11: return i18n::translate_c('INSTRUMENTAL', 'Thermidor');
+ case 12: return i18n::translate_c('INSTRUMENTAL', 'Fructidor');
+ case 13: return i18n::translate_c('INSTRUMENTAL', 'jours complémentaires');
+ }
+ }
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ // TODO: Do these have short names?
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+ static function NUM_TO_GEDCOM_MONTH($n, $leap_year) {
+ switch ($n) {
+ case 1: return 'VEND';
+ case 2: return 'BRUM';
+ case 3: return 'FRIM';
+ case 4: return 'NIVO';
+ case 5: return 'PLUV';
+ case 6: return 'VENT';
+ case 7: return 'GERM';
+ case 8: return 'FLOR';
+ case 9: return 'PRAI';
+ case 10: return 'MESS';
+ case 11: return 'THER';
+ case 12: return 'FRUC';
+ case 13: return 'COMP';
+ }
+ }
+ static function NUM_MONTHS() {
+ return 13;
+ }
+ static function LONG_DAYS_OF_WEEK($n) {
+ switch ($n) {
+ case 0: return i18n::translate('Primidi');
+ case 1: return i18n::translate('Duodi');
+ case 2: return i18n::translate('Tridi');
+ case 3: return i18n::translate('Quartidi');
+ case 4: return i18n::translate('Quintidi');
+ case 5: return i18n::translate('Sextidi');
+ case 6: return i18n::translate('Septidi');
+ case 7: return i18n::translate('Octidi');
+ case 8: return i18n::translate('Nonidi');
+ case 9: return i18n::translate('Decidi');
+ }
+ }
+ static function SHORT_DAYS_OF_WEEK($n) {
+ // TODO: Do these have short names?
+ return self::LONG_DAYS_OF_WEEK($n);
+ }
+ static function NUM_DAYS_OF_WEEK() {
+ return 10; // A "metric" week of 10 unimaginatively named days.
+ }
+ static function CAL_START_JD() {
+ return 2375840; // 22 SEP 1792 = 01 VEND 0001
+ }
+ static function CAL_END_JD() {
+ return 2380687; // 31 DEC 1805 = 10 NIVO 0014
+ }
+
+ // Leap years were based on astronomical observations. Only years 3, 7 and 11
+ // were ever observed. Moves to a gregorian-like (fixed) system were proposed
+ // but never implemented. These functions are valid over the range years 1-14.
+ function IsLeapYear() {
+ return $this->y%4==3;
+ }
+
+ static function YMDtoJD($y, $m, $d) {
+ return 2375444+$d+$m*30+$y*365+floor($y/4);
+ }
+
+ static function JDtoYMD($j) {
+ $y=floor(($j-2375109)*4/1461)-1;
+ $m=floor(($j-2375475-$y*365-floor($y/4))/30)+1;
+ $d=$j-2375444-$m*30-$y*365-floor($y/4);
+ return array($y, $m, $d);
+ }
+
+ // Years were written using roman numerals
+ function FormatLongYear() {
+ return $this->NumToRoman($this->y);
+ }
+}
diff --git a/library/WT/Date/Gregorian.php b/library/WT/Date/Gregorian.php
new file mode 100644
index 0000000000..2f5d08dd95
--- /dev/null
+++ b/library/WT/Date/Gregorian.php
@@ -0,0 +1,70 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Gregorian calendar
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Gregorian extends WT_Date_Calendar {
+ static function CALENDAR_ESCAPE() {
+ return '@#DGREGORIAN@';
+ }
+ static function CAL_START_JD() {
+ return 2299161; // 15 OCT 1582
+ }
+
+ function IsLeapYear() {
+ return $this->y%4==0 && $this->y%100!=0 || $this->y%400==0;
+ }
+
+ static function YMDtoJD($y, $m, $d) {
+ if ($y<0) // 0=1BC, -1=2BC, etc.
+ ++$y;
+ $a=floor((14-$m)/12);
+ $y=$y+4800-$a;
+ $m=$m+12*$a-3;
+ return $d+floor((153*$m+2)/5)+365*$y+floor($y/4)-floor($y/100)+floor($y/400)-32045;
+ }
+
+ static function JDtoYMD($j) {
+ $a=$j+32044;
+ $b=floor((4*$a+3)/146097);
+ $c=$a-floor($b*146097/4);
+ $d=floor((4*$c+3)/1461);
+ $e=$c-floor((1461*$d)/4);
+ $m=floor((5*$e+2)/153);
+ $day=$e-floor((153*$m+2)/5)+1;
+ $month=$m+3-12*floor($m/10);
+ $year=$b*100+$d-4800+floor($m/10);
+ if ($year<1) // 0=1BC, -1=2BC, etc.
+ --$year;
+ return array($year, $month, $day);
+ }
+}
diff --git a/library/WT/Date/Hebrew.php b/library/WT/Date/Hebrew.php
new file mode 100644
index 0000000000..1e0f7dfc95
--- /dev/null
+++ b/library/WT/Date/Hebrew.php
@@ -0,0 +1,172 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Hebrew calendar.
+// NOTE - this is the same as the Jewish Calendar, but displays dates in hebrew
+// rather than the local language.
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Hebrew extends WT_Date_Jewish {
+ const GERSHAYIM="״";
+ const GERSH="׳";
+ const ALAFIM="אלפים";
+
+ function FormatDayZeros() {
+ return $this->NumToHebrew($this->d);
+ }
+
+ function FormatDay() {
+ return $this->NumToHebrew($this->d);
+ }
+
+ static function LONG_DAYS_OF_WEEK($n) {
+ // Do not translate these - they are supposed to be hebrew, whatever language is shown.
+ switch ($n) {
+ case 0: return 'שני';
+ case 1: return 'שלישי';
+ case 2: return 'רביעי';
+ case 3: return 'חמישי';
+ case 4: return 'ששי';
+ case 5: return 'שבת';
+ case 6: return 'ראשון';
+ }
+ }
+ static function SHORT_DAYS_OF_WEEK($n) {
+ // TODO: Do these have short names?
+ return LONG_DAYS_OF_WEEK($n);
+ }
+
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ // Do not translate these - they are supposed to be hebrew, whatever language is shown.
+ switch ($n) {
+ case 1: return 'תשרי';
+ case 2: return 'חשוון';
+ case 3: return 'כסלו';
+ case 4: return 'טבת';
+ case 5: return 'שבט';
+ case 6: if ($leap_year) return 'אדר א׳'; else return 'אדר';
+ case 7: return 'אדר ב׳';
+ case 8: return 'ניסן';
+ case 9: return 'אייר';
+ case 10: return 'סיוון';
+ case 11: return 'תמוז';
+ case 12: return 'אב';
+ case 13: return 'אלול';
+ default: return '';
+ }
+ }
+
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ // Hebrew does not have genitive forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ // Hebrew does not have locative forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ // Hebrew does not have instrumental forms
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ // TODO: Do these have short names?
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+
+ function FormatShortYear() {
+ return $this->NumToHebrew($this->y%1000);
+ }
+
+ function FormatLongYear() {
+ return $this->NumToHebrew($this->y);
+ }
+ // Convert a decimal number to hebrew - like roman numerals, but with extra punctuation
+ // and special rules.
+ static function NumToHebrew($num) {
+ global $DISPLAY_JEWISH_THOUSANDS;
+
+ static $jHundreds = array("", "ק", "ר", "ש", "ת", "תק", "תר","תש", "תת", "תתק");
+ static $jTens = array("", "י", "כ", "ל", "מ", "נ", "ס", "ע", "פ", "צ");
+ static $jTenEnds = array("", "י", "ך", "ל", "ם", "ן", "ס", "ע", "ף", "ץ");
+ static $tavTaz = array("ט״ו", "ט״ז");
+ static $jOnes = array("", "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט");
+
+ $shortYear = $num %1000; //discard thousands
+ //next check for all possible single Hebrew digit years
+ $singleDigitYear=($shortYear < 11 || ($shortYear <100 && $shortYear % 10 == 0) || ($shortYear <= 400 && $shortYear % 100 ==0));
+ $thousands = $num / 1000; //get # thousands
+ $sb = "";
+ //append thousands to String
+ if ($num % 1000 == 0) { // in year is 5000, 4000 etc
+ $sb .= $jOnes[$thousands];
+ $sb .= self::GERSH;
+ $sb .= " ";
+ $sb .= self::ALAFIM; //add # of thousands plus word thousand (overide alafim boolean)
+ } else if ($DISPLAY_JEWISH_THOUSANDS) { // if alafim boolean display thousands
+ $sb .= $jOnes[$thousands];
+ $sb .= self::GERSH; //append thousands quote
+ $sb .= " ";
+ }
+ $num = $num % 1000; //remove 1000s
+ $hundreds = $num / 100; // # of hundreds
+ $sb .= $jHundreds[$hundreds]; //add hundreds to String
+ $num = $num % 100; //remove 100s
+ if ($num == 15) { //special case 15
+ $sb .= $tavTaz[0];
+ } else if ($num == 16) { //special case 16
+ $sb .= $tavTaz[1];
+ } else {
+ $tens = $num / 10;
+ if ($num % 10 == 0) { // if evenly divisable by 10
+ if ($singleDigitYear == false) {
+ $sb .= $jTenEnds[$tens]; // use end letters so that for example 5750 will end with an end nun
+ } else {
+ $sb .= $jTens[$tens]; // use standard letters so that for example 5050 will end with a regular nun
+ }
+ } else {
+ $sb .= $jTens[$tens];
+ $num = $num % 10;
+ $sb .= $jOnes[$num];
+ }
+ }
+ if ($singleDigitYear == true) {
+ $sb .= self::GERSH; //append single quote
+ } else { // append double quote before last digit
+ $pos1 = strlen($sb)-2;
+ $sb = substr($sb, 0, $pos1) . self::GERSHAYIM . substr($sb, $pos1);
+ $sb = str_replace(self::GERSHAYIM . self::GERSHAYIM, self::GERSHAYIM, $sb); //replace double gershayim with single instance
+ }
+ return $sb;
+ }
+}
diff --git a/library/WT/Date/Hijri.php b/library/WT/Date/Hijri.php
new file mode 100644
index 0000000000..ec06d88f9e
--- /dev/null
+++ b/library/WT/Date/Hijri.php
@@ -0,0 +1,155 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Hijri calendar. Note that these are "theoretical" dates.
+// "True" dates are based on local lunar observations, and can be a +/- one day.
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Hijri extends WT_Date_Calendar {
+ static function CALENDAR_ESCAPE() {
+ return '@#DHIJRI@';
+ }
+ static function MONTH_TO_NUM($m) {
+ static $months=array(''=>0, 'muhar'=>1, 'safar'=>2, 'rabia'=>3, 'rabit'=>4, 'jumaa'=>5, 'jumat'=>6, 'rajab'=>7, 'shaab'=>8, 'ramad'=>9, 'shaww'=>10, 'dhuaq'=>11, 'dhuah'=>12);
+ if (isset($months[$m])) {
+ return $months[$m];
+ } else {
+ return null;
+ }
+ }
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('NOMINATIVE', 'Muharram');
+ case 2: return i18n::translate_c('NOMINATIVE', 'Safar');
+ case 3: return i18n::translate_c('NOMINATIVE', 'Rabi\' al-awwal');
+ case 4: return i18n::translate_c('NOMINATIVE', 'Rabi\' al-thani');
+ case 5: return i18n::translate_c('NOMINATIVE', 'Jumada al-awwal');
+ case 6: return i18n::translate_c('NOMINATIVE', 'Jumada al-thani');
+ case 7: return i18n::translate_c('NOMINATIVE', 'Rajab');
+ case 8: return i18n::translate_c('NOMINATIVE', 'Sha\'aban');
+ case 9: return i18n::translate_c('NOMINATIVE', 'Ramadan');
+ case 10: return i18n::translate_c('NOMINATIVE', 'Shawwal');
+ case 11: return i18n::translate_c('NOMINATIVE', 'Dhu al-Qi\'dah');
+ case 12: return i18n::translate_c('NOMINATIVE', 'Dhu al-Hijjah');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('GENITIVE', 'Muharram');
+ case 2: return i18n::translate_c('GENITIVE', 'Safar');
+ case 3: return i18n::translate_c('GENITIVE', 'Rabi\' al-awwal');
+ case 4: return i18n::translate_c('GENITIVE', 'Rabi\' al-thani');
+ case 5: return i18n::translate_c('GENITIVE', 'Jumada al-awwal');
+ case 6: return i18n::translate_c('GENITIVE', 'Jumada al-thani');
+ case 7: return i18n::translate_c('GENITIVE', 'Rajab');
+ case 8: return i18n::translate_c('GENITIVE', 'Sha\'aban');
+ case 9: return i18n::translate_c('GENITIVE', 'Ramadan');
+ case 10: return i18n::translate_c('GENITIVE', 'Shawwal');
+ case 11: return i18n::translate_c('GENITIVE', 'Dhu al-Qi\'dah');
+ case 12: return i18n::translate_c('GENITIVE', 'Dhu al-Hijjah');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('LOCATIVE', 'Muharram');
+ case 2: return i18n::translate_c('LOCATIVE', 'Safar');
+ case 3: return i18n::translate_c('LOCATIVE', 'Rabi\' al-awwal');
+ case 4: return i18n::translate_c('LOCATIVE', 'Rabi\' al-thani');
+ case 5: return i18n::translate_c('LOCATIVE', 'Jumada al-awwal');
+ case 6: return i18n::translate_c('LOCATIVE', 'Jumada al-thani');
+ case 7: return i18n::translate_c('LOCATIVE', 'Rajab');
+ case 8: return i18n::translate_c('LOCATIVE', 'Sha\'aban');
+ case 9: return i18n::translate_c('LOCATIVE', 'Ramadan');
+ case 10: return i18n::translate_c('LOCATIVE', 'Shawwal');
+ case 11: return i18n::translate_c('LOCATIVE', 'Dhu al-Qi\'dah');
+ case 12: return i18n::translate_c('LOCATIVE', 'Dhu al-Hijjah');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('INSTRUMENTAL', 'Muharram');
+ case 2: return i18n::translate_c('INSTRUMENTAL', 'Safar');
+ case 3: return i18n::translate_c('INSTRUMENTAL', 'Rabi\' al-awwal');
+ case 4: return i18n::translate_c('INSTRUMENTAL', 'Rabi\' al-thani');
+ case 5: return i18n::translate_c('INSTRUMENTAL', 'Jumada al-awwal');
+ case 6: return i18n::translate_c('INSTRUMENTAL', 'Jumada al-thani');
+ case 7: return i18n::translate_c('INSTRUMENTAL', 'Rajab');
+ case 8: return i18n::translate_c('INSTRUMENTAL', 'Sha\'aban');
+ case 9: return i18n::translate_c('INSTRUMENTAL', 'Ramadan');
+ case 10: return i18n::translate_c('INSTRUMENTAL', 'Shawwal');
+ case 11: return i18n::translate_c('INSTRUMENTAL', 'Dhu al-Qi\'dah');
+ case 12: return i18n::translate_c('INSTRUMENTAL', 'Dhu al-Hijjah');
+ default: return '';
+ }
+ }
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ // TODO: Do these have short names?
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+ static function NUM_TO_GEDCOM_MONTH($n, $leap_year) {
+ switch ($n) {
+ case 1: return 'MUHAR';
+ case 2: return 'SAFAR';
+ case 3: return 'RABIA';
+ case 4: return 'RABIT';
+ case 5: return 'JUMAA';
+ case 6: return 'JUMAT';
+ case 7: return 'RAJAB';
+ case 8: return 'SHAAB';
+ case 9: return 'RAMAD';
+ case 10: return 'SHAWW';
+ case 11: return 'DHUAQ';
+ case 12: return 'DHUAH';
+ default: return '';
+ }
+ }
+ static function CAL_START_JD() {
+ return 1948440; // @#DHIJRI@ 1 MUHAR 0001 = @#JULIAN@ 16 JUL 0622
+ }
+
+ function IsLeapYear() {
+ return ((11*$this->y+14)%30)<11;
+ }
+
+ static function YMDtoJD($y, $m, $d) {
+ return $d+29*($m-1)+floor((6*$m-1)/11)+$y*354+floor((3+11*$y)/30)+1948085;
+ }
+
+ static function JDtoYMD($j) {
+ $y=floor((30*($j-1948440)+10646)/10631);
+ $m=floor((11*($j-$y*354-floor((3+11*$y)/30)-1948086)+330)/325);
+ $d=$j-29*($m-1)-floor((6*$m-1)/11)-$y*354-floor((3+11*$y)/30)-1948085;
+ return array($y, $m, $d);
+ }
+}
diff --git a/library/WT/Date/Jewish.php b/library/WT/Date/Jewish.php
new file mode 100644
index 0000000000..4cc3420381
--- /dev/null
+++ b/library/WT/Date/Jewish.php
@@ -0,0 +1,177 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Jewish calendar
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Jewish extends WT_Date_Calendar {
+ static function CALENDAR_ESCAPE() {
+ return '@#DHEBREW@';
+ }
+
+ static function MONTH_TO_NUM($m) {
+ static $months=array(''=>0, 'tsh'=>1, 'csh'=>2, 'ksl'=>3, 'tvt'=>4, 'shv'=>5, 'adr'=>6, 'ads'=>7, 'nsn'=>8, 'iyr'=>9, 'svn'=>10, 'tmz'=>11, 'aav'=>12, 'ell'=>13);
+ if (isset($months[$m])) {
+ return $months[$m];
+ } else {
+ return null;
+ }
+ }
+ static function NUM_TO_MONTH_NOMINATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('NOMINATIVE', 'Tishrei');
+ case 2: return i18n::translate_c('NOMINATIVE', 'Heshvan');
+ case 3: return i18n::translate_c('NOMINATIVE', 'Kislev');
+ case 4: return i18n::translate_c('NOMINATIVE', 'Tevet');
+ case 5: return i18n::translate_c('NOMINATIVE', 'Shevat');
+ case 6: if ($leap_year) return i18n::translate_c('NOMINATIVE', 'Adar'); else return i18n::translate_c('NOMINATIVE', 'Adar I');
+ case 7: return i18n::translate_c('NOMINATIVE', 'Adar II');
+ case 8: return i18n::translate_c('NOMINATIVE', 'Nissan');
+ case 9: return i18n::translate_c('NOMINATIVE', 'Iyar');
+ case 10: return i18n::translate_c('NOMINATIVE', 'Sivan');
+ case 11: return i18n::translate_c('NOMINATIVE', 'Tamuz');
+ case 12: return i18n::translate_c('NOMINATIVE', 'Av');
+ case 13: return i18n::translate_c('NOMINATIVE', 'Elul');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_GENITIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('GENITIVE', 'Tishrei');
+ case 2: return i18n::translate_c('GENITIVE', 'Heshvan');
+ case 3: return i18n::translate_c('GENITIVE', 'Kislev');
+ case 4: return i18n::translate_c('GENITIVE', 'Tevet');
+ case 5: return i18n::translate_c('GENITIVE', 'Shevat');
+ case 6: if ($leap_year) return i18n::translate_c('GENITIVE', 'Adar'); else return i18n::translate_c('GENITIVE', 'Adar I');
+ case 7: return i18n::translate_c('GENITIVE', 'Adar II');
+ case 8: return i18n::translate_c('GENITIVE', 'Nissan');
+ case 9: return i18n::translate_c('GENITIVE', 'Iyar');
+ case 10: return i18n::translate_c('GENITIVE', 'Sivan');
+ case 11: return i18n::translate_c('GENITIVE', 'Tamuz');
+ case 12: return i18n::translate_c('GENITIVE', 'Av');
+ case 13: return i18n::translate_c('GENITIVE', 'Elul');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_LOCATIVE($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('LOCATIVE', 'Tishrei');
+ case 2: return i18n::translate_c('LOCATIVE', 'Heshvan');
+ case 3: return i18n::translate_c('LOCATIVE', 'Kislev');
+ case 4: return i18n::translate_c('LOCATIVE', 'Tevet');
+ case 5: return i18n::translate_c('LOCATIVE', 'Shevat');
+ case 6: if ($leap_year) return i18n::translate_c('LOCATIVE', 'Adar'); else return i18n::translate_c('LOCATIVE', 'Adar I');
+ case 7: return i18n::translate_c('LOCATIVE', 'Adar II');
+ case 8: return i18n::translate_c('LOCATIVE', 'Nissan');
+ case 9: return i18n::translate_c('LOCATIVE', 'Iyar');
+ case 10: return i18n::translate_c('LOCATIVE', 'Sivan');
+ case 11: return i18n::translate_c('LOCATIVE', 'Tamuz');
+ case 12: return i18n::translate_c('LOCATIVE', 'Av');
+ case 13: return i18n::translate_c('LOCATIVE', 'Elul');
+ default: return '';
+ }
+ }
+ static function NUM_TO_MONTH_INSTRUMENTAL($n, $leap_year) {
+ switch ($n) {
+ case 1: return i18n::translate_c('INSTRUMENTAL', 'Tishrei');
+ case 2: return i18n::translate_c('INSTRUMENTAL', 'Heshvan');
+ case 3: return i18n::translate_c('INSTRUMENTAL', 'Kislev');
+ case 4: return i18n::translate_c('INSTRUMENTAL', 'Tevet');
+ case 5: return i18n::translate_c('INSTRUMENTAL', 'Shevat');
+ case 6: if ($leap_year) return i18n::translate_c('INSTRUMENTAL', 'Adar'); else return i18n::translate_c('INSTRUMENTAL', 'Adar I');
+ case 7: return i18n::translate_c('INSTRUMENTAL', 'Adar II');
+ case 8: return i18n::translate_c('INSTRUMENTAL', 'Nissan');
+ case 9: return i18n::translate_c('INSTRUMENTAL', 'Iyar');
+ case 10: return i18n::translate_c('INSTRUMENTAL', 'Sivan');
+ case 11: return i18n::translate_c('INSTRUMENTAL', 'Tamuz');
+ case 12: return i18n::translate_c('INSTRUMENTAL', 'Av');
+ case 13: return i18n::translate_c('INSTRUMENTAL', 'Elul');
+ default: return '';
+ }
+ }
+ static function NUM_TO_SHORT_MONTH($n, $leap_year) {
+ // TODO: Do these have short names?
+ return self::NUM_TO_MONTH_NOMINATIVE($n, $leap_year);
+ }
+ static function NUM_TO_GEDCOM_MONTH($n, $leap_year) {
+ // TODO: Do these have short names in English?
+ switch ($n) {
+ case 1: return 'TSH';
+ case 2: return 'CSH';
+ case 3: return 'KSL';
+ case 4: return 'TVT';
+ case 5: return 'SHV';
+ case 6: return 'ADR';
+ case 7: return 'ADS';
+ case 8: return 'NSN';
+ case 9: return 'IYR';
+ case 10: return 'SVN';
+ case 11: return 'TMZ';
+ case 12: return 'AAV';
+ case 13: return 'ELL';
+ default: return '';
+ }
+ }
+ static function NUM_MONTHS() {
+ return 13;
+ }
+ static function CAL_START_JD() {
+ return 347998; // 01 TSH 0001 = @#JULIAN@ 7 OCT 3761B.C.
+ }
+
+ function NextMonth() {
+ if ($this->m==6 && !$this->IsLeapYear())
+ return array($this->y, 8);
+ else
+ return array($this->y+($this->m==13?1:0), ($this->m%13)+1);
+ }
+
+ function IsLeapYear() {
+ return ((7*$this->y+1)%19)<7;
+ }
+
+ // TODO implement this function locally
+ static function YMDtoJD($y, $mh, $d) {
+ if (function_exists('JewishToJD'))
+ return JewishToJD($mh, $d, $y);
+ else
+ return 0;
+ }
+
+ // TODO implement this function locally
+ static function JDtoYMD($j) {
+ if (function_exists('JdToJewish'))
+ list($m, $d, $y)=explode('/', JDToJewish($j));
+ else
+ list($m, $d, $y)=array(0, 0, 0);
+ return array($y, $m, $d);
+ }
+}
diff --git a/library/WT/Date/Julian.php b/library/WT/Date/Julian.php
new file mode 100644
index 0000000000..f900401053
--- /dev/null
+++ b/library/WT/Date/Julian.php
@@ -0,0 +1,109 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Julian Proleptic calendar
+// (Proleptic means we extend it backwards, prior to its introduction in 46BC)
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Julian extends WT_Date_Calendar {
+ var $new_old_style=false;
+
+ static function CALENDAR_ESCAPE() {
+ return '@#DJULIAN@';
+ }
+
+ static function NextYear($y) {
+ if ($y==-1)
+ return 1;
+ else
+ return $y+1;
+ }
+
+ function IsLeapYear() {
+ return $this->y%4==0;
+ }
+
+ static function YMDtoJD($y, $m, $d) {
+ if ($y<0) // 0=1BC, -1=2BC, etc.
+ ++$y;
+ $a=floor((14-$m)/12);
+ $y=$y+4800-$a;
+ $m=$m+12*$a-3;
+ return $d+floor((153*$m+2)/5)+365*$y+floor($y/4)-32083;
+ }
+
+ static function JDtoYMD($j) {
+ $c=$j+32082;
+ $d=floor((4*$c+3)/1461);
+ $e=$c-floor(1461*$d/4);
+ $m=floor((5*$e+2)/153);
+ $day=$e-floor((153*$m+2)/5)+1;
+ $month=$m+3-12*floor($m/10);
+ $year=$d-4800+floor($m/10);
+ if ($year<1) // 0=1BC, -1=2BC, etc.
+ --$year;
+ return array($year, $month, $day);
+ }
+
+ // Process new-style/old-style years and years BC
+ function ExtractYear($year) {
+ if (preg_match('/^(\d\d\d\d) \/ \d{1,4}$/', $year, $match)) { // Assume the first year is correct
+ $this->new_old_style=true;
+ return $match[1]+1;
+ } else
+ if (preg_match('/^(\d+) b ?c$/', $year, $match))
+ return -$match[1];
+ else
+ return (int)$year;
+ }
+
+ function FormatLongYear() {
+ if ($this->y<0) {
+ // I18N: Number of years "before christ"
+ return i18n::translate('%d B.C.', -$this->y);
+ } else {
+ if ($this->new_old_style) {
+ return sprintf('%d/%02d', $this->y-1, $this->y % 100);
+ } else
+ return $this->y;
+ }
+ }
+
+ function FormatGedcomYear() {
+ if ($this->y<0)
+ return sprintf('%04dB.C.', -$this->y);
+ else
+ if ($this->new_old_style) {
+ return sprintf('%04d/%02d', $this->y-1, $this->y % 100);
+ } else
+ return sprintf('%04d', $this->y);
+ }
+}
diff --git a/library/WT/Date/Roman.php b/library/WT/Date/Roman.php
new file mode 100644
index 0000000000..5d04421bff
--- /dev/null
+++ b/library/WT/Date/Roman.php
@@ -0,0 +1,50 @@
+<?php
+// Classes for Gedcom Date/Calendar functionality.
+//
+// Definitions for the Roman calendar
+// TODO The 5.5.1 gedcom spec mentions this calendar, but gives no details of
+// how it is to be represented.... This class is just a place holder so that
+// webtrees won't compain if it receives one.
+//
+// NOTE: Since different calendars start their days at different times, (civil
+// midnight, solar midnight, sunset, sunrise, etc.), we convert on the basis of
+// midday.
+//
+// webtrees: Web based Family History software
+// Copyright (C) 2011 webtrees development team.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// @author Greg Roach
+// @version $Id$
+
+if (!defined('WT_WEBTREES')) {
+ header('HTTP/1.0 403 Forbidden');
+ exit;
+}
+
+class WT_Date_Roman extends WT_Date_Calendar {
+ static function CALENDAR_ESCAPE() {
+ return '@#DROMAN@';
+ }
+
+ function FormatGedcomYear() {
+ return sprintf('%04dAUC',$this->y);
+ }
+
+ function FormatLongYear() {
+ return $this->y.'AUC';
+ }
+}