Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Android JSON.Net serilization fails on 4.2.2 device only TimeZoneNotFound exception

I am using JSON.Net to serialize a DTO and I am getting the following exception on the physical device. THis works on all other devices we have tested on, but is failing on the Samsung Galaxy Tab 3lite SM-T110 running version 4.2.2.

I was having this issue but when I upgraded to Xamarin 3.0, it turned into this below:

06-03 21:17:41.687 E/mono-rt (22071): [ERROR] FATAL UNHANDLED EXCEPTION: System.TimeZoneNotFoundException: Exception of type 'System.TimeZoneNotFoundException' was thrown.
06-03 21:17:41.687 E/mono-rt (22071):   at System.TimeZoneInfo.get_Local () [0x00000] in <filename unknown>:0 
06-03 21:17:41.687 E/mono-rt (22071):   at Newtonsoft.Json.Utilities.DateTimeUtils.GetUtcOffset (DateTime d) [0x00000] in <filename unknown>:0 
06-03 21:17:41.687 E/mono-rt (22071):   at Newtonsoft.Json.Utilities.DateTimeUtils.WriteDateTimeString (System.Char[] chars, Int32 start, DateTime value, Nullable`1 offset, DateTimeKind kind, DateFormatHandling format) [0x00000] in <filename unknown>:0 
06-03 21:17:41.687 E/mono-rt (22071):   at Newtonsoft.Json.JsonTextWriter.WriteValue (DateTime value) [0x00000] in <filename unknown>:0 
06-03 21:17:41.687 E/mono-rt (22071):   at Newtonsoft.Json.JsonWriter.WriteValue (Newtonsoft.Json.JsonWriter writer, PrimitiveTypeCode typeCode, System.Object value) [0x00000] in <filename unknown>:0 
06-03 21:17:41.687 E/mono-rt (22071):   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializePrimitive (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonPrimitiveContract contract, Newtonsoft.Json.Serialization.
06-03 21:17:41.695 I/mono-stdout(22071): Executing Command: UpdateRegistration
The program 'Mono' has exited with code 0 (0x0).

EDIT This is the DTO that I am trying to serialize. You can see when the constructor gets called, it sets it to DateTime.Now. Shouldnt this just assume the local timezone?

  public class RecordTargetFrequency : BaseCommand, ICommand {
        public Guid TargetId { get; private set; }
        public Guid? LocationId { get; private set; }
        public Guid TherapistId { get; private set; }
        public bool? Increase { get; private set; }
        public DateTime TimeStamp { get; private set; }

        public RecordTargetFrequency(Guid targetId, Guid? locationId, Guid therapistId, bool? increase) {
            TimeStamp = DateTime.Now;
            TargetId = targetId;
            LocationId = locationId;
            TherapistId = therapistId;
            Increase = increase;
        }

        public void Validate() {
            ValidateGuid(TargetId);
            ValidateGuid(TherapistId);
        }
    }

I am then serializing it with this line:

JsonConvert.SerializeObject(executedCommand);
like image 954
Chris Kooken Avatar asked Jun 03 '14 21:06

Chris Kooken


3 Answers

Yes, TimeZoneInfo.Local is somewhat broken on Mono. I've seen three different failure modes here:

  • Returning a time zone which acts correctly, but has an ID of "Local"
  • Returning a null reference
  • Throwing an exception

I don't have a good solution for that, but if you're trying to record a timestamp, it would be a much better idea to use DateTime.UtcNow anyway. Then you don't need to worry about time zones at all. In most cases, you should be storing and handling date/time values just in terms of an instant in time, without caring about the time zone or even which calendar is involved.

If you can get a time zone ID from Java.Util.TimeZone.Default as it looks like you can - you'd want America/New_York here - then another alternative is to use my Noda Time project, which includes the IANA database. So you could create a ZonedDateTime which represents exactly what you need. We support Json.NET serialization too, so that part shouldn't be a problem.

Again though, I would encourage you to just use UTC unless you really need the local time.

A couple more options - you might want to consider using DateTimeOffset instead of DateTime; if you're just trying to serialize the value as "a date and time with a UTC offset" then DateTimeOffset.Now might do exactly what you want.

(If none of this helps, please give more context as to what you need and what you expect the JSON to look like.)

like image 133
Jon Skeet Avatar answered Nov 13 '22 18:11

Jon Skeet


Check you time setting for the device. This usually happens when you device time has a wrong time set. Even the time is correct, check if the Time zone is the correct one (eg. for me it's GMT -3 = Greenwich time -3 hours)

A good test is to set it by your self. Sometimes the carrier doesn't give you the correct timezone.

This setting is in System > Date & Time setting.

like image 20
Rafael Brasil Avatar answered Nov 13 '22 19:11

Rafael Brasil


Ultimately, the problem comes down to the internal use of TimeZoneInfo.Local on non-Windows devices, but you can avoid this.

If you follow through the source code of DateTimeUtils.WriteDateTimeString, you'll find that you need to do two things:

  1. Switch to DateFormatHandling.IsoDateFormat. This should be the default anyway, and is the recommended best practice.

  2. Switch to DateTime.UtcNow. It usually doesn't make sense to send local times to the server anyway. If for some reason you do, then try serializing a DateTimeOffset instead, and use DateTimeOffset.Now.

like image 1
Matt Johnson-Pint Avatar answered Nov 13 '22 19:11

Matt Johnson-Pint