Issue with dates in Telerik's grid (admin)

1 2 >
Posted: December 19, 2011 at 1:14 PM Quote #57794
Hi,

I'm using NopCommerce 2.3, slighty modified but no modifications in direct relation with issue. I'm using SQL Server 2008 Express R2.

I'm experiencing an issue with the display of DateTimes in telerik's ajax grid. My store's time zone is configured as EST (eastern UTC -5). My web server's time zone is PST (pacific UTC -8). I oly use the store time, customers can't set their own timezone. For instance, I have an order created on 19-12-2011 4:08 AM UTC that shows as created on 19-12-2011 2:08 AM (store time) in the grid. I'm expecting to see 18-12-2011 11:08 PM (store time).

There's a thread in telerik's forums explaining this problem.

In my case, I made the following calculations to the "wrong" date in th grid:
19-12-2011 4:08 UTC > user (store) time : -5h = 18-12-2011 11:08 EST
18-12-2011 11:08 EST serialized (must be in utc, so converted from local to utc, but using 18-12-2011 11:08 EST as local) : +8h = 19-12-2011 7:08 UTC
19-12-2011 7:08 UTC converted to client's local time (telerik) : -5h = 19-12-2011 2:08 EST (wrong)

I temporarily took their solution to convert it back to utc on client side and then reapply the webserver's utc offset (-8) to get to the good date/time. This allowed me to only modify the code in the views and not in the controllers.
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Alexandre Soares
http://www.sowee.ca
Posted: December 19, 2011 at 1:25 PM Quote #57796
On what admin page are you getting this error?
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
Posted: December 19, 2011 at 1:32 PM Quote #57799
And what browser are you using?
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
Posted: December 19, 2011 at 1:50 PM Quote #57805
I got the error on the orders' list page (Sales > Orders in admin menu) but I think the issue exists wherever there's a telerik grid with a date in it (ex logs). I am using IE9 but could also experience the issue in the latest chrome).
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Alexandre Soares
http://www.sowee.ca
Posted: December 20, 2011 at 12:30 AM Quote #57849
It works fine on my machine, but I see this issue on the admin demo site. I'll investigate it.

Thanks for reporting
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
Posted: December 20, 2011 at 11:59 AM Quote #57904
The issue will only show up when there's a difference in the client's (browser) timezone and the server's timezone.
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Alexandre Soares
http://www.sowee.ca
Posted: May 10, 2012 at 1:34 PM Quote #69462
Hi Andrei,

Did you check this issue?

I should create a fork and add my contributions there from my local cloned repository but since I haven't done that yet, here's my fix:

In _AdminLayout.cshtml, in the server side code at the top I added:


    var delta = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).TotalHours;


and then in the head section:

<script type="text/javascript">
        function toServerTime(date)
        {
            var timeAdjustment = @delta;
            var utcDate = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds());
            // offset measured in milliseconds
            var serverOffset = timeAdjustment * 60 * 60 * 1000;
            var serverTime = new Date(utcDate.getTime() + serverOffset);
            return $.telerik.formatString("{0:G}", serverTime);
         }
    </script>


Then whenever you want to convert a time in a grid, use (ex. with CreatedOn property):

columns.Bound(x => x.CreatedOn).ClientTemplate("<#= toServerTime(CreatedOn) #>");
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Alexandre Soares
http://www.sowee.ca
Posted: May 10, 2012 at 1:45 PM Quote #69467
asoares wrote:
Did you check this issue?

Yes. But I still did not find a good solution. Your one also doesn't work well for all scenarios. When setting delta value in your code you're using current datetime. It won't work for old dates. I described it with some example on Telerik forums here (seems that their forums don't work right now)

Maybe, the most correct way will be returning string value of dates. But it's not very elegant.
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
Posted: May 11, 2012 at 4:40 PM Quote #69539
I think I have a solution.

In every controller method that returns date time objects, instead of returning a JsonResult, do:


JavaScriptSerializer js = new JavaScriptSerializer();
js.RegisterConverters(new List<JavaScriptConverter> { new DateTimeJavaScriptConverter(_dateTimeHelper) });

string output = js.Serialize(gridModel);

//Add backslashes and single quotes to date in serialized output (it cant be done in the converter because the uri gets url encoded)
var modifiedOutput = System.Text.RegularExpressions.Regex.Replace(output, "/Date\\((.*?)\\)/", "\\/Date('$1')\\/");

return Content(modifiedOutput, "application/json");


The DateTimeJavaScriptConverter is (slighty modified from this post):


public class DateTimeJavaScriptConverter : JavaScriptConverter
        {
            private readonly IDateTimeHelper _dateTimeHelper;

            public DateTimeJavaScriptConverter(IDateTimeHelper dateTimeHelper)
            {
                _dateTimeHelper = dateTimeHelper;
            }

            public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
            {
                return new JavaScriptSerializer().ConvertToType(dictionary, type);
            }

            public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
            {
                if (!(obj is DateTime)) return null;
                var date = (DateTime)obj;
                var dateOffset = new DateTimeOffset(date, _dateTimeHelper.CurrentTimeZone.GetUtcOffset(date));

                return new CustomString("/Date(" + dateOffset.ToString("yyyy-MM-ddTHH:mm:sszzz") + ")/");
            }

            public override IEnumerable<Type> SupportedTypes
            {
                get { return new[] { typeof(DateTime) }; }
            }

            private class CustomString : Uri, IDictionary<string, object>
            {
                public CustomString(string str)
                    : base(str, UriKind.Relative)
                {
                }

                void IDictionary<string, object>.Add(string key, object value)
                {
                    throw new NotImplementedException();
                }

                bool IDictionary<string, object>.ContainsKey(string key)
                {
                    throw new NotImplementedException();
                }

                ICollection<string> IDictionary<string, object>.Keys
                {
                    get { throw new NotImplementedException(); }
                }

                bool IDictionary<string, object>.Remove(string key)
                {
                    throw new NotImplementedException();
                }

                bool IDictionary<string, object>.TryGetValue(string key, out object value)
                {
                    throw new NotImplementedException();
                }

                ICollection<object> IDictionary<string, object>.Values
                {
                    get { throw new NotImplementedException(); }
                }

                object IDictionary<string, object>.this[string key]
                {
                    get
                    {
                        throw new NotImplementedException();
                    }
                    set
                    {
                        throw new NotImplementedException();
                    }
                }

                void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
                {
                    throw new NotImplementedException();
                }

                void ICollection<KeyValuePair<string, object>>.Clear()
                {
                    throw new NotImplementedException();
                }

                bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
                {
                    throw new NotImplementedException();
                }

                void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
                {
                    throw new NotImplementedException();
                }

                int ICollection<KeyValuePair<string, object>>.Count
                {
                    get { throw new NotImplementedException(); }
                }

                bool ICollection<KeyValuePair<string, object>>.IsReadOnly
                {
                    get { throw new NotImplementedException(); }
                }

                bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
                {
                    throw new NotImplementedException();
                }

                IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
                {
                    throw new NotImplementedException();
                }

                IEnumerator IEnumerable.GetEnumerator()
                {
                    throw new NotImplementedException();
                }
            }
        }


Let me know what you think of this solution. The conversion is now done on the server for each date.
This post/answer is useful
1
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Alexandre Soares
http://www.sowee.ca
Posted: May 12, 2012 at 10:10 AM Quote #69557
Thanks.
I'm a big fan of keep it simple principle. I'm not sure but it looks more like a hack. I think it should be well tested in order to find any hidden pitfalls. Furthermore, new developers just won't understand why it's used. Personally I think about returning string representation of dates (it's more simple)
This post/answer is useful
0
This post/answer is not useful

Please login or register
to vote for this post.

(click on this box to dismiss)
Interested in the dedicated Premium support services provided by core developers? Please visit http://www.nopcommerce.com/supportservices.aspx

Regards,
Andrei Mazulnitsyn
1 2 >
Premium support services
  • Dedicated premium support services provided by core developers are intended for persons who run mission critical websites, work on projects with tight deadlines, or want to get dedicated support.
Professional services
  • Want to open a new store? Want to take your store to the next level? Need a custom extension? We can customize nopCommerce to fit your store perfectly. Request a quote to get started.