In my C# models I use both DateTime and DateTimeOffset, eg:
class Foo
{
public DateTime Date { get; set; }
public DateTimeOffset CreationDate { get; set; }
}
When I serialize to JSON, I do it like this:
Foo foo = new Foo();
foo.Date = DateTime.UtcNow;
foo.CreationDate = DateTime.UtcNow;
var isoDateTimeConverter = new IsoDateTimeConverter();
isoDateTimeConverter.DateTimeFormat = "yyyy'-'MM'-'dd";
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(isoDateTimeConverter);
JsonSerializer serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, foo);
This will produce this JSON:
{
Date = "2019-02-26",
CreationDate = "2019-02-26"
}
Both Date and CreationDate are serialized the same way due to IsoDateTimeConverter
What I'd like to do is to differentiate the serialization of DateTime and DateTimeOffset
My goal is to get this JSON:
{
Date = "2019-02-26",
CreationDate = "2019-02-26T12:03:00-03:00"
}
How can I achieve this?
Additional info:
When my C# model uses DateTime, I save it as Date in SQL Server
When my C# model uses DateTimeOffset, I save it as DateTimeOffset in SQL Server
I'm using EF Code First
You can try this
public class DateFormatConverter : IsoDateTimeConverter
{
public DateFormatConverter(string format)
{
DateTimeFormat = format;
}
}
Specify the format for each Date properties
public class Foo
{
[JsonConverter(typeof(DateFormatConverter), "yyyy-MM-dd")]
public DateTime Date { get; set; }
[JsonConverter(typeof(DateFormatConverter), "yyyy-MM-ddTHH:mm:ssK")]
public DateTimeOffset CreationDate { get; set; }
}
Without additional settings, you can Serialize
Foo foo = new Foo();
foo.Date = DateTime.UtcNow;
foo.CreationDate = DateTime.UtcNow;
string isoJson = JsonConvert.SerializeObject(foo);
OUTPUT
{"Date":"2020-02-26","CreationDate":"2020-02-26T15:30:19-03:00"}
Related
We have a MVC.Core project where we're having problems deserializing a DateTime value from a string into a DateTime property.
Here's an example:
public class TestClass
{
public RecordStatus RecordStatus { get; set; } = RecordStatus.Active;
[System.ComponentModel.Browsable(false)]
public DateTime CreatedOn { get; private set; }
}
string s = #"{""recordStatus"":""Active"",""createdOn"":""2018-03-02T21:39:22.075Z""}";
JsonSerializerSettings jsonSettings = new JsonSerializerSettings()
{
DateFormatString = "yyyy-MM-ddTHH:mm:ss.FFFZ",
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
MissingMemberHandling = MissingMemberHandling.Ignore
};
TestClass psm = JsonConvert.DeserializeObject<TestClass>(s, jsonSettings);
The value of psm.CreatedOn is being set to
{1/1/0001 12:00:00 AM}
I've tried a bunch of different combination of values for the serializer settings with no luck. What am I missing here? I know I'm missing something obvious, but it's on of those days.
Thanks
The problem here isn't the format string, that is just fine (as we can see here):
Console.WriteLine(DateTime.ParseExact("2018-03-02T21:39:22.075Z","yyyy-MM-ddTHH:mm:ss.FFFZ", CultureInfo.InvariantCulture));
Yields:
02-Mar-18 4:39:22 PM
The issue is actually the private setter on CreatedOn. If you remove that everything works. In fact, it works with the default serializer settings, so there is no need to do anything with that format string.
If you are set on having a private setter for that field, then I would suggest looking into something like this to make Json.NET use the private setter.
public class TestClass
{
public RecordStatus RecordStatus { get; set; } = RecordStatus.Active;
[System.ComponentModel.Browsable(false)]
public DateTime CreatedOn { get; set; }
}
Remove the private access modifier from the setter for the CreatedOn property
I'm trying to create a filter and find records by a date range. The thing is, the dates in our mongo server are (sadly, its historically like this and was imported from another database) stored as string type, in the format of "dd/MM/yyyy hh:mm:ss". How can I filter from user input dates? No matter what I've tried I got wrong results.
Here's some code:
[Serializable]
public class Error
{
[BsonElement("projectName")]
public string projectName { get; set; }
[BsonElement("programName")]
public string programName { get; set; }
[BsonElement("errorDescription")]
public string errorDescription { get; set; }
[BsonElement("userDescription")]
public string userDescription { get; set; }
[BsonElement("logDateTime")]
[BsonSerializer(typeof(DateSerializer))]
public DateTime logDateTime { get; set; }
[BsonElement("writeDateTime")]
public string writeDateTime { get; set; }
}
And here's the DateSerializer:
public class DateSerializer: SerializerBase<DateTime>
{
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value)
{
context.Writer.WriteString(value.ToString(CultureInfo.InvariantCulture));
}
public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var dateText = context.Reader.ReadString();
return convertToDate(dateText);
}
private DateTime convertToDate(string dateText)
{
CultureInfo ci = new CultureInfo(CultureInfo.CurrentCulture.Name);
ci.DateTimeFormat.ShortDatePattern = "dd'/'MM'/'yyyy";
ci.DateTimeFormat.LongTimePattern = "hh':'mm tt";
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
string format = "dd/MM/yyyy HH:mm:ss";
DateTime dateTime = DateTime.ParseExact(dateText, format, ci);
return dateTime;
}
}
on my filter:
DateTime dateTime2 = DateTime.ParseExact(date, format, ci);
var fromDateFilter = builder.Gte(x => x.error.logDateTime, dateTime2);
and eventually:
filter = filter & fromDateFilter;
var results = errorCollection.Find(filter).ToList();
I've tried many different ways but can't seem to get the comparison to work properly.
Edit: I only care about the dates. The time is not very important.
Edit 2 - Apparently it only compares the day, for example it returns that 01/06/2017 (dd/mm/yyyy) is smaller than 06/02/2017 (dd/mm/yyyy). This is also true for 02-05/06/2017 until 06/06/2017.
Say I have 2 classes with the same set of properties:
public class MyDto
{
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
}
public class MyViewModel
{
public int Id { get; set; }
public DateTime CreatedOn { get; set; }
}
I want to map with AutoMapper, adjusting the UTC date of the input class to local time of the output class, e.g., granted I am in UK where UTC offset currently is 1h:
var input = new MyDto {Id = 1, CreatedOn = DateTime.Parse("01-01-2015 14:30")};
var output = Mapper.Map<MyViewModel>(input); // output.CreatedOn = "01-01-2015 15:30"
Can I cofigure AutoMapper to this automatically for all DateTime properties?
N.B. to adjust the time I use DateTime.SpecifyKind(value, DateTimeKind.Utc)
You can create a custom type converter:
public class CustomDateTimeConverter : ITypeConverter<DateTime, DateTime> {
public DateTime Convert(ResolutionContext context) {
var inputDate = (DateTime) context.SourceValue;
var timeInUtc = DateTime.SpecifyKind(inputDate, DateTimeKind.Utc);
return TimeZoneInfo.ConvertTime(timeInUtc, TimeZoneInfo.Local);
}
}
This will make AutoMapper perform the conversion from UTC to local time for every mapping between two DateTime properties.
I'm facing an issue with date formatting. Upon calling up the UpdateItem action, the date format for CreatedAt gets messed up. I'm using JSON by the way, so must be something to do with date serialization.
Model:
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string CreatedBy { get; set; }
public DateTime? CreatedAt { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedAt { get; set; }
}
Create action:
public int CreateItem(Item item)
{
var item = new Item();
viewModel.CopyToItem(item);
item.CreatedBy = WebSecurity.CurrentUserName;
item.CreatedAt = DateTime.Now;
db.Items.Add(item);
db.SaveChanges();
return item.ItemId;
}
Update action:
public void UpdateItem(Item item)
{
item.UpdatedBy = WebSecurity.CurrentUserName;
item.UpdatedAt = DateTime.Now;
db.SaveChanges();
}
The incorrect date format:
/Date(1395366469723)/
It should be:
2014-03-21T09:50:01.747
I tried this in the controller but get a String was not recognized as a valid DateTime' error.
string isoJson = JsonConvert.SerializeObject(DateTime.Now, new IsoDateTimeConverter());
item.CreatedAt = DateTime.ParseExact(isoJson, "yyyy-MM-dd hh:mm:ss.ttt", CultureInfo.InvariantCulture);
Using non-nullable DateTime in the model didn't fix it either.
Javascript uses Unix Time. If you are wanting to get a DateTime object with the given javascript date value, create a new DateTime object from 1/1/1970 and then add the milliseconds.
Observe:
var dt = new DateTime(1970, 1, 1).AddMilliseconds(1395366469723);
// "21/03/2014 1:47:49 AM"
I get a System.FormatException thrown when i try to parse XML into an object. As far as I can tell, it's due to the culture used in System.Xml.Serialization.XmlSerializer.Deserialize, wich expects a dot as the decimal character, but the xml contains a comma.
The object looks as follows:
public sealed class Transaction
{
[XmlElement("transactionDate")]
public DateTime TransactionDate { get; set; }
[XmlElement("transactionAmount")]
public decimal Amount { get; set; }
[XmlElement("transactionDescription")]
public string Description { get; set; }
[XmlElement("transactionType")]
public int Type { get; set; }
public static Transaction FromXmlString(string xmlString)
{
var reader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(Transaction));
var instance = (Transaction) serializer.Deserialize(reader);
return instance;
}
}
The xml:
<transaction>
<transactionDate> 2013-07-02 <transactionDate>
<transactionAmount>-459,00</transactionAmount>
<transactionDescription>description</transactionDescription>
<transactionType>1</transactionType>
</transaction>
I've made it work by introducing a second property that parses the first using my own culture:
namespace MyNamespace
{
[XmlRoot("transaction"), XmlType("Transaction")]
public sealed class Transaction
{
[XmlElement("transactionDate")]
public DateTime TransactionDate { get; set; }
[XmlElement("transactionAmount")]
public string Amount { get; set; }
public decimal AmountAsDecimal {
get
{
decimal value;
Decimal.TryParse(Amount, NumberStyles.Any, CultureInfo.CreateSpecificCulture("sv-SE"), out value);
return value;
}
}
[XmlElement("transactionDescription")]
public string Description { get; set; }
[XmlElement("transactionType")]
public int Type { get; set; }
public static Transaction FromXmlString(string xmlString)
{
var reader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(Transaction));
var instance = (Transaction) serializer.Deserialize(reader);
return instance;
}
}
}
which exposes an extra property that i don't want there.
So my question is: is there another way to do this, without iterating over each element and parsing/assigning it to the object "manually"?
XML serializer uses a standardized Number and DateTime format, the standard is defined in the W3C schema datatype specification http://www.w3.org/TR/xmlschema-2/.
Don't expect XmlSerializer to pay attention to the thread's CultureInfo, it intentionally uses a standardized format to ensure you can serialize/deserialize independent of the culture/locale.
What you can do instead is have a property that will be used to serialize/deserialize the decimal.
See: Partially deserialize XML to Object
[XmlType("transaction")]
public sealed class Transaction
{
[XmlElement("transactionDate")]
public DateTime TransactionDate { get; set; }
[XmlIgnore]
public decimal Amount { get; set; }
[XmlElement("transactionAmount")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public string AmountSerialized
{
get
{
return Amount.ToString(CultureInfo.CreateSpecificCulture("sv-SE"));
}
set
{
decimal amount;
Decimal.TryParse(value, NumberStyles.Any, CultureInfo.CreateSpecificCulture("sv-SE"), out amount);
Amount = amount;
}
}
[XmlElement("transactionDescription")]
public string Description { get; set; }
[XmlElement("transactionType")]
public int Type { get; set; }
public static Transaction FromXmlString(string xmlString)
{
var reader = new StringReader(xmlString);
var serializer = new XmlSerializer(typeof(Transaction));
var instance = (Transaction) serializer.Deserialize(reader);
return instance;
}
}
This way you can get/set the Amount without needing to worry about how it is serialized. Since this is a DTO you can create another class without the AmountSerialized as your domain object (and use something like AutoMapper to make conversion painless).
Usage:
var data = #"<transaction>
<transactionDate>2013-07-02</transactionDate>
<transactionAmount>-459,00</transactionAmount>
<transactionDescription>description</transactionDescription>
<transactionType>1</transactionType>
</transaction>";
var serializer = new XmlSerializer(typeof(Transaction));
using(var stream = new StringReader(data))
using(var reader = XmlReader.Create(stream))
{
Console.Write(serializer.Deserialize(reader));
}
Also there was a typo in the ending tag for transactionDate.
If you know the culture that the XML was generated in, one easy solution is to switch the current thread's culture to that culture prior to deserializing.
System.Globalization.CultureInfo oCurrentCulture = null;
try
{
// Save the current culture
oCurrentCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-DE");
// Do your work
}
finally
{
// Restore the saved culture
System.Threading.Thread.CurrentThread.CurrentCulture = oCurrentCulture;
}