f7b64dc
Implement customization of date and time format with new FF91+ preferences
f7b64dc
"intl.date_time.pattern_override.{date,time}_{short.medium,long,full}", plus
f7b64dc
"intl.date_time.pattern_override.connector_short".
f7b64dc
f7b64dc
See user guide at https://support.mozilla.org/en-US/kb/customize-date-time-formats-thunderbird
f7b64dc
f7b64dc
As usual, a lot of additional things were changed during the implementation of this feature.
f7b64dc
Originally, it is bug 1426907 (5 commits) plus 1674212 plus 1706318. As prerequisite, bug 1669573
f7b64dc
(6 commits) plus 1671532 plus 1671695 plus 1681251 are needed (and then two commits of 1672548
f7b64dc
to return back a feature which was broken by 1669573-3).
f7b64dc
f7b64dc
Fortunately, the actually needed changes can be safely applied in a minimal form:
f7b64dc
f7b64dc
 - get 1426907 commit 5 (https://bugzilla.mozilla.org/show_bug.cgi?id=1426907#c179)
f7b64dc
 - drop observer/callback stuff for simplicity (the cost is user have to restart SM
f7b64dc
to take changes effect), drop tests
f7b64dc
 - add 1674212, drop tests
f7b64dc
 - add 1706318 (it just renames pref from 1674212)
f7b64dc
 - in the changes added, revert back from "CString" to "String" (since we do not apply 1426907 part 4).
f7b64dc
f7b64dc
This patch is against SeaMonkey 2.53.8.1 tree. 
f7b64dc
f7b64dc
f7b64dc
--- mozilla/intl/locale/OSPreferences.cpp.orig	2020-10-20 22:17:58.000000000 +0300
f7b64dc
+++ mozilla/intl/locale/OSPreferences.cpp	2021-08-23 21:32:05.586135214 +0300
f7b64dc
@@ -226,16 +226,136 @@ OSPreferences::GetDateTimeSkeletonForSty
f7b64dc
     return false;
f7b64dc
   }
f7b64dc
 
f7b64dc
   aRetVal.Assign((const char16_t*)skeleton, skelsize);
f7b64dc
   return true;
f7b64dc
 }
f7b64dc
 
f7b64dc
 /**
f7b64dc
+ * This method checks for preferences that override the defaults
f7b64dc
+ */
f7b64dc
+bool OSPreferences::OverrideDateTimePattern(DateTimeFormatStyle aDateStyle,
f7b64dc
+                                            DateTimeFormatStyle aTimeStyle,
f7b64dc
+                                            const nsACString& aLocale,
f7b64dc
+                                            nsAString& aRetVal) {
f7b64dc
+  const auto PrefToMaybeString = [](const char* pref) -> Maybe<nsAutoString> {
f7b64dc
+    nsAutoString value;
f7b64dc
+    nsresult nr = Preferences::GetString(pref, value);
f7b64dc
+    if (NS_FAILED(nr) || value.IsEmpty()) {
f7b64dc
+      return Nothing();
f7b64dc
+    }
f7b64dc
+    return Some(std::move(value));
f7b64dc
+  };
f7b64dc
+
f7b64dc
+  Maybe<nsAutoString> timeSkeleton;
f7b64dc
+  switch (aTimeStyle) {
f7b64dc
+    case DateTimeFormatStyle::Short:
f7b64dc
+      timeSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.time_short");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Medium:
f7b64dc
+      timeSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.time_medium");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Long:
f7b64dc
+      timeSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.time_long");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Full:
f7b64dc
+      timeSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.time_full");
f7b64dc
+      break;
f7b64dc
+    default:
f7b64dc
+      break;
f7b64dc
+  }
f7b64dc
+
f7b64dc
+  Maybe<nsAutoString> dateSkeleton;
f7b64dc
+  switch (aDateStyle) {
f7b64dc
+    case DateTimeFormatStyle::Short:
f7b64dc
+      dateSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.date_short");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Medium:
f7b64dc
+      dateSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.date_medium");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Long:
f7b64dc
+      dateSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.date_long");
f7b64dc
+      break;
f7b64dc
+    case DateTimeFormatStyle::Full:
f7b64dc
+      dateSkeleton =
f7b64dc
+          PrefToMaybeString("intl.date_time.pattern_override.date_full");
f7b64dc
+      break;
f7b64dc
+    default:
f7b64dc
+      break;
f7b64dc
+  }
f7b64dc
+
f7b64dc
+  nsAutoCString locale;
f7b64dc
+  if (aLocale.IsEmpty()) {
f7b64dc
+    AutoTArray<nsCString, 10> regionalPrefsLocales;
f7b64dc
+    LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
f7b64dc
+    locale.Assign(regionalPrefsLocales[0]);
f7b64dc
+  } else {
f7b64dc
+    locale.Assign(aLocale);
f7b64dc
+  }
f7b64dc
+
f7b64dc
+  const auto FillConnectorPattern = [&locale](
f7b64dc
+                                        const nsAutoString& datePattern,
f7b64dc
+                                        const nsAutoString& timePattern) {
f7b64dc
+    nsAutoString pattern;
f7b64dc
+    GetDateTimeConnectorPattern(nsDependentCString(locale.get()), pattern);
f7b64dc
+    int32_t index = pattern.Find("{1}");
f7b64dc
+    if (index != kNotFound) {
f7b64dc
+      pattern.Replace(index, 3, datePattern);
f7b64dc
+    }
f7b64dc
+    index = pattern.Find("{0}");
f7b64dc
+    if (index != kNotFound) {
f7b64dc
+      pattern.Replace(index, 3, timePattern);
f7b64dc
+    }
f7b64dc
+    return pattern;
f7b64dc
+  };
f7b64dc
+
f7b64dc
+  if (timeSkeleton && dateSkeleton) {
f7b64dc
+    aRetVal.Assign(FillConnectorPattern(*dateSkeleton, *timeSkeleton));
f7b64dc
+  } else if (timeSkeleton) {
f7b64dc
+    if (aDateStyle != DateTimeFormatStyle::None) {
f7b64dc
+      nsAutoString pattern;
f7b64dc
+      if (!ReadDateTimePattern(aDateStyle, DateTimeFormatStyle::None, aLocale,
f7b64dc
+                               pattern) &&
f7b64dc
+          !GetDateTimePatternForStyle(aDateStyle, DateTimeFormatStyle::None,
f7b64dc
+                                      aLocale, pattern)) {
f7b64dc
+        return false;
f7b64dc
+      }
f7b64dc
+      aRetVal.Assign(FillConnectorPattern(pattern, *timeSkeleton));
f7b64dc
+    } else {
f7b64dc
+      aRetVal.Assign(*timeSkeleton);
f7b64dc
+    }
f7b64dc
+  } else if (dateSkeleton) {
f7b64dc
+    if (aTimeStyle != DateTimeFormatStyle::None) {
f7b64dc
+      nsAutoString pattern;
f7b64dc
+      if (!ReadDateTimePattern(DateTimeFormatStyle::None, aTimeStyle, aLocale,
f7b64dc
+                               pattern) &&
f7b64dc
+          !GetDateTimePatternForStyle(DateTimeFormatStyle::None, aTimeStyle,
f7b64dc
+                                      aLocale, pattern)) {
f7b64dc
+        return false;
f7b64dc
+      }
f7b64dc
+      aRetVal.Assign(FillConnectorPattern(*dateSkeleton, pattern));
f7b64dc
+    } else {
f7b64dc
+      aRetVal.Assign(*dateSkeleton);
f7b64dc
+    }
f7b64dc
+  } else {
f7b64dc
+    return false;
f7b64dc
+  }
f7b64dc
+
f7b64dc
+  return true;
f7b64dc
+}
f7b64dc
+
f7b64dc
+/**
f7b64dc
  * This function is a counterpart to GetDateTimeSkeletonForStyle.
f7b64dc
  *
f7b64dc
  * It takes a skeleton and returns the best available pattern for a given locale
f7b64dc
  * that represents the provided skeleton.
f7b64dc
  *
f7b64dc
  * For example:
f7b64dc
  * "Hm" skeleton for "en-US" will return "H:m"
f7b64dc
  */
f7b64dc
@@ -275,16 +395,27 @@ OSPreferences::GetPatternForSkeleton(con
f7b64dc
  *
f7b64dc
  * An example output is "{1}, {0}".
f7b64dc
  */
f7b64dc
 bool
f7b64dc
 OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale,
f7b64dc
                                            nsAString& aRetVal)
f7b64dc
 {
f7b64dc
   bool result = false;
f7b64dc
+
f7b64dc
+  // Check for a valid override pref and use that if present.
f7b64dc
+  nsAutoString value;
f7b64dc
+  nsresult nr = Preferences::GetString(
f7b64dc
+      "intl.date_time.pattern_override.connector_short", value);
f7b64dc
+  if (NS_SUCCEEDED(nr) && value.Find("{0}") != kNotFound &&
f7b64dc
+      value.Find("{1}") != kNotFound) {
f7b64dc
+    aRetVal = std::move(value);
f7b64dc
+    return true;
f7b64dc
+  }
f7b64dc
+
f7b64dc
   UErrorCode status = U_ZERO_ERROR;
f7b64dc
   UDateTimePatternGenerator* pg = udatpg_open(PromiseFlatCString(aLocale).get(), &status);
f7b64dc
   if (U_SUCCESS(status)) {
f7b64dc
     int32_t resultSize;
f7b64dc
     const UChar* value = udatpg_getDateTimeFormat(pg, &resultSize);
f7b64dc
     MOZ_ASSERT(resultSize >= 0);
f7b64dc
 
f7b64dc
     aRetVal.Assign((char16_t*)value, resultSize);
f7b64dc
@@ -409,19 +540,21 @@ OSPreferences::GetDateTimePattern(int32_
f7b64dc
   key.AppendInt(aTimeFormatStyle);
f7b64dc
 
f7b64dc
   nsString pattern;
f7b64dc
   if (mPatternCache.Get(key, &pattern)) {
f7b64dc
     aRetVal = pattern;
f7b64dc
     return NS_OK;
f7b64dc
   }
f7b64dc
 
f7b64dc
-  if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
f7b64dc
-    if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, pattern)) {
f7b64dc
-      return NS_ERROR_FAILURE;
f7b64dc
+  if (!OverrideDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
f7b64dc
+    if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
f7b64dc
+      if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, pattern)) {
f7b64dc
+        return NS_ERROR_FAILURE;
f7b64dc
+      }
f7b64dc
     }
f7b64dc
   }
f7b64dc
 
f7b64dc
   if (mPatternCache.Count() == kMaxCachedPatterns) {
f7b64dc
     // Don't allow unlimited cache growth; just throw it away in the case of
f7b64dc
     // pathological behavior where a page keeps requesting different formats
f7b64dc
     // and locales.
f7b64dc
     NS_WARNING("flushing DateTimePattern cache");
f7b64dc
--- mozilla/intl/locale/OSPreferences.h.orig	2020-10-20 22:17:58.000000000 +0300
f7b64dc
+++ mozilla/intl/locale/OSPreferences.h	2021-08-23 22:07:02.638953273 +0300
f7b64dc
@@ -163,16 +163,21 @@ private:
f7b64dc
                                   const nsACString& aLocale,
f7b64dc
                                   nsAString& aRetVal);
f7b64dc
 
f7b64dc
   bool GetDateTimeSkeletonForStyle(DateTimeFormatStyle aDateStyle,
f7b64dc
                                    DateTimeFormatStyle aTimeStyle,
f7b64dc
                                    const nsACString& aLocale,
f7b64dc
                                    nsAString& aRetVal);
f7b64dc
 
f7b64dc
+  bool OverrideDateTimePattern(DateTimeFormatStyle aDateStyle,
f7b64dc
+                               DateTimeFormatStyle aTimeStyle,
f7b64dc
+                               const nsACString& aLocale,
f7b64dc
+                               nsAString& aRetVal);
f7b64dc
+
f7b64dc
   bool GetPatternForSkeleton(const nsAString& aSkeleton,
f7b64dc
                              const nsACString& aLocale,
f7b64dc
                              nsAString& aRetVal);
f7b64dc
 
f7b64dc
   /**
f7b64dc
    * This is a host environment specific method that will be implemented
f7b64dc
    * separately for each platform.
f7b64dc
    *