guix_mirror_bot pushed a commit to branch master
in repository guix.

commit fee676c7285c00128d5cebfd64bab761314146bf
Author: Ian Eure <[email protected]>
AuthorDate: Tue Jan 13 12:37:45 2026 -0800

    gnu: Add icu4c-78.
    
    * gnu/packages/icu4c.scm (icu4c-78): New variable.
    * gnu/local.mk (dist_patch_DATA): Add icu4c patches.
    * gnu/packages/patches/icu4c-78-double-conversion.patch: New file.
    * gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch: New file.
    * 
gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch:
 New file.
    * 
gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch: 
New file.
    * gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch: New 
file.
    * gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch: New 
file.
    * 
gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch:
 New file.
    * gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch: 
New file.
    * gnu/packages/patches/icu4c-suppress-warnings.patch: New file.
---
 gnu/local.mk                                       |  25 +-
 gnu/packages/icu4c.scm                             |  27 +
 .../patches/icu4c-78-double-conversion.patch       | 198 +++++
 .../icu4c-bug-1706949-wasi-workaround.patch        | 832 +++++++++++++++++++++
 ...90071-ICU-22132-standardize-vtzone-output.patch |  28 +
 ...g-1856290-ICU-20548-dateinterval-timezone.patch | 163 ++++
 ...icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch |  69 ++
 .../icu4c-bug-1972781-chinese-based-calendar.patch |  25 +
 ...-ICU-23264-increase-measure-unit-capacity.patch |  27 +
 ...c-bug-2002735-ICU-23277-coptic-single-era.patch | 109 +++
 gnu/packages/patches/icu4c-suppress-warnings.patch |  80 ++
 11 files changed, 1575 insertions(+), 8 deletions(-)

diff --git a/gnu/local.mk b/gnu/local.mk
index bbf13bf614..e2498d58f0 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1610,15 +1610,24 @@ dist_patch_DATA =                                       
        \
   %D%/packages/patches/icedove-observer-fix.patch               \
   %D%/packages/patches/icedtea-7-hotspot-aarch64-use-c++98.patch       \
   %D%/packages/patches/icedtea-7-hotspot-pointer-comparison.patch      \
-  %D%/packages/patches/icu4c-icu-22132-fix-vtimezone.patch     \
-  %D%/packages/patches/icu4c-20548-dateinterval-timezone.patch  \
-  %D%/packages/patches/icu4c-22132-standardize-vtzone-output.patch      \
-  %D%/packages/patches/icu4c-23069-rosh-hashanah-postponement.patch     \
-  %D%/packages/patches/icu4c-dayperiod-fractional-seconds.patch \
-  %D%/packages/patches/icu4c-double-conversion.patch            \
-  %D%/packages/patches/icu4c-dtitvfmt-adopt-calendar.patch      \
+  %D%/packages/patches/icu4c-20548-dateinterval-timezone.patch \
+  %D%/packages/patches/icu4c-22132-standardize-vtzone-output.patch     \
+  %D%/packages/patches/icu4c-23069-rosh-hashanah-postponement.patch    \
+  %D%/packages/patches/icu4c-78-double-conversion.patch                \
+  %D%/packages/patches/icu4c-bug-1706949-wasi-workaround.patch \
+  
%D%/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch
     \
+  %D%/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch 
\
+  %D%/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch \
+  %D%/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch  \
+  
%D%/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch
        \
+  %D%/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch     
\
+  %D%/packages/patches/icu4c-dayperiod-fractional-seconds.patch        \
+  %D%/packages/patches/icu4c-double-conversion.patch           \
+  %D%/packages/patches/icu4c-dtitvfmt-adopt-calendar.patch     \
   %D%/packages/patches/icu4c-fix-TestHebrewCalendarInTemporalLeapYear.patch    
\
-  %D%/packages/patches/icu4c-wasi-workaround.patch              \
+  %D%/packages/patches/icu4c-icu-22132-fix-vtimezone.patch     \
+  %D%/packages/patches/icu4c-suppress-warnings.patch           \
+  %D%/packages/patches/icu4c-wasi-workaround.patch             \
   %D%/packages/patches/id3lib-CVE-2007-4460.patch                      \
   %D%/packages/patches/id3lib-UTF16-writing-bug.patch                  \
   %D%/packages/patches/ilmbase-fix-tests.patch                 \
diff --git a/gnu/packages/icu4c.scm b/gnu/packages/icu4c.scm
index 492812cc12..eaf1bd1780 100644
--- a/gnu/packages/icu4c.scm
+++ b/gnu/packages/icu4c.scm
@@ -52,6 +52,12 @@
    (string-map (lambda (x) (if (char=? x #\.) #\_ x)) version)
    "-src.tgz"))
 
+;; The URI format has changed starting with 78.1.
+(define (icu4c-uri->=78 version)
+  (string-append
+   "https://github.com/unicode-org/icu/releases/download/release-";
+   version "/icu4c-" version "-sources.tgz"))
+
 (define-public icu4c-73
   (package
     (name "icu4c")
@@ -201,6 +207,27 @@ C/C++ part.")
          "icu4c-dtitvfmt-adopt-calendar.patch"
          "icu4c-wasi-workaround.patch"))))))
 
+(define-public icu4c-78
+  (package
+    (inherit icu4c-77)
+    (name "icu4c")
+    (version "78.2")
+    (source
+     (origin
+       (inherit (package-source icu4c-77))
+       (uri (icu4c-uri->=78 version))
+       (sha256 (base32 "0dfzi4yf0wmng1866y2yd22cj1lrnzmx5qihjqh4npa3bixni69y"))
+       (patches
+        (search-patches
+         "icu4c-bug-1706949-wasi-workaround.patch"
+         "icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch"
+         "icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch"
+         "icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch"
+         "icu4c-bug-1972781-chinese-based-calendar.patch"
+         "icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch"
+         "icu4c-78-double-conversion.patch"
+         "icu4c-suppress-warnings.patch"))))))
+
 (define-public icu4c-build-root
   (package
     (inherit icu4c)
diff --git a/gnu/packages/patches/icu4c-78-double-conversion.patch 
b/gnu/packages/patches/icu4c-78-double-conversion.patch
new file mode 100644
index 0000000000..c4c54ec97b
--- /dev/null
+++ b/gnu/packages/patches/icu4c-78-double-conversion.patch
@@ -0,0 +1,198 @@
+diff --git a/source/i18n/measunit_extra.cpp b/source/i18n/measunit_extra.cpp
+--- a/source/i18n/measunit_extra.cpp
++++ b/source/i18n/measunit_extra.cpp
+@@ -10,17 +10,21 @@
+ 
+ // Allow implicit conversion from char16_t* to UnicodeString for this file:
+ // Helpful in toString methods and elsewhere.
+ #define UNISTR_FROM_STRING_EXPLICIT
+ 
+ #include "charstr.h"
+ #include "cmemory.h"
+ #include "cstring.h"
++#ifdef JS_HAS_INTL_API
++#include "double-conversion/string-to-double.h"
++#else
+ #include "double-conversion-string-to-double.h"
++#endif
+ #include "measunit_impl.h"
+ #include "resource.h"
+ #include "uarrsort.h"
+ #include "uassert.h"
+ #include "ucln_in.h"
+ #include "umutex.h"
+ #include "unicode/bytestrie.h"
+ #include "unicode/bytestriebuilder.h"
+@@ -33,17 +37,21 @@
+ #include "util.h"
+ #include <limits.h>
+ #include <cstdlib>
+ U_NAMESPACE_BEGIN
+ 
+ 
+ namespace {
+ 
++#ifdef JS_HAS_INTL_API
++using double_conversion::StringToDoubleConverter;
++#else
+ using icu::double_conversion::StringToDoubleConverter;
++#endif
+ 
+ // TODO: Propose a new error code for this?
+ constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
+ 
+ // Trie value offset for SI or binary prefixes. This is big enough to ensure 
we only
+ // insert positive integers into the trie.
+ constexpr int32_t kPrefixOffset = 64;
+ static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0,
+diff --git a/source/i18n/number_decimalquantity.cpp 
b/source/i18n/number_decimalquantity.cpp
+--- a/source/i18n/number_decimalquantity.cpp
++++ b/source/i18n/number_decimalquantity.cpp
+@@ -11,28 +11,37 @@
+ #include <stdlib.h>
+ 
+ #include "unicode/plurrule.h"
+ #include "cmemory.h"
+ #include "number_decnum.h"
+ #include "putilimp.h"
+ #include "number_decimalquantity.h"
+ #include "number_roundingutils.h"
++#ifdef JS_HAS_INTL_API
++#include "double-conversion/double-conversion.h"
++#else
+ #include "double-conversion.h"
++#endif
+ #include "charstr.h"
+ #include "number_utils.h"
+ #include "uassert.h"
+ #include "util.h"
+ 
+ using namespace icu;
+ using namespace icu::number;
+ using namespace icu::number::impl;
+ 
++#ifdef JS_HAS_INTL_API
++using double_conversion::DoubleToStringConverter;
++using double_conversion::StringToDoubleConverter;
++#else
+ using icu::double_conversion::DoubleToStringConverter;
+ using icu::double_conversion::StringToDoubleConverter;
++#endif
+ 
+ namespace {
+ 
+ int8_t NEGATIVE_FLAG = 1;
+ int8_t INFINITY_FLAG = 2;
+ int8_t NAN_FLAG = 4;
+ 
+ /** Helper function for safe subtraction (no overflow). */
+diff --git a/source/i18n/number_rounding.cpp b/source/i18n/number_rounding.cpp
+--- a/source/i18n/number_rounding.cpp
++++ b/source/i18n/number_rounding.cpp
+@@ -5,17 +5,21 @@
+ 
+ #if !UCONFIG_NO_FORMATTING
+ 
+ #include "charstr.h"
+ #include "uassert.h"
+ #include "unicode/numberformatter.h"
+ #include "number_types.h"
+ #include "number_decimalquantity.h"
++#ifdef JS_HAS_INTL_API
++#include "double-conversion/double-conversion.h"
++#else
+ #include "double-conversion.h"
++#endif
+ #include "number_roundingutils.h"
+ #include "number_skeletons.h"
+ #include "number_decnum.h"
+ #include "putilimp.h"
+ #include "string_segment.h"
+ 
+ using namespace icu;
+ using namespace icu::number;
+diff --git a/source/i18n/number_utils.cpp b/source/i18n/number_utils.cpp
+--- a/source/i18n/number_utils.cpp
++++ b/source/i18n/number_utils.cpp
+@@ -12,26 +12,34 @@
+ #include <stdlib.h>
+ #include <cmath>
+ #include "number_decnum.h"
+ #include "number_types.h"
+ #include "number_utils.h"
+ #include "charstr.h"
+ #include "decContext.h"
+ #include "decNumber.h"
++#ifdef JS_HAS_INTL_API
++#include "double-conversion/double-conversion.h"
++#else
+ #include "double-conversion.h"
++#endif
+ #include "fphdlimp.h"
+ #include "uresimp.h"
+ #include "ureslocs.h"
+ 
+ using namespace icu;
+ using namespace icu::number;
+ using namespace icu::number::impl;
+ 
++#ifdef JS_HAS_INTL_API
++using double_conversion::DoubleToStringConverter;
++#else
+ using icu::double_conversion::DoubleToStringConverter;
++#endif
+ 
+ 
+ namespace {
+ 
+ const char16_t*
+ doGetPattern(UResourceBundle* res, const char* nsName, const char* 
patternKey, UErrorCode& publicStatus,
+              UErrorCode& localStatus) {
+     // Construct the path into the resource bundle
+diff --git a/source/i18n/units_converter.cpp b/source/i18n/units_converter.cpp
+--- a/source/i18n/units_converter.cpp
++++ b/source/i18n/units_converter.cpp
+@@ -3,17 +3,21 @@
+ 
+ #include "unicode/utypes.h"
+ 
+ #if !UCONFIG_NO_FORMATTING
+ 
+ #include "charstr.h"
+ #include "cmemory.h"
+ #include "cstring.h"
++#ifdef JS_HAS_INTL_API
++#include "double-conversion/string-to-double.h"
++#else
+ #include "double-conversion-string-to-double.h"
++#endif
+ #include "measunit_impl.h"
+ #include "putilimp.h"
+ #include "uassert.h"
+ #include "unicode/errorcode.h"
+ #include "unicode/localpointer.h"
+ #include "unicode/stringpiece.h"
+ #include "units_converter.h"
+ #include <algorithm>
+@@ -105,17 +109,21 @@ void Factor::substituteConstants() {
+         this->constantExponents[i] = 0;
+     }
+ }
+ 
+ namespace {
+ 
+ /* Helpers */
+ 
++#ifdef JS_HAS_INTL_API
++using double_conversion::StringToDoubleConverter;
++#else
+ using icu::double_conversion::StringToDoubleConverter;
++#endif
+ 
+ // TODO: Make this a shared-utility function.
+ // Returns `double` from a scientific number(i.e. "1", "2.01" or "3.09E+4")
+ double strToDouble(StringPiece strNum, UErrorCode &status) {
+     // We are processing well-formed input, so we don't need any special 
options to
+     // StringToDoubleConverter.
+     StringToDoubleConverter converter(0, 0, 0, "", "");
+     int32_t count;
diff --git a/gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch 
b/gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch
new file mode 100644
index 0000000000..2b1a0d32f7
--- /dev/null
+++ b/gnu/packages/patches/icu4c-bug-1706949-wasi-workaround.patch
@@ -0,0 +1,832 @@
+# Handle WASI lack of support for <thread> and <atomic>.
+#
+# WASI issue: https://github.com/WebAssembly/wasi-sdk/issues/180
+
+diff --git a/source/common/putilimp.h b/source/common/putilimp.h
+--- a/source/common/putilimp.h
++++ b/source/common/putilimp.h
+@@ -105,10 +105,12 @@ typedef size_t uintptr_t;
+ #endif
+ #elif U_PLATFORM == U_PF_OS400
+    /* not defined */
+ #elif U_PLATFORM == U_PF_HAIKU
+    /* not defined */
++#elif defined(__wasi__)
++   /* not defined */
+ #else
+ #   define U_TZSET tzset
+ #endif
+ 
+ #if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE)
+@@ -130,10 +132,12 @@ typedef size_t uintptr_t;
+    /* not defined */
+ #elif U_PLATFORM == U_PF_OS400
+    /* not defined */
+ #elif U_PLATFORM == U_PF_IPHONE
+    /* not defined */
++#elif defined(__wasi__)
++   /* not defined */
+ #else
+ #   define U_TIMEZONE timezone
+ #endif
+ 
+ #if defined(U_TZNAME) || defined(U_HAVE_TZNAME)
+@@ -145,10 +149,12 @@ typedef size_t uintptr_t;
+ #endif
+ #elif U_PLATFORM == U_PF_OS400
+    /* not defined */
+ #elif U_PLATFORM == U_PF_HAIKU
+     /* not defined, (well it is but a loop back to icu) */
++#elif defined(__wasi__)
++   /* not defined */
+ #else
+ #   define U_TZNAME tzname
+ #endif
+ 
+ #ifdef U_HAVE_MMAP
+diff --git a/source/common/umapfile.h b/source/common/umapfile.h
+--- a/source/common/umapfile.h
++++ b/source/common/umapfile.h
+@@ -38,10 +38,12 @@ U_CFUNC void  uprv_unmapFile(UDataMemory
+ #define MAP_POSIX       2
+ #define MAP_STDIO       3
+ 
+ #if UCONFIG_NO_FILE_IO
+ #   define MAP_IMPLEMENTATION MAP_NONE
++#elif defined(__wasi__)
++#   define MAP_IMPLEMENTATION MAP_STDIO
+ #elif U_PLATFORM_USES_ONLY_WIN32_API
+ #   define MAP_IMPLEMENTATION MAP_WIN32
+ #elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390
+ #   define MAP_IMPLEMENTATION MAP_POSIX
+ #else /* unknown platform, no memory map implementation: use stdio.h and 
uprv_malloc() instead */
+diff --git a/source/common/umutex.cpp b/source/common/umutex.cpp
+--- a/source/common/umutex.cpp
++++ b/source/common/umutex.cpp
+@@ -41,10 +41,11 @@ U_NAMESPACE_BEGIN
+  *
+  *  ICU Mutex wrappers.
+  *
+  
*************************************************************************************************/
+ 
++#ifndef __wasi__
+ namespace {
+ std::mutex *initMutex;
+ std::condition_variable *initCondition;
+ 
+ // The ICU global mutex.
+@@ -53,32 +54,38 @@ UMutex globalMutex;
+ 
+ std::once_flag initFlag;
+ std::once_flag *pInitFlag = &initFlag;
+ 
+ }  // Anonymous namespace
++#endif
+ 
+ U_CDECL_BEGIN
+ static UBool U_CALLCONV umtx_cleanup() {
++#ifndef __wasi__
+     initMutex->~mutex();
+     initCondition->~condition_variable();
+     UMutex::cleanup();
+ 
+     // Reset the once_flag, by destructing it and creating a fresh one in its 
place.
+     // Do not use this trick anywhere else in ICU; use umtx_initOnce, not 
std::call_once().
+     pInitFlag->~once_flag();
+     pInitFlag = new(&initFlag) std::once_flag();
++#endif
+     return true;
+ }
+ 
+ static void U_CALLCONV umtx_init() {
++#ifndef __wasi__
+     initMutex = STATIC_NEW(std::mutex);
+     initCondition = STATIC_NEW(std::condition_variable);
+     ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
++#endif
+ }
+ U_CDECL_END
+ 
+ 
++#ifndef __wasi__
+ std::mutex *UMutex::getMutex() {
+     std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
+     if (retPtr == nullptr) {
+         std::call_once(*pInitFlag, umtx_init);
+         std::lock_guard<std::mutex> guard(*initMutex);
+@@ -91,41 +98,48 @@ std::mutex *UMutex::getMutex() {
+         }
+     }
+     U_ASSERT(retPtr != nullptr);
+     return retPtr;
+ }
++#endif
+ 
+ UMutex *UMutex::gListHead = nullptr;
+ 
+ void UMutex::cleanup() {
+     UMutex *next = nullptr;
+     for (UMutex *m = gListHead; m != nullptr; m = next) {
++#ifndef __wasi__
+         (*m->fMutex).~mutex();
+         m->fMutex = nullptr;
++#endif
+         next = m->fListLink;
+         m->fListLink = nullptr;
+     }
+     gListHead = nullptr;
+ }
+ 
+ 
+ U_CAPI void  U_EXPORT2
+ umtx_lock(UMutex *mutex) {
++#ifndef __wasi__
+     if (mutex == nullptr) {
+         mutex = &globalMutex;
+     }
+     mutex->lock();
++#endif
+ }
+ 
+ 
+ U_CAPI void  U_EXPORT2
+ umtx_unlock(UMutex* mutex)
+ {
++#ifndef __wasi__
+     if (mutex == nullptr) {
+         mutex = &globalMutex;
+     }
+     mutex->unlock();
++#endif
+ }
+ 
+ 
+ 
/*************************************************************************************************
+  *
+@@ -141,22 +155,26 @@ umtx_unlock(UMutex* mutex)
+ //   that knows the C++ types involved. This function returns true if
+ //   the caller needs to call the Init function.
+ //
+ U_COMMON_API UBool U_EXPORT2
+ umtx_initImplPreInit(UInitOnce &uio) {
++#ifndef __wasi__
+     std::call_once(*pInitFlag, umtx_init);
+     std::unique_lock<std::mutex> lock(*initMutex);
++#endif
+     if (umtx_loadAcquire(uio.fState) == 0) {
+         umtx_storeRelease(uio.fState, 1);
+         return true;      // Caller will next call the init function.
+     } else {
++#ifndef __wasi__
+         while (umtx_loadAcquire(uio.fState) == 1) {
+             // Another thread is currently running the initialization.
+             // Wait until it completes.
+             initCondition->wait(lock);
+         }
+         U_ASSERT(uio.fState == 2);
++#endif
+         return false;
+     }
+ }
+ 
+ 
+@@ -166,15 +184,17 @@ umtx_initImplPreInit(UInitOnce &uio) {
+ //   Some threads may be racing to test the fState variable outside of the 
mutex,
+ //   requiring the use of store/release when changing its value.
+ 
+ U_COMMON_API void U_EXPORT2
+ umtx_initImplPostInit(UInitOnce &uio) {
++#ifndef __wasi__
+     {
+         std::unique_lock<std::mutex> lock(*initMutex);
+         umtx_storeRelease(uio.fState, 2);
+     }
+     initCondition->notify_all();
++#endif
+ }
+ 
+ U_NAMESPACE_END
+ 
+ 
/*************************************************************************************************
+diff --git a/source/common/umutex.h b/source/common/umutex.h
+--- a/source/common/umutex.h
++++ b/source/common/umutex.h
+@@ -18,13 +18,16 @@
+ */
+ 
+ #ifndef UMUTEX_H
+ #define UMUTEX_H
+ 
++#ifndef __wasi__
+ #include <atomic>
+ #include <condition_variable>
+ #include <mutex>
++#endif
++
+ #include <type_traits>
+ 
+ #include "unicode/utypes.h"
+ #include "unicode/uclean.h"
+ #include "unicode/uobject.h"
+@@ -43,10 +46,12 @@ U_NAMESPACE_BEGIN
+  *
+  *   Low Level Atomic Operations, ICU wrappers for.
+  *
+  ****************************************************************************/
+ 
++#ifndef __wasi__
++
+ typedef std::atomic<int32_t> u_atomic_int32_t;
+ 
+ inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
+     return var.load(std::memory_order_acquire);
+ }
+@@ -61,10 +66,31 @@ inline int32_t umtx_atomic_inc(u_atomic_
+ 
+ inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
+     return var->fetch_sub(1) - 1;
+ }
+ 
++#else
++
++typedef int32_t u_atomic_int32_t;
++
++inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
++    return var;
++}
++
++inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
++    var = val;
++}
++
++inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
++    return ++(*var);
++}
++
++inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
++    return --(*var);
++}
++
++#endif
+ 
+ 
/*************************************************************************************************
+  *
+  *  UInitOnce Definitions.
+  *
+@@ -211,21 +237,29 @@ public:
+     U_COMMON_API UMutex& operator=(const UMutex& other) = delete;
+     U_COMMON_API void* operator new(size_t) = delete;
+ 
+     // requirements for C++ BasicLockable, allows UMutex to work with 
std::lock_guard
+     U_COMMON_API void lock() {
++#ifndef __wasi__
+         std::mutex *m = fMutex.load(std::memory_order_acquire);
+         if (m == nullptr) { m = getMutex(); }
+         m->lock();
++#endif
+     }
+-    U_COMMON_API void unlock() { 
fMutex.load(std::memory_order_relaxed)->unlock(); }
++    U_COMMON_API void unlock() {
++#ifndef __wasi__
++        fMutex.load(std::memory_order_relaxed)->unlock();
++#endif
++    }
+ 
+     U_COMMON_API static void cleanup();
+ 
+ private:
++#ifndef __wasi__
+     alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
+     std::atomic<std::mutex *> fMutex { nullptr };
++#endif
+ 
+     /** All initialized UMutexes are kept in a linked list, so that they can 
be found,
+      * and the underlying std::mutex destructed, by u_cleanup().
+      */
+     UMutex *fListLink { nullptr };
+@@ -233,11 +267,13 @@ private:
+ 
+     /** Out-of-line function to lazily initialize a UMutex on first use.
+      * Initial fast check is inline, in lock().  The returned value may never
+      * be nullptr.
+      */
++#ifndef __wasi__
+     std::mutex *getMutex();
++#endif
+ };
+ 
+ 
+ /* Lock a mutex.
+  * @param mutex The given mutex to be locked.  Pass NULL to specify
+diff --git a/source/common/unifiedcache.cpp b/source/common/unifiedcache.cpp
+--- a/source/common/unifiedcache.cpp
++++ b/source/common/unifiedcache.cpp
+@@ -11,19 +11,23 @@
+ */
+ 
+ #include "unifiedcache.h"
+ 
+ #include <algorithm>      // For std::max()
++#ifndef __wasi__
+ #include <mutex>
++#endif
+ 
+ #include "uassert.h"
+ #include "uhash.h"
+ #include "ucln_cmn.h"
+ 
+ static icu::UnifiedCache *gCache = nullptr;
++#ifndef __wasi__
+ static std::mutex *gCacheMutex = nullptr;
+ static std::condition_variable *gInProgressValueAddedCond;
++#endif
+ static icu::UInitOnce gCacheInitOnce {};
+ 
+ static const int32_t MAX_EVICT_ITERATIONS = 10;
+ static const int32_t DEFAULT_MAX_UNUSED = 1000;
+ static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100;
+@@ -32,14 +36,16 @@ static const int32_t DEFAULT_PERCENTAGE_
+ U_CDECL_BEGIN
+ static UBool U_CALLCONV unifiedcache_cleanup() {
+     gCacheInitOnce.reset();
+     delete gCache;
+     gCache = nullptr;
++#ifndef __wasi__
+     gCacheMutex->~mutex();
+     gCacheMutex = nullptr;
+     gInProgressValueAddedCond->~condition_variable();
+     gInProgressValueAddedCond = nullptr;
++#endif
+     return true;
+ }
+ U_CDECL_END
+ 
+ 
+@@ -70,12 +76,14 @@ CacheKeyBase::~CacheKeyBase() {
+ static void U_CALLCONV cacheInit(UErrorCode &status) {
+     U_ASSERT(gCache == nullptr);
+     ucln_common_registerCleanup(
+             UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup);
+ 
++#ifndef __wasi__
+     gCacheMutex = STATIC_NEW(std::mutex);
+     gInProgressValueAddedCond = STATIC_NEW(std::condition_variable);
++#endif
+     gCache = new UnifiedCache(status);
+     if (gCache == nullptr) {
+         status = U_MEMORY_ALLOCATION_ERROR;
+     }
+     if (U_FAILURE(status)) {
+@@ -133,41 +141,53 @@ void UnifiedCache::setEvictionPolicy(
+     }
+     if (count < 0 || percentageOfInUseItems < 0) {
+         status = U_ILLEGAL_ARGUMENT_ERROR;
+         return;
+     }
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     fMaxUnused = count;
+     fMaxPercentageOfInUse = percentageOfInUseItems;
+ }
+ 
+ int32_t UnifiedCache::unusedCount() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     return uhash_count(fHashtable) - fNumValuesInUse;
+ }
+ 
+ int64_t UnifiedCache::autoEvictedCount() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     return fAutoEvictedCount;
+ }
+ 
+ int32_t UnifiedCache::keyCount() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     return uhash_count(fHashtable);
+ }
+ 
+ void UnifiedCache::flush() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+ 
+     // Use a loop in case cache items that are flushed held hard references to
+     // other cache items making those additional cache items eligible for
+     // flushing.
+     while (_flush(false));
+ }
+ 
+ void UnifiedCache::handleUnreferencedObject() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     --fNumValuesInUse;
+     _runEvictionSlice();
+ }
+ 
+ #ifdef UNIFIED_CACHE_DEBUG
+@@ -182,11 +202,13 @@ void UnifiedCache::dump() {
+     }
+     cache->dumpContents();
+ }
+ 
+ void UnifiedCache::dumpContents() const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     _dumpContents();
+ }
+ 
+ // Dumps content of cache.
+ // On entry, gCacheMutex must be held.
+@@ -222,11 +244,13 @@ UnifiedCache::~UnifiedCache() {
+     flush();
+     {
+         // Now all that should be left in the cache are entries that refer to
+         // each other and entries with hard references from outside the cache.
+         // Nothing we can do about these so proceed to wipe out the cache.
++#ifndef __wasi__
+         std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+         _flush(true);
+     }
+     uhash_close(fHashtable);
+     fHashtable = nullptr;
+     delete fNoValue;
+@@ -323,11 +347,13 @@ void UnifiedCache::_putNew(
+ 
+ void UnifiedCache::_putIfAbsentAndGet(
+         const CacheKeyBase &key,
+         const SharedObject *&value,
+         UErrorCode &status) const {
++#ifndef __wasi__
+     std::lock_guard<std::mutex> lock(*gCacheMutex);
++#endif
+     const UHashElement *element = uhash_find(fHashtable, &key);
+     if (element != nullptr && !_inProgress(element)) {
+         _fetch(element, value, status);
+         return;
+     }
+@@ -348,18 +374,22 @@ UBool UnifiedCache::_poll(
+         const CacheKeyBase &key,
+         const SharedObject *&value,
+         UErrorCode &status) const {
+     U_ASSERT(value == nullptr);
+     U_ASSERT(status == U_ZERO_ERROR);
++#ifndef __wasi__
+     std::unique_lock<std::mutex> lock(*gCacheMutex);
++#endif
+     const UHashElement *element = uhash_find(fHashtable, &key);
+ 
+     // If the hash table contains an inProgress placeholder entry for this 
key,
+     // this means that another thread is currently constructing the value 
object.
+     // Loop, waiting for that construction to complete.
+      while (element != nullptr && _inProgress(element)) {
++#ifndef __wasi__
+          gInProgressValueAddedCond->wait(lock);
++#endif
+          element = uhash_find(fHashtable, &key);
+     }
+ 
+     // If the hash table contains an entry for the key,
+     // fetch out the contents and return them.
+@@ -426,13 +456,15 @@ void UnifiedCache::_put(
+     UHashElement *ptr = const_cast<UHashElement *>(element);
+     ptr->value.pointer = (void *) value;
+     U_ASSERT(oldValue == fNoValue);
+     removeSoftRef(oldValue);
+ 
++#ifndef __wasi__
+     // Tell waiting threads that we replace in-progress status with
+     // an error.
+     gInProgressValueAddedCond->notify_all();
++#endif
+ }
+ 
+ void UnifiedCache::_fetch(
+         const UHashElement *element,
+         const SharedObject *&value,
+diff --git a/source/i18n/decContext.h b/source/i18n/decContext.h
+--- a/source/i18n/decContext.h
++++ b/source/i18n/decContext.h
+@@ -59,11 +59,13 @@
+ 
+   #if !defined(int32_t)
+ /* #include <stdint.h>   */         /* C99 standard integers           */
+   #endif
+   #include <stdio.h>               /* for printf, etc.                */
++#ifndef __wasi__
+   #include <signal.h>              /* for traps                       */
++#endif
+ 
+   /* Extended flags setting -- set this to 0 to use only IEEE flags   */
+   #if !defined(DECEXTFLAG)
+   #define DECEXTFLAG 1             /* 1=enable extended flags         */
+   #endif
+diff --git a/source/i18n/decimfmt.cpp b/source/i18n/decimfmt.cpp
+--- a/source/i18n/decimfmt.cpp
++++ b/source/i18n/decimfmt.cpp
+@@ -478,12 +478,17 @@ DecimalFormat& DecimalFormat::operator=(
+ }
+ 
+ DecimalFormat::~DecimalFormat() {
+     if (fields == nullptr) { return; }
+ 
++#ifndef __wasi__
+     delete fields->atomicParser.exchange(nullptr);
+     delete fields->atomicCurrencyParser.exchange(nullptr);
++#else
++    delete fields->atomicParser;
++    delete fields->atomicCurrencyParser;
++#endif
+     delete fields;
+ }
+ 
+ DecimalFormat* DecimalFormat::clone() const {
+     // can only clone valid objects.
+@@ -1635,12 +1640,17 @@ void DecimalFormat::touch(UErrorCode& st
+     
+     // Do this after fields->exportedProperties are set up
+     setupFastFormat();
+ 
+     // Delete the parsers if they were made previously
++#ifndef __wasi__
+     delete fields->atomicParser.exchange(nullptr);
+     delete fields->atomicCurrencyParser.exchange(nullptr);
++#else
++    delete fields->atomicParser;
++    delete fields->atomicCurrencyParser;
++#endif
+ 
+     // In order for the getters to work, we need to populate some fields in 
NumberFormat.
+     
NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(),
 status);
+     
NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
+     
NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
+@@ -1671,11 +1681,15 @@ const numparse::impl::NumberParserImpl* 
+     if (U_FAILURE(status)) {
+         return nullptr;
+     }
+ 
+     // First try to get the pre-computed parser
++#ifndef __wasi__
+     auto* ptr = fields->atomicParser.load();
++#else
++    auto* ptr = fields->atomicParser;
++#endif
+     if (ptr != nullptr) {
+         return ptr;
+     }
+ 
+     // Try computing the parser on our own
+@@ -1690,25 +1704,34 @@ const numparse::impl::NumberParserImpl* 
+ 
+     // Note: ptr starts as nullptr; during compare_exchange,
+     // it is set to what is actually stored in the atomic
+     // if another thread beat us to computing the parser object.
+     auto* nonConstThis = const_cast<DecimalFormat*>(this);
++#ifndef __wasi__
+     if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, 
temp)) {
+         // Another thread beat us to computing the parser
+         delete temp;
+         return ptr;
+     } else {
+         // Our copy of the parser got stored in the atomic
+         return temp;
+     }
++#else
++    nonConstThis->fields->atomicParser = temp;
++    return temp;
++#endif
+ }
+ 
+ const numparse::impl::NumberParserImpl* 
DecimalFormat::getCurrencyParser(UErrorCode& status) const {
+     if (U_FAILURE(status)) { return nullptr; }
+ 
+     // First try to get the pre-computed parser
++#ifndef __wasi__
+     auto* ptr = fields->atomicCurrencyParser.load();
++#else
++    auto* ptr = fields->atomicCurrencyParser;
++#endif
+     if (ptr != nullptr) {
+         return ptr;
+     }
+ 
+     // Try computing the parser on our own
+@@ -1719,18 +1742,23 @@ const numparse::impl::NumberParserImpl* 
+     }
+ 
+     // Note: ptr starts as nullptr; during compare_exchange, it is set to 
what is actually stored in the
+     // atomic if another thread beat us to computing the parser object.
+     auto* nonConstThis = const_cast<DecimalFormat*>(this);
++#ifndef __wasi__
+     if 
(!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, 
temp)) {
+         // Another thread beat us to computing the parser
+         delete temp;
+         return ptr;
+     } else {
+         // Our copy of the parser got stored in the atomic
+         return temp;
+     }
++#else
++    nonConstThis->fields->atomicCurrencyParser = temp;
++    return temp;
++#endif
+ }
+ 
+ void
+ DecimalFormat::fieldPositionHelper(
+         const UFormattedNumberData& formatted,
+diff --git a/source/i18n/number_mapper.h b/source/i18n/number_mapper.h
+--- a/source/i18n/number_mapper.h
++++ b/source/i18n/number_mapper.h
+@@ -5,18 +5,21 @@
+ 
+ #if !UCONFIG_NO_FORMATTING
+ #ifndef __NUMBER_MAPPER_H__
+ #define __NUMBER_MAPPER_H__
+ 
+-#include <atomic>
+ #include "number_types.h"
+ #include "unicode/currpinf.h"
+ #include "standardplural.h"
+ #include "number_patternstring.h"
+ #include "number_currencysymbols.h"
+ #include "numparse_impl.h"
+ 
++#ifndef __wasi__
++#include <atomic>
++#endif
++
+ U_NAMESPACE_BEGIN
+ namespace number::impl {
+ 
+ class AutoAffixPatternProvider;
+ class CurrencyPluralInfoAffixProvider;
+@@ -194,14 +197,22 @@ struct DecimalFormatFields : public UMem
+     * #format} method uses the formatter directly without needing to 
synchronize.
+     */
+     LocalizedNumberFormatter formatter;
+ 
+     /** The lazy-computed parser for .parse() */
++#ifndef __wasi__
+     std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {};
++#else
++    ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr;
++#endif
+ 
+     /** The lazy-computed parser for .parseCurrency() */
++#ifndef __wasi__
+     std::atomic<::icu::numparse::impl::NumberParserImpl*> 
atomicCurrencyParser = {};
++#else
++    ::icu::numparse::impl::NumberParserImpl* atomicCurrencyParser = {};
++#endif
+ 
+     /** Small object ownership warehouse for the formatter and parser */
+     DecimalFormatWarehouse warehouse;
+ 
+     /** The effective properties as exported from the formatter object. Used 
by some getters. */
+diff --git a/source/i18n/numrange_fluent.cpp b/source/i18n/numrange_fluent.cpp
+--- a/source/i18n/numrange_fluent.cpp
++++ b/source/i18n/numrange_fluent.cpp
+@@ -246,33 +246,53 @@ LocalizedNumberRangeFormatter::Localized
+ 
+ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) 
noexcept
+         : NFS<LNF>(std::move(src)) {
+     // Steal the compiled formatter
+     LNF&& _src = static_cast<LNF&&>(src);
++#ifndef __wasi__
+     auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
+     delete fAtomicFormatter.exchange(stolen);
++#else
++    delete fAtomicFormatter;
++    fAtomicFormatter = _src.fAtomicFormatter;
++    _src.fAtomicFormatter = nullptr;
++#endif
+ }
+ 
+ LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const 
LNF& other) {
+     if (this == &other) { return *this; }  // self-assignment: no-op
+     NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
+     // Do not steal; just clear
++#ifndef __wasi__
+     delete fAtomicFormatter.exchange(nullptr);
++#else
++    delete fAtomicFormatter;
++#endif
+     return *this;
+ }
+ 
+ LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& 
src) noexcept {
+     NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
+     // Steal the compiled formatter
++#ifndef __wasi__
+     auto* stolen = src.fAtomicFormatter.exchange(nullptr);
+     delete fAtomicFormatter.exchange(stolen);
++#else
++    delete fAtomicFormatter;
++    fAtomicFormatter = src.fAtomicFormatter;
++    src.fAtomicFormatter = nullptr;
++#endif
+     return *this;
+ }
+ 
+ 
+ LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
++#ifndef __wasi__
+     delete fAtomicFormatter.exchange(nullptr);
++#else
++    delete fAtomicFormatter;
++#endif
+ }
+ 
+ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const 
RangeMacroProps& macros, const Locale& locale) {
+     fMacros = macros;
+     fMacros.locale = locale;
+@@ -363,11 +383,15 @@ LocalizedNumberRangeFormatter::getFormat
+     if (U_FAILURE(status)) {
+         return nullptr;
+     }
+ 
+     // First try to get the pre-computed formatter
++#ifndef __wasi__
+     auto* ptr = fAtomicFormatter.load();
++#else
++    auto* ptr = fAtomicFormatter;
++#endif
+     if (ptr != nullptr) {
+         return ptr;
+     }
+ 
+     // Try computing the formatter on our own
+@@ -378,17 +402,22 @@ LocalizedNumberRangeFormatter::getFormat
+ 
+     // Note: ptr starts as nullptr; during compare_exchange,
+     // it is set to what is actually stored in the atomic
+     // if another thread beat us to computing the formatter object.
+     auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
++#ifndef __wasi__
+     if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, 
temp.getAlias())) {
+         // Another thread beat us to computing the formatter
+         return ptr;
+     } else {
+         // Our copy of the formatter got stored in the atomic
+         return temp.orphan();
+     }
++#else
++    nonConstThis->fAtomicFormatter = temp.getAlias();
++    return temp.orphan();
++#endif
+ 
+ }
+ 
+ 
+ #endif /* #if !UCONFIG_NO_FORMATTING */
+diff --git a/source/i18n/unicode/numberrangeformatter.h 
b/source/i18n/unicode/numberrangeformatter.h
+--- a/source/i18n/unicode/numberrangeformatter.h
++++ b/source/i18n/unicode/numberrangeformatter.h
+@@ -8,18 +8,21 @@
+ 
+ #if U_SHOW_CPLUSPLUS_API
+ 
+ #if !UCONFIG_NO_FORMATTING
+ 
+-#include <atomic>
+ #include "unicode/appendable.h"
+ #include "unicode/fieldpos.h"
+ #include "unicode/formattedvalue.h"
+ #include "unicode/fpositer.h"
+ #include "unicode/numberformatter.h"
+ #include "unicode/unumberrangeformatter.h"
+ 
++#ifndef __wasi__
++#include <atomic>
++#endif
++
+ /**
+  * \file
+  * \brief C++ API: Library for localized formatting of number, currency, and 
unit ranges.
+  *
+  * The main entrypoint to the formatting of ranges of numbers, including 
currencies and other units of measurement.
+@@ -559,11 +562,15 @@ class U_I18N_API_CLASS LocalizedNumberRa
+      * @stable ICU 63
+      */
+     U_I18N_API ~LocalizedNumberRangeFormatter();
+ 
+   private:
++#ifndef __wasi__
+     std::atomic<impl::NumberRangeFormatterImpl*> fAtomicFormatter = {};
++#else
++    impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr;
++#endif
+ 
+     const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) 
const;
+ 
+     explicit LocalizedNumberRangeFormatter(
+         const NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>& 
other);
diff --git 
a/gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch
 
b/gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch
new file mode 100644
index 0000000000..6f9bfacbe9
--- /dev/null
+++ 
b/gnu/packages/patches/icu4c-bug-1790071-ICU-22132-standardize-vtzone-output.patch
@@ -0,0 +1,28 @@
+diff --git a/source/i18n/vtzone.cpp b/source/i18n/vtzone.cpp
+--- a/source/i18n/vtzone.cpp
++++ b/source/i18n/vtzone.cpp
+@@ -1735,14 +1735,17 @@ VTimeZone::write(VTZWriter& writer, UErr
+             }
+         }
+     } else {
+-        UnicodeString icutzprop;
+-        UVector customProps(nullptr, uhash_compareUnicodeString, status);
++        UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, 
status);
+         if (olsonzid.length() > 0 && icutzver.length() > 0) {
+-            icutzprop.append(olsonzid);
+-            icutzprop.append(u'[');
+-            icutzprop.append(icutzver);
+-            icutzprop.append(u']');
+-            customProps.addElement(&icutzprop, status);
++            LocalPointer<UnicodeString> icutzprop(new 
UnicodeString(ICU_TZINFO_PROP), status);
++            if (U_FAILURE(status)) {
++                return;
++            }
++            icutzprop->append(olsonzid);
++            icutzprop->append(u'[');
++            icutzprop->append(icutzver);
++            icutzprop->append(u']');
++            customProps.adoptElement(icutzprop.orphan(), status);
+         }
+         writeZone(writer, *tz, &customProps, status);
+     }
diff --git 
a/gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch 
b/gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch
new file mode 100644
index 0000000000..b6d518c76b
--- /dev/null
+++ 
b/gnu/packages/patches/icu4c-bug-1856290-ICU-20548-dateinterval-timezone.patch
@@ -0,0 +1,163 @@
+# Handle 'O' time zone skeleton in DateIntervalFormat.
+# Keep time zone skeleton field widths in DateIntervalFormat.
+#
+# ICU bug: https://unicode-org.atlassian.net/browse/ICU-20548
+
+diff --git a/source/i18n/dtitv_impl.h b/source/i18n/dtitv_impl.h
+--- a/source/i18n/dtitv_impl.h
++++ b/source/i18n/dtitv_impl.h
+@@ -84,16 +84,19 @@
+ #define CAP_W             ((char16_t)0x0057)
+ #define CAP_Y             ((char16_t)0x0059)
+ #define CAP_Z             ((char16_t)0x005A)
+ 
+ //#define MINIMUM_SUPPORTED_CALENDAR_FIELD    UCAL_MINUTE
+ 
+ #define MAX_E_COUNT      5
+ #define MAX_M_COUNT      5
++#define MAX_z_COUNT      4
++#define MAX_v_COUNT      4
++#define MAX_O_COUNT      4
+ //#define MAX_INTERVAL_INDEX 4
+ #define MAX_POSITIVE_INT  56632
+ 
+ 
+ #endif /* #if !UCONFIG_NO_FORMATTING */
+ 
+ #endif 
+ //eof
+diff --git a/source/i18n/dtitvfmt.cpp b/source/i18n/dtitvfmt.cpp
+--- a/source/i18n/dtitvfmt.cpp
++++ b/source/i18n/dtitvfmt.cpp
+@@ -1061,16 +1061,17 @@ DateIntervalFormat::getDateTimeSkeleton(
+     // timeSkeleton follows the sequence of hm*[v|z]?
+     int32_t ECount = 0;
+     int32_t dCount = 0;
+     int32_t MCount = 0;
+     int32_t yCount = 0;
+     int32_t mCount = 0;
+     int32_t vCount = 0;
+     int32_t zCount = 0;
++    int32_t OCount = 0;
+     char16_t hourChar = u'\0';
+     int32_t i;
+ 
+     for (i = 0; i < skeleton.length(); ++i) {
+         char16_t ch = skeleton[i];
+         switch ( ch ) {
+           case CAP_E:
+             dateSkeleton.append(ch);
+@@ -1123,16 +1124,20 @@ DateIntervalFormat::getDateTimeSkeleton(
+           case LOW_Z:
+             ++zCount;
+             timeSkeleton.append(ch);
+             break;
+           case LOW_V:
+             ++vCount;
+             timeSkeleton.append(ch);
+             break;
++          case CAP_O:
++            ++OCount;
++            timeSkeleton.append(ch);
++            break;
+           case LOW_A:
+           case CAP_V:
+           case CAP_Z:
+           case LOW_J:
+           case LOW_S:
+           case CAP_S:
+           case CAP_A:
+           case LOW_B:
+@@ -1174,20 +1179,41 @@ DateIntervalFormat::getDateTimeSkeleton(
+     /* generate normalized form for time */
+     if ( hourChar != u'\0' ) {
+         normalizedTimeSkeleton.append(hourChar);
+     }
+     if ( mCount != 0 ) {
+         normalizedTimeSkeleton.append(LOW_M);
+     }
+     if ( zCount != 0 ) {
+-        normalizedTimeSkeleton.append(LOW_Z);
++        if ( zCount <= 3 ) {
++            normalizedTimeSkeleton.append(LOW_Z);
++        } else {
++            for ( int32_t j = 0; j < zCount && j < MAX_z_COUNT; ++j ) {
++                 normalizedTimeSkeleton.append(LOW_Z);
++            }
++        }
+     }
+     if ( vCount != 0 ) {
+-        normalizedTimeSkeleton.append(LOW_V);
++        if ( vCount <= 3 ) {
++            normalizedTimeSkeleton.append(LOW_V);
++        } else {
++            for ( int32_t j = 0; j < vCount && j < MAX_v_COUNT; ++j ) {
++                 normalizedTimeSkeleton.append(LOW_V);
++            }
++        }
++    }
++    if ( OCount != 0 ) {
++        if ( OCount <= 3 ) {
++            normalizedTimeSkeleton.append(CAP_O);
++        } else {
++            for ( int32_t j = 0; j < OCount && j < MAX_O_COUNT; ++j ) {
++                 normalizedTimeSkeleton.append(CAP_O);
++            }
++        }
+     }
+ }
+ 
+ 
+ /**
+  * Generate date or time interval pattern from resource,
+  * and set them into the interval pattern locale to this formatter.
+  *
+@@ -1732,18 +1758,23 @@ DateIntervalFormat::adjustFieldWidth(con
+         findReplaceInPattern(adjustedPtn, UnicodeString(u"a\u202F",-1), 
UnicodeString());
+         findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), 
UnicodeString());
+         // adjust interior double spaces, remove exterior whitespace
+         findReplaceInPattern(adjustedPtn, UnicodeString("  "), 
UnicodeString(" "));
+         adjustedPtn.trim();
+     }
+     if ( differenceInfo == 2 ) {
+         if (inputSkeleton.indexOf(LOW_Z) != -1) {
++             bestMatchSkeletonFieldWidth[(int)(LOW_Z - PATTERN_CHAR_BASE)] = 
bestMatchSkeletonFieldWidth[(int)(LOW_V - PATTERN_CHAR_BASE)];
+              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), 
UnicodeString(LOW_Z));
+          }
++         if (inputSkeleton.indexOf(CAP_O) != -1) {
++             bestMatchSkeletonFieldWidth[(int)(CAP_O - PATTERN_CHAR_BASE)] = 
bestMatchSkeletonFieldWidth[(int)(LOW_V - PATTERN_CHAR_BASE)];
++             findReplaceInPattern(adjustedPtn, UnicodeString(LOW_V), 
UnicodeString(CAP_O));
++         }
+          if (inputSkeleton.indexOf(CAP_K) != -1) {
+              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_H), 
UnicodeString(CAP_K));
+          }
+          if (inputSkeleton.indexOf(LOW_K) != -1) {
+              findReplaceInPattern(adjustedPtn, UnicodeString(CAP_H), 
UnicodeString(LOW_K));
+          }
+          if (inputSkeleton.indexOf(LOW_B) != -1) {
+              findReplaceInPattern(adjustedPtn, UnicodeString(LOW_A), 
UnicodeString(LOW_B));
+diff --git a/source/i18n/dtitvinf.cpp b/source/i18n/dtitvinf.cpp
+--- a/source/i18n/dtitvinf.cpp
++++ b/source/i18n/dtitvinf.cpp
+@@ -582,19 +582,20 @@ DateIntervalInfo::getBestSkeleton(const 
+ 
+     // hack for certain alternate characters
+     // resource bundles only have time skeletons containing 'v', 'h', and 'H'
+     // but not time skeletons containing 'z', 'K', or 'k'
+     // the skeleton may also include 'a' or 'b', which never occur in the 
resource bundles, so strip them out too
+     UBool replacedAlternateChars = false;
+     const UnicodeString* inputSkeleton = &skeleton;
+     UnicodeString copySkeleton;
+-    if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(LOW_K) != -1 || 
skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || 
skeleton.indexOf(LOW_B) != -1 ) {
++    if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(CAP_O) != -1 || 
skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || 
skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) {
+         copySkeleton = skeleton;
+         copySkeleton.findAndReplace(UnicodeString(LOW_Z), 
UnicodeString(LOW_V));
++        copySkeleton.findAndReplace(UnicodeString(CAP_O), 
UnicodeString(LOW_V));
+         copySkeleton.findAndReplace(UnicodeString(LOW_K), 
UnicodeString(CAP_H));
+         copySkeleton.findAndReplace(UnicodeString(CAP_K), 
UnicodeString(LOW_H));
+         copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString());
+         copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString());
+         inputSkeleton = &copySkeleton;
+         replacedAlternateChars = true;
+     }
+ 
diff --git 
a/gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch 
b/gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch
new file mode 100644
index 0000000000..5a0f573203
--- /dev/null
+++ b/gnu/packages/patches/icu4c-bug-1954138-dtitvfmt-adopt-calendar.patch
@@ -0,0 +1,69 @@
+diff --git a/source/i18n/dtitvfmt.cpp b/source/i18n/dtitvfmt.cpp
+--- a/source/i18n/dtitvfmt.cpp
++++ b/source/i18n/dtitvfmt.cpp
+@@ -630,16 +630,38 @@ DateIntervalFormat::getTimeZone() const
+ {
+     if (fDateFormat != nullptr) {
+         Mutex lock(&gFormatterMutex);
+         return fDateFormat->getTimeZone();
+     }
+     // If fDateFormat is nullptr (unexpected), create default timezone.
+     return *(TimeZone::createDefault());
+ }
++ 
++void DateIntervalFormat::adoptCalendar(Calendar *calendarToAdopt) {
++    if (fDateFormat != nullptr) {
++        fDateFormat->adoptCalendar(calendarToAdopt);
++    }
++
++    // The fDateFormat has the primary calendar for the DateIntervalFormat 
and has
++    // ownership of any adopted Calendar; fFromCalendar and fToCalendar are 
internal
++    // work clones of that calendar.
++
++    delete fFromCalendar;
++    fFromCalendar = nullptr;
++
++    delete fToCalendar;
++    fToCalendar = nullptr;
++
++    const Calendar *calendar = fDateFormat->getCalendar();
++    if (calendar != nullptr) {
++        fFromCalendar = calendar->clone();
++        fToCalendar = calendar->clone();
++    }
++}
+ 
+ void
+ DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status)
+ {
+     if (U_FAILURE(status))
+         return;
+     if (static_cast<UDisplayContextType>(static_cast<uint32_t>(value) >> 8) 
== UDISPCTX_TYPE_CAPITALIZATION) {
+         fCapitalizationContext = value;
+diff --git a/source/i18n/unicode/dtitvfmt.h b/source/i18n/unicode/dtitvfmt.h
+--- a/source/i18n/unicode/dtitvfmt.h
++++ b/source/i18n/unicode/dtitvfmt.h
+@@ -625,16 +625,23 @@ public:
+     /**
+      * Sets the time zone for the calendar used by this DateIntervalFormat 
object.
+      * @param zone the new time zone.
+      * @stable ICU 4.8
+      */
+     U_I18N_API virtual void setTimeZone(const TimeZone& zone);
+ 
+     /**
++     * Sets the calendar used by this DateIntervalFormat object. The caller 
no longer owns
++     * the Calendar object and should not delete it after this call.
++     * @param calendarToAdopt the Calendar to be adopted.
++     */
++    U_I18N_API virtual void adoptCalendar(Calendar *calendarToAdopt);
++
++    /**
+      * Set a particular UDisplayContext value in the formatter, such as
+      * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+      * result to be capitalized appropriately for the context in which
+      * it is intended to be used, considering both the locale and the
+      * type of field at the beginning of the formatted result.
+      * @param value The UDisplayContext value to set.
+      * @param status Input/output status. If at entry this indicates a failure
+      *               status, the function will do nothing; otherwise this 
will be
diff --git 
a/gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch 
b/gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch
new file mode 100644
index 0000000000..5bcca8b621
--- /dev/null
+++ b/gnu/packages/patches/icu4c-bug-1972781-chinese-based-calendar.patch
@@ -0,0 +1,25 @@
+diff --git a/source/i18n/smpdtfmt.cpp b/source/i18n/smpdtfmt.cpp
+--- a/source/i18n/smpdtfmt.cpp
++++ b/source/i18n/smpdtfmt.cpp
+@@ -1523,18 +1523,19 @@ SimpleDateFormat::subFormat(UnicodeStrin
+     }
+ 
+     switch (patternCharIndex) {
+ 
+     // for any "G" symbol, write out the appropriate era string
+     // "GGGG" is wide era name, "GGGGG" is narrow era name, anything else is 
abbreviated name
+     case UDAT_ERA_FIELD:
+         {
+-            if (typeid(cal) == typeid(ChineseCalendar) ||
+-                typeid(cal) == typeid(DangiCalendar)) {
++            const char* type = cal.getType();
++            if (strcmp(type, "chinese") == 0 ||
++                strcmp(type, "dangi") == 0) {
+                 zeroPaddingNumber(currentNumberFormat,appendTo, value, 1, 9); 
// as in ICU4J
+             } else {
+                 if (count == 5) {
+                     _appendSymbol(appendTo, value, fSymbols->fNarrowEras, 
fSymbols->fNarrowErasCount);
+                     capContextUsageType = 
DateFormatSymbols::kCapContextUsageEraNarrow;
+                 } else if (count == 4) {
+                     _appendSymbol(appendTo, value, fSymbols->fEraNames, 
fSymbols->fEraNamesCount);
+                     capContextUsageType = 
DateFormatSymbols::kCapContextUsageEraWide;
diff --git 
a/gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch
 
b/gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch
new file mode 100644
index 0000000000..2f9bae22ea
--- /dev/null
+++ 
b/gnu/packages/patches/icu4c-bug-2000225-ICU-23264-increase-measure-unit-capacity.patch
@@ -0,0 +1,27 @@
+# Increase measure unit capacity for ICU 78.
+#
+# ICU bug: https://unicode-org.atlassian.net/browse/ICU-23264
+
+diff --git a/source/i18n/number_skeletons.cpp 
b/source/i18n/number_skeletons.cpp
+--- a/source/i18n/number_skeletons.cpp
++++ b/source/i18n/number_skeletons.cpp
+@@ -1067,18 +1067,17 @@ void blueprint_helpers::parseMeasureUnit
+     }
+ 
+     // Need to do char <-> char16_t conversion...
+     CharString type;
+     SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
+     CharString subType;
+     SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, 
stemString.length(), status);
+ 
+-    // Note: the largest type as of this writing (Aug 2020) is "volume", 
which has 33 units.
+-    static constexpr int32_t CAPACITY = 40;
++    static constexpr int32_t CAPACITY = 50;
+     MeasureUnit units[CAPACITY];
+     UErrorCode localStatus = U_ZERO_ERROR;
+     int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, 
CAPACITY, localStatus);
+     if (U_FAILURE(localStatus)) {
+         // More than 30 units in this type?
+         status = U_INTERNAL_PROGRAM_ERROR;
+         return;
+     }
diff --git 
a/gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch 
b/gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch
new file mode 100644
index 0000000000..e6cba7e2ee
--- /dev/null
+++ b/gnu/packages/patches/icu4c-bug-2002735-ICU-23277-coptic-single-era.patch
@@ -0,0 +1,109 @@
+diff --git a/source/i18n/coptccal.cpp b/source/i18n/coptccal.cpp
+--- a/source/i18n/coptccal.cpp
++++ b/source/i18n/coptccal.cpp
+@@ -61,46 +61,43 @@ int32_t
+ CopticCalendar::handleGetExtendedYear(UErrorCode& status)
+ {
+     if (U_FAILURE(status)) {
+         return 0;
+     }
+     if (newerField(UCAL_EXTENDED_YEAR, UCAL_YEAR) == UCAL_EXTENDED_YEAR) {
+         return internalGet(UCAL_EXTENDED_YEAR, 1); // Default to year 1
+     }
+-    // The year defaults to the epoch start, the era to CE
+-    int32_t era = internalGet(UCAL_ERA, CE);
+-    if (era == BCE) {
+-        return 1 - internalGet(UCAL_YEAR, 1); // Convert to extended year
+-    }
+-    if (era == CE){
+-        return internalGet(UCAL_YEAR, 1); // Default to year 1
+-    }
+-    status = U_ILLEGAL_ARGUMENT_ERROR;
+-    return 0;
++    // The year defaults to the epoch start
++    return internalGet(UCAL_YEAR, 1); // Default to year 1
+ }
+ 
+ IMPL_SYSTEM_DEFAULT_CENTURY(CopticCalendar, "@calendar=coptic")
+ 
+ int32_t
+ CopticCalendar::getJDEpochOffset() const
+ {
+     return COPTIC_JD_EPOCH_OFFSET;
+ }
+ 
+ int32_t CopticCalendar::extendedYearToEra(int32_t extendedYear) const {
+-    return extendedYear <= 0 ? BCE : CE;
++    return CE;
+ }
+ 
+ int32_t CopticCalendar::extendedYearToYear(int32_t extendedYear) const {
+-    return extendedYear <= 0 ? 1 - extendedYear : extendedYear;
++    return extendedYear;
+ }
+ 
+-bool CopticCalendar::isEra0CountingBackward() const {
+-    return true;
++int32_t
++CopticCalendar::handleGetLimit(UCalendarDateFields field, ELimitType 
limitType) const
++{
++    if (field == UCAL_ERA) {
++        return 1; // Only one era, era is always 1
++    }
++    return CECalendar::handleGetLimit(field, limitType);
+ }
+ 
+ int32_t
+ CopticCalendar::getRelatedYearDifference() const {
+     constexpr int32_t kCopticCalendarRelatedYearDifference = 284;
+     return kCopticCalendarRelatedYearDifference;
+ }
+ 
+diff --git a/source/i18n/coptccal.h b/source/i18n/coptccal.h
+--- a/source/i18n/coptccal.h
++++ b/source/i18n/coptccal.h
+@@ -165,16 +165,22 @@ protected:
+     int32_t getRelatedYearDifference() const override;
+ 
+     /**
+      * Return the extended year defined by the current fields.
+      * @internal
+      */
+     virtual int32_t handleGetExtendedYear(UErrorCode& status) override;
+ 
++    /**
++     * Calculate the limit for a specified type of limit and field
++     * @internal
++     */
++    virtual int32_t handleGetLimit(UCalendarDateFields field, ELimitType 
limitType) const override;
++
+     DECLARE_OVERRIDE_SYSTEM_DEFAULT_CENTURY
+ 
+     /**
+      * Return the date offset from Julian
+      * @internal
+      */
+     int32_t getJDEpochOffset() const override;
+ 
+@@ -184,21 +190,16 @@ protected:
+      */
+     int32_t extendedYearToEra(int32_t extendedYear) const override;
+ 
+     /**
+      * Compute the year from extended year.
+      * @internal
+      */
+     int32_t extendedYearToYear(int32_t extendedYear) const override;
+-
+-    /**
+-     * @internal
+-     */
+-    bool isEra0CountingBackward() const override;
+ public:
+     /**
+      * Override Calendar Returns a unique class ID POLYMORPHICALLY. Pure 
virtual
+      * override. This method is to implement a simple version of RTTI, since 
not all C++
+      * compilers support genuine RTTI. Polymorphic operator==() and clone() 
methods call
+      * this method.
+      *
+      * @return   The class ID for this object. All objects of a given class 
have the
diff --git a/gnu/packages/patches/icu4c-suppress-warnings.patch 
b/gnu/packages/patches/icu4c-suppress-warnings.patch
new file mode 100644
index 0000000000..e8c3e34b4a
--- /dev/null
+++ b/gnu/packages/patches/icu4c-suppress-warnings.patch
@@ -0,0 +1,80 @@
+diff --git a/source/acinclude.m4 b/source/acinclude.m4
+--- a/source/acinclude.m4
++++ b/source/acinclude.m4
+@@ -459,30 +459,36 @@ AC_DEFUN([AC_CHECK_STRICT_COMPILE],
+       ], [ac_use_strict_options=yes])
+     AC_MSG_RESULT($ac_use_strict_options)
+ 
+     if test "$ac_use_strict_options" = yes
+     then
+         if test "$GCC" = yes
+         then
+             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith 
-Wmissing-prototypes -Wwrite-strings"
++
++            # Suppress clang C warnings:
++            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CFLAGS="$CFLAGS /W4"
+                 fi ;;
+             *-*-mingw*)
+                 CFLAGS="$CFLAGS -W4" ;;
+             esac
+         fi
+         if test "$GXX" = yes
+         then
+             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith 
-Wwrite-strings -Wno-long-long"
++
++            # Suppress clang C++ warnings:
++            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CXXFLAGS="$CXXFLAGS /W4"
+                 fi ;;
+             *-*-mingw*)
+diff --git a/source/configure b/source/configure
+--- a/source/configure
++++ b/source/configure
+@@ -5227,30 +5227,36 @@ fi
+     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: 
$ac_use_strict_options" >&5
+ printf "%s\n" "$ac_use_strict_options" >&6; }
+ 
+     if test "$ac_use_strict_options" = yes
+     then
+         if test "$GCC" = yes
+         then
+             CFLAGS="$CFLAGS -Wall -pedantic -Wshadow -Wpointer-arith 
-Wmissing-prototypes -Wwrite-strings"
++
++            # Suppress clang C warnings:
++            CFLAGS="$CFLAGS -Wno-sign-compare -Wno-unused"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CFLAGS="$CFLAGS /W4"
+                 fi ;;
+             *-*-mingw*)
+                 CFLAGS="$CFLAGS -W4" ;;
+             esac
+         fi
+         if test "$GXX" = yes
+         then
+             CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith 
-Wwrite-strings -Wno-long-long"
++
++            # Suppress clang C++ warnings:
++            CXXFLAGS="$CXXFLAGS -Wno-unused -Wno-unused-parameter"
+         else
+             case "${host}" in
+             *-*-cygwin)
+                 if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
+                 then
+                     CXXFLAGS="$CXXFLAGS /W4"
+                 fi ;;
+             *-*-mingw*)

Reply via email to