Methode zum Addieren von Tagen



  • Servus frohe Gemeinde,

    ich stocke hier gerade und dachte mir, evtl. kann mir ja jemand helfen den Fehler zu finden. Ich habe eine Klasse die ein Datum enthält, bestehend aus 3 int Variablen, für Tag, Monat und Jahr.
    Jetzt habe ich eine Methode zum Überladen des Operators += geschrieben, zum hinzuaddieren von Tagen. Dabei sollen auch die Schaltjahre berücksichtigt werden. An bestimmten Stellen bleibt das Programm aber immer wieder hängen, wie z.B. jetzt wenn ich 98 Tage zum "27.10.2000" hinzuaddieren will. Bei 97 Tagen funktioniert alles, sobald 98 Tage addiert werden sollen, funktioniert das Programm nicht...
    Wäre super wenn mir jemand nen Tip geben könnte, bin noch nicht allzu erfahren in der Sache...

    Date & Date::operator+=(const int days)
    {
    	int rest = days;
    
    	while (rest)
    	{
    		if (month == 2)
    		{
    			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
    			{
    				if (day < 29)
    				{
    					++day;
    					--rest;
    				}
    				else
    				{
    					day = 1;
    					++month;
    					--rest;
    				}
    			}
    		}
    		else if (month == 2)
    		{
    			if (day < 28)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				++month;
    				--rest;
    			}
    		}
    		else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10)
    		{
    			if (day < 31)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				++month;
    				--rest;
    			}
    		}
    		else if (month == 4 || month == 6 || month == 9 || month == 11)
    		{
    			if (day < 30)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				++month;
    				--rest;
    			}
    		}
    		else if (month == 12)
    		{
    			if (day < 31)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				month = 1;
    				++year;
    				--rest;
    			}
    		}
    	}
    	return *this;
    }
    


  • ++day; mit const int day?



  • Nee, die Variable day ist nicht const. const int days ist der Parameter der die Tage enthält die hinzuaddiert werden sollen.



  • Benutze einen Debugger.

    Zweimal month == 2? Das würde ich mir mal genauer ansehen.



  • Das ist vielleicht nicht die Antwort die Du lesen willst, aber wenn Du stattdessen das Datum als eine Anzahl Tagen ab einem bestimmen Zeitpunkt darstellst (z. B. JulianDay), dann vereinfacht sich die Methode zu:

    Date & Date::operator+=(const int days)
    {
        daysSinceEpoch += days;
        return *this;
    }
    

    Ist trotzdem interessant zu wissen, dass man so ein Datum auf verschiedene Arten darstellen kann, und je nachdem werden die einen oder anderen Berechnungen einfacher oder schwieriger. Man kann auch zwischen den beiden Repräsentationen wechseln, also z. B: bei Anfrage des Monats einfach umrechnen in YearMonthDay und dann den Monat zurückgeben.



  • @Kuklinski sagte in Methode zum Addieren von Tagen:

    Ich habe eine Klasse die ein Datum enthält, bestehend aus 3 int Variablen, für Tag, Monat und Jahr.

    Gibt es einen Grund für eine eigene Klasse oder ist das nur zu Übungszwecken? Ansonsten nimm dafür besser die Klasse date::year_month_day. Lade dir dazu date.h von https://github.com/HowardHinnant/date/blob/master/include/date/date.h herunter.

    Damit ist das ein Einzeiler:

    using namespace date;
    year_month_day d = sys_days{year(2000)/10/27} + days{98};
    std::cout << d << '\n';
    


  • Meines Erachtens ist die sinnvollste Methode die Systemfunktionen zu nutzen, weil da auch alle Änderungen berücksichtigt werden z.B. Schaltsekunden. Unter UNIX/Linux ist es daher am sinnvollsten die UNIX Epoch zu nutzen, d.h. intern im Programm ausschließlich die Sekunden seit dem 1.1.1970 UTC zu verwenden, und die bürgerlichen Datumsangaben sofort über die Systemfunktionen in UNIX Epoch um zurechnen und umgekehrt bei der Ausgabe zurückzurechnen. Der Vorteil ist, dass man sich nicht Zeitzonen, Schaltsekunden, Schaltjahren etc. herumschlagen muss. Unter Windows gibt es FILETIME mit einem anderen Bezugspunkt, aber es funktioniert ganz ähnlich. Man sollte unbedingt dem Drang widerstehen so etwas selbst zu programmieren, weil immer wieder Änderungen aus diversen Gründen stattfinden und man diese nach pflegen müsste. Das System bekommt regelmäßig Updates.



  • Ja, da gebe ich Euch recht, Systemfunktionen sind dem Ganzen hier wohl vorzuziehen, aber ich mache das Ganze zu Übungszwecken um ein besseres Verständnis zu bekommmen. Und es funktioniert ja, bloß bleibt es an unbestimmten Stellen immer wieder hängen. Den Debugger habe ich schon benutzt, das Problem ist dass der Fehler erst beim 98. Durchlauf auftritt. Da bin ich alt und grau bis ich alle Durchläufe manuell durchlaufen habe.
    Gibt es eigentlich im Debugger des Visual Studio 2017 eine Möglichkeit direkt zu einem Bestimmten Durchlauf zu springen?



  • @john-0 sagte in Methode zum Addieren von Tagen:

    weil da auch alle Änderungen berücksichtigt werden z.B. Schaltsekunden. Unter UNIX/Linux ist es daher am sinnvollsten die UNIX Epoch zu nutzen, d.h. intern im Programm ausschließlich die Sekunden seit dem 1.1.1970 UTC zu verwende

    Seit sind Schaltsekunden in der Epoch enthalten? Schaltsekunden werden dort gerade nicht behandelt. Hier:

    $ date --date='@0'
    Do 1. Jan 01:00:00 CET 1970
    $ date --date="@$(((49*365 + 12)*86400))"
    Di 1. Jan 01:00:00 CET 2019
    

    Ich habe ganzzahlige Vielfache von 86400 addiert, dennoch sind die Sekunden insgesamt gleich geblieben.

    Sollen Schaltsekunden berücksichtigt werden, einfach mal https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#but-what-about-leap-seconds lesen.



  • @Kuklinski sagte in Methode zum Addieren von Tagen:

    Ja, da gebe ich Euch recht, Systemfunktionen sind dem Ganzen hier wohl vorzuziehen, aber ich mache das Ganze zu Übungszwecken um ein besseres Verständnis zu bekommmen. Und es funktioniert ja, bloß bleibt es an unbestimmten Stellen immer wieder hängen. Den Debugger habe ich schon benutzt, das Problem ist dass der Fehler erst beim 98. Durchlauf auftritt. Da bin ich alt und grau bis ich alle Durchläufe manuell durchlaufen habe.
    Gibt es eigentlich im Debugger des Visual Studio 2017 eine Möglichkeit direkt zu einem Bestimmten Durchlauf zu springen?

    ich mach das immer so:

    for ( unsigend i = 0; i < 100;++i )
    {
        if ( i == 98 )
            int k = 0; // hier Breakpoint setzen
        ...
    }
    

    Manche IDEs haben aber konditionelle Breakpoints, wo du quasi die Bedingung fürs anhalten direkt festlegen kannst.


  • Mod

    @wob sagte in Methode zum Addieren von Tagen:

    @john-0 sagte in Methode zum Addieren von Tagen:

    weil da auch alle Änderungen berücksichtigt werden z.B. Schaltsekunden. Unter UNIX/Linux ist es daher am sinnvollsten die UNIX Epoch zu nutzen, d.h. intern im Programm ausschließlich die Sekunden seit dem 1.1.1970 UTC zu verwende

    Seit sind Schaltsekunden in der Epoch enthalten? Schaltsekunden werden dort gerade nicht behandelt. Hier:

    $ date --date='@0'
    Do 1. Jan 01:00:00 CET 1970
    $ date --date="@$(((49*365 + 12)*86400))"
    Di 1. Jan 01:00:00 CET 2019
    

    Ich habe ganzzahlige Vielfache von 86400 addiert, dennoch sind die Sekunden insgesamt gleich geblieben.

    Sollen Schaltsekunden berücksichtigt werden, einfach mal https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes#but-what-about-leap-seconds lesen.

    Der Witz an Schaltsekunden ist doch gerade, dass sie in dem ganzen Datumskram gar nicht vorkommen. Man braucht sie nicht zu beachten, man darf sie auch nicht beachten. Bei den Schaltsekunden hat die Welt einfach beschlossen, für eine Sekunde die Uhren anzuhalten, und so zu tun, als wäre nie etwas gewesen. Die einzige Gelegenheit, bei der man Schaltsekunden beachten muss, ist, wenn man eine eigene Atomuhr betreibt, die anderen Uhren die Zeit diktiert. Für alle anderen Uhren hat jedes Jahr exakt 86400 Sekunden.



  • @wob sagte in Methode zum Addieren von Tagen:

    Seit sind Schaltsekunden in der Epoch enthalten? Schaltsekunden werden dort gerade nicht behandelt.

    Die UNIX Epoch ist immer frei von Schaltsekunden (d.h. die Zeit in Sekunden seit dem 1.1.1970 00:00 UTC) und zählt direkt in Sekunden weiter, weil es sonst zu internen Problemen kommen könnte bis hin zum Absturz von Programmen. Ob in der bürgerlichen Zeitdarstellung die Schaltsekunden berücksichtigt werden, ist systemabhängig.



  • @john-0 sagte in Methode zum Addieren von Tagen:

    Die UNIX Epoch ist immer frei von Schaltsekunden

    Genau. Ich hatte dein vorheriges Posting nur genau andersrum verstanden.



  • @It0101 sagte in Methode zum Addieren von Tagen:

    @Kuklinski sagte in Methode zum Addieren von Tagen:

    Ja, da gebe ich Euch recht, Systemfunktionen sind dem Ganzen hier wohl vorzuziehen, aber ich mache das Ganze zu Übungszwecken um ein besseres Verständnis zu bekommmen. Und es funktioniert ja, bloß bleibt es an unbestimmten Stellen immer wieder hängen. Den Debugger habe ich schon benutzt, das Problem ist dass der Fehler erst beim 98. Durchlauf auftritt. Da bin ich alt und grau bis ich alle Durchläufe manuell durchlaufen habe.
    Gibt es eigentlich im Debugger des Visual Studio 2017 eine Möglichkeit direkt zu einem Bestimmten Durchlauf zu springen?

    ich mach das immer so:

    for ( unsigend i = 0; i < 100;++i )
    {
        if ( i == 98 )
            int k = 0; // hier Breakpoint setzen
        ...
    }
    

    Manche IDEs haben aber konditionelle Breakpoints, wo du quasi die Bedingung fürs anhalten direkt festlegen kannst.

    Ich hab das jetzt so ähnlich gemacht, danke für den Tip! Es hat sich rausgestellt dass ihm meine if Logik wohl nicht so ganz gepasst hat. 97 mal ging alles gut und beim 98. passte es ihm auf einmal nicht mehr und es gab eine Endlosschleife. Hab das ganze jetzt ein Bisschen umgeschrieben und es funktioniert hervorragend.
    Hier der code, sollte das jemand mal brauchen:

    Date & Date::operator+=(const int days)
    {
    	int rest = days;
    
    	while (rest)
    	{
    		if (month == 2)
    		{
    			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
    			{
    				if (day < 29)
    				{
    					++day;
    					--rest;
    				}
    				else
    				{
    					day = 1;
    					++month;
    					--rest;
    				}
    			}
    			else
    			{
    				if (day < 28)
    				{
    					++day;
    					--rest;
    				}
    				else
    				{
    					day = 1;
    					++month;
    					--rest;
    				}
    			}
    		}
    		else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10)
    		{
    			if (day < 31)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				++month;
    				--rest;
    			}
    		}
    		else if (month == 4 || month == 6 || month == 9 || month == 11)
    		{
    			if (day < 30)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				++month;
    				--rest;
    			}
    		}
    		else if (month == 12)
    		{
    			if (day < 31)
    			{
    				++day;
    				--rest;
    			}
    			else
    			{
    				day = 1;
    				month = 1;
    				++year;
    				--rest;
    			}
    		}
    	}
    	return *this;
    }
    


  • @Kuklinski sagte in Methode zum Addieren von Tagen:

    Hier der code, sollte das jemand mal brauchen:

    ne, den Mist braucht kein Mensch.



  • Hier mal dein Quelltext in weniger Code 😉 Habs jetzt nicht getestet, hab auch keine Ahnung, was du da treibst, es geht nur ums Prinzip.

    void doStuff( int maxday, int &day, int &rest, int &mo )
    {
        if ( day < maxday )
            ++day;
        else
        {
            day = 1;
            ++month;
        }
        --rest;
    }
    
    
    
    Date & Date::operator+=(const int days)
    {
        enum MonthType { mt30Days = 0, mt31Days, mtOther, mtDec };
        std::vector<MonthType> Months = { mt31Days, mtOther, mt31Days, mt30Days, mt31Days, mt30Days, mt31Days, mt31Days, mt30Days, mt31Days, mt30Days, mtDec };
    
        int month = 3;
        int year = 1982;
    	int rest = days;
    	while ( rest )
    	{
    		if ( Months[ month ] == mtOther )
    		{
    			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
    			    doStuff( 29, day, rest, month );
    			else
                               doStuff( 28, day, rest, month );
    		}
    		else if ( Months[ month ] == mt31Days )
    		    doStuff( 31, day, rest, month );
    		else if ( Months[ month ] == mt30Days )
    		    doStuff( 30, day, rest, month );
    		else if ( Months[ month ] == mtDec )
    		{
    		    doStuff( 31, day, rest, month );
    		    if ( day >= 31 )
    		    {
    		         month = 1; // Januar
                             ++year; // Neues Jahr
            	    }
    		}
    	}
    	return *this;
    }
    


  • Ich würds nicht so machen. Der beste Tipp wurde schon genannt: Die Anzahl der Tage seit einem Referenzdatum. Da Umrechnungen nur an zwei Stellen (hin und zurück) stattfinden, sind auch Daten vor 1582 (Umstellung auf den Gregorianischen Kalender) kein Problem.
    Als Bonus hat man die Wochentagbestimmung frei Haus (seit 1.1.1 -> (days+5)%7 [1.1.1 war ein Samstag]).
    Überläufe braucht man überhaupt nicht beachten und es kann kein ungültiges Datum geben. Mit einer Zeitklasse, die intern in (Milli-) Sekunden seit 0:0:00 arbeitet, und einer Momentklasse, die beide (Datum und Zeit) einbindet, können auch beliebige Zeitpunkte und Differenzen betrachtet werden.



  • @It0101 sagte in Methode zum Addieren von Tagen:

    // [dreckiger code]
    

    Noch so ein Held. Die Bestimmung ob irgendwas ein Schaltjahr ist hat in einer Funktion zum addieren von Tagen nichts verloren.

    Und überhaupt: https://stackoverflow.com/a/11595914/3975177



  • @Swordfish sagte in Methode zum Addieren von Tagen:

    @It0101 sagte in Methode zum Addieren von Tagen:

    // [dreckiger code]
    

    Noch so ein Held. Die Bestimmung ob irgendwas ein Schaltjahr ist hat in einer Funktion zum addieren von Tagen nichts verloren.

    Und überhaupt: https://stackoverflow.com/a/11595914/3975177

    Ich hab doch gesagt, mir ist egal was er da treibt. Ich wollte nur seinen Quelltext etwas "hübscher" machen. Ich mag eben schöne Dinge 😃


  • Mod

    @yahendrik sagte in Methode zum Addieren von Tagen:

    Ich würds nicht so machen. Der beste Tipp wurde schon genannt: Die Anzahl der Tage seit einem Referenzdatum. Da Umrechnungen nur an zwei Stellen (hin und zurück) stattfinden, sind auch Daten vor 1582 (Umstellung auf den Gregorianischen Kalender) kein Problem.

    Besser noch: Man ist so gar nicht mehr auf irgendwelche Kalender festgelegt, egal ob gregorianisch, julianisch, chinesischer Mondkalender, oder nordkoreanischer Revolutionskalender. Dann hat man intern nur noch Zeitpunkte als abstraktes Konzept, ganz ohne eine Einheit. Der tatsächliche Kalander spielt dann nur bei der Interatkion mit einem Menschen die Rolle einer Darstellungsweise.


Log in to reply