COleDateTime/VB6-Datum zu was Sinnvollem konvertieren
-
Hi,
ich habe Datums- und Zeitangaben im COleDateTime-Format bzw. VB6-Date-Format (was wohl dasselbe ist) vorliegen, d. h. wohl, dass das Datum in einer 8-Byte-Fließkommazahl steht, wobei der Ganzzahlanteil die vergangenen Tage seit dem 30. Dezember 1899 darstellt und der Nachkommateil die Uhrzeit festlegt.
Diesen Typ würde ich gerne in etwas für mich Brauchbares konvertieren, ohne die MFC zu benutzen. Ob das time_t, UTC, Unixtime oder was auch immer ist, ist mir relativ egal, Hauptsache ich kanns dann in boost::datetime reinwerfen und muss mich nicht mehr mit Sommerzeit, Zeitzonen etc. rumschlagen. Hat jemand eine passende Konvertierungsfunktion für mich?
Danke.
-
COleDateTime hat nichts mit der MFC zu tun, sondern gehört zum gemeinsamen Code Basis von ATL/MFC (seit VC-200x).
Es ist template Code geworden und kann unabhängig in jedem Code eingesetzt werden, genauso wie CString! Weder ATL noch MFC Grundgerüste sind nötig.Es spricht also nichts dagegen COleDateTime zu verwenden.
Weiterhin ist dieses Format (DATE) in der WinApi und COM verankert und in den VARIANTs. Der Datentyp ist in windows.h definiert (siehe typedef double DATE; in wtypes.h). Er wird durch X-API Funktionen unterstützt.
Siehe VariantTimeToSystemTime, ConvertSystemTimeToVariantTime, VarBSTRFromDate etc.
-
Edit: gelöscht
-
Vielen Dank erstmal für die Hilfe.
Meine Funktion sieht jetzt folgendermaßen aus:
string dateToString(double date) { DATE dateAsDATE; VarDateFromR8(date, &dateAsDATE); BSTR buffer = SysAllocString(L"t"); // Hab ich das richtig verstanden, dass ich hier nur irgendwas allokieren muss? HRESULT res = VarBstrFromDate(dateAsDATE, 1033, // USA VAR_CALENDAR_GREGORIAN | VAR_FOURDIGITYEARS | LOCALE_NOUSEROVERRIDE, &buffer); if(res != 0) return lexical_cast<string>(date); else { bstr_t buffer2 = buffer; return string(buffer2); } }
Gibts an dieser Funktion was auszusetzen? Wie siehts mit Sommerzeitumstellung, Zeitzonen etc. aus? Die double-Daten wurden wohl in VB einfach mit Now() erzeugt. Bekomme ich so immer genau die Uhrzeit, die es ursprünglich war?
Edit: Achja, das LOCALE_NOUSEROVERRIDE tut noch nicht das, was ich möchte: die Formatierung der Systemsprache entsprechend gestalten. Was muss ich ändern?
-
Auszusetzen ist an der Funktion zu allererst mal dass sie langsam ist - falls das für dich eine Rolle spielt.
EDIT: OK, wenn du nen String brauchst, dann wird alles mehr-oder-weniger langsam sein. Ich hatte immer noch deinen ersten Beitrag im Kopf, wo du nach einer Konvertierung in einen Boost.DateTime Typen fragst. Darauf bezieht sich mein Beitrag. Die Resource-Leaks sind allerdings so-oder-so ein Problem. /EDIT
Weiters ist das SysAllocString denke ich falsch.
Der BSTR Parameter von VarBstrFromDate ist als [out] definiert, d.h. du solltest da etwas übergeben was einfach überschrieben werden kann, und nicht freigegeben werden muss. Idealerweise einfach Null.
Vermutlich wird VarBstrFromDate den von dir angeforderten String nicht freigeben, das wäre das 1. Resource-Leak in deinem Code.Weiters ist deine Abfrage des HRESULT falsch, da du auf >= 0 für success prüfen solltest, bzw. einfach das SUCCEEDED(hr) Makro verwenden.
Im Fehlerfall einfach mit lexical_cast draufzuhauen halte ich auch für falsch bzw. zumindest sehr sehr fragwürdig.
buffer2 ist auch unnötig. Falls das bstr_t ein Vertipper ist, und hier ein _ fehlt, dann solltest du lieber den (BSTR, bool) Konstruktor verwenden, und hier "false" mitgeben. Dadurch übernimmt die _bstr_t Klasse Ownership, und der BSTR wird am Ende korrekt freigegeben. Dass der von VarBstrFromDate erzeugte BSTR nirgends freigegeben wird ist nämlich das 2. Resource-Leak in deinem Code.
Ich würde, wenn ich eine boost::posix_time::ptime haben wollte, das inetwa so implementieren:
boost::posix_time::ptime ptime_from_com_time(double com_time) { using boost::int64_t; static int64_t const seconds_per_day = 86400; static int64_t const offset_days = 25569; // Unterschied zwischen OLE und UNIX Epoche in Tagen static int64_t const offset_seconds = offset_days * seconds_per_day; int64_t const unix_time = static_cast<int64_t>(com_time * static_cast<double>(seconds_per_day)) - offset_seconds; return boost::posix_time::from_time_t(unix_time); // time_t better be 64 bit... }
Oder aber gleich nach FILETIME konvertieren (analog zu oben - Multiplizieren, Casten und Offset draufrechnen -> fertig), wenn der Code Windows-spezifisch ist.
-
Na da hab ich meine Unwissenheit aber wieder schön zur Schau gestellt
Mit deinem Code hats funktioniert. Vielen Dank.
-
Machst du die Formatierung jetzt mit der Boost, oder brauchst du die doch nicht?
-
Genau, ich sag einfach nur noch to_iso_extended_string(). Klappt auch wunderbar mit Zeitzonen und Sommerzeitumstellung