I've tried deserializing a Json string containing DateTime.MinValue in every conceivable way, but when the set method gets called on my object. The date always gets changed from -01-01-01- to -01-01-02-.
The Json being parsed clearly contains
"inception_date": "0001-01-01T00:00:00+00:00"
I then call JsonConvert on it:
return JsonConvert.DeserializeObject<T>(json, deserializerSettings);
Where T is a basic struct that contains a property: DateTime inception_date { get; set; } property. The deserializer settings are as follows:
deserializerSettings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc
};
Yet somewhere in the bowels of Newtonsoft.Json.dll, the above time gets converted to the following jObject.
"inception_date": "0001-01-02T00:00:00Z"
I have no way of stepping into their code, but by the damage is done before my code sees another call. ReadJson in the JsonConverter calls serializer.Populate(jObject.CreateReader(), target); where target is an instance of my class T, and jObject has somehow been rendered with the above incorrect date.
Can anyone figure out why this is happening or how I can prevent it? The jObject seems to be getting created in a way that is ignoring my serializer settings, which explicitly say not to screw with the date string (DateParseHandling.None).
I've taken screen shots to illustrate exactly where Newtonsoft's JsonConvert method seems to have lost a vital configuration value.
As you can see, this is the point in the code where I call JsonConvert:
The dateParseHandling value is set to None, and that's how it should be for this to work.
At this next step, I've jumped a few internal Newtonsoft calls and landed in a generic implementation of the JsonConverter which I borrowed from an accepted reference implementation to be able to see what was going on. The JsonReader passed in suddenly has lost that dateParseHandling value:
Because of this value being switched back to DateTime - the internal workings of the jObject try to represent this as an internal localized DateTime, which underflows because my timezone is negative, and we're already representing the minimum DateTime value, resulting in the conversion back to UTC adding a full day.
Try:
deserializerSettings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset
}
This resulted in me getting 1/1/0001 12:00:00 AM instead of 1/2/0001 12:00:00 AM
Here is my test code (written in LINQPad)
void Main()
{
var deserializerSettings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc
};
var json = "{\"inception_date\": \"0001-01-01T00:00:00+00:00\"}";
var parsedObj = JsonConvert.DeserializeObject<TestClass>(json, deserializerSettings);
Console.WriteLine(parsedObj);
deserializerSettings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset
};
parsedObj = JsonConvert.DeserializeObject<TestClass>(json, deserializerSettings);
Console.WriteLine(parsedObj);
}
public class TestClass
{
public DateTime inception_date {get;set;}
}
Outputs:
Related
DateTimeStamp : 2022-04-29 15:19:41.350 after serialization 2022-04-29T22:19:41.35Z instead of 2022-04-29T22:19:41.350Z
I have fixed this using below code :
public static void ServiceJsonSettings(HttpConfiguration config, IServiceMetadata serviceMetadata)
{
config.Formatters.Insert(0, new JsonNewtonsoftJilFallbackFormatter());
IsoDateTimeConverter dateConverter = new IsoDateTimeConverter
{
DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'",
DateTimeStyles = DateTimeStyles.AdjustToUniversal
};
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(dateConverter);
}
Now for large object serialization i am using Jil Serializer configured as below:
public static readonly Options JilOptions = new Options(excludeNulls: true, includeInherited: true,
serializationNameFormat: SerializationNameFormat.CamelCase, dateFormat: DateTimeFormat.ISO8601);
Using both Serializer Newtonsoft and JIL the output I am getting expected result : 2022-04-29T22:19:41.350Z.
But after testing it i found JIL serializer Serializer is trimming the zeros in millisecond part if it ends with 00 for ex: DateTimeStamp : 2022-04-29 15:19:41.300 serialized to 2022-04-29T22:19:41.3Z. On the other hand newtonsoft serializer produce expected result 2022-04-29T22:19:41.300Z.
Is there any way to provide dateFormat as "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'" in JilOptions ?
i got a problem while i try to deserialize a DateTimeOffset from a json. I saw a lot of questions here, but no one seems to work. I got from Json this dateTime : 05/04/2019 02:39:33 PM GMT and i want to keep the offset to zero. After the deserialization, by the way, i got my object with same exact time(In this case 02:39:33 PM) but with my time zone ( +02:00). I tried these two workaround, without success:
First of all, i tried to setup setting to my deserializer:
JsonSerializerSettings serializerSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
DateParseHandling = DateParseHandling.DateTimeOffset,
DateFormatString = "dd/MM/yyyy hh:mm:ss tt 'GMT'"
};
I tried this converter too:
class DateFormatConverter : IsoDateTimeConverter
{
public DateFormatConverter(string format)
{
DateTimeFormat = format;
DateTimeStyles = System.Globalization.DateTimeStyles.AssumeUniversal;
}
So, i expected this reseult:
05/04/2019 02:39:33 PM +00:00
thanks to all that will answer me!
Maybe change DateParseHandling.DateTimeOffset to DateParseHandling.None?
I managed to got the expected result by change DateParseHandling = DateParseHandling.None in the Setting of serializer.
We are sending JSON to an API defined by swagger that some properties are DateTime in the format yyyy-MM-ddThh:mm:ss.000Z (the milliseconds must be 3 digits or it fails validation at the endpoint) and some are Date (no time) properties.
I have seen many messages saying use the formatters like this:
var jsonSettings = new JsonSerializerSettings();
jsonSettings.DateFormatString = "yyyy-MM-ddThh:mm:ss.000Z"; //try .fffZ too
var jsonObject= Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json , setting);
but this does not convert the DateTimes into the correct format, and how does C# deal with a Date only type? It always seems to serialise as DateTime.MinValue()
Here is an example:
Someone sends me json as string but the the dates and datetimes in the incorrect format to be sent to the endpoint. I was hoping that the swagger class and json deserialisation would format them but it is not.
This is the swagger generated class
public class OurSwaggerObject
{
[Newtonsoft.Json.JsonProperty("dateTimeField", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.RegularExpression(#"^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$")]
public DateTime dateTimeField { get; set; }
[Newtonsoft.Json.JsonProperty("dateField", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required]
[System.ComponentModel.DataAnnotations.RegularExpression(#"^\d{4}-\d\d-\d\d$")]
public DateTime dateField { get; set; }
}
So I try and coerce the json to be correct but I'm doing it wrong or something is missing
string json = #"{ 'dateTimeField': '1995-04-07T00:00:00',
'dateField': '1995-04-07T00:00:00'
}";
/* The json we need to satisfy the swagger endpoint is:
{ 'dateTimeField': '1995-04-07T00:00:00.000Z',
'dateField': '1995-04-07'
}
*/
OurSwaggerObject deserialisedIntoObject = Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json);
string serialisedToString = Newtonsoft.Json.JsonConvert.SerializeObject(deserialisedIntoObject);
//serialisedToString= "{\"dateTimeField\":\"1995-04-07T00:00:00\",\"dateField\":\"1995-04-07T00:00:00\"}"
var jsonSettings = new JsonSerializerSettings();
jsonSettings.DateFormatString = "yyyy-MM-ddThh:mm:ss.fffZ"; //this won't help much for the 'date' only field!
deserialisedIntoObject = Newtonsoft.Json.JsonConvert.DeserializeObject<OurSwaggerObject>(json,jsonSettings);
serialisedToString = Newtonsoft.Json.JsonConvert.SerializeObject(deserialisedIntoObject, jsonSettings);
//serialisedToString="{\"dateTimeField\":\"1995-04-07T00:00:00\",\"dateField\":\"1995-04-07T00:00:00\"}"
As I mentioned in a comment, there is no standard date representation in JSON. The ISO8601 is the de-facto standard, ie most people started using this some years ago. ISO8601 does not require milliseconds. If the other endpoint requires them, it's violating the defacto standard.
Json.NET has been using IOS8601 since version 4.5. The current one is 10.0.3. The following code :
JsonConvert.SerializeObject(DateTime.Now)
returns
"2017-09-08T19:01:55.714942+03:00"
On my machine. Notice the timezone offset. That's also part of the standard. Z means UTC.
You can specify your own time format, provided it's the correct one. In this case, it should be yyyy-MM-ddTHH:mm:ss.fffZ. Notice the fff for milliseconds and HH for 24-hour.
The following code
var settings=new JsonSerializerSettings{DateFormatString ="yyyy-MM-ddTHH:mm:ss.fffZ"};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);
returns
"2017-09-08T19:04:14.480Z"
The format string does not force a timezone translation. You can tell Json.NET to treat the time as Local or Utc through the DateTimeZoneHandling setting :
var settings=new JsonSerializerSettings{
DateFormatString ="yyyy-MM-ddTH:mm:ss.fffZ",
DateTimeZoneHandling=DateTimeZoneHandling.Utc};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);
Returns :
"2017-09-08T16:08:19.290Z"
UPDATE
As Matt Johnson explains, Z is just a literal, while K generates either Z or an offset, depending on the DateTimeZoneHandling setting.
The format string yyyy-MM-ddTH:mm:ss.fffK with DateTimeZoneHandling.Utc :
var settings=new JsonSerializerSettings{
DateFormatString ="yyyy-MM-ddTH:mm:ss.fffK",
DateTimeZoneHandling=DateTimeZoneHandling.Utc};
var json=JsonConvert.SerializeObject(DateTime.Now,settings);
Will return :
2017-09-11T9:10:08.293Z
Changing to DateTimeZoneHandling.Utc will return
2017-09-11T12:15:12.862+03:00
Which, by the way is the default behaviour of Json.NET, apart from the forced millisecond precision.
Finally, .NET doesn't have a Date-only type yet. DateTime is used for both dates and date+time values. You can get the date part of a DateTime with the DateTime.Date property. You can retrieve the current date with DateTime.Today.
Time of day is represented by the Timespan type. You can extract the time of day from a DateTime value with DateTime.TimeOfDay. Timespan isn't strictly a time-of-day type as it can represent more than 24 hours.
What was that yet?
Support for explicit Date, TimeOfDay is comming through the CoreFX Lab project. This contains "experimental" features that are extremely likely to appear in the .NET Runtime like UTF8 support, Date, String, Channles. Some of these already appear as separate NuGet packages.
One can use the System.Time classes already, either by copying the code or adding them through the experimental NuGet source
Get current universaltime to json date time format and vice versa:
DateTime currentDateTime = DateTime.Now.ToUniversalTime();
var jsonDateTime = GetJSONFromUserDateTime(currentDateTime);
DateTime getDateTime = GetUserDateTimeFromJSON(jsonDateTime);
Here are both methods:
/// <summary>
/// Convert UserDateTime({9/7/2018 8:37:20 AM}) to JSON datetime(1536309440373) format
/// </summary>
/// <param name="givenDateTime"></param>
/// <returns></returns>
public static string GetJSONFromUserDateTime(DateTime givenDateTime)
{
string jsonDateTime = string.Empty;
if (givenDateTime != null)
{
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
jsonDateTime = JsonConvert.SerializeObject(givenDateTime, microsoftDateFormatSettings);
jsonDateTime = jsonDateTime.Replace("\"\\/Date(", "").Replace(")\\/\"", "");
}
return jsonDateTime;
}
/// <summary>
/// Convert JSON datetime(1536309440373) to user datetime({9/7/2018 8:37:20 AM})
/// </summary>
/// <param name="jsonDateTime"></param>
/// <returns></returns>
public static dynamic GetUserDateTimeFromJSON(string jsonDateTime)
{
dynamic userDateTime = null;
if (!string.IsNullOrEmpty(jsonDateTime))
{
JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
};
userDateTime = JsonConvert.DeserializeObject("\"\\/Date(" + jsonDateTime + ")\\/\"", microsoftDateFormatSettings);
}
return userDateTime;
}
If, like Willie Esteche you wish to convert a number that is too low, you have probably got a Unix Date Time value. His solution does indeed work, but as of .Net Framework 4.6 there is an easier way to do things.
Given a value of 1500013000, first you convert this to a DateTimeOffset with the FromUnixTimeSeconds() method, then simply grab the DateTime component.
DateTime dt = DateTimeOffset.FromUnixTimeSeconds(1500013000).UtcDateTime;
Conversion back (assuming UTC) is performed like so:
long Udt = new DateTimeOffset(dt,TimeSpan.Zero).ToUnixTimeSeconds();
In my case, i using this config in vb.net, it's so easy translate to c #
settings.NullValueHandling = NullValueHandling.Ignore
settings.DateTimeZoneHandling = DateTimeZoneHandling.Local
BE = JsonConvert.DeserializeObject(Of EN_BE)(str, settings)
I get this format date from laravel API
2020-08-02T01:41:05.000000Z
and i convert to correct value same to database
2020-08-01 20:41:05
You can try this:
DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
I serialize objects having DateTime properties, like this: JsonConvert.SerializeObject(my_object), then on another machine I deserialize: result = JsonConvert.DeserializeObject<Result>(r);. And some dates are wrong. I've checked through fiddler that when date in json looks like this Date=2014-11-29T18:41:41.1672899 then it's deserialized correctly, but every 10th or so entry looks like Date=2014-11-29T18:55:39.1175417+00:00 (note the +00:00) and then it's deserialized as date shifted by two hours. Why is this and how to fix this?
You can explicitly set DateTimeZoneHandling to DateTimeZoneHandling.Utc on JsonSerializerSettings:
Result result = JsonConvert.DeserializeObject<Result>(r, new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
I'm using WebApi 2 to send some Json down to the client and its using the old style Date serialization and not ISO 8601.
I'm seeing:
"current_period_start": "\/Date(1388153705)\/",
"current_period_end": "\/Date(1390832105)\/",
"start": "\/Date(1388332525)\/",
My Global.asax.cs looks like this:
GlobalConfiguration.Configure(WebApiConfig.Register);
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Converters.Add(new IsoDateTimeConverter());
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Newtonsoft.Json defaults to IsoDateTimeConverter. So even if you do not specify anything, you should get the correct Iso format (and for me your code also works just fine btw.).
Read this or Scott's blog for further info about the defaults
You are most likely setting the converter somewhere else again, maybe you are using some custom converter with specific settings? Or are you using a very old version of Newtonsoft.Json?
Just a quick comment on #Toolkit's comment about the formatting.
The reason why you get a date string without the Z at the end is the datetime variable that you are parsing have the Kind as Unspecified.
It is likely that you are parsing a record coming from a database with a DateTime property, the database field lose the Kind once is saved.
This is why json is not adding the 'Z' at the end, it doesn't know if it is utc or local.
You can use a DatetimeOffset instead of Datetime, if you can change the model without too much impact. My option was to write a custom parser for Datetime and force it to be UTC if was not specified.
public class CustomJsonDateConverter : DateTimeConverterBase
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var date = DateTime.Parse(reader.Value.ToString());
if (date.Kind == DateTimeKind.Unspecified)
{
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
}
return date;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var date = (DateTime)value;
if (date.Kind == DateTimeKind.Unspecified)
{
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
}
writer.WriteValue(date);
}
}
PD: I don't have enough points to write a comment, this isn't an answer to the main issue.