From adbc9863b227146a0bdb35a5b85d3f030f2547c0 Mon Sep 17 00:00:00 2001 From: Dimitri Schoolwerth Date: Thu, 9 Apr 2015 04:39:35 +0400 Subject: [PATCH] Handle quotes in wxTranslateFromUnicodeFormat. When parsing Unicode date formats text inside single quotes should not be escaped and instead treated as literal text. In addition two single quotes (either inside or outside quoted text) should be interpreted as a single quote. Fixes #16118. --- src/common/intl.cpp | 77 +++++++++++++++++++++++++++++---- tests/datetime/datetimetest.cpp | 35 +++++++++++++++ 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 3f46ff1bb6..9ae59c0bd8 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -1135,6 +1135,22 @@ wxString wxLocale::GetHeaderValue(const wxString& header, namespace { +bool IsAtTwoSingleQuotes(const wxString& fmt, wxString::const_iterator p) +{ + if ( p != fmt.end() && *p == '\'') + { + ++p; + if ( p != fmt.end() && *p == '\'') + { + return true; + } + } + + return false; +} + +} // anonymous namespace + // This function translates from Unicode date formats described at // // http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns @@ -1142,7 +1158,7 @@ namespace // to strftime()-like syntax. This translation is not lossless but we try to do // our best. -static wxString TranslateFromUnicodeFormat(const wxString& fmt) +wxString wxTranslateFromUnicodeFormat(const wxString& fmt) { wxString fmtWX; fmtWX.reserve(fmt.length()); @@ -1385,20 +1401,63 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt) if ( p == fmt.end() ) break; - // not a special character so must be just a separator, treat as is - if ( *p == wxT('%') ) + /* + Handle single quotes: + "Two single quotes represents [sic] a literal single quote, either + inside or outside single quotes. Text within single quotes is not + interpreted in any way (except for two adjacent single quotes)." + */ + + if ( IsAtTwoSingleQuotes(fmt, p) ) { - // this one needs to be escaped - fmtWX += wxT('%'); + fmtWX += '\''; + ++p; // the 2nd single quote is skipped by the for loop's increment + continue; } - fmtWX += *p; + bool isEndQuote = false; + if ( *p == '\'' ) + { + ++p; + while ( p != fmt.end() ) + { + if ( IsAtTwoSingleQuotes(fmt, p) ) + { + fmtWX += '\''; + p += 2; + continue; + } + + if ( *p == '\'' ) + { + isEndQuote = true; + break; + } + + fmtWX += *p; + ++p; + } + } + + if ( p == fmt.end() ) + break; + + if ( !isEndQuote ) + { + // not a special character so must be just a separator, treat as is + if ( *p == wxT('%') ) + { + // this one needs to be escaped + fmtWX += wxT('%'); + } + + fmtWX += *p; + } } return fmtWX; } -} // anonymous namespace #endif // __WINDOWS__ || __WXOSX__ @@ -1476,7 +1535,7 @@ GetInfoFromLCID(LCID lcid, if ( ::GetLocaleInfo(lcid, GetLCTYPEFormatFromLocalInfo(index), buf, WXSIZEOF(buf)) ) { - return TranslateFromUnicodeFormat(buf); + return wxTranslateFromUnicodeFormat(buf); } break; @@ -1630,7 +1689,7 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat)) wxCFRef dateFormatter( CFDateFormatterCreate (NULL, userLocaleRef, dateStyle, timeStyle)); wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter )); - wxString format = TranslateFromUnicodeFormat(cfs.AsString()); + wxString format = wxTranslateFromUnicodeFormat(cfs.AsString()); // we always want full years format.Replace("%y","%Y"); return format; diff --git a/tests/datetime/datetimetest.cpp b/tests/datetime/datetimetest.cpp index 7005e81fbd..08ac272282 100644 --- a/tests/datetime/datetimetest.cpp +++ b/tests/datetime/datetimetest.cpp @@ -232,6 +232,7 @@ private: CPPUNIT_TEST( TestTimeArithmetics ); CPPUNIT_TEST( TestDSTBug ); CPPUNIT_TEST( TestDateOnly ); + CPPUNIT_TEST( TestTranslateFromUnicodeFormat ); CPPUNIT_TEST_SUITE_END(); void TestLeapYears(); @@ -252,6 +253,7 @@ private: void TestTimeArithmetics(); void TestDSTBug(); void TestDateOnly(); + void TestTranslateFromUnicodeFormat(); DECLARE_NO_COPY_CLASS(DateTimeTestCase) }; @@ -1447,4 +1449,37 @@ void DateTimeTestCase::TestDateOnly() CPPUNIT_ASSERT_EQUAL( wxDateTime::Today(), wxDateTime::Now().GetDateOnly() ); } +// Forward declaration +wxString wxTranslateFromUnicodeFormat(const wxString& fmt); + +void DateTimeTestCase::TestTranslateFromUnicodeFormat() +{ + // Test single quote handling... + + CPPUNIT_ASSERT_EQUAL("", wxTranslateFromUnicodeFormat("'")); + CPPUNIT_ASSERT_EQUAL("%H", wxTranslateFromUnicodeFormat("H'")); + CPPUNIT_ASSERT_EQUAL("H", wxTranslateFromUnicodeFormat("'H")); + + CPPUNIT_ASSERT_EQUAL("'", wxTranslateFromUnicodeFormat("''")); + CPPUNIT_ASSERT_EQUAL("%H'", wxTranslateFromUnicodeFormat("H''")); + CPPUNIT_ASSERT_EQUAL("H", wxTranslateFromUnicodeFormat("'H'")); + CPPUNIT_ASSERT_EQUAL("'%H", wxTranslateFromUnicodeFormat("''H")); + + CPPUNIT_ASSERT_EQUAL("'", wxTranslateFromUnicodeFormat("'''")); + CPPUNIT_ASSERT_EQUAL("%H'", wxTranslateFromUnicodeFormat("H'''")); + CPPUNIT_ASSERT_EQUAL("H'", wxTranslateFromUnicodeFormat("'H''")); + CPPUNIT_ASSERT_EQUAL("'%H", wxTranslateFromUnicodeFormat("''H'")); + CPPUNIT_ASSERT_EQUAL("'H", wxTranslateFromUnicodeFormat("'''H")); + + CPPUNIT_ASSERT_EQUAL("''", wxTranslateFromUnicodeFormat("''''")); + CPPUNIT_ASSERT_EQUAL("%H''", wxTranslateFromUnicodeFormat("H''''")); + CPPUNIT_ASSERT_EQUAL("H'", wxTranslateFromUnicodeFormat("'H'''")); + CPPUNIT_ASSERT_EQUAL("'%H'", wxTranslateFromUnicodeFormat("''H''")); + CPPUNIT_ASSERT_EQUAL("'H", wxTranslateFromUnicodeFormat("'''H'")); + CPPUNIT_ASSERT_EQUAL("''%H", wxTranslateFromUnicodeFormat("''''H")); + + CPPUNIT_ASSERT_EQUAL("'%H o'clock: It's about time'", + wxTranslateFromUnicodeFormat("''H 'o''clock: It''s about time'''")); +} + #endif // wxUSE_DATETIME