time() umrechnen

Thomasio

Erfahrenes Mitglied
Ich schreibe an einem Spiel, was die aktuelle Zeit im Spiel anzeigen soll und zwar so, dass alle Spieler immer dieselbe Zeit sehen, egal in welcher Zeitzone der Spieler sitzt.
Das Spiel beginnt am 1.1.2000 und soll tageweise vorwärts laufen, sprich immer wenn alle Spieler alle Aktionen des Tages abgeschlossen haben, soll das Spiel einen Tag vorrücken und alle Spielern sollen das neue Datum angezeigt bekommen.
Zusätzlich soll (für Spielunterbrechungen) das aktuelle Datum in MySql gespeichert werden.

Ich habe herumexperimentiert mit time(), gmtime(), mktime(), usw., aber ich sehe den Wald vor lauter Bäumen nicht.
Wie stelle ich es an, zwischen dem Server und den Clients unabhängig von Zeitzonen immer dasselbe Datum anzuzeigen, und wie konvertiere ich das in ein Format, was sich auch in MySql speichern lässt?
In PHP ist das kein Problem, weil ich einfach den timestamp als UINT speichern kann, aber in C++ muss das time_t sein und da sagen die Experten, man soll nicht einfach UINT zu time_t casten.
 
Mh... ich bin mir nicht sicher, ob ich dich verstanden habe, aber es klingt ein bisschen nach "Virtuelle Spielzeit"

Mit Virtuelle Spielzeit meine ich, dass alle Spieler zu einem beliebigen Zeitpunkt, sagen wir, <<PST :: 18.08.2014 :: 07:24:56>> in ein Spiel einsteigen und sie bekommen Angzeigt: <<01.01.2000 :: 08:00:00>>

Realisieren würde ich das vielleicht mit einer Unsigned Long-Variable (damit die Leute sehr lang spielen können), die du zum Startzeitpunkt auf 0 setzt und nach jedem Spielzyklus um 1 hochsetzt. So weißt du immer, wieviele Tage seit 1.1.2000 vergangen sind und kannst aus dem Wert entsprechend ein Datum berechnen.

Ein Server müsste dazu dann alle Spiel-Sessions verwalten und, sobald alle Sessions ein Done gemeldet haben, einen Datum-Broadcast fahren. Das kann von mir aus ein einfacher String sein, oder eben diese Variable... Dann müsstest du natürlich das Datum lokal berechnen. Da kann man etwas optimieren...

Edit:

Ach ja... ich weiß nicht, obs eine so gute Idee ist, hinter so ein Spiel eine Datenbank zu klatschen...
 
Zuletzt bearbeitet:
Also ich würde das folgendermassen machen:
Dein Server hat eine tm-struktur in seiner Konfiguration (XML?) gespeichert mit dem letzten Tag. Wenn alle Spieler die nötigen Aktionen abgeschlossen haben erhöhst du tm_days der Struktur und speicherst sie in die XML. Anschliessend kannst du mktime verwenden um die tm-Struktur in die time_t umzuwandeln. Diese time_t schickst du dann an die clients die wiederum bsp ctime verwenden können um einen zeitzonenunabhängigen string zu bekommen.
 
Im Moment speichert der Server nur die Anzahl Tage in einem UINT, das kann ich leicht konvertieren, z.B. in char* fürs senden via Socket und es bleibt dem Client überlassen, daraus das passende Datum zu errechnen.
Ich muss also am Client "nur" den 1.1.2000 setzen, Tage dazu zählen und ausgeben.

Code:
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_sec = 0;
timeinfo->tm_min = 0;
timeinfo->tm_hour = 0;
timeinfo->tm_mday = 1;
timeinfo->tm_mon = 0;
timeinfo->tm_year = 100;
char MyTime[80];
strftime(MyTime,80,"%d.%m.%Y",timeinfo);

Der Gibt mir schön brav den 01.01.2000 aus.
Dumm ist nur, wenn ich jetzt die Tage dazu zählen will

Code:
UINT Days = 500; // nur als Beispiel

time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_sec = 0;
timeinfo->tm_min = 0;
timeinfo->tm_hour = 0;
timeinfo->tm_mday = 1 + Days;
timeinfo->tm_mon = 0;
timeinfo->tm_year = 100;
char MyTime[80];
strftime(MyTime,80,"%d.%m.%Y",timeinfo);

dann gibt er GAR NICHTS mehr aus.
Mit jedem Wert über 31 ist MyTime einfach leer.
 
Zum Rechnen ist time_t besser, das ist in Sekunden ab bestimmten Anfangsdatum. Die Zeit-Funktionen in C sind leider ziemlich bescheiden, und genau eine Umrechnung fehlt in der Standardbibliothek.

Sinnvoll aufzubewahren ist nur ein time_t in der UTC-Zeitzone. Niemals lokale Zeit (es gibt bei der gräßlichen Sommerzeitumstellung zweideutige und ungültige lokale Zeitwerte!)

Mit std::mktime machst du aus einer std::tm Struktur ein time_t.
Mit std::localtime machst du aus einem time_t ein std::tm in Lokalzeit.

Mit einem time_t zählst du Tage so dazu:
+= Days * 60 * 60 * 24

Was offiziell fehlt ist aus einem std::tm mit UTC-Zeitpunkt ein time_t zu errechnen, dazu kann ich folgendes Snippet anbieten:

Code:
int DateTime::tmcomp( std::tm* atmp, std::tm* btmp )
    {
	    int	result = 0;

	    if ( ( result = ( atmp->tm_year - btmp->tm_year ) ) == 0 
      &&   ( result = ( atmp->tm_mon - btmp->tm_mon ) ) == 0 
      &&   ( result = ( atmp->tm_mday - btmp->tm_mday ) ) == 0 
      &&   ( result = ( atmp->tm_hour - btmp->tm_hour ) ) == 0 
      &&   ( result = ( atmp->tm_min - btmp->tm_min ) ) == 0 )
      {
			  result = atmp->tm_sec - btmp->tm_sec;
      }
	    return result;
    }

time_t DateTime::MakeTimeFromUTCTM() const
    {
#if ( OPERATING_SYSTEM == OS_WINDOWS ) && ( _MSC_VER >= 1400 )
      std::tm     oldTM( m_Date );

      return _mkgmtime( &oldTM );
#else
	    register int			dir;
	    register int			bits;
	    register int			saved_seconds;
	    time_t				t;
	    struct tm			yourtm, *mytm;

	    yourtm = m_Date;
	    saved_seconds = yourtm.tm_sec;
	    yourtm.tm_sec = 0;
	    /*
	    ** Calculate the number of magnitude bits in a time_t
	    ** (this works regardless of whether time_t is
	    ** signed or unsigned, though lint complains if unsigned).
	    */
	    for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
		    ;
	    /*
	    ** If time_t is signed, then 0 is the median value,
	    ** if time_t is unsigned, then 1 << bits is median.
	    */
	    t = (t < 0) ? 0 : ((time_t) 1 << bits);

	    /* Some gmtime() implementations are broken and will return
	     * NULL for time_ts larger than 40 bits even on 64-bit platforms
	     * so we'll just cap it at 40 bits */
	    if(bits > 35) bits = 35;

	    for ( ; ; ) {
		    mytm = gmtime(&t);

		    if(!mytm) return -1;

		    dir = tmcomp(mytm, &yourtm);
		    if (dir != 0) {
			    if (bits-- < 0)
				    return -1;
			    if (bits < 0)
				    --t;
			    else if (dir > 0)
				    t -= (time_t) 1 << bits;
			    else	t += (time_t) 1 << bits;
			    continue;
		    }
		    break;
	    }
	    t += saved_seconds;
	    return t;
#endif
    }
 
Wow Endurion, vielen Dank für die Mühe, das muss ich mir genauer zu Gemüte führen.

Die Sache mit mktime() ist mir beim herumprobieren schon aufgefallen, da verstehe ich eins nicht.
Ich hatte bei MSDN schon gelesen, dass mktime() ein time_t zurück gibt, aber in allen Beispielen die ich gefunden habe steht nur:

mktime(timeinfo);

Das stellt in timeinfo nur die Werte für tm_wday und tm_yday ein, aber die Rückgabe, sprich time_t, wird ignoriert.
Andererseits macht es auch wenig Sinn hier das time_t zwischenzuspeichern, denn das Einzige was ich damit dann mache ist ja nur wieder time() was wieder ein struct tm daraus macht, was ich aber nach mktime() sowieso schon habe.

Eine andere Sache ist das casten von time_t.
Ich habe an verschiedenen Stellen gelesen, man soll int oder UINT oder long nicht nach time_t casten (warum man das nicht soll ist mir nicht klar) aber eine Rechnung ala

+= Days * 60 * 60 * 24

ist für mich dasselbe Prinzip, denn auch damit gehst du davon aus, dass ein time_t plus ein UINT kein Problem ist.

Und weil ich gerade dabei bin, noch eine Zusatzfrage, auch wenn ich sie für mein Spiel im Moment nicht brauche, bin ich beim herumprobieren auf eine Sache gestossen.

struct tm rechnet mit Jahren ab 1900, strftime() kann daraus ohne Probleme auch ein Datum/Uhrzeit vor 1970 generieren.
Nur wenn ich diesen (negativen) timestamp durch mktime() schicke und dann strftime() ausführe, kommt wieder GAR NICHTS dabei raus.
 
Zuletzt bearbeitet:
Das liegt daran, dass time_t keinen Zeitpunkt vor 1970 darstellen kann. Wenn du Zeitpunkte vor 1970 speichern willst, musst du auf etwas anderes ausweichen.

Das mit dem sollst-nicht-time_t-casten fusst wohl darauf, dass time_t auf verschiedenen Systemen unterschiedlich definiert ist. Unter Windows ist time_t ein __int64. Unter anderen Systemen, die ich kenne, ist es ein long (im Prinzip auch ein 64-bit signed int). Damit ist wohl gemeint, dass man ein time_t immer in einem time_t ablegen soll, nicht in einem anderen Typ.
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück