JSON Date and DateTime serialisation in c# & newtonsoft - c#

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");

Related

How can I get the value of a date into a string in the format YYYY-MM-DDThh:mm:ss.sssZ for CosmosDB?

The CosmosDB database I am using suggests I store DateTime in a string.
Working with Dates in Azure Cosmos DB
So I am trying to do this:
string DateAndTime = DateTime.Now.ToString()
but it's not giving me the correct format. Is there a method to do this and to convert it to this exact format: YYYY-MM-DDThh:mm:ss.sssZ ?
If you're not already using the DocumentClient, you could use this to interface with your collection.
Using the DocumentClient means that you can define your document types as you wish, without having to manually convert fields to string. For example, you could use the following type to create/query documents out of the collection:
public class CosmosDocument
{
[JsonProperty("id")]
public string Id { get; set; }
public DateTime TimeStamp { get; set; }
}
The DocumentClient will then serialize/deserialize/model-bind the data, including DateTime values for you.
The serialization is configurable if you need to make any changes to the default settings.
EDIT
As noted in the comments, CosmosClient is the newer version that provides the same functionality described above.
According to Custom date and time format strings, you should use something like that
string DateAndTime = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ss.sssz");
It will give you the following string 2020-04-04T11:19:06.06+3
yyyy means four-digit year (YYYY format doesn't exist), dd represents the day of the month (from 01 to 31). z is used for offset between your local time zone and UTC
The capital Z literal in string indicates that datetime is in UTC format (according to ISO 8601 standard). To get it literally in a result string you may use DateTime.UtcNow instead of DateTime.Now with Z literal or K specifier at the end
string DateAndTime = DateTime.UtcNow.ToString("yyyy-MM-ddThh:mm:ss.sssK");
It gives you 2020-04-04T08:49:22.22Z (current datetime in UTC format). K specifier will add Z literal for UTC dates and offset (in zzz format) for local dates

json Deserialization changes date

Hey I'm getting diffrent date after deserialization.
before the date is 30.03.2017, and after the date is 29.03.2017.
before
after
Json string (stam value):
[{"a_id":1,"auctionname":"computer","deadLine":"\/Date(1490821200000)\/"},{"a_id":2,"auctionname":"keyboard","deadLine":"\/Date(1490821200000)\/"},{"a_id":3,"auctionname":"mouse","deadLine":"\/Date(1490821200000)\/"}]
my code:
[Test]
public void GetAuctionsByJson_works()
{
Mock<IAuctionRespository> mockAuction = new Mock<IAuctionRespository>();
mockAuction.Setup(m => m.Auctions).Returns(new Auction[]
{
new Auction { a_id=1, auctionname="computer", deadLine=DateTime.Today},
new Auction { a_id=2, auctionname="keyboard", deadLine=DateTime.Today},
new Auction { a_id=3, auctionname="mouse", deadLine=DateTime.Today}
}.AsQueryable());
CustomerController controller = new CustomerController(mockAuction.Object);
var actual = controller.GetAuctionsByJson() as JsonResult;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string stam = serializer.Serialize(actual.Data);
List<Auction> result = serializer.Deserialize<List<Auction>>(serializer.Serialize(actual.Data));
//List<Auction> result = ser.ReadObject(actual);// as List<Auction>; //null --> decirialized
}
There are multiple issues.
First, the format \/Date(1490821200000)\/ isn't used anymore. It was used in the early 2000s when there was no defacto standard to represen time in AJAX calls. The number is an escaped number of ticks in UTC. The defacto standard is ISO8601, 2017-03-30T00:00:00Z.
Second JavascriptSerializer isn't used anymore. It's been replaced by Json.NET even in ASP.NET Web API. I'm not sure if it understands ISO8601 or not. It isn't used anyway, except in legacy code.
Finally, DateTime.Today returns the local time. DateTime.Today in a +3 timezone is 30/3/2017 12:00:00 am but 29/3/2017 09:00:00 pmin UTC. Its .Kind property is Local. Even Json.NET would serialize this as 2017-03-30T00:00:00+03:00
To get the current date in UTC, one should use DateTime.UtcNow.Date. This has a Kind value of Utc. Json.NET will serialize this as 2017-03-30T00:00:00Z
Try changind all calls to
DateTime.Today
with
DateTime.UtcNow.Date

Json.Net cannot deserialize DateTime.MinValue

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:

Set the year that is assumed when parsing partial date values in .NET

I need to parse a date string which could be in any reasonable format. For example:
2012-12-25
25 december 2012
25 dec
17:35
Some of these strings contain ambiguous dates which can result in several possible DateTime values (E.g. 25 dec can be interpreted as 2012-12-25, 2011-12-25, 1066-12-25, etc).
The way DateTime.Parse currently handles these ambiguous values is by using the current system date to determine the context. So if the current date is 26th July 2012 the string 25 dec is assumed to be in the current year and is parsed as 2012-12-25
Is it somehow possible to change this behaviour and set the current date context myself?
The only thing I can think of is post processing the date. You have the string afterwards and you have the year in the DateTime object. If the string does not contain the year then set the year yourself.
if(! string.contains(DateTime.Year.toString() ) {
// Set the year yourself
}
If you expect to get "incomplete" date/time information in various formats, you could try parsing the text as specific different formats least-detailed to most-detailed. For example:
var text = "June 15";
DateTime datetime;
if(DateTime.TryParseExact(text, "m", CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out datetime))
{
// was just month, day, replace year with specific value:
datetime = new DateTime(1966, datetime.Month, datetime.Day);
}
else
{
// wasn't just month, day, try parsing a "whole" date/time:
datetime = DateTime.Parse(text);
}
...this code tries to parse month/day format in the current culture (if you have a specific one, independent of current culture, you can replace "CultureInfo.CurrentCulture" with a culture that has the format you want). If that fails, it assumes the text is more detailed and goes on to parse it as you normally would.
If your date/times are not local, don't use DateTimeStyles.AssumeLocal. I always recommend that date/time data that is stored in any way (like serialized to text) that you always use Universal; because you don't know what culture was in play when the data was serialized. Universal is the only reliable way to get date/time data on level playing field. In which case use DateTimeStyles.AssumeUnivesal.
You could try to deal with IFormatProvider stuff but that may take a while. As a quick solution I can propose an extension method:
public static class MyDateTimeStringExtensions
{
public static DateTime ToDateTimeWithYear(this string source, int year)
{
var dateTime = DateTime.Parse(source);
return dateTime.AddYears(year - dateTime.Year);
}
}
....
"2/2".ToDateTimeWithYear(2001) // returns 2/2/2001 12:00:00 AM
I had a very similar problem. DateTime.Parse or DateTime.TryParse will assume the time of day is 00:00:00 when the string contains no time of day information. Just as with the year assumption, there is no way to specify a different time of day to use as the default. This is a real problem, because the time to set these defaults is before the parsing method goes through all its detailed steps. Otherwise, you have to very painfully reinvent the wheel to determine whether the string contained information that would override the default.
I looked at the source code of DateTime.TryParse, and as to be expected, Microsoft have gone out of their way to make it hard to extend the DateTime class. So I've cooked up some code that uses reflection to leverage what it can from the source code of DateTime. This has some significant drawbacks:
The reflection-using code is awkward
The reflection-using code calls internal members which might change if the .Net framework is upgraded
The reflection-using code will run slower than some hypothetical alternative that didn't need to use reflection
In my case, I judged that nothing could be more awkward than having to reinvent DateTime.TryParse from scratch. I have unit tests that will indicate if the internal members have changed. And I believe the performance penalty is insignificant in my case.
My code is below. This code is used to override the default hour/minute/second, but I think it could be easily modified or expanded to override the default year or something else. The code is faithfully imitating the internal code of one of the overloads of the internal System.DateTimeParse.TryParse (it does the real work of DateTime.TryParse), although I had to use awkward reflection to do so. The only thing effectively different from System.DateTimeParse.TryParse is that it assigns a default hour/minute/second instead of leaving them all at zero.
For reference, this is the method of class DateTimeParse that I am imitating
internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result) {
result = DateTime.MinValue;
DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
resultData.Init();
if (TryParse(s, dtfi, styles, ref resultData)) {
result = resultData.parsedDate;
return true;
}
return false;
}
And here is my code
public static class TimeExtensions
{
private static Assembly _sysAssembly;
private static Type _dateTimeParseType, _dateTimeResultType;
private static MethodInfo _tryParseMethod, _dateTimeResultInitMethod;
private static FieldInfo _dateTimeResultParsedDateField,
_dateTimeResultHourField, _dateTimeResultMinuteField, _dateTimeResultSecondField;
/// <summary>
/// This private method initializes the private fields that store reflection information
/// that is used in this class. The method is designed so that it only needs to be called
/// one time.
/// </summary>
private static void InitializeReflection()
{
// Get a reference to the Assembly containing the 'System' namespace
_sysAssembly = typeof(DateTime).Assembly;
// Get non-public types of 'System' namespace
_dateTimeParseType = _sysAssembly.GetType("System.DateTimeParse");
_dateTimeResultType = _sysAssembly.GetType("System.DateTimeResult");
// Array of types for matching the proper overload of method System.DateTimeParse.TryParse
Type[] argTypes = new Type[]
{
typeof(String),
typeof(DateTimeFormatInfo),
typeof(DateTimeStyles),
_dateTimeResultType.MakeByRefType()
};
_tryParseMethod = _dateTimeParseType.GetMethod("TryParse",
BindingFlags.Static | BindingFlags.NonPublic, null, argTypes, null);
_dateTimeResultInitMethod = _dateTimeResultType.GetMethod("Init",
BindingFlags.Instance | BindingFlags.NonPublic);
_dateTimeResultParsedDateField = _dateTimeResultType.GetField("parsedDate",
BindingFlags.Instance | BindingFlags.NonPublic);
_dateTimeResultHourField = _dateTimeResultType.GetField("Hour",
BindingFlags.Instance | BindingFlags.NonPublic);
_dateTimeResultMinuteField = _dateTimeResultType.GetField("Minute",
BindingFlags.Instance | BindingFlags.NonPublic);
_dateTimeResultSecondField = _dateTimeResultType.GetField("Second",
BindingFlags.Instance | BindingFlags.NonPublic);
}
/// <summary>
/// This method converts the given string representation of a date and time to its DateTime
/// equivalent and returns true if the conversion succeeded or false if no conversion could be
/// done. The method is a close imitation of the System.DateTime.TryParse method, with the
/// exception that this method takes a parameter that allows the caller to specify what the time
/// value should be when the given string contains no time-of-day information. In contrast,
/// the method System.DateTime.TryParse will always apply a value of midnight (beginning of day)
/// when the given string contains no time-of-day information.
/// </summary>
/// <param name="s">the string that is to be converted to a DateTime</param>
/// <param name="result">the DateTime equivalent of the given string</param>
/// <param name="defaultTime">a DateTime object whose Hour, Minute, and Second values are used
/// as the default in the 'result' parameter. If the 's' parameter contains time-of-day
/// information, then it overrides the value of 'defaultTime'</param>
public static Boolean TryParse(String s, out DateTime result, DateTime defaultTime)
{
// Value of the result if no conversion can be done
result = DateTime.MinValue;
// Create the buffer that stores the parsed result
if (_sysAssembly == null) InitializeReflection();
dynamic resultData = Activator.CreateInstance(_dateTimeResultType);
_dateTimeResultInitMethod.Invoke(resultData, new Object[] { });
// Override the default time values of the buffer, using this method's parameter
_dateTimeResultHourField.SetValue(resultData, defaultTime.Hour);
_dateTimeResultMinuteField.SetValue(resultData, defaultTime.Minute);
_dateTimeResultSecondField.SetValue(resultData, defaultTime.Second);
// Create array parameters that can be passed (using reflection) to
// the non-public method DateTimeParse.TryParse, which does the real work
Object[] tryParseParams = new Object[]
{
s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, resultData
};
// Call non-public method DateTimeParse.TryParse
Boolean success = (Boolean)_tryParseMethod.Invoke(null, tryParseParams);
if (success)
{
// Because the DateTimeResult object was passed as a 'ref' parameter, we need to
// pull its new value out of the array of method parameters
result = _dateTimeResultParsedDateField.GetValue((dynamic)tryParseParams[3]);
return true;
}
return false;
}
}
--EDIT--
I subsequently realized that I need to do the same thing for method DateTime.TryParseExact. However, the above approach did not work for TryParseExact, which leads me to worry that the approach is even more fragile than I thought. Oh well. Happily, I was able to think of a very different approach for TryParseExact which does not use any reflection
public static Boolean TryParseExact(String s, String format, IFormatProvider provider,
DateTimeStyles style, out DateTime result, DateTime defaultTime)
{
// Determine whether the format requires that the time-of-day is in the string to be converted.
// We do this by creating two strings from the format, which have the same date but different
// time of day. If the two strings are equal, then clearly the format contains no time-of-day
// information.
Boolean willApplyDefaultTime = false;
DateTime testDate1 = new DateTime(2000, 1, 1, 2, 15, 15);
DateTime testDate2 = new DateTime(2000, 1, 1, 17, 47, 29);
String testString1 = testDate1.ToString(format);
String testString2 = testDate2.ToString(format);
if (testString1 == testString2)
willApplyDefaultTime = true;
// Let method DateTime.TryParseExact do all the hard work
Boolean success = DateTime.TryParseExact(s, format, provider, style, out result);
if (success && willApplyDefaultTime)
{
DateTime rawResult = result;
// If the format contains no time-of-day information, then apply the default from
// this method's parameter value.
result = new DateTime(rawResult.Year, rawResult.Month, rawResult.Day,
defaultTime.Hour, defaultTime.Minute, defaultTime.Second);
}
return success;
}

Force XmlSerializer to serialize DateTime as 'YYYY-MM-DD hh:mm:ss'

I have a XSD schema for some RESTful service. When used in conjunction with xsd.exe tool to generate C# code, XSD's xs:date generates the following code:
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="date")]
public System.DateTime time {
get {
return this.timeField;
}
set {
this.timeField = value;
}
}
When deserializing XML to objects using XmlSerializer all seems to be well. The problem I am facing is that the service expects dates to be formatted as YYYY-MM-DD hh:mm:ss and the XSD generated code seems to produce only YYYY-MM-DD.
If I modify XSD manually to xs:dateTime type, the generated C# code produces: 2010-08-20T20:07:03.915039Z.
Basically, how do I force serialization to produce YYYY-MM-DD hh:mm:ss? Is there something to do to XSD or is there something I can do to alter generated C# code?
In the past, I've done the following to control datetime serialization:
Ignore the DateTime property.
Create a dummy string property that serializes/deserializes the way I want
Here is an example:
public class SomeClass
{
[XmlIgnore]
public DateTime SomeDate { get; set; }
[XmlElement("SomeDate")]
public string SomeDateString
{
get { return this.SomeDate.ToString("yyyy-MM-dd HH:mm:ss"); }
set { this.SomeDate = DateTime.Parse(value); }
}
}
Use [XmlElement(DataType = "date")] attribute to format your DateTime property value as you need.
From MSDN:
Note:
The attribute that annotates the publicationdate field has a
DataType property. There is no type in the .NET Framework that matches
the type xs:date completely. The closest match is System.DateTime,
which stores date and time data. Specifying the DataType property as a
"date" ensures that the XmlSerializer will only serialize the date
part of the DateTime object.
If you only need to clear out the millisecond part. Refer to:
How to truncate milliseconds off of a .NET DateTime
And basicly do something like:
startDateTimeToUse = startDateTimeToUse.AddTicks(-(startDateTimeToUse.Ticks % TimeSpan.TicksPerSecond));
endDate = endDate.AddTicks(-(endDate.Ticks % TimeSpan.TicksPerSecond));
I can confirm that this serializes to:
<startDate>2015-10-31T12:13:04</startDate>
<endDate>2016-11-10T12:13:06</endDate>
I must also state that Before clearing the milliseconds I'm doing this:
var startDateTimeToUse = ssStartDateTime.ToUniversalTime();
var endDate = DateTime.Now.ToUniversalTime();
startDateTimeToUse = DateTime.SpecifyKind(startDateTimeToUse, DateTimeKind.Unspecified);
endDate = DateTime.SpecifyKind(endDate, DateTimeKind.Unspecified);
Which I don't know if it's having any effect on the serialization or not at this point
I believe implementing IXmlSerializable interface will do a trick. You can then control how you serialize and deserialize your object.
see answers above but to add-- if you only wanted output when the value is non-null (e.g. XML maxOccurs=0) you can utilize something like this:
private System.DateTime? someDateField;
public string someDate
{
get
{
return someDateField?.ToString("MM-dd-yyyy");
}
set
{
dobField = System.DateTime.Parse(value);
}
}
I may have another option.
When setting your DateTime just subtract the number of ticks of everything after the seconds, like:
public DateTime Dt
{
get => _dt;
set
{
_dt = value;
long elapsedTicks = _dt.Ticks - new DateTime(_dt.Year, _dt.Month, _dt.Day, _dt.Hour, _dt.Minute, _dt.Second).Ticks;
TimeSpan elapsedSpan = new TimeSpan(elapsedTicks);
_dt = _dt.Subtract(elapsedSpan);
}
}
private DateTime _dt = default(DateTime);
That way when you serialize your DateTime (Dt) the milliseconds won't be used and you'll have a value hh:mm:ss, that is at least what it gave me. That way no need to modify anything inside your XML definition.

Categories

Resources