Opened 5 years ago

Closed 5 years ago

#13159 closed Bugs (fixed)

Assert hit in wcsftime.cpp on MSVC due to tm_year out of range

Reported by: dstftw@… Owned by: James E. King, III
Milestone: Boost 1.67.0 Component: date_time
Version: Boost 1.63.0 Severity: Problem
Keywords: Cc:

Description

The issue is only reproducible on MSVC (toolsets tested v120, v140, v141) and caused by a limitation imposed on tm_year of tm to belong to the range [-1900; 8099] (see assert below).

Here is the sample code that reproduces the problem:

#include "stdafx.h"

#include <sstream>
#include <boost/date_time.hpp>

int main()
{
	std::ostringstream os;
	os.imbue(std::locale(std::locale::classic(), new boost::posix_time::time_facet("%Y")));
	os << boost::posix_time::ptime(boost::gregorian::date(10000, 1, 1));
	return 0;
}

Stack trace:

 	ucrtbased.dll!expand_time(__crt_locale_pointers * locale, wchar_t specifier, const tm * timeptr, wchar_t * * string, unsigned __int64 * left, const __crt_lc_time_data * lc_time, bool alternate_form) Line 971	C++
 	ucrtbased.dll!_Wcsftime_l(wchar_t * string, unsigned __int64 max_size, const wchar_t * format, const tm * timeptr, void * lc_time_arg, __crt_locale_pointers * locale) Line 1134	C++
 	ucrtbased.dll!_Strftime_l(char * const string, const unsigned __int64 maxsize, const char * const format, const tm * const timeptr, void * const lc_time_arg, __crt_locale_pointers * const locale) Line 169	C++
 	ucrtbased.dll!_Strftime(char * string, unsigned __int64 max_size, const char * format, const tm * timeptr, void * lc_time_arg) Line 197	C++
>	msvcp140d.dll!std::time_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char,std::char_traits<char> > _Dest, std::ios_base & __formal, char __formal, const tm * _Pt, char _Specifier, char _Modifier) Line 799	C++
 	msvcp140d.dll!std::time_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::put(std::ostreambuf_iterator<char,std::char_traits<char> > _Dest, std::ios_base & _Iosbase, char _Fill, const tm * _Pt, const char * _Fmtfirst, const char * _Fmtlast) Line 732	C++
 	DateTimePlayground.exe!boost::date_time::date_facet<boost::gregorian::date,char,std::ostreambuf_iterator<char,std::char_traits<char> > >::do_put_tm(std::ostreambuf_iterator<char,std::char_traits<char> > next, std::ios_base & a_ios, char fill_char, const tm & tm_value, std::basic_string<char,std::char_traits<char>,std::allocator<char> > a_format) Line 342	C++
 	DateTimePlayground.exe!boost::date_time::time_facet<boost::posix_time::ptime,char,std::ostreambuf_iterator<char,std::char_traits<char> > >::put(std::ostreambuf_iterator<char,std::char_traits<char> > next_arg, std::ios_base & ios_arg, char fill_arg, const boost::posix_time::ptime & time_arg) Line 427	C++
 	DateTimePlayground.exe!boost::posix_time::operator<<<char,std::char_traits<char> >(std::basic_ostream<char,std::char_traits<char> > & os, const boost::posix_time::ptime & p) Line 52	C++
 	DateTimePlayground.exe!main() Line 11	C++
 	[External Code]	

Assert:

Debug Assertion Failed!

Program: ...\Projects\DateTimePlayground\x64\Debug\DateTimePlayground.exe
File: minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp
Line: 971

Expression: timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

Any date with year 10000 will hit this assert. Starting with year 10001 boost::gregorian::bad_year is thrown instead.

Such behavior is caused by max year (include/boost/date_time/gregorian/greg_year.hpp#L28) that allows year 10000. This causes tm_year of tm to be 8100 out of range [-1900; 8099] when converting ptime to tm (include/boost/date_time/time_facet.hpp#L428).

I would expect consistent boost::gregorian::bad_year thrown in all cases without crashes.

One possible solution is to consider changing max year to 9999. This would also fix #12630 and make %Y format specifier to be more consistent with its definition (xmldoc/format_flags.xml#L220) - a Four digit year. Currently, the following code on Linux outputs 10000 instead of throwing boost::gregorian::bad_year that is at least confusing since 10000 does not look like a four digit number as promised by %Y:

#include <sstream>
#include <boost/date_time.hpp>

int main()
{
	std::ostringstream os;
	os.imbue(std::locale(std::locale::classic(), new boost::posix_time::time_facet("%Y")));
	os << boost::posix_time::ptime(boost::gregorian::date(10000, 1, 1));
	std::cout << os.str();
	return 0;
}

Change History (7)

comment:1 by anonymous, 5 years ago

Summary: Assert hit in wcsftime.cpp on Windows due to tm_year out of rangeAssert hit in wcsftime.cpp on MSVC due to tm_year out of range

comment:2 by anonymous, 5 years ago

Component: Nonedate_time
Owner: set to az_sw_dude
Version: Boost 1.63.0Boost Development Trunk

comment:3 by James E. King, III, 5 years ago

Owner: changed from az_sw_dude to James E. King, III
Status: newassigned

comment:4 by James E. King, III, 5 years ago

Version: Boost Development TrunkBoost 1.63.0

Moving original version found back to 1.63.0. Not saying older versions don't do this, but it's better than "development trunk".

comment:6 by James E. King, III, 5 years ago

Milestone: To Be DeterminedBoost 1.67.0

comment:7 by James E. King, III, 5 years ago

Resolution: fixed
Status: assignedclosed

Fix merged to master; resolved.

Note: See TracTickets for help on using tickets.