Boost DateTime, Locales and Facets

Boost DateTime extends the standard library facets for formatting dates and times according to locale.

I have had some trouble with these libraries. It seems that there is something of a flaw in the design of the standard library locales. Following are some functions that look like they work but don’t, and one that work, but looks like it shouldn’t.

In the following C++ fragments, the required headers might include

//#include <string>
//#include <strstream>
//#include <ostream>
#include <boost/thread/xtime.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/local_time_adjustor.hpp>
#include <boost/date_time/c_local_time_adjustor.hpp>
#include <boost/date_time/date_facet.hpp>
#include <boost/date_time/time_facet.hpp>
//#include <boost/date_time/local_time/tz_database.hpp>
#include <clocale>

The following code is faulty:

std::string locale_formatted_datetime (::boost::posix_time::ptime pt)
{
  std::string s;
  std::ostringstream datetime_ss;
  ::boost::posix_time::time_facet time_output;
  std::locale special_locale (std::locale(""), & time_output); // wrong!
  datetime_ss.imbue (special_locale);
  time_output.format("%x %X"); // date time
  datetime_ss << pt;
  s = datetime_ss.str().c_str();
  return s;
} // CRASH!

this code is also wrong:

std::string locale_formatted_datetime (::boost::posix_time::ptime pt)
{
  std::string s;
  std::ostringstream datetime_ss;
  ::boost::posix_time::time_facet * p_time_output = new ::boost::posix_time::time_facet;
  std::locale special_locale (std::locale(""), p_time_output);
  datetime_ss.imbue (special_locale);
  (*p_time_output).format("%x %X"); // date time
  datetime_ss << pt;
  s = datetime_ss.str().c_str();
  if (p_time_output)
  {
    delete p_time_output; // wrong!
    p_time_output = NULL;
  }
  return s;
} // CRASH!

The problem in the above blocks of code is that the destructor for special_locale itself deletes the time facet. The second attempt to perform the deletion causes a crash as the associated memory is now deallocated, or being used for something else.

In the first version above, the time facet is a local variable on the stack, whose address is passed to the locale. The destructor for the facet is called as a result of the facet going out of scope, and the locale going out of scope too.

In the second version, the facet is allocated on the heap. Its destructor is called both explicitly by the delete, and by the locale going out of scope.

Here is the code that works:

std::string locale_formatted_datetime (::boost::posix_time::ptime pt)
{
  std::string s;
  std::ostringstream datetime_ss;
  ::boost::posix_time::time_facet * p_time_output = new ::boost::posix_time::time_facet;
  std::locale special_locale (std::locale(""), p_time_output);
    // special_locale takes ownership of the p_time_output facet
  datetime_ss.imbue (special_locale);
  (*p_time_output).format("%x %X"); // date time
  datetime_ss << pt;
  s = datetime_ss.str().c_str();
  // don't explicitly delete p_time_output
  return s;
}

Now, to me, it looks as though this code ought to have a memory leak, as there appears to be no delete to match the new. Explicitly checking this code for memory leak will show that it is clear of such problems. The delete is implicit in the destruction of special_locale. That is, std::locale takes ownership of the facet.

This, I think, is a design fault in std::locale: the locale should not take ownership of the facet, and should not have responsibility for its deletion.

As things stand, you must remember that if the facet is not used in the locale, then it must be deleted (otherwise there is a memory leak), but if it is used by the locale, then the deletion must be left to the locale. The locale has no way to indicate that it has performed the deletion, for example by nullifying the address, as the facet address is taken by value.

The same facet can’t be used by two locales, otherwise they will both attempt to perform the deletion, again resulting in a crash.

[This post is an update of a similar previous post.]

3 Responses to “Boost DateTime, Locales and Facets”

  1. Jason Says:

    Thanks for figuring this out… I thought I was going crazy!!

  2. artyom Says:

    This, I think, is a design fault in std::locale: the locale should not take ownership of the facet, and should not have responsibility for its deletion.

    No it is not, facets are designed to be owned by std::locale only, most std::xxx_facet have protected destructors by design.

    The same facet can’t be used by two locales, otherwise they will both attempt to perform the deletion, again resulting in a crash.

    It is not correct.

    Each class derived from std::locale::facet has
    built-in reference counters. It is shared between different std::locale
    objects so you can efficiently copy/assign and do anything you
    want wit std::locale objects in quite efficient way.

  3. petke Says:

    The first version is correct if you change it to ::boost::posix_time::time_facet time_output(1); //1 means no funny business with lifetimes and ownership

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.