DateTimeZoneHandling - Values are serialized with Zulu Time

Page 1 of 2 12 LastLast
  1. #1

    DateTimeZoneHandling - Values are serialized with Zulu Time

    Hello, how can I set the DateTimeZoneHandling to Local in new .net5 projects?
    Moreover I get documentation about Newtonsoft, but I think default serializer are ext.

    When I post a DateField value I get zulu time and date changes.

    Thank you!!
  2. #2
    Hello @bbros!

    I'm not sure how you're handling it, could you provide a test case that illustrates the unexpected time zone behavior?

    There may be an issue related to that due to a breaking change Sencha introduced with client-side serialization around late 4.x and 5.x versions that could affect your scenario but we'd only be able to help you if we know we are on the same page.

    Looking forward to your follow-up!
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Sure!
    here is the repro project:

    DateTimeZoneHandling.cshtml
    @page
    @model ExtCookbook.Pages.DateTimeZoneHandlingModel
    @{
    }
    <ext-section target="Main">
        <ext-container region="Center" scrollable="true" paddingAsString="30 20 30 50">
            <content>
                <h1>DateField</h1>
                <ext-container id="MainContainer" model="Model.MainContainer">
                </ext-container>
            </content>
        </ext-container>
    </ext-section>
    DateTimeZoneHandling.cshtml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Ext.Net;
    using Ext.Net.Core;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    
    namespace ExtCookbook.Pages
    {
        public class DateTimeZoneHandlingModel : PageModel
        {
            public FieldContainer MainContainer { get; set; }
    
            public void OnGet()
            {
                MainContainer = new FieldContainer()
                {
                    Anchor = "100%"
                };
                MainContainer.Items.Add(new DateField()
                { 
                    Id="myDateField"
                });
                var btn = new Button()
                {
                    Text = "Click me"
                };
                btn.DirectEvents.Click.Method = HttpMethod.POST;
                btn.DirectEvents.Click.Url = $"?handler=ClickMeButtonClick";
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myDate", Value = "App.myDateField.value", Mode = ParameterMode.Raw });
    
                MainContainer.Items.Add(btn);
    
            }
    
            public IActionResult OnPostClickMeButtonClick(JsObject jObj)
            {
                string postedValue = jObj.GetValueOrDefault("myDate").Value.ToString();
                this.X().Toast(postedValue);
    
                return this.Direct();
            }
        }
    }
    and the result, here in Italy where we are GMT+2, is this:
    Click image for larger version. 

Name:	DateTimeZoneHandling.png 
Views:	79 
Size:	24.3 KB 
ID:	25554
  4. #4
    Converting to local time serverside I get what I need, anyhow it is confusing.
    TimeField as well returns a DateTime instead of TimeSpan, and it's date part is 2008-01-01 which could have a different daylight saving time.

    var date = DateTime.Parse(postedValue, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
  5. #5
    Hello @bbros!

    Thanks for the test case and also for sharing the solution you went with so far.

    Your solution will work just fine for you, if you can ensure everyone will be in the same time zone of your server, or if you do extra math to infer the time zone the clients are in, and calculate the "UTC skew" or something like it. But no matter how we look at it, it really sounds complicated and error-prone, don't you agree?

    So what if we could pass just what we see in the field?

    I have changed line 32 from your model code to this:

    btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myDate", Value = "App.myDateField.getRawValue()", Mode = ParameterMode.Raw });
    So that we send the date that is filled and displayed in the field, instead of letting client browsers go awol with their complex and perhaps mostly unnecessary time zone and skew calculus and give the server hard times. I've seen some discussions on browsers handling differently different time zones so my advice is, whenever you can avoid letting the browser handle it for you, do avoid it.

    And in fact, as I pointed in our last post, a change from Sencha's ExtJS (Ext.NET's underlying framework), from late Ext.NET 4.x, induced a bit more "browser date handling" while serializing data to the server. But as per your test case, it was fortunately not the case and you already got full control on what you're sending, so it's a good scenario where you can "tame" what's going in to the server.

    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  6. #6
    Thank you, getRawValue() is the solution.

    great!
  7. #7
    Glad we could be of help! Thanks for the feedback!
    Fabrício Murta
    Developer & Support Expert
  8. #8
    I share other few lines to keep backwards compatibility.

    My JsObject values are assumed to be DateTime and not String; so using those as SqlCommand Parameters, SQL Server handles datetime conversion in unpredictable way (in my case wrong because of Italian OS over US Sql server).

    Assuming to have more than one parameter inside a JsObject I did this way

    public IActionResult OnPostClickMeButtonClick(JsObject jObj)
    {
       foreach (var obj in jObj)
                {
                    if (Microsoft.VisualBasic.Information.IsDate(obj.Value.Value)) obj.Value.Value = DateTime.Parse(obj.Value.Value.ToString());
                }
    
    [...]
    }
  9. #9
    Hello again @bbros! Thanks for sharing the bits that helped you expand on the goal you have there.

    I'm not sure it is safe to let it convert to DateTime, as again, it may imply time zone to the server and, if you move to another server, or if you distribute the load in different servers, they can differently "assume" this or that time zone.

    If this is also a concern for you, and you want to keep using JsObject, it'd probably be a good idea to wrap your parameter in double quotes to ensure it is passed and interpreted as a string, and you extract the date segments from the string instead of letting the system go with conversions. Basically I'm raising up what was suggested in this post in our concurrent thread.

    Of course in case it is convenient a conversion and don't bring any trouble for you, it'd be fine to leave as-is. Just keep an eye open for subtle changes in the date values as it is inserted in the database.

    Thanks again for the follow up!
    Fabrício Murta
    Developer & Support Expert
  10. #10
    Hello, even I was still thinking of a bomb-proof solution.

    I believe that if the page seds a value that for me is well-defined, I am able to calculate how many minutes has been modified by the POST method.

    So the project could be modified by inserting a hidden field that contains the date 2020-01-01 (for example) and that is sent as a parameter.
    Evaluating the offset of the value received with the date 2020-01-01 I can reconstruct the value that the user on the other side of the world would have liked to send me.

    Changes are in lines 29-34, 47, 64-68

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using Ext.Net;
    using Ext.Net.Core;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    
    namespace ExtCookbook.Pages
    {
        public class DateTimeZoneHandlingModel : PageModel
        {
            public FieldContainer MainContainer { get; set; }
    
            public void OnGet()
            {
                MainContainer = new FieldContainer()
                {
                    Anchor = "100%"
                };
                MainContainer.Items.Add(new DateField()
                {
                    Id = "myDateField"
                });
                MainContainer.Items.Add(new TimeField()
                {
                    Id = "myTimeField"
                });
                MainContainer.Items.Add(new DateField()
                {
                    Id = "zuluTime",
                    Value = new DateTime(2020, 1, 1).ToShortDateString(),
                    FieldLabel = "zulu time offset calculator",                                
                });
                var btn = new Button()
                {
                    Text = "Click me"
                };
                btn.DirectEvents.Click.Method = HttpMethod.POST;
                btn.DirectEvents.Click.Url = $"?handler=ClickMeButtonClick";
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myDate", Value = "App.myDateField.getValue()", Mode = ParameterMode.Raw });
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myTime", Value = "App.myTimeField.getValue()", Mode = ParameterMode.Raw });
    
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myDateString", Value = "App.myDateField.getRawValue()", Mode = ParameterMode.Raw });
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "myTimeString", Value = "App.myTimeField.getRawValue()", Mode = ParameterMode.Raw });
    
                btn.DirectEvents.Click.ExtraParams.Add(new DirectEventParameter() { Key = "zuluTime", Value = "App.zuluTime.getValue()", Mode = ParameterMode.Raw });
    
                MainContainer.Items.Add(btn);
    
            }
    
            public IActionResult OnPostClickMeButtonClick(JsObject jObj)
            {
                string postedDateValue = jObj.GetValueOrDefault("myDate").Value.ToString();
                string postedTimeValue = jObj.GetValueOrDefault("myTime").Value.ToString();
    
                var date = DateTime.Parse(postedDateValue, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
                var time = DateTime.Parse(postedTimeValue, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
    
                var dateString = DateTime.Parse(postedDateValue);
                var timeString = DateTime.Parse(postedTimeValue);
    
                DateTime postedZuluTimeValue = DateTime.Parse(jObj.GetValueOrDefault("zuluTime").Value.ToString());
                var Offset = Microsoft.VisualBasic.DateAndTime.DateDiff(Microsoft.VisualBasic.DateInterval.Minute, new DateTime(2020, 01, 01), postedZuluTimeValue);
    
                var realDate = DateTime.Parse(postedDateValue).AddMinutes(-Offset);
                var realTime = DateTime.Parse(postedTimeValue).AddMinutes(-Offset);
    
                this.X().Toast(@$"Posted date value is {postedDateValue} and converted to local time is {date.ToShortDateString()}; string value is {dateString}<br />
    Posted date value is {postedTimeValue} and converted to local time is {time.ToShortTimeString()}; string value is {timeString}<br />
    <b>Offset in minutes is {Offset}</b></br>
    Submitted date value is <b>{realDate.ToShortDateString()}</b> and time is <b>{realTime.ToShortTimeString()}</b>");
    
                return this.Direct();
            }
        }
    }
    In this example, DateField is not hidden, server clock is GMT+2 and client clock is GMT+8.
    And the result is in this image:
    Click image for larger version. 

Name:	image_2021_09_15T14_27_00_123Z.png 
Views:	65 
Size:	44.0 KB 
ID:	25556

    About conversion from string I fear to get different format depending on the user local settings.
    I mean, here in Italy date to string is dd/MM/yyyy, but in US is MM/dd/yyyy.
    I don't know if 01/02/2003 is 1st of february or 2nd of january.
    Last edited by bbros; Sep 15, 2021 at 2:59 PM.
Page 1 of 2 12 LastLast

Similar Threads

  1. [CLOSED] Calling Web API and accessing page serialized fields
    By VSPL in forum 2.x Legacy Premium Help
    Replies: 4
    Last Post: Jul 23, 2015, 4:44 AM
  2. [CLOSED] Specify how object is serialized in DirectMethod
    By jchau in forum 2.x Legacy Premium Help
    Replies: 1
    Last Post: Jan 12, 2013, 7:18 PM
  3. [CLOSED] Serialized Microsoft Date Format
    By mkshields9w57 in forum 1.x Help
    Replies: 8
    Last Post: Jul 29, 2011, 5:17 PM
  4. DateTime.MinValue serialized as null
    By nextSTEP in forum 1.x Help
    Replies: 3
    Last Post: Mar 11, 2011, 5:46 PM
  5. Replies: 1
    Last Post: Aug 25, 2009, 10:27 AM

Posting Permissions