Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Formatting Behavior in COleDateTime for MFC8

3.67/5 (10 votes)
3 Aug 20062 min read 1   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 ASSERTs 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here