better native date locale support
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59861 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1061,6 +1061,227 @@ static wxString GetLocaleDateFormat()
|
|||||||
|
|
||||||
#endif // __WINDOWS__
|
#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<CFLocaleRef> currentLocale( CFLocaleCopyCurrent() );
|
||||||
|
|
||||||
|
wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
|
||||||
|
(NULL, currentLocale, kCFDateFormatterShortStyle, kCFDateFormatterNoStyle));
|
||||||
|
wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
|
||||||
|
return TranslateFromUnicodeFormat(cfs.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
static wxString GetLocaleFullDateFormat()
|
||||||
|
{
|
||||||
|
wxCFRef<CFLocaleRef> currentLocale( CFLocaleCopyCurrent() );
|
||||||
|
|
||||||
|
wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
|
||||||
|
(NULL, currentLocale, kCFDateFormatterLongStyle, kCFDateFormatterMediumStyle));
|
||||||
|
wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
|
||||||
|
return TranslateFromUnicodeFormat(cfs.AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __WXOSX__
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wxDateTime::ParseFormat(const wxString& date,
|
wxDateTime::ParseFormat(const wxString& date,
|
||||||
const wxString& format,
|
const wxString& format,
|
||||||
@@ -1222,21 +1443,43 @@ wxDateTime::ParseFormat(const wxString& date,
|
|||||||
else // strptime() failed; try generic heuristic code
|
else // strptime() failed; try generic heuristic code
|
||||||
#endif // HAVE_STRPTIME
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// try the format which corresponds to ctime() output
|
if ( !hasValidDate )
|
||||||
// first, then the generic date/time formats
|
#endif // __WXOSX__
|
||||||
const wxDateTime dt = ParseFormatAt
|
{
|
||||||
(
|
// try the format which corresponds to ctime() output
|
||||||
input,
|
// first, then the generic date/time formats
|
||||||
end,
|
const wxDateTime dt = ParseFormatAt
|
||||||
wxS("%a %b %d %H:%M:%S %Y"),
|
(
|
||||||
wxS("%x %X"),
|
input,
|
||||||
wxS("%X %x")
|
end,
|
||||||
);
|
wxS("%a %b %d %H:%M:%S %Y"),
|
||||||
if ( !dt.IsValid() )
|
wxS("%x %X"),
|
||||||
return false;
|
wxS("%X %x")
|
||||||
|
);
|
||||||
|
if ( !dt.IsValid() )
|
||||||
|
return false;
|
||||||
|
tm = dt.GetTm();
|
||||||
|
}
|
||||||
|
|
||||||
Tm tm = dt.GetTm();
|
|
||||||
|
|
||||||
hour = tm.hour;
|
hour = tm.hour;
|
||||||
min = tm.min;
|
min = tm.min;
|
||||||
@@ -1459,9 +1702,9 @@ wxDateTime::ParseFormat(const wxString& date,
|
|||||||
wxString fmtDate,
|
wxString fmtDate,
|
||||||
fmtDateAlt;
|
fmtDateAlt;
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#if defined( __WINDOWS__ ) || defined( __WXOSX__ )
|
||||||
// The above doesn't work for all locales, try to query
|
// 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();
|
fmtDate = GetLocaleDateFormat();
|
||||||
if ( fmtDate.empty() )
|
if ( fmtDate.empty() )
|
||||||
#endif // __WINDOWS__
|
#endif // __WINDOWS__
|
||||||
@@ -1471,21 +1714,41 @@ wxDateTime::ParseFormat(const wxString& date,
|
|||||||
{
|
{
|
||||||
fmtDate = _T("%d/%m/%y");
|
fmtDate = _T("%d/%m/%y");
|
||||||
fmtDateAlt = _T("%m/%d/%y");
|
fmtDateAlt = _T("%m/%d/%y");
|
||||||
}
|
}
|
||||||
else // assume USA
|
else // assume USA
|
||||||
{
|
{
|
||||||
fmtDate = _T("%m/%d/%y");
|
fmtDate = _T("%d/%m/%y");
|
||||||
fmtDateAlt = _T("%d/%m/%y");
|
fmtDateAlt = _T("%m/%d/%y");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxDateTime
|
const wxDateTime
|
||||||
dt = ParseFormatAt(input, end,
|
dt = ParseFormatAt(input, end,
|
||||||
fmtDate, fmtDateAlt);
|
fmtDate, fmtDateAlt);
|
||||||
if ( !dt.IsValid() )
|
Tm tm;
|
||||||
return false;
|
|
||||||
|
|
||||||
Tm tm = dt.GetTm();
|
if ( !dt.IsValid() )
|
||||||
|
{
|
||||||
|
wxString fmtDateLong = fmtDate;
|
||||||
|
wxString fmtDateLongAlt = fmtDateAlt;
|
||||||
|
|
||||||
|
|
||||||
|
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 =
|
haveDay =
|
||||||
haveMon =
|
haveMon =
|
||||||
|
Reference in New Issue
Block a user