Pit: php Datum formatieren

Hallo,

ich habe 2 Strings, die ich jeweils in ein gültiges mysql-Datumsformat umwandeln möchte, bzw. es schon gemacht habe. Allerdings habe ich die Strings zerlegt und die Einzelteile wieder zusammengesetzt.

Ich frage mich, ob das auch effektiver geht.

  1. 20180412
  2. 26.05.15

Gruß, Pit

  1. ...vielleicht anbei noch meine Lösung mit Zerlegen und wieder Zusammensetzen (das datum befindet sich jeweils in $data[$c]:

    // 20180412
    $arr = str_split($data[$c],2);
    $data[$c] = $arr[0].$arr[1]."-".$arr[2]."-".$arr[3];
    
    // 12.04.18
    $datum_uf = explode(".",$data[$c]);
    $datum_uf[2] = $datum_uf[2]+2000;
    $data[$c] = $datum_uf[2]."-".$datum_uf[1]."-".$datum_uf[0];
    

    Pit

    1. Es kann sein, dass das Slicen mit substr hier die schnellste Variante ist. Ich gehe einfach davon aus, dass die primitivsten Operationen, die sich um "nichts" (hier die Gültigkeit von Daten) kümmern, auch die schnellsten sind.

      Ersetze einfach die Zeilen $dummy = ... mit der Methode Deiner Wahl und teste selbst.

      Test 1:

      <?php
      
      $dates=[];
      for ( $j=1; $j < 100; $j++ ) {
      	for ( $m=1; $m < 13; $m++ ) {
      		for ( $d=1; $d < 31; $d++ ) {
      			#$dates[] = sprintf("%04d-%02d-%02d", $j+2000, $m, $d);
      			#$dates[] = sprintf("%04d%02d%02d", 2000+$j, $m, $d);
      			$dates[] = sprintf("%02d.%02d.%02d", $d, $m, $j);
      	  }
      	}
      }
      $start=microtime( true );
      
      foreach ( $dates as $date ) {
      	$dummy = substr( $date, 6, 2 ) . '-' . substr( $date, 3, 2 ) . '-' . substr( $date, 0, 2 );
      }
      $end=microtime( true );
      echo ( $end - $start ) * 1000 . " Millisekunden. (Für ". count($dates)." Werte)\n";
      echo ( $end - $start ) / count($dates) * 1000000 . " Microsekunden pro Wert\n";
      

      Test2:

      <?php
      
      $dates=[];
      for ( $j=1; $j < 100; $j++ ) {
      	for ( $m=1; $m < 13; $m++ ) {
      		for ( $d=1; $d < 31; $d++ ) {
      			#$dates[] = sprintf("%04d-%02d-%02d", $j+2000, $m, $d);
      			$dates[] = sprintf("%04d%02d%02d", 2000+$j, $m, $d);
      	  }
      	}
      }
      $start=microtime( true );
      
      foreach ( $dates as $date ) {
      	$dummy = substr( $date, 0, 4 ) . '-' . substr( $date, 4, 2 ) . '-' . substr( $date, 6, 2 );
      }
      $end=microtime( true );
      echo ( $end - $start ) * 1000 . " Mllisekunden. (Für ". count( $dates )." Werte)\n";
      echo ( $end - $start ) / count( $dates ) * 1000000 . " Microsekunden pro Wert\n";
      
  2. Tach!

    Ich frage mich, ob das auch effektiver geht.

    Du meinst effizienter? Alle Methoden, die das Ziel erreichen, sind effektiv. Sie können sich aber in der Effizienz unterscheiden (Verhältnis zwischen Aufwand und Nutzen).

    Wie auch immer, PHP hat auch Parsefunktionen für das Datum. Einige versuchen das Format zu erraten und sind dabei nicht unbedingt effektiv, weil die Werte von Monat, Tag und Jahr nicht immer eindeutig zuzuordnen sind (z.B. strtotime(), date_create()/new DateTime()). Aber es gibt auch welche, da kannst du das Format per Hand angeben (strptime(), DateTime::createFromFormat()).

    dedlfix.

    1. Hi dedlfix,

      Ich frage mich, ob das auch effektiver geht.

      Du meinst effizienter? Alle Methoden, die das Ziel erreichen, sind effektiv. Sie können sich aber in der Effizienz unterscheiden (Verhältnis zwischen Aufwand und Nutzen).

      Da es sich um viele Datensätze handelt, ist fraglich, ob diese Methoden ihr Ziel erreichen. Somit sind Effizienz und Effektivität hier 2 Seiten einer Medallie.

      Aber es gibt auch welche, da kannst du das Format per Hand angeben (strptime(), DateTime::createFromFormat()).

      Ja, diese Funktionen könnten mir zusagen, ich schau sie mir mal an.

      Danke und Gruß, Pit

      1. Testskript:

        <?php
        
        $dates=[];
        for ( $j=1; $j < 100; $j++ ) {
        	for ( $m=1; $m < 13; $m++ ) {
        		for ( $d=1; $d < 31; $d++ ) {
        			#$dates[] = sprintf("%04d-%02d-%02d", $j+2000, $m, $d);
        			$dates[] = sprintf("%04d%02d%02d", 2000+$j, $m, $d);
        	  }
        	}
        }
        $start=microtime( true );
        
        foreach ( $dates as $date ) {
        	$dummy = substr( $date, 0, 4 ) . '-' . substr( $date, 4, 2 ) . '-' . substr( $date, 6, 2 );
        }
        $end=microtime( true );
        
        echo "Methode: substr:\n================\n";
        echo ( $end - $start ) * 1000 . " Millisekunden. (Für ". count( $dates )." Werte)\n";
        echo ( $end - $start ) / count( $dates ) * 1000000 . " Microsekunden pro Wert\n\n";
        
        $start=microtime( true );
        
        foreach ( $dates as $date ) {
        	$dummy = DateTime::createFromFormat( 'Ymd', $date ) -> format('Y-m-d');
        }
        $end=microtime( true );
        
        echo "Methode: DateTime::createFromFormat:\n====================================\n";
        echo ( $end - $start ) * 1000 . " Millisekunden. (Für ". count( $dates )." Werte)\n";
        echo ( $end - $start ) / count( $dates ) * 1000000 . " Microsekunden pro Wert\n";
        

        Ergebnis auf amd64 (PHP 7.2.10-0ubuntu0.18.04.1):

        Methode: substr:
        ================
        70.26195526123 Millisekunden. (Für 35640 Werte)
        1.9714353328067 Microsekunden pro Wert
        
        Methode: DateTime::createFromFormat:
        ====================================
        392.22884178162 Millisekunden. (Für 35640 Werte)
        11.005298590954 Microsekunden pro Wert
        

        Ergebnis auf einem armhf (Banana Pi),PHP 7.0.30-0+deb9u1:

        Methode: substr:
        ================
        105.8349609375 Mllisekunden. (Für 35640 Werte)
        2.9695555818603 Microsekunden pro Wert
        
        Methode: DateTime::createFromFormat:
        ====================================
        2328.6981582642 Millisekunden. (Für 35640 Werte)
        65.33945449675 Microsekunden pro Wert
        

        Die Angabe von 14 signifikanten Stellen für die gezeigten Milli- bzw. Microsekunden ist natürlich "sportlich bis nicht ernst zu nehmen", weil diese Genauigkeit auch mit den maximalen 4 Gigaherz des Prozessors (die selbst nicht einmal erreicht werden) nicht erreicht werden kann. Dennoch ist das folgende Fazit zu ziehen:

        Wie von mir mit den Worten "Ich gehe einfach davon aus, dass die primitivsten Operationen, die sich um "nichts" (hier die Gültigkeit von Daten) kümmern, auch die schnellsten sind." vorhergesagt ist die primitive Operation mit substr() ca. 5-6 (AMD64) bzw. ~30 (armhf) mal schneller als die immerhin fest kompilierten Methoden der DateTime-Klasse. Die können zwar mehr - aber wenn dieses "mehr" nicht gebraucht wird sollte man bei der Verarbeitung von Massendaten (Pit: "Da es sich um viele Datensätze handelt") diese teuren Funktionen auch meiden.

        1. Hallo ursus,

          solche Laufzeitmessungen sind immer interessant und bin auch oft dazu verleitet, aber dabei darf man zweierlei nicht vergessen:

          • wieviele solcher Konvertierungen macht man während eines PHP Requests?
          • wieviele PHP Requests mit solchen Konvertierungen macht der Server pro Zeiteinheit?

          Sprich: Kommen pro Script-Aufruf 3 solcher Konvertierungen vor, und der Server bekommt 1000 Requeste pro Stunde, spart man 3000 x 62µs = 186ms CPU-Last pro Stunde. Auf der Banane. Das sind, unter der Annahme dass die Banane ein single-core ist, 0,005% der CPU-Kapazität.

          Auf dem AMD64 sind es 27ms oder 0,00075% der Leistung eines Kerns, wovon er aber mindestens 4 haben dürfte.

          Führt man solche Optimierungen an 1000 Programmstellen durch, spart man auf dem BananaPi immerhin 5% der Kapazität ein und auf dem AMD64-Core 0,75%.

          PHP ist dafür gemacht, ohne viel Grübeln über Algorithmen zu einem Ergebnis zu kommen. Wenn der Server mit dem Ergebnis überlastet ist, weil zu viele User kommen, bringen Mikrooptimierungen wie diese hier nur minimale Abhilfe. Sowas lohnt nur, wenn der fragliche Code extrem häufig aufgerufen wird.

          Die Laufzeitprobleme entstehen eher bei externen Zugriffen (File, DB) oder wegen zu wenig Arbeitsspeicher (in dem man opcache oder memcached einsetzen könnte).

          Rolf

          --
          sumpsi - posui - clusi
          1. Das sind, unter der Annahme dass die Banane ein single-core ist

            octa-core - Allerdings muss der Banana Pi ohne meine Kaufempfehlung auskommen.

            Sowas lohnt nur, wenn der fragliche Code extrem häufig aufgerufen wird.

            Ja. Klar. Ich habe darauf Bezug genommen:

            (Pit: "Da es sich um viele Datensätze handelt")

            PHP ist dafür gemacht, ohne viel Grübeln über Algorithmen zu einem Ergebnis zu kommen.

            Naja. Manchmal lohnt es sich (das Grübeln). Ich erinnere mich an das "größte deutsche Hilfeforum", des GröDiPaZ (Größter Dialerparasit aller Zeiten) mit seinen stolzen "26 Datenbankfragen zur Erzeugung der Startseite" - welches beim 10. Drücken auf [F5] zuverlässig mit einer Überlastmeldung (mysql: to many connections - das auch noch treudoof auf der Webseite angezeigt) ausstieg, während meine Seite dank sinnvollen Cachings der quasistatischen Inhalte die versuchte DDoS-Attacke lächelnd wegsteckte. (Allerdings auch auch weil die Hälfte der für den Angriff genutzten Kapazitäten auf blogspot.com gerichtet wurde … also nicht mal das konnte der GröDiPaZ richtig.)

            Seit dem bin ein ausgesprochener Fan von micro- und allen anderen Optimierungen…

            1. Hallo ursus,

              (Pit: "Da es sich um viele Datensätze handelt")

              Siehste: Lesen bildet 😳. Nicht lesen führt zu Einbildung 😕

              Ich versuche mal, mich rauszuquatschen: Was ist "viele"? Und ist das jetzt in einer Online-Query, oder ein Batch?

              Mit ungebildeten Grüßen
              Rolf

              --
              sumpsi - posui - clusi
              1. Ich versuche mal, mich rauszuquatschen: Was ist "viele"?

                Sehr gute Frage. Für mich beginnt das in solchen Fällen irgendwo bei 10^3 und einem ganzzahligen Multiplikator, der selbst auch gerne mal als n * 10^x [mit x > 2] und einem ganzzahligen Multiplikator beschrieben werden darf

                Und ist das jetzt in einer Online-Query, oder ein Batch?

                Tja. Daraus (und vor allem im Hinblick auf die Datenherkunft) ergibt sich womöglich eine weitere, wesentlich effektivere Optimierungsmöglichkeit…

              2. hallo

                Ich versuche mal, mich rauszuquatschen: Was ist "viele"? Und ist das jetzt in einer Online-Query, oder ein Batch?

                Viele ist alles mit einer Tendenz zu Wachstum.