Heyho
Ich arbeite aktuell an einer Klasse, welche mir abhängig von den vorher definierten Start- und Enddaten einen Array zusammenstellt, welcher jegliche Datums-Informationen zu diesem Zeitraum enthält.
Dabei soll es möglich sein pure Jahres- Monats- Tageslisten, aber auch völlig rekursive ([$monat]['tage'] = array($day1,$day2..)) zu erstellen.
Außerdem soll sie zwei verschiedene Modi unterstützen:
1) Ich gebe ein Initial-Datum an und kann die Grenzen in Vergangenheit und Zukunft als simple Ziffern angeben
Beispiel bei Jahresliste: (3, 2014, 2) = 2011,2012,2013,2014,2015,2016
2) Ich gebe Start- und Enddatum direkt an.
Also z.B. alle Tage zwischen 03.08.2014 und 21.08.2014
Der Verwendungszweck sollte dabei ziemlich abstrakt bleiben.
Sprich man könnte die so erzeugte Liste via Ajax abrufen / Form-Elemente befüllen / Zeitstrahlen oder Datepicker oder Kalender etc. erstellen (und was man halt noch so alles mit Datumslisten machen könnte).
Nachdem ich mir jetzt seit ein paar Tagen darüber den Kopf zerbrochen habe bin ich derzeit bei diesem (funktionierenden) Stand angekommen:
Ein paar Test-Cases sind bereits angehangen, ist also copy&paste mäßig bereits lauffähig.
Mich würde grundsätzlich eure Meinung und/oder eure Ideen zu dem Thema interessieren.
Eine Sache die mich an dieser Klasse z.B. stört ist, dass ich dem Script nicht sagen kann:
Gib mir den MONAT September mit TAG 5 bis 7
und so etwas zurückbekomme:
[$monat]['tage'] = (5,6,7)
Stattdessen kann ich entweder nur sagen:
Gib mir den MONAT September KOMPLETT
oder
Gib mir alle TAGE zwischen 5. September und 7. September
was allerdings soetwas zurückgibt:
[$tag1=arr('5'), $tag2=arr('6'), $tag3=arr('7')]
Aber wie gesagt, mich würde grundsätzlich auch einfach mal interessieren wie ihr soetwas angehen würdet
Gruß!
Ich arbeite aktuell an einer Klasse, welche mir abhängig von den vorher definierten Start- und Enddaten einen Array zusammenstellt, welcher jegliche Datums-Informationen zu diesem Zeitraum enthält.
Dabei soll es möglich sein pure Jahres- Monats- Tageslisten, aber auch völlig rekursive ([$monat]['tage'] = array($day1,$day2..)) zu erstellen.
Außerdem soll sie zwei verschiedene Modi unterstützen:
1) Ich gebe ein Initial-Datum an und kann die Grenzen in Vergangenheit und Zukunft als simple Ziffern angeben
Beispiel bei Jahresliste: (3, 2014, 2) = 2011,2012,2013,2014,2015,2016
2) Ich gebe Start- und Enddatum direkt an.
Also z.B. alle Tage zwischen 03.08.2014 und 21.08.2014
Der Verwendungszweck sollte dabei ziemlich abstrakt bleiben.
Sprich man könnte die so erzeugte Liste via Ajax abrufen / Form-Elemente befüllen / Zeitstrahlen oder Datepicker oder Kalender etc. erstellen (und was man halt noch so alles mit Datumslisten machen könnte).
Nachdem ich mir jetzt seit ein paar Tagen darüber den Kopf zerbrochen habe bin ich derzeit bei diesem (funktionierenden) Stand angekommen:
PHP-Code:
<?php
/**
* Erzeugt Datenstruktur eines definierbaren Kalenderausschnitts
*/
class core_date_timeline {
// ID um mehrere Timelines in einer Collection halten zu können
private $id = '';
private $currentLevel;
private $recurseDepth = 0;
private $startDate = null;
private $endDate = null;
private $initialDate = null;
private $cntPast = 0;
private $cntFuture = 0;
const LEVEL_YEAR = 0;
const LEVEL_MONTH = 1;
const LEVEL_DAY = 2;
private $config = array(
0 => array(
'type' => 1,
'modifier' => 'year',
'dateIntervalCode' => 'Y',
'formatId' => 'Y'
),
1 => array(
'type' => 2,
'modifier' => 'months',
'dateIntervalCode' => 'M',
'formatId' => 'Ym'
),
2 => array(
'type' => 3,
'modifier' => 'days',
'dateIntervalCode' => 'D',
'formatId' => 'Ymd'
)
);
private $list;
public function __construct($id, $initLevel, $initDate=null) {
if (!is_null($initDate)) {
$this->initialDate = new DateTime($initDate);
} else {
$this->initialDate = new DateTime();
}
$this->currentLevel = $initLevel;
}
// Wieviele Jahre/Monate/Tage in die Vergangenheit von $initialDate aus
public function setCntPast($cnt) {
if (is_int($cnt))
$this->cntPast = $cnt;
else
throw new Exception("ERROR: cntPast must be Integer", 1);
}
// Wieviele Jahre/Monate/Tage in die Zukunft von $initialDate aus
public function setCntFuture($cnt) {
if (is_int($cnt))
$this->cntFuture = $cnt;
else
throw new Exception("Error: cntFuture must be Integer", 1);
}
// Ausgangsdatum
public function setInitialDate($initDate) {
$this->initialDate = new DateTime($initDate);
}
// Manuelles Setzen von Startdatum
public function setStartDate($startDate) {
$this->startDate = new DateTime($startDate);
}
// Manuelles Setzen von Enddatum
public function setEndDate($endDate) {
$this->endDate = new DateTime($endDate);
}
// Anzahl der Ebenen die rekursiv verarbeitet werden sollen
public function setRecurseDepth($depth) {
if (is_int($depth))
$this->recurseDepth = $depth;
else
throw new Exception("ERROR: RecurseDepth must be Integer", 1);
}
// Erzeugt String für DateInterval
private function getIntervalString() {
return 'P1'.$this->config[$this->currentLevel]['dateIntervalCode'];
}
public function build($arr = null) {
// Falls Start- und/oder Enddatum nicht manuell gesetzt durch cntPast/cntFuture berechnen und wenn nicht vorhanden HEUTE wählen
if (is_null($this->startDate)) {
if ($this->cntPast && $this->cntPast > 0) {
$startDate = clone $this->initialDate;
$this->startDate = $startDate->modify('-'.$this->cntPast.' '.$this->config[$this->currentLevel]['modifier']);
} else {
$this->startDate = new DateTime();
}
}
if (is_null($this->endDate)) {
if ($this->cntFuture && $this->cntFuture > 0) {
$endDate = clone $this->initialDate;
$this->endDate = $endDate->modify('+'.$this->cntFuture.' '.$this->config[$this->currentLevel]['modifier']);
} else {
$this->endDate = new DateTime();
}
}
if ($this->startDate > $this->endDate) {
throw new Exception("ERROR: Startdate later than Enddate", 1);
}
$this->list = $this->populate();
return $this->list;
}
private $parentStart = null;
private $parentEnd = null;
private $depth = 0;
private function populate() {
$start = is_null($this->parentStart) ? clone $this->startDate : $this->parentStart;
$end = is_null($this->parentEnd) ? clone $this->endDate : $this->parentEnd;
$dateInterval = new DateInterval( $this->getIntervalString() );
// +1 day um Enddatum mit einzuschließen
$dateRange = new DatePeriod( $start, $dateInterval, $end->modify('+1 day') );
foreach( $dateRange as $date ) {
// Beliebige Meta-Daten zum Datum
$list[$date->format($this->config[$this->currentLevel]['formatId'])]['current'] = true;
$list[$date->format($this->config[$this->currentLevel]['formatId'])]['type'] = $this->config[$this->currentLevel]['modifier'];
$list[$date->format($this->config[$this->currentLevel]['formatId'])]['Y'] = $date->format('Y');
$list[$date->format($this->config[$this->currentLevel]['formatId'])]['M'] = $date->format('m');
$list[$date->format($this->config[$this->currentLevel]['formatId'])]['D'] = $date->format('d');
// Rekursive Erzeugung von Monaten und/oder Tagen
if ($this->recurseDepth !== 0 && $this->depth < $this->recurseDepth && array_key_exists($this->currentLevel+1, $this->config)) {
$parentLvl = $this->currentLevel;
$this->currentLevel++;
$this->depth++;
// Spezielle Regeln für Monats- und TagesRanges setzen bei rekursivem Aufruf
if ($this->currentLevel == self::LEVEL_MONTH) $this->createMonthBoundaries($date->format('Y'));
if ($this->currentLevel == self::LEVEL_DAY) $this->createDayBoundaries($date);
$list[$date->format($this->config[$parentLvl]['formatId'])]['children'] = $this->populate();
$this->currentLevel--;
$this->depth--;
}
}
return $list;
}
// Start-End Definitionen für 1 Jahr um in DatePeriod über Monate zu iterieren
private function createMonthBoundaries($year) {
$this->parentStart = new DateTime($year.'-01-01');
$this->parentEnd = new DateTime($year.'-12-01');
}
// Start-End Definitionen für 1 Monat um in DatePeriod über Tage zu iterieren
private function createDayBoundaries($date) {
$this->parentStart = clone $date;
$this->parentEnd = clone $date;
$this->parentStart->modify('first day of this month');
$this->parentEnd->modify('last day of this month');
}
}
function dump($title, $after, $before, $result) {
echo "<pre>";
echo $title."\n";
echo ($after-$before) . " sec/timeline\n";
// var_dump($result);
echo "</pre>";
echo "<hr />";
}
// 1 - Dieses Jahr
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_YEAR);
$result = $timeline->build();
$after = microtime(true);
dump('1 Jahr', $after, $before, $result);
// 2 - Dieser Monat
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_MONTH);
$result = $timeline->build();
$after = microtime(true);
dump('1 Monat', $after, $before, $result);
// 3 - Heutiger Tag
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_DAY);
$result = $timeline->build();
$after = microtime(true);
dump('1 Tag', $after, $before, $result);
// 4 - Dieses Jahr + 3 Jahre Vergangenheit + 2 Jahre Zukunft
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_YEAR);
$timeline->setCntPast(3);
$timeline->setCntFuture(2);
$result = $timeline->build();
$after = microtime(true);
dump('6 Jahre', $after, $before, $result);
// 4 - Alle Monate zwischen 01.02.2014 und 01.10.2014 - Rekursiv bis auf Tage
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_MONTH);
$timeline->setStartDate('2014-02-01');
$timeline->setEndDate('2014-10-01');
$timeline->setRecurseDepth(1);
$result = $timeline->build();
$after = microtime(true);
dump('9 Monate Rekursiv bis auf Tage', $after, $before, $result);
// 5 - Dieses Jahr + 4 Jahre Vergangenheit + 2 Jahre Zukunft - Rekursiv Monate und Tage
$before = microtime(true);
$timeline = new core_date_timeline('id', core_date_timeline::LEVEL_YEAR);
$timeline->setCntPast(3);
$timeline->setCntFuture(2);
$timeline->setRecurseDepth(2);
$result = $timeline->build();
$after = microtime(true);
dump('6 Jahre Rekursiv bis auf Tage', $after, $before, $result);
?>
Mich würde grundsätzlich eure Meinung und/oder eure Ideen zu dem Thema interessieren.
Eine Sache die mich an dieser Klasse z.B. stört ist, dass ich dem Script nicht sagen kann:
Gib mir den MONAT September mit TAG 5 bis 7
und so etwas zurückbekomme:
[$monat]['tage'] = (5,6,7)
Stattdessen kann ich entweder nur sagen:
Gib mir den MONAT September KOMPLETT
oder
Gib mir alle TAGE zwischen 5. September und 7. September
was allerdings soetwas zurückgibt:
[$tag1=arr('5'), $tag2=arr('6'), $tag3=arr('7')]
Aber wie gesagt, mich würde grundsätzlich auch einfach mal interessieren wie ihr soetwas angehen würdet
Gruß!
Kommentar