Closed JasonRDT closed 3 years ago
No, this will break people who depend on existing behavior.
Then why don't you simply create an overload of this method that allows someone to pass in their desired culture? This particular issue caused our company a LOT of problems and time spent investigating what was happening.
Like this method? https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JValue_ToString_1.htm
JValue v = new JValue(DateTime.UtcNow);
string invariant = v.ToString(CultureInfo.InvariantCulture);
// 05/17/2021 08:21:52
string current = v.ToString(new System.Globalization.CultureInfo("en-NZ"));
// 17/05/2021 8:21:52 AM
Yes, except for our data is already an instance of JToken as it is being returned from a call to JToken.SelectTokens.
JToken.ToString() does not support passing in the culture, which is why I suggested passing it into the Value
internal static U? Convert<T, U>(this T token, CultureInfo? culture = null) where T : JToken?
{
if (token == null)
{
#pragma warning disable CS8653 // A default expression introduces a null value for a type parameter.
return default;
#pragma warning restore CS8653 // A default expression introduces a null value for a type parameter.
}
if (culture == null)
{
culture = CultureInfo.InvariantCulture;
}
if (token is U castValue
// don't want to cast JValue to its interfaces, want to get the internal value
&& typeof(U) != typeof(IComparable) && typeof(U) != typeof(IFormattable))
{
return castValue;
}
else
{
if (!(token is JValue value))
{
throw new InvalidCastException("Cannot cast {0} to {1}.".FormatWith(culture, token.GetType(), typeof(T)));
}
if (value.Value is U u)
{
return u;
}
Type targetType = typeof(U);
if (ReflectionUtils.IsNullableType(targetType))
{
if (value.Value == null)
{
#pragma warning disable CS8653 // A default expression introduces a null value for a type parameter.
return default;
#pragma warning restore CS8653 // A default expression introduces a null value for a type parameter.
}
targetType = Nullable.GetUnderlyingType(targetType);
}
return (U)System.Convert.ChangeType(value.Value, targetType, culture);
}
}
There appears to be an issue with the call to the JToken.Value<T> method, when working with JTokenType.Date tokens. I am in the UK and would expect to see dates always formatted in UK style i.e. as "dd/MM/yyyy". When calling the JToken.Value<string> method on a token whose token Type is JTokenType.Date, the returned date is always serialised in US format as "MM/dd/yyyy".
You can reproduce this issue by setting Region->Format to English (United Kingdom) in Control Panel, and then running the following code:
I would have expected to see the following output in the console window:
Calling DateTime.ToString: 06/05/2021 12:00:00 Calling JToken.Value<string>: 06/05/2021 12:00:00
But what I actually see returned is the following:
Calling DateTime.ToString: 06/05/2021 12:00:00 Calling JToken.Value<string>: 05/06/2021 12:00:00
As you can see, the call to JToken.Value<string> returns the serialised date with the day and month transposed into US format as "MM/dd/yyyy". This call should be preserving local culture settings when returning the serialised date.
UPDATE:
I have traced this behaviour to the following line of code in Newtonsoft.Json/Linq/Extensions.cs, line 297:
InvariantCulture uses many en-US formatted date and time strings. Switching this to use CurrentCulture resolves the issue, as you can see by executing the following code:
Can this be corrected?