REST converting ISO dates in character fields to US dates

Anyone want to take bets how long it takes Mark to come in and pimp System.Text.Json ? :rofl:

Who had 10:13 PM EDT in the over-under? :face_with_hand_over_mouth:

I mean, Jimmy works for Microsoft now. :man_shrugging:t2:

Hopefully they reigned in his attitude.

He does have a JSON schema validator module if you want to pay him though.

CS0003782348 - ISO dates in character fields are convertd to dates throughout Kinetic, including REST API.

I don’t get what the usefulness is of the datetime handling in the Newtonsoft parser. It just transforms one string representation of a datetime into another string representation. And arguably the least useful of all string representations.

My original title was REST API converts ISO dates in character fields to US dates .

They changed that to include the Kinetic UI, but left out the “US”. As if US date strings are “dates” and everything else needs to be labelled.

Not quite. It actually makes it into a datetime. And since string was requested, it gets a ToString() called on it.

Which is arguably even worse, because by default, it not only made an unrequested assumption, it did not even offer you an opportunity to change the format lol.

Would this work the other way round? Disable the datetime handling in the parser, pass both dates and strings as strings, and have the strings converted to dates automagically when a date is requested?

From what I understood, it’s only a datetime internally, when it comes out as string, it’s gonna be mangled.

If you disabled the DateTime handling, it should behave as we would expect.

Does it always come out as a string? If so, then my claim that this is just a transformation from one string format to another is true.

If you convert @josecgomez 's example to this, it behaves as expected:

namespace jsonthing;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

class Program
{
    static void Main(string[] args)
    {
        JToken jtokenValue = JsonConvert.DeserializeObject<JToken>(@"{
                            ""ds"": {
                                ""UD40"": [
                                {
                                    ""Company"": ""C001"",
                                    ""Key1"": ""2023-07-17T15:43:42.461Z"",
                            }
                                    ]
                            }
                            }", new JsonSerializerSettings(){ DateParseHandling = DateParseHandling.None });

                    var serialized = new JsonSerializer();
                    JTokenReader jsonReader = new JTokenReader(jtokenValue);

                    //Loop through token reader
                    while (jsonReader.Read())
                    {
                        if (jsonReader.TokenType == JsonToken.PropertyName)
                        {
                            if (jsonReader.Value.ToString() == "Key1")
                            {
                                jsonReader.Read();
                                Console.WriteLine(jsonReader.Value);
                            }
                        }
                    }
    }
}

Output: 2023-07-17T15:43:42.461Z

You can’t just add:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None
};

and expect it to behave, because the Parse methods don’t follow it, if you switch to DeserializeObject, you can however, add that, or specify it on the method like shown.

I didn’t take it apart, but I suspect if we look a method or two before the

private static void CreateRow(JsonReader reader, ....) in Epicors server code, we could see how to fix it.

Newtonsoft does the same thing with Microsoft Dates as well

namespace jsonthing;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

class Program
{
    static void Main(string[] args)
    {
        string jsonPart1 = @"{
                            ""ds"": {
                                ""UD40"": [
                                {
                                    ""Company"": ""C001"",
                                    ""Key1"": """;

        string jsonPart3 = @""",
                            }
                                    ]
                            }
                            }";

        JToken jtokenValue1 = JToken.Parse($"{jsonPart1}2023-07-17T15:43:42.461Z{jsonPart3}");
        JToken jtokenValue2 = JsonConvert.DeserializeObject<JToken>($"{jsonPart1}2023-07-17T15:43:42.461Z{jsonPart3}", new JsonSerializerSettings(){ DateParseHandling = DateParseHandling.None });

        JToken jtokenValue3 = JToken.Parse($"{jsonPart1}/Date(1198908717056)/{jsonPart3}");
        JToken jtokenValue4 = JsonConvert.DeserializeObject<JToken>($"{jsonPart1}/Date(1198908717056)/{jsonPart3}", new JsonSerializerSettings(){ DateParseHandling = DateParseHandling.None });

        List<JToken> listJToken = new List<JToken>();
        listJToken.Add(jtokenValue1);
        listJToken.Add(jtokenValue2);
        listJToken.Add(jtokenValue3);
        listJToken.Add(jtokenValue4);

        foreach(JToken jtokenValue in listJToken)
        {
            var serialized = new JsonSerializer();
            JTokenReader jsonReader = new JTokenReader(jtokenValue);

            //Loop through token reader
            while (jsonReader.Read())
            {
                if (jsonReader.TokenType == JsonToken.PropertyName)
                {
                    if (jsonReader.Value.ToString() == "Key1")
                    {
                        jsonReader.Read();
                        Console.WriteLine(jsonReader.Value);
                    }
                }
            }
        }
    }
}

Output:

7/17/2023 3:43:42 PM
2023-07-17T15:43:42.461Z
12/29/2007 6:11:57 AM
/Date(1198908717056)/
1 Like

Well, if it is still one line to change, then JamesNK is not a liar.

1 Like

Right but then if you sent an actual date it wouldn’t work

I don’t think we said he was, we said he was an :peach: .

That’s the responsibility of the the class providing the type no? Maybe I’m not understanding what you are saying.

I think I understand. No, not the way they have it coded, but they are already building the row column by column. A simple check on the column type for datetime, and a DateTime.Parse will take care of it.

If you pass in Date01 and have that DateHandling set to none you won’t be able to assign the result to the typed Date01 unless you specifically handle each date yourself so you wouldn’t be able to easily say just deserialize into this type

You’d have to go and manually parse ir handle each date field or give it a custom date handler that would somehow know the difference between a string string and a string date lol

That’s what I’m saying they need to do.

I don’t think that’s a big deal, since they are already building the row, column by column.

If newtonsoft had added where it would do the conversion, but keep the original string value, you could leave their stupid defaults, and get the original string. Then epicor could just do a simple check on column type and shove the correct value in based on type, bypassing the parse step. (Of course the parse in that case was done, just in Newtonsoft land)