summaryrefslogtreecommitdiff
path: root/library/WT/Date/Calendar.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/WT/Date/Calendar.php')
-rw-r--r--library/WT/Date/Calendar.php615
1 files changed, 615 insertions, 0 deletions
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;
+ }
+}