Formatting Behavior in COleDateTime for MFC8






3.67/5 (10 votes)
Aug 3, 2006
2 min read

32361

153
A passable workaround for the COleDateTime formatting behavior in MFC8.
Introduction
Most of our apps use Oracle databases, and over the last 5 years, we've come to rely heavily on our COleDateTime
objects' ability to deal with a m_dt
value of 0.0 (12/30/1899). Long before I joined the team, I also relied on this. The COleDateTme
considered a value of 0.0 to be "valid", and it would happily format the date as expected. Until February of 2006, we used VC6, but then our team began using VS2005.
It seems that Microsoft changed the COleDateTime
class so that a m_dt
value of 0.0 was still considered "valid", but the class ASSERT
s if you attempt to format any date that falls on the back-side of 01/01/1900. This new behaviour instantly broke millions of lines of code for us (and that's no exaggeration).
How We Fixed It
As usual, we were under a time crunch to get our code converted, so we had to come up with something quick and dirty. We simply wrote a static function in one of our utility classes, and here it is. I made liberal use of the CString::Replace()
method. One thing (that strikes me as a bit weird) about this is that I had to use the CString
constructor to pass literal strings into the various CString
functions. I might have been missing a compiler definition in my test app or something that our converted projects had, but in any case, this should work with or without that weirdness. Without further delay, here's the code:
// This function assumes that the only text in the format string is, in fact, // formatting instructions for a COleDateTime. If Any other text is contained // in the string, the results will be fairly unpredictable, especially if that // format string contains numeric characters. Since our code always uses // something like "%Y/%m/%d %H:%M", this shouldn't be an issue. CString FormatOleDateTime(COleDateTime odt, LPCTSTR strFormatString) { CString strDateTime(""); CString sFormatString(strFormatString); if (odt.m_dt == 0.0) { // assumtion - we are free to do anything we want // to the date since m_dt is set to 0.0 // // consideration(s) - the user could potentially // embed the very numbers we're going to use // in our fake date, so we may want to account for them, // maybe via a table or some sort of map. // Since we're in a hurry, we'll // assume that this will never happen. :) // we require leading zeroes, so we have // to strip the # character from // the appropriate formatting commands... sFormatString.Replace(CString("%#d"), CString("%d")); sFormatString.Replace(CString("%#H"), CString("%H")); sFormatString.Replace(CString("%#I"), CString("%I")); sFormatString.Replace(CString("%#j"), CString("%j")); sFormatString.Replace(CString("%#m"), CString("%m")); sFormatString.Replace(CString("%#M"), CString("%M")); sFormatString.Replace(CString("%#S"), CString("%S")); sFormatString.Replace(CString("%#U"), CString("%U")); // I know, we're technically replacing this twice sFormatString.Replace(CString("%#w"), CString("%w")); sFormatString.Replace(CString("%#W"), CString("%W")); sFormatString.Replace(CString("%#y"), CString("%y")); sFormatString.Replace(CString("%#Y"), CString("%Y")); // the following formatting commands could potentially // screw up our carefully laid plans, so we'll eliminate them. // day of year sFormatString.Replace(CString("%j"), CString("")); // day of week (0=sunday) sFormatString.Replace(CString("%w"), CString("")); // these formatting commands need to be intercepted since they // have specific values associated with the date we're trying // to set (12/30/1899 00:00:00). // day of week - abbreviated sFormatString.Replace(CString("%a"), CString("Sat")); // day of week - fulls sFormatString.Replace(CString("%A"), CString("Saturday")); // am or pm sFormatString.Replace(CString("%p"), CString("PM")); // you'll see below why I set some fairly bizarre values here. odt.SetDateTime(1900, 12, 29, 23, 59, 59); strDateTime = odt.Format(sFormatString); // this one's easy... if (sFormatString.Find(CString("%Y")) >= 0 && strDateTime.Find(CString("1900")) >= 0) { strDateTime.Replace(CString("1900"), CString("1899")); } // if the programmer wants a 2-digit year, make a reasonable // attempt to cover the most typical formatting possibilities if (sFormatString.Find(CString("%y")) >= 0) { // my first thought was to make sure the substring I wanted to // replace was actually in the string, but that would have been // inefficient (two "find" operations, followed by the actual // replacement). So, I figured I'd just call replace() for all // of the possibilities strDateTime.Replace(CString("-00"), CString("-99")); strDateTime.Replace(CString("/00"), CString("/99")); strDateTime.Replace(CString(",00"), CString(",99")); strDateTime.Replace(CString(".00"), CString(".99")); // always do this one last strDateTime.Replace(CString("00"), CString("99")); } // now replace the hour (23 = 11pm) strDateTime.Replace(CString("23"), CString("00")); // the minutes and seconds strDateTime.Replace(CString("59"), CString("00")); // and finally the day of the month - we do // this last to account for // non-standard formatting possibilities strDateTime.Replace(CString("29"), CString("30")); } else { strDateTime = odt.Format(strFormatString); } return strDateTime; }
Upcoming VS2005 Service Pack
Microsoft claims there's a service pack on the way, and that they have done something to the COleDateTime
class as a direct result of complaints they've been receiving, but I haven't seen anywhere where they mentioned what they did. I guess we're going to have to wait and see, but until we get more info (and/or the service pack), this is a passable workaround.