From be8dbffa0053e4fe942da6d11d753e7c7d6399d8 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Thu, 26 Mar 2009 10:17:19 +0000 Subject: [PATCH] better native date locale support git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59861 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/common/datetimefmt.cpp | 305 ++++++++++++++++++++++++++++++++++--- 1 file changed, 284 insertions(+), 21 deletions(-) diff --git a/src/common/datetimefmt.cpp b/src/common/datetimefmt.cpp index ece5bff59f..2b6dedbef0 100644 --- a/src/common/datetimefmt.cpp +++ b/src/common/datetimefmt.cpp @@ -1061,6 +1061,227 @@ static wxString GetLocaleDateFormat() #endif // __WINDOWS__ +#ifdef __WXOSX__ + +#include "wx/osx/private.h" + +// under OSX locale formats are defined using +// http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns +// +// so we need a translation function, bluntly copied from the windows +// version above and enhanced with the additional elements needed + +static wxString TranslateFromUnicodeFormat( const wxString& fmt) +{ + + wxString fmtWX; + + wxChar chLast = _T('\0'); + size_t lastCount = 0; + for ( const wxChar *p = fmt; /* NUL handled inside */; p++ ) + { + if ( *p == chLast ) + { + lastCount++; + continue; + } + + switch ( *p ) + { + // these characters come in groups, start counting them + case _T('d'): + case _T('M'): + case _T('y'): + case _T('g'): + case _T('h'): + case _T('H'): + case _T('m'): + case _T('s'): + chLast = *p; + lastCount = 1; + break; + + default: + // first deal with any special characters we have had + if ( lastCount ) + { + switch ( chLast ) + { + case _T('d'): + switch ( lastCount ) + { + case 1: // d + case 2: // dd + // these two are the same as we + // don't distinguish between 1 and + // 2 digits for days + fmtWX += _T("%d"); + break; + + case 3: // ddd + fmtWX += _T("%a"); + break; + + case 4: // dddd + fmtWX += _T("%A"); + break; + + default: + wxFAIL_MSG( _T("too many 'd's") ); + } + break; + + case _T('M'): + switch ( lastCount ) + { + case 1: // M + case 2: // MM + // as for 'd' and 'dd' above + fmtWX += _T("%m"); + break; + + case 3: + fmtWX += _T("%b"); + break; + + case 4: + fmtWX += _T("%B"); + break; + + default: + wxFAIL_MSG( _T("too many 'M's") ); + } + break; + + case _T('y'): + switch ( lastCount ) + { + case 1: // y + case 2: // yy + fmtWX += _T("%y"); + break; + + case 4: // yyyy + fmtWX += _T("%Y"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 'y's") ); + } + break; + + case _T('H'): + switch ( lastCount ) + { + case 1: // H + case 2: // HH + fmtWX += _T("%H"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 'H's") ); + } + break; + + case _T('h'): + switch ( lastCount ) + { + case 1: // h + case 2: // hh + fmtWX += _T("%h"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 'h's") ); + } + break; + + case _T('m'): + switch ( lastCount ) + { + case 1: // m + case 2: // mm + fmtWX += _T("%M"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 'm's") ); + } + break; + + case _T('s'): + switch ( lastCount ) + { + case 1: // s + case 2: // ss + fmtWX += _T("%S"); + break; + + default: + wxFAIL_MSG( _T("wrong number of 's's") ); + } + break; + + case _T('g'): + // strftime() doesn't have era string, + // ignore this format + wxASSERT_MSG( lastCount <= 2, + _T("too many 'g's") ); + break; + + default: + wxFAIL_MSG( _T("unreachable") ); + } + + chLast = _T('\0'); + lastCount = 0; + } + + // not a special character so must be just a separator, + // treat as is + if ( *p != _T('\0') ) + { + if ( *p == _T('%') ) + { + // this one needs to be escaped + fmtWX += _T('%'); + } + + fmtWX += *p; + } + } + + if ( *p == _T('\0') ) + break; + } + + return fmtWX; +} + +static wxString GetLocaleDateFormat() +{ + wxCFRef currentLocale( CFLocaleCopyCurrent() ); + + wxCFRef dateFormatter( CFDateFormatterCreate + (NULL, currentLocale, kCFDateFormatterShortStyle, kCFDateFormatterNoStyle)); + wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); + return TranslateFromUnicodeFormat(cfs.AsString()); +} + +static wxString GetLocaleFullDateFormat() +{ + wxCFRef currentLocale( CFLocaleCopyCurrent() ); + + wxCFRef dateFormatter( CFDateFormatterCreate + (NULL, currentLocale, kCFDateFormatterLongStyle, kCFDateFormatterMediumStyle)); + wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); + return TranslateFromUnicodeFormat(cfs.AsString()); +} + + + +#endif // __WXOSX__ + bool wxDateTime::ParseFormat(const wxString& date, const wxString& format, @@ -1222,21 +1443,43 @@ wxDateTime::ParseFormat(const wxString& date, else // strptime() failed; try generic heuristic code #endif // HAVE_STRPTIME { + Tm tm; +#ifdef __WXOSX__ + bool hasValidDate = false; + wxString fmtDate = GetLocaleFullDateFormat(); + if ( !fmtDate.empty() ) + { + const wxDateTime dt = ParseFormatAt + ( + input, + end, + fmtDate + ); + if ( dt.IsValid() ) + { + tm = dt.GetTm(); + hasValidDate = true; + } + } + + if ( !hasValidDate ) +#endif // __WXOSX__ + { + // try the format which corresponds to ctime() output + // first, then the generic date/time formats + const wxDateTime dt = ParseFormatAt + ( + input, + end, + wxS("%a %b %d %H:%M:%S %Y"), + wxS("%x %X"), + wxS("%X %x") + ); + if ( !dt.IsValid() ) + return false; + tm = dt.GetTm(); + } - // try the format which corresponds to ctime() output - // first, then the generic date/time formats - const wxDateTime dt = ParseFormatAt - ( - input, - end, - wxS("%a %b %d %H:%M:%S %Y"), - wxS("%x %X"), - wxS("%X %x") - ); - if ( !dt.IsValid() ) - return false; - - Tm tm = dt.GetTm(); hour = tm.hour; min = tm.min; @@ -1459,9 +1702,9 @@ wxDateTime::ParseFormat(const wxString& date, wxString fmtDate, fmtDateAlt; -#ifdef __WINDOWS__ +#if defined( __WINDOWS__ ) || defined( __WXOSX__ ) // The above doesn't work for all locales, try to query - // Windows for the right way of formatting the date: + // the OS for the right way of formatting the date: fmtDate = GetLocaleDateFormat(); if ( fmtDate.empty() ) #endif // __WINDOWS__ @@ -1471,21 +1714,41 @@ wxDateTime::ParseFormat(const wxString& date, { fmtDate = _T("%d/%m/%y"); fmtDateAlt = _T("%m/%d/%y"); - } + } else // assume USA { - fmtDate = _T("%m/%d/%y"); - fmtDateAlt = _T("%d/%m/%y"); + fmtDate = _T("%d/%m/%y"); + fmtDateAlt = _T("%m/%d/%y"); } } const wxDateTime dt = ParseFormatAt(input, end, fmtDate, fmtDateAlt); + Tm tm; + if ( !dt.IsValid() ) - return false; + { + wxString fmtDateLong = fmtDate; + wxString fmtDateLongAlt = fmtDateAlt; + - Tm tm = dt.GetTm(); + if ( !fmtDateLong.empty() ) + { + fmtDateLong.Replace("%y","%Y"); + fmtDateLongAlt.Replace("%y","%Y"); + const wxDateTime dtLong = ParseFormatAt(input, end, + fmtDateLong, fmtDateLongAlt); + if ( !dtLong.IsValid() ) + return false; + + tm = dtLong.GetTm(); + } + else + return false; + } + else + tm = dt.GetTm(); haveDay = haveMon =