Nodatime Formatting Long Date Pattern From A Specific Culture But Using Month Names From Another Culture

In Noda Time, I want to format a Long Date Pattern using a specific culture but using month names and day names from another culture.

My initial code is:

var dtfi = (DateTimeFormatInfo) CultureInfo.CurrentCulture.DateTimeFormat.Clone(); // fr-FR
dtfi.DayNames = CultureInfo.CurrentUICulture.DateTimeFormat.DayNames; // en-US
dtfi.MonthNames = CultureInfo.CurrentUICulture.DateTimeFormat.MonthNames;
dtfi.MonthGenitiveNames = CultureInfo.CurrentUICulture.DateTimeFormat.MonthGenitiveNames;
return localDate.ToString("D", dtfi);

Cloning DateTimeFormat didn't work. The date was still printed with day names and month names still in French. But if I cloned the CurrentCulture, it would work:

var ci = (CultureInfo) CultureInfo.CurrentCulture.Clone(); // fr-FR
ci.DateTimeFormat.DayNames = CultureInfo.CurrentUICulture.DateTimeFormat.DayNames; // en-US
ci.DateTimeFormat.MonthNames = CultureInfo.CurrentUICulture.DateTimeFormat.MonthNames;
ci.DateTimeFormat.MonthGenitiveNames = CultureInfo.CurrentUICulture.DateTimeFormat.MonthGenitiveNames;
return localDate.ToString("D", ci);

In both code snippets, I traced the code and can see that the day names, month names and month genitive names were assigned before the call to localDate.ToString.

Can anyone explain why the first snippet doesn't work?

p.s. Noda Time is an amazing library and a reason why I still have hair.

Jon Skeet
people
quotationmark

Yes, this is a problem with the architecture of IFormatProvider, basically - which we don't handle as well as we might. Both DateTimeFormatInfo and CultureInfo implement IFormatProvider, but in order to perform formatting in some cases, we need more than a DateTimeFormatInfo - we need the NumberFormatInfo (for positive and negative signs) and the CompareInfo (for text comparisons).

As a result, we basically do the wrong thing when we're given an IFormatInfo which isn't a CultureInfo - we end up using the current culture.

We have an issue in github for something similar, but while it's obvious that if you pass in just a NumberFormatInfo, that can't get at date/time settings, it's less obvious that things will go wrong when passing in a DateTimeFormatInfo.

I could potentially make such calls throw an exception with a useful message instead of just doing the wrong thing... or I could just use the invariant culture to obtain the CompareInfo and NumberFormatInfo if you pass in a DateTimeFormatInfo.

Note that this may end up being quite slow as we'd have to rebuild the internal NodaTimeFormatInfo on each call, as you're passing in a mutable DateTimeFormatInfo or CultureInfo in each case. It would be better to create a LocalDatePattern from a CultureInfo - in which case it's up to you not to change the CultureInfo any further afterwards. (Basically, CultureInfo being mutable is a massive pain.)

people

See more on this question at Stackoverflow