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.
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.)
See more on this question at Stackoverflow