Ankündigung

Einklappen
Keine Ankündigung bisher.

Erzeugen von Kalenderdaten auf purer Datenebene

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Erzeugen von Kalenderdaten auf purer Datenebene

    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:
    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(
            
    => array(
                
    'type' => 1,
                
    'modifier' => 'year',
                
    'dateIntervalCode' => 'Y',
                
    'formatId' => 'Y'
                
    ),
            
    => array(
                
    'type' => 2,
                
    'modifier' => 'months',
                
    'dateIntervalCode' => 'M',
                
    'formatId' => 'Ym'
                
    ),
            
    => 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 !== && $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);

    ?>
    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ß!


  • #2
    Verstehe ich das richtig, dass die Tage immer durchgehend sind, d.h. keine lücken entstehen können (5, 6, 8, 9)?

    Im Prinzip sollte es reichen, das Start- und Enddatum als Datetime oder das Startdatum und ein Interval zu speichern. Je nach Verwendungszweck kann man dann mithilfe der Methoden die Datetime anbietet ziemlich einfach die Tage die dazwischen liegen berechnen (oder durchiterieren).
    In dem Fall bräuchtest du dir dann nur Gedanken machen, wie du aus unterschiedlichem Input jeweils Start- und Enddatum berechnest.
    Zitat von nikosch
    Macht doch alle was Ihr wollt mit Eurem Billigscheiß. Von mir aus sollen alle Eure Server abrauchen.

    Kommentar


    • #3
      Einen Intervall oder ausgeschlossene Tage/Monate/Jahre sind jetzt initial nicht in der Anforderung aber das wäre natürlich etwas, dass irgendwann mal aufkommen könnte.

      Intervalle an sich wären auch nicht so kompliziert, hatte ich sogar schonmal in nem älteren Entwurf drinnen. Statt
      PHP-Code:
          private function getIntervalString() { 
              return 
      'P1'.$this->config[$this->currentLevel]['dateIntervalCode']; 
          }
      //P1D - Tagesintervalll
      //P1M - Monatsintervall
      //P1Y - Jahresintervall 
      wäre es dann halt sowas wie

      PHP-Code:
          private function getIntervalString() { 
              return 
      'P'.$this->intervall.$this->config[$this->currentLevel]['dateIntervalCode']; 
          }
      // P2D Alle 2 Tage
      // P3M Alle 3 Monate etc. 

      An sich ist es auch nicht schwer eine liste

      PHP-Code:
      $excluded = array('2014-08-03''2014-08-13'); 
      zu erstellen und eben jene dann zu überspringen.

      Komplizierter wird es ab dem Moment wo ich sage

      Ich möchte
      ALLE Tage
      jedes ZWEITEN MONATS
      ZWISCHEN Juli UND Dezember
      AUSSER dem 15 und 20. September

      Warum auch immer so eine Anfrage jemals kommen sollte.. Es geht darum eine derart abstrakte Klasse zu haben, das es schlichtweg irgendwie möglich ist.

      Kommentar


      • #4
        Zitat von EpicRisc Beitrag anzeigen
        Komplizierter wird es ab dem Moment wo ich sage

        Ich möchte
        ALLE Tage
        jedes ZWEITEN MONATS
        ZWISCHEN Juli UND Dezember
        AUSSER dem 15 und 20. September
        Das klingt dann schon wieder nach einer anderen Anforderung. In dem Fall wird es schwierig und umständlich. Ich glaube, für so eine Anfrage würde ich eine "Anforderungs"-Klasse schreiben, wobei der Timeline-Klasse dann Anforderungen hinzugefügt werden können. Hierbei brauchst du dann auch wieder ein Start- und Enddatum, allerdings werden beim durchiterieren dann für jedes einzelne Datum die Anforderungen ausgeführt und so geprüft, ob das Datum included oder excluded ist.
        Zitat von nikosch
        Macht doch alle was Ihr wollt mit Eurem Billigscheiß. Von mir aus sollen alle Eure Server abrauchen.

        Kommentar


        • #5
          Mich würde grundsätzlich eure Meinung und/oder eure Ideen zu dem Thema interessieren.
          Ich finde das eine völlig verquastete Anforderung und auch eine Idee, die niemand benutzt, weil viel zu umständlich.
          --

          „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
          Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


          --

          Kommentar


          • #6
            Zitat von nikosch Beitrag anzeigen
            Ich finde das eine völlig verquastete Anforderung und auch eine Idee, die niemand benutzt, weil viel zu umständlich.
            Ja ich bin auch schon an viele Ecken und Kanten gestoßen, deshalb auch mal die grundsätzliche Frage hier in die Runde.

            Allerdings sehe ich schon, dass wenn man so etwas Mal funktionstüchtig hat, es ein ziemlich nettes Core-Modul wäre, da es eben recht vielschichtig benutzt werden kann.
            Und an Kalendern ändert sich jetzt auch nicht soviel (Jahre Monate Tage.. alles fix berechenbare Dinge), sprich der Aufwand mag initial vll schon etwas größer sein, ist auf lange Sicht dann doch absehbar klein.

            Kommentar


            • #7
              Der Verwendungszweck sollte dabei ziemlich abstrakt bleiben.
              da es eben recht vielschichtig benutzt werden kann.
              Habe ich bis jetzt noch nicht gesehen.
              --

              „Emoticons machen einen Beitrag etwas freundlicher. Deine wirken zwar fachlich richtig sein, aber meist ziemlich uninteressant.
              Wenn man nur Text sieht, haben viele junge Entwickler keine interesse, diese stumpfen Texte zu lesen.“


              --

              Kommentar


              • #8
                Mir würde für sowas aber tatsächlich auch kein sinnvoller Anwendungsfall einfallen, außer vielleicht die Schaltjahresberechnung, aber dafür brauch ich mir kein komplett generisches System schreiben
                Zitat von nikosch
                Macht doch alle was Ihr wollt mit Eurem Billigscheiß. Von mir aus sollen alle Eure Server abrauchen.

                Kommentar


                • #9
                  Erzeugen von Kalenderdaten auf purer Datenebene

                  Im Endeffekt ist das ja einfach ein neue Variante neben DateInterval, das bereits den grössten Teil der Anforderungen abdeckt. Ich sehe keinen UseCase
                  GitHub.com - ChrisAndChris - RowMapper und QueryBuilder für MySQL-Datenbanken

                  Kommentar


                  • #10
                    Ich hab schon etwas ähnliches erstellt. Eine Klasse multipDatePeriod, welche als Intervall auch Arrays entgegennimmt. Denkbare Anwendungsfälle:
                    - Die 4 Adventssonntage für dieses und nächstes Jahr berechnen
                    - Für ein wählbaren Zeitraum jeden Mittwoch und Samstag die Zeitpunkte 7:00 und 8:30 erstellen.

                    Der Aufruf für die Adventssonntage sieht so aus:
                    PHP-Code:
                    $interval = array(
                      array(
                    'next year 11/26','next Sunday'),  //1.Advent
                      
                    'next Sunday',  //2.Advent
                      
                    'next Sunday',  //3.Advent
                      
                    'next Sunday',  //4.Advent
                    );
                    $mdp = new multipDatePeriod('last year',$interval,8,multipDatePeriod::EXCLUDE_START_DATE); 
                    Der 3.Parameter ist entweder die Anzahl der gewünschten Intervalle (Hier 8 ) oder ein Enddatum.
                    Als Ergebnis wird ein Objekt geliefert, über welches mit foreach die Zeitpunkte abgerufen werden können.

                    Ich hab mit der Klasse bisher nur ein paar Sachen probiert. In einem echten Projekt wurde sie bisher noch nicht eingesetzt / benötigt, da solche Aufgaben oft mit wenigen Zeilen erledigt sind, bevor man sich die Benutzung der Klasse verinnerlicht hat.

                    LG jspit
                    PHP-Klassen auf github

                    Kommentar

                    Lädt...
                    X