coder1979: Per Javascript Datum auf Gültigkeit prüfen

Hi zusammen,

ich machs kurz ;-)

Der folgende Code bringt beim Firefox "Ungültig" und beim Opera "Gültig"

var dt = new Date("2022-06-31");
var day_no=dt.getDay();
if (isNaN(day_no)) {
  alert("ungültig!");
} else {
  alert("Gültig!");
}

Habt ihr ne Idee, wie ich auch beim Opera beim 31.06. (oder bei anderen, nicht gültigen Tagen) ein ungültig bekomme?

LG Daniel

  1. Hallo,

    Der folgende Code bringt beim Firefox "Ungültig" und beim Opera "Gültig"

    var dt = new Date("2022-06-31");
    var day_no=dt.getDay();
    if (isNaN(day_no)) {
      alert("ungültig!");
    } else {
      alert("Gültig!");
    }
    

    das Problem hier ist, dass du die Zwischenschritte nicht überprüfst. Denn bereits die erste Zeile ist falsch. Wird dem Date-Konstruktor ein Datum in String-Format übergeben, muss es in einem bestimmten Format vorliegen. Das von dir verwendete ISO-8610-Format (das mir auch viel sympathischer ist), passt hier nicht.

    Ich bin aber auch gerade etwas überrascht, dass das Date-Objekt anscheinend keine Methode anbietet, ein falsches Datum zu erkennen.

    Einen schönen Tag noch
     Martin

    --
    Ich fürchte, ich brauche ein neues Portemonnaie. Das alte ist leer.
    1. @@Der Martin

      Wird dem Date-Konstruktor ein Datum in String-Format übergeben, muss es in einem bestimmten Format vorliegen. Das von dir verwendete ISO-8610-Format (das mir auch viel sympathischer ist), passt hier nicht.

      Sagt wer? SELFHTML? 🤣

      Bei dem, was im Wiki steht, ist es oft ratsam, eine zweite Meinung einzuholen. Was im Wiki steht, könnte falsch sein.

      Schauen wir doch mal, was MDN sagt.

      “A string value representing a date, in a format recognized by the Date.parse() method. (The ECMA262 spec specifies a simplified version of ISO 8601, but other formats can be implementation-defined, which commonly include IETF-compliant RFC 2822 timestamps.)”

      Wer hat im Zweifelsfall wohl recht?

      Ich bin aber auch gerade etwas überrascht, dass das Date-Objekt anscheinend keine Methode anbietet, ein falsches Datum zu erkennen.

      Tut es doch.

      var dt = new Date("2022-06-31") // undefined

      Sowohl im Firefox als auch im Chrome. Da Opera auch ein Chromium ist, wird es da wohl auch so sein? Es sei denn, coder1979 hat einen Prä-Chromium-Opera ausgegraben, also ein Museumsstück.

      🖖 Живіть довго і процвітайте

      --
      When the power of love overcomes the love of power the world will know peace.
      — Jimi Hendrix
      1. @@Der Martin Es sei denn, coder1979 hat einen Prä-Chromium-Opera ausgegraben, also ein Museumsstück.

        Hi,

        ich habe es mit der folgenden Opera-Version gerade eben getestet: 87.0.4390.25

        Da bekomm ich ein "Gültig" für den 31.06.2022 denn Opera mach daraus automatisch den 01.07.2022. Probiert es gerne aus.

        Aber: Ich hab die folgende Funktion ergoogelt und die macht genau das, was sie soll:

        function isValidDateCheck(dString) {
        
        // test it is nn/nn/nnnn or nn/nn/nn
        var dRe = /^(\d{1,2})([\-\/])(\d{1,2})\2(\d{4}|\d{2})$/
        
        if (!dRe.exec(dString)) {
          return false; 
        }   
        
        // make sure it parses as date 
        // replace this part if you do not allow dashes        
        dString.replace(/-/g,"/"); 
        
        var date = new Date(dString); // create a date object
        if (!isNaN(date)) { // it may give NaN - if not test the parts
            var parts = dString.split("/"); // split on slash
            var dd = parseInt(parts[1],10); // day number
            var mm = parseInt(parts[0],10)-1; // month - JS months start at 0
            var yyyy = parseInt(parts[2],10); // year
            // return true if all parts match
            return dd===date.getDate() && mm === date.getMonth() && yyyy===date.getFullYear();
        }
        // here the date was not parsed as a date
        return false;
        }
        
        

        Ich bin aber noch nicht genau dahinter gestiegen, was diese Funktion anders macht. Werd mich morgen nochmal gedanklich damit beschäftigen, heut ist irgendwie der Wurm drin ;-)

        1. @@coder1979

          ich habe es mit der folgenden Opera-Version gerade eben getestet: 87.0.4390.25

          Da bekomm ich ein "Gültig" für den 31.06.2022 denn Opera mach daraus automatisch den 01.07.2022.

          Das glaube ich dir. Ja, jetzt seh ich’s auch. Opera wie andere Chromia (Chrome, Edge, …) auch.

          Aber: Ich hab die folgende Funktion ergoogelt und die macht genau das, was sie soll:

          Das glaube ich dir nicht. Die Funktion erwartet einen Datumstring im amerikanischen Format Monat/Tag/Jahr.

          Sagtest du nicht, dein Datumstring wäre im Format Jahr–Monat–Tag?

          Ich bin aber noch nicht genau dahinter gestiegen, was diese Funktion anders macht.

          Sie vergleicht die Bestandteile des Datumstrings mit dem daraus ermittelten Datum. Für 06/30/2022 ist 6 = 6 und 30 = 30. Für 06/31/2022 ist 6 ≠ 7 und 31 ≠ 1.

          Es sollte aber genügen, die Tage zu vergleichen. Monate und Jahre braucht man nicht.

          🖖 Живіть довго і процвітайте

          --
          When the power of love overcomes the love of power the world will know peace.
          — Jimi Hendrix
          1. @@coder1979

            ich habe es mit der folgenden Opera-Version gerade eben getestet: 87.0.4390.25

            Da bekomm ich ein "Gültig" für den 31.06.2022 denn Opera mach daraus automatisch den 01.07.2022.

            Das glaube ich dir. Ja, jetzt seh ich’s auch. Opera wie andere Chromia (Chrome, Edge, …) auch.

            Aber: Ich hab die folgende Funktion ergoogelt und die macht genau das, was sie soll:

            Das glaube ich dir nicht. Die Funktion erwartet einen Datumstring im amerikanischen Format Monat/Tag/Jahr.

            Sagtest du nicht, dein Datumstring wäre im Format Jahr–Monat–Tag?

            Ja, so hatte ich das ursprünglich, habs aber für dieses Beispiel kurzfristig aufs amerikanische Format geändert. Kann man ja aber auch wieder entsprechend umschreiben / in der Funktion entsprechend anpassen.

            Ich bin aber noch nicht genau dahinter gestiegen, was diese Funktion anders macht.

            Sie vergleicht die Bestandteile des Datumstrings mit dem daraus ermittelten Datum. Für 06/30/2022 ist 6 = 6 und 30 = 30. Für 06/31/2022 ist 6 ≠ 7 und 31 ≠ 1.

            Es sollte aber genügen, die Tage zu vergleichen. Monate und Jahre braucht man nicht.

            Dankeschön, nun hab ichs kapiert und bin erstmal happy mit der Lösung.

            LG Daniel

        2. @@coder1979

          function isValidDateCheck(dString) {
          
          // test it is nn/nn/nnnn or nn/nn/nn
          var dRe = /^(\d{1,2})([\-\/])(\d{1,2})\2(\d{4}|\d{2})$/
          
          if (!dRe.exec(dString)) {
            return false; 
          }   
          
          // make sure it parses as date 
          // replace this part if you do not allow dashes        
          dString.replace(/-/g,"/"); 
          

          Wenn man da schon mit regulären Ausdrücken rumhantiert, kann man auch gleich damit prüfen, ob der String ein gültiges Datum darstellt. 🤓

          🖖 Живіть довго і процвітайте

          PS: Nein, nicht machen, Kinder!

          PS2: Das hab ich letztens auch den Kollegen präsentiert – zusammen mit anderen Dingen, die ihren Ursprung hier im Forum haben.

          --
          When the power of love overcomes the love of power the world will know peace.
          — Jimi Hendrix
    2. Moin

      Der folgende Code bringt beim Firefox "Ungültig" und beim Opera "Gültig"

      var dt = new Date("2022-06-31");
      

      Wenn ich das in der Browser-Konsole eintippe, sagt mir der Firefox bereits „Invalid Date“.

      var day_no=dt.getDay();
      if (isNaN(day_no)) {
        alert("ungültig!");
      } else {
        alert("Gültig!");
      }
      

      das Problem hier ist, dass du die Zwischenschritte nicht überprüfst. Denn bereits die erste Zeile ist falsch. Wird dem Date-Konstruktor ein Datum in String-Format übergeben, muss es in einem bestimmten Format vorliegen. Das von dir verwendete ISO-8610-Format (das mir auch viel sympathischer ist), passt hier nicht.

      In meinem Firefox passt ISO 8601:

      let dt = new Date('2022-06-30');
      dt.getDay();    // 4
      

      Ich bin aber auch gerade etwas überrascht, dass das Date-Objekt anscheinend keine Methode anbietet, ein falsches Datum zu erkennen.

      Doch.

      Viele Grüße
      Robert

      1. @@Robert B.

        var dt = new Date("2022-06-31");
        

        Wenn ich das in der Browser-Konsole eintippe, sagt mir der Firefox bereits „Invalid Date“.

        TIL, dass sich Chromia in der Browser-Konsole und außerhalb anders verhalten. Scheint was mit dem Schlüsselwort var zu tun zu haben. (Selbes Verhalten bei const und let.)

        Das mal weggelassen:

        dt = new Date("2022-06-31"); // Fri Jul 01 2022 02:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)

        Und im normalen Programmablauf kommt das in Chromia auch mit Schlüsselwort var, const oder let raus. dt.getDay() liefert den truthy Wert 5. (Der 1. Juli 2022 ist ein Freitag.)

        Firefox hingegen macht aus dem 31. Juni nicht den 1. Juli, sondern liefert null. dt.getDay() ergibt dann NaN (falsy).

        🖖 Живіть довго і процвітайте

        --
        When the power of love overcomes the love of power the world will know peace.
        — Jimi Hendrix
    3. // file: test.js
      var dt = new Date("2022-06-31");
      console.log(dt);
      

      Ausführen:

      node test.js
      2022-07-01T00:00:00.000Z
      

      Nicht wundern:

      console.log( dt.toString() ); liefert

      Fri Jul 01 2022 02:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)
      

      Aktuell?

      node -v
      v12.22.9
      

      Ok: nodejs mag bei 16.15.1 sein, aber das ist „distributionsaktuell“.

      Nächster Test: Im Browser (Chromium, Version 101.0.4951.15 (Entwickler-Build) Ubuntu 22.04 (64-Bit)):

      <html>
      <body>
      <div id="output"></div>
      <script>
      var dt = new Date("2022-06-31");
      document.getElementById("output").innerHTML=dt.toString();
      </script>
      </body>
      </html>
      

      Ausgabe:

      Fri Jul 01 2022 02:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)
      
      • Getestet: Keine Änderung mit ['const','var','let','']: (in Nodejs und im Browser)
      • Getestet: Ausgabe im HTML und in der Konsole stimmt im Chromium exakt überein.

      Fazit: JS kann das Datum (die Zeit) wie angegeben verarbeiten. Aber genau wie PHP wertet es dieses so aus, dass bei formal ungültigen Datumsangaben (wie hier der 31. Juno) dieses so ausgewertet wird, dass ein gültigiger Zeitpunkt (1. Julei) berechnet wird.

      Das ist oft nützlich.

      Naja. Wenn man das will, dann muss man wohl das Datum selbst zerlegen und prüfen oder aber etwas wie Moment.js verwenden. Das kann angeblich solche „ungültigen“ Angaben erkennen.

      1. Naja. Wenn man das will, dann muss man wohl das Datum selbst zerlegen und prüfen oder aber etwas wie Moment.js verwenden. Das kann angeblich solche „ungültigen“ Angaben erkennen.

        Wenn Du das selbst machen willst, dann sei Dir folgender Ansatz empfohlen:

        Du prüfst, ob sich der String zerlegen lässt, ob da auch 3 Elemente herauskommen - wenn nicht ist das Datum ungültig

        Dann checkst Du,

        • ob der Monat 31 Tage hat und der Tag kleiner als 32 ist, sodann
        • ob der Monat 30 Tage hat und der Tag kleiner als 31 ist, sodann
        • ob es der Februar ist und der Tag kleiner als 29 ist.
        • wenn der Monat der Februar ist, ob es denn ein Schaltjahr ist und der Tag kleiner als 30 ist.

        Ausgaben:

        • In den zutreffenden/positiven Fällen habe ich mit entschlossen, das überprüfte Array zurückzuliefern.
        • Wenn nichts davon zutrifft ist es kein gültiges Datum, die Funktion liefert false

        Achso:

        Ein paar Tests mit gültigen und ungültigen Angaben habe ich eingebaut. Es wird sich vorhersehbar Kritik daran entzünden, dass mein Vorgehen wie auch die Notation antiquiert, gar altmodisch sei. Naja: Ich kann das eben noch…

        function dateCheckerYmd( input ) {
        
        	var ar=input.split(/[^0-9]/);
        
        	if ( 3 !=  ar.length ) {
        		return false;
        	}
        
        	var y = parseInt( ar[0] );
        	var m = parseInt( ar[1] );
        	var d = parseInt( ar[2] );
           
        	var m31 = new Array( 1, 3, 5, 7, 8, 10, 12 );
        	var m30 = new Array( 4, 6, 9, 11 );
        
        	if ( m31.indexOf( m ) != -1 ) {
        		if ( ( d > 0 )  && ( d < 32 ) ) {
        			return new Array( y, m, d );
        		}
        	}
        	if ( m30.indexOf( m ) != -1 ) {
        		if ( ( d > 0 )  && ( d < 31 ) ) {
        			return new Array( y, m, d );
        		}
        	} 
        
        	if ( m == 2 && d > 0 && d < 29 ) {
        		return new Array( y, m, d );
        	}
        
        	var Feb29 = false;
        	if ( 
        				Number.isInteger( y/4 )
        			&& ( 
        						false == Number.isInteger( y/100 )
        					||  true  == Number.isInteger( y/400 )
        				)
        	) {
        		Feb29 = true;	
        	}
        
        	if ( ( Feb29 == true ) && ( d > 0 )  && ( d < 30 ) ) {
        		return new Array( y, m, d );
        	} 
        	if ( Feb29 == false  && ( d > 0 )  && ( d < 29 ) ) {
        		return new Array( y, m, d );
        	}
        	return false;
        }
        
        function teste( s ) {
        	console.log( s + ': ' + dateCheckerYmd( s ) );
        }
        
        tests = new Array (
        	'2000-02-29',
        	'2001-02-29',
        	'2020-02-29',
        	'2100-02-29',
        	'2000-13-01',
        	'2000-12-31',
        	'2000 12 31',
        	'2000-12-32',
        	'2000-12',
        	'2000-12-01-1'
        );
        
        tests.forEach ( teste );
        
        
        

        Ausgaben:

        2000-02-29: 2000,2,29
        2001-02-29: false
        2020-02-29: 2020,2,29
        2100-02-29: false
        2000-13-01: 2000,13,1
        2000-12-31: 2000,12,31
        2000 12 31: 2000,12,31
        2000-12-32: false
        2000-12: false
        2000-12-01-1: false
        
        1. @@Raketenwilli

          Wenn Du das selbst machen willst, dann sei Dir folgender Ansatz empfohlen:

          Du prüfst, ob sich der String zerlegen lässt, ob da auch 3 Elemente herauskommen - wenn nicht ist das Datum ungültig

          Dann checkst Du,

          • ob der Monat 31 Tage hat und der Tag kleiner als 32 ist, sodann
          • ob der Monat 30 Tage hat und der Tag kleiner als 31 ist, sodann
          • ob es der Februar ist und der Tag kleiner als 29 ist.
          • wenn der Monat der Februar ist, ob es denn ein Schaltjahr ist und der Tag kleiner als 30 ist.

          Den Ansatz würde ich nicht empfehlen. Zu aufwendig.

          Ob da 3 Zahlen herauskommen – klar.

          Dann kann man die aber gleich als Datum interpretieren und prüfen, ob der Tag des so ermittelten Datums mit der entsprechenden Zahl im String übereinstimmt. Das sollte genügen.

          🖖 Живіть довго і процвітайте

          --
          When the power of love overcomes the love of power the world will know peace.
          — Jimi Hendrix
          1. Zu aufwendig.

            Dann kann man die aber gleich als Datum interpretieren und prüfen, ob der Tag des so ermittelten Datums mit der entsprechenden Zahl im String übereinstimmt. Das sollte genügen.

            Für Dich sind das ein paar Zeilen weniger - aber im Hintergrund wird dann die Eingabe geparst, unter Berücksichtigung von Schaltjahren und sogar Schaltsekunden in eine Zahl von Millissekunden seit dem 1.1.1970 00:00:00 GMT umgerechnet, und dann - erneut unter Berücksichtigung von Schaltjahren und sogar Schaltsekunden - daraus wieder das Jahr, der Monat und der Tag berechnet.

            Für mich klingt das nach hohen Aufwand und nach Verwendung von ziemlich viel Speicher und vor allem Prozessortakten…

            Dafür ist das die date-Klasse freilich schon kompiliert. Allerdings verwendet mein Skript ausgesprochen primitive (also schnelle) Anweisungen.

            Also: Ohne umfangreiche Tests und eine vorherige, sachangemsssene Definition von „Aufwand“ würde ich keine Aussage darüber treffen wollen, was „aufwändiger“ ist.

            1. Hallo Raketenwilli,

              sachangemsssene Definition von „Aufwand“

              Die muss man vorher treffen, sicher. Dazu gehört zumeist aber auch der Programmier- und Testaufwand. Der Verarbeitungsaufwand ist, so weh das einem mit Assembler digitalisierten Boomer wie Dir und mir auch tut, vernachlässigbar. Außer in wenigen Ausnahmefällen…

              Rolf

              --
              sumpsi - posui - obstruxi
              1. Der Verarbeitungsaufwand ist, so weh das einem mit Assembler digitalisierten Boomer wie Dir und mir auch tut, vernachlässigbar.

                Ah! Deshalb wird heutzutage rein verbal „optimiert“. Und erwartet, dass alles unendlich groß sein darf.

          2. Zu aufwendig? Man messe wie folgt:

            function dateCheckerYmd( input ) {
            
            	var ar = input.split( /[^0-9]/ );
            
            	if ( 3 !=  ar.length ) {
            		return false;
            	}
            
            	var y = parseInt( ar[0] );
            	var m = parseInt( ar[1] );
            	var d = parseInt( ar[2] );
               
            	var m31 = new Array( 1, 3, 5, 7, 8, 10, 12 );
            	if ( m31.indexOf( m ) != -1 ) {
            		if ( ( d > 0 )  && ( d < 32 ) ) {
            			return new Array( y, m, d );
            		}
            	}
            	
            	var m30 = new Array( 4, 6, 9, 11 );
            	if ( m30.indexOf( m ) != -1 ) {
            		if ( ( d > 0 )  && ( d < 31 ) ) {
            			return new Array(y, m, d);
            		}
            	} 
            
            	if ( m == 2 && d > 0 && d < 29 ) {
            		return new Array( y, m, d );
            	}
            
            	var Feb29 = false;
            	if ( 
            				Number.isInteger( y/4 )
            			&& ( 
            						false == Number.isInteger( y/100 )
            					||  true  == Number.isInteger( y/400 )
            				)
            	) {
            		Feb29 = true;	
            	}
            
            	if ( ( Feb29 == true ) && ( d > 0 )  && ( d < 30 ) ) {
            		return new Array(y, m, d);
            	} 
            	if ( Feb29 == false  && ( d > 0 )  && ( d < 29 ) ) {
            		return new Array( y, m, d );
            	}
            	return false;
            }
            
            output='';
            function teste( s ) {
            	 output = output + s + ': ' + dateCheckerYmd( s ) + "\n";
            }
            
            tests = new Array (
            	'2000-02-29',
            	'2001-02-29',
            	'2020-02-29',
            	'2100-02-29',
            	'2000-13-01',
            	'2000-12-31',
            	'2000 12 31',
            	'2000-12-32',
            	'2000-12',
            	'2000-12-01-1'
            );
            const { performance } = require('perf_hooks');
            
            t = performance.now();
            tests.forEach ( teste );
            console.log( t = performance.now() - t + ' Millisekunden' );
            console.log( output );
            

            vers.

            function dateCheckerYmd( input ) {
            
            	var ar = input.split( /[^0-9]/ );
            
            	if ( 3 !=  ar.length ) {
            		return false;
            	}
            	
                dto = new Date( input );
                
                var d = parseInt( ar[2] );
                if ( dto.getDate()     != d ) { return false; }
                
                var m = parseInt( ar[1] ) - 1;
                if ( dto.getMonth()    != m ) { return false; }
            	
                var y = parseInt( ar[0] );	
                if ( dto.getFullYear() != y ) { return false; }
            
                return dto;
            }
            
            output='';
            function teste( s ) {
            	 output = output + s + ': ' + dateCheckerYmd( s ) + "\n";
            }
            
            tests = new Array (
            	'2000-02-29',
            	'2001-02-29',
            	'2020-02-29',
            	'2100-02-29',
            	'2000-13-01',
            	'2000-12-31',
            	'2000 12 31',
            	'2000-12-32',
            	'2000-12',
            	'2000-12-01-1'
            );
            const { performance } = require('perf_hooks');
            t = performance.now();
            tests.forEach ( teste );
            console.log( t = performance.now() - t + ' Millisekunden' );
            console.log( output );
            
            
            

            Dann kann man die aber gleich als Datum interpretieren und prüfen, ob der Tag des so ermittelten Datums mit der entsprechenden Zahl im String übereinstimmt. Das sollte genügen.

            Mit der Messmethode ist das Skript gemäß Deiner Vorgehensweise auf dem Raspi400 (mit nodejs v12.22.9) um den Faktor 1,7 langsamer als meine „zu Fuß-Methode“:

            1.051170002669096 Millisekunden
            

            vers.

            1.861379001289606 Millisekunden
            

            (Macht man die Ausgaben einzeln erhöht sich die Zeit auf unverschämte 15 Millisekunden - das das Öffnen und schließen des Ausgabekanals „teuer“ ist habe ich - glaub ich - mal von Dir gelernt.)