diff --git a/docs/design/features/globalization-hybrid-mode.md b/docs/design/features/globalization-hybrid-mode.md
index 7f7b3bba2c03b..373804adfd9ff 100644
--- a/docs/design/features/globalization-hybrid-mode.md
+++ b/docs/design/features/globalization-hybrid-mode.md
@@ -272,6 +272,61 @@ Using `IgnoreNonSpace` for these two with `HybridGlobalization` off, also return
new CultureInfo("de-DE").CompareInfo.IndexOf("strasse", "stra\u00DFe", 0, CompareOptions.IgnoreNonSpace); // 0 or -1
```
+**Calandars**
+
+Affected public APIs:
+- DateTimeFormatInfo.AbbreviatedDayNames
+- DateTimeFormatInfo.GetAbbreviatedDayName()
+- DateTimeFormatInfo.AbbreviatedMonthGenitiveNames
+- DateTimeFormatInfo.AbbreviatedMonthNames
+- DateTimeFormatInfo.GetAbbreviatedMonthName()
+- DateTimeFormatInfo.AMDesignator
+- DateTimeFormatInfo.CalendarWeekRule
+- DateTimeFormatInfo.DayNames
+- DateTimeFormatInfo.GetDayName
+- DateTimeFormatInfo.GetAbbreviatedEraName()
+- DateTimeFormatInfo.GetEraName()
+- DateTimeFormatInfo.FirstDayOfWeek
+- DateTimeFormatInfo.FullDateTimePattern
+- DateTimeFormatInfo.LongDatePattern
+- DateTimeFormatInfo.LongTimePattern
+- DateTimeFormatInfo.MonthDayPattern
+- DateTimeFormatInfo.MonthGenitiveNames
+- DateTimeFormatInfo.MonthNames
+- DateTimeFormatInfo.GetMonthName()
+- DateTimeFormatInfo.NativeCalendarName
+- DateTimeFormatInfo.PMDesignator
+- DateTimeFormatInfo.ShortDatePattern
+- DateTimeFormatInfo.ShortestDayNames
+- DateTimeFormatInfo.GetShortestDayName()
+- DateTimeFormatInfo.ShortTimePattern
+- DateTimeFormatInfo.YearMonthPattern
+
+
+The Hybrid responses may differ because they use Web API functions. To better ilustrate the mechanism we provide an example for each endpoint. All exceptions cannot be listed, for reference check the response of specific version of Web API on your host.
+| **API** | **Functions used** | **Example of difference for locale** | **non-Hybrid** | **Hybrid** |
+|:-----------------------------:|:----------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------:|:------------------:|:-----------------:|
+| AbbreviatedDayNames | `Date.prototype.toLocaleDateString(locale, { weekday: "short" })` | en-CA | Sun. | Sun |
+| AbbreviatedMonthGenitiveNames | `Date.prototype.toLocaleDateString(locale, { month: "short", day: "numeric"})` | kn-IN | ಆಗ | ಆಗಸ್ಟ್ |
+| AbbreviatedMonthNames | `Date.prototype.toLocaleDateString(locale, { month: "short" })` | lt-LT | saus. | 01 |
+| AMDesignator | `Date.prototype.toLocaleTimeString(locale, { hourCycle: "h12"})`; `Date.prototype.toLocaleTimeString(locale, { hourCycle: "h24"})` | sr-Cyrl-RS | пре подне | AM |
+| CalendarWeekRule | `Intl.Locale.prototype.getWeekInfo().minimalDay` | none | - | - |
+| DayNames | `Date.prototype.toLocaleDateString(locale, { weekday: "long" })` | none | - | - |
+| GetAbbreviatedEraName() | `Date.prototype.toLocaleDateString(locale, { era: "narrow" })` | bn-IN | খৃষ্টাব্দ | খ্রিঃ |
+| GetEraName() | `Date.prototype.toLocaleDateString(locale, { era: "short" })` | vi-VI | sau CN | CN |
+| FirstDayOfWeek | `Intl.Locale.prototype.getWeekInfo().firstDay` | zn-CN | Sunday | Monday |
+| FullDateTimePattern | `LongDatePattern` and `LongTimePattern` | - | | |
+| LongDatePattern | `Intl.DateTimeFormat(locale, { weekday: "long", year: "numeric", month: "long", day: "numeric"}).format(date)` | en-BW | dddd, dd MMMM yyyy | dddd, d MMMM yyyy |
+| LongTimePattern | `Intl.DateTimeFormat(locale, { timeStyle: "medium" })` | zn-CN | tth:mm:ss | HH:mm:ss |
+| MonthDayPattern | `Date.prototype.toLocaleDateString(locale, { month: "long", day: "numeric"})` | en-PH | d MMMM | MMMM d |
+| MonthGenitiveNames | `Date.prototype.toLocaleDateString(locale, { month: "long", day: "numeric"})` | ca-AD | de gener | gener |
+| MonthNames | `Date.prototype.toLocaleDateString(locale, { month: "long" })` | el-GR | Ιανουαρίου | Ιανουάριος |
+| NativeCalendarName | `Intl.Locale.prototype.getCalendars()` | for all locales it has English names | Gregorian Calendar | gregory |
+| PMDesignator | `Date.prototype.toLocaleTimeString(locale, { hourCycle: "h12"})`; `Date.prototype.toLocaleTimeString(locale, { hourCycle: "h24"})` | mr-IN | म.उ. | PM |
+| ShortDatePattern | `Date.prototype.toLocaleDateString(locale, {dateStyle: "short"})` | en-CH | dd.MM.yyyy | dd/MM/yyyy |
+| ShortestDayNames | `Date.prototype.toLocaleDateString(locale, { weekday: "narrow" })` | none | - | - |
+| ShortTimePattern | `Intl.DateTimeFormat(locale, { timeStyle: "medium" })` | bg-BG | HH:mm | H:mm |
+| YearMonthPattern | `Date.prototype.toLocaleDateString(locale, { year: "numeric", month: "long" })` | ar-SA | MMMM yyyy | MMMM yyyy g |
### OSX
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Calendar.cs b/src/libraries/Common/src/Interop/Browser/Interop.Calendar.cs
new file mode 100644
index 0000000000000..2a7b1d02ad21a
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Calendar.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using System.Runtime.CompilerServices;
+
+internal static partial class Interop
+{
+ internal static unsafe partial class JsGlobalization
+ {
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern unsafe int GetCalendarInfo(in string culture, CalendarId calendarId, char* buffer, int bufferLength, out int exceptionalResult, out object result);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Locale.cs b/src/libraries/Common/src/Interop/Browser/Interop.Locale.cs
new file mode 100644
index 0000000000000..c882d88afac25
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Locale.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+internal static partial class Interop
+{
+ internal static unsafe partial class JsGlobalization
+ {
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern unsafe int GetCultureInfo(in string culture, char* buffer, int bufferLength, out int exceptionalResult, out object result);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern unsafe int GetFirstDayOfWeek(in string culture, out int exceptionalResult, out object result);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern unsafe int GetFirstWeekOfYear(in string culture, out int exceptionalResult, out object result);
+ }
+}
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
index da450c017006b..c0bbcaa90df2c 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs
@@ -366,6 +366,7 @@ public static string GetDistroVersionString()
public static bool IsNotHybridGlobalizationOnBrowser => !IsHybridGlobalizationOnBrowser;
public static bool IsNotInvariantGlobalization => !IsInvariantGlobalization;
public static bool IsIcuGlobalization => ICUVersion > new Version(0, 0, 0, 0);
+ public static bool IsIcuGlobalizationAndNotHybridOnBrowser => IsIcuGlobalization && IsNotHybridGlobalizationOnBrowser;
public static bool IsNlsGlobalization => IsNotInvariantGlobalization && !IsIcuGlobalization;
public static bool IsSubstAvailable
diff --git a/src/libraries/System.Globalization.Calendars/tests/Hybrid/System.Globalization.Calendars.Hybrid.WASM.Tests.csproj b/src/libraries/System.Globalization.Calendars/tests/Hybrid/System.Globalization.Calendars.Hybrid.WASM.Tests.csproj
new file mode 100644
index 0000000000000..5b89836376408
--- /dev/null
+++ b/src/libraries/System.Globalization.Calendars/tests/Hybrid/System.Globalization.Calendars.Hybrid.WASM.Tests.csproj
@@ -0,0 +1,109 @@
+
+
+ $(NetCoreAppCurrent)-browser
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs b/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs
index 62e1b36006987..c99b883aa7728 100644
--- a/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs
+++ b/src/libraries/System.Globalization.Calendars/tests/System/Globalization/CalendarTestBase.cs
@@ -430,7 +430,10 @@ public void GetEra_Invalid_ThrowsArgumentOutOfRangeException()
Assert.All(DateTime_TestData(calendar), dt =>
{
// JapaneseCalendar throws on ICU, but not on NLS
- if ((calendar is JapaneseCalendar && PlatformDetection.IsNlsGlobalization) || calendar is HebrewCalendar || calendar is TaiwanLunisolarCalendar || calendar is JapaneseLunisolarCalendar)
+ if ((calendar is JapaneseCalendar && PlatformDetection.IsNlsGlobalization) ||
+ calendar is HebrewCalendar ||
+ calendar is TaiwanLunisolarCalendar ||
+ calendar is JapaneseLunisolarCalendar)
{
calendar.GetEra(dt);
}
diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs
index f1af25e43ba69..37175600777b4 100644
--- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs
+++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs
@@ -442,7 +442,7 @@ public void TestCreationWithTemporaryLCID(int lcid)
[InlineData("de-DE-u-co-phonebk-t-xx", "de-DE-t-xx", "de-DE-t-xx_phoneboo")]
[InlineData("de-DE-u-co-phonebk-t-xx-u-yy", "de-DE-t-xx-u-yy", "de-DE-t-xx-u-yy_phoneboo")]
[InlineData("de-DE", "de-DE", "de-DE")]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization))]
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser))]
public void TestCreationWithMangledSortName(string cultureName, string expectedCultureName, string expectedSortName)
{
CultureInfo ci = CultureInfo.GetCultureInfo(cultureName);
@@ -457,7 +457,7 @@ public void TestCreationWithMangledSortName(string cultureName, string expectedC
[InlineData("qps-plocm", "qps-PLOCM")] // ICU normalize this name to "qps--plocm" which we normalize it back to "qps-plocm"
[InlineData("zh_CN", "zh_cn")]
[InlineData("km_KH", "km_kh")]
- [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalization), nameof(PlatformDetection.IsNotWindowsServerCore))]
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser), nameof(PlatformDetection.IsNotWindowsServerCore))]
public void TestCreationWithICUNormalizedNames(string cultureName, string expectedCultureName)
{
CultureInfo ci = CultureInfo.GetCultureInfo(cultureName);
diff --git a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoAMDesignator.cs b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoAMDesignator.cs
index 5c284c5ef872e..0eeb6d9c4a738 100644
--- a/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoAMDesignator.cs
+++ b/src/libraries/System.Globalization/tests/DateTimeFormatInfo/DateTimeFormatInfoAMDesignator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Xunit;
+using System.Collections.Generic;
namespace System.Globalization.Tests
{
@@ -13,6 +14,204 @@ public void AMDesignator_GetInvariantInfo_ReturnsExpected()
Assert.Equal("AM", DateTimeFormatInfo.InvariantInfo.AMDesignator);
}
+ public static IEnumerable