AutoMapper configuration to use local time for all DateTime properties - c#

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.

Related

IsoDateTimeConverter and JsonSerializerSettings: differentiate DateTime with DateTimeOffset

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"}

ASP.NET MVC: How can I put the StartDate of a current record on the EndDate of an earlier record

I have a class that makes appointments, the person making a appointments only inserts the start date and time, but the end date must be equal to the start date of the next appointments. My difficulty is in ensuring that the previous appointments always receives the EndDate as the StartDate of the current appointments
public class InfoAppointments : Entity
{
public bool Active { get; set; }
public bool Excluded { get; set; }
public string Status { get; set; }
public string Observation{ get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
EDIT
My Repository:
public class InfoAppointmentsRepository : Repository<InfoAppointments>, IAppointmentsRepository
{
public InfoAppointmentsRepository(RveContext rveContext) : base(rveContext)
{
}
public InfoAppointments FindByName(string name)
{
return Search(c => c.Name== name).FirstOrDefault();
}
public InfoAppointments FindByStatus()
{
throw new NotImplementedException();
}
public override void Remove(Guid id)
{
throw new NotImplementedException();
}
}
}
There are several possible solutions to this, and it may depend on your preference for adding this sort of business logic in your application code or in SQL (e.g. as a trigger). I would personally recommend the former, as this requirement may evolve over time, and may affect other components of your business logic.
I've made a few assumptions: 1) That you are using Entity Framework, and 2) Appointments don't overlap, and EndDate is unique. If that is the case, you could implement this functionality using similar logic to the following:
public class AppointmentService
{
private readonly MyContext _db;
public AppointmentService(MyContext db) => _db = db;
public void AddAppointment(InfoAppointments appointment)
{
// Update the previous appointment's end date
var previousAppointment = _db.Appointments
.OrderByDescending(e => e.EndDate)
.FirstOrDefault();
if (previousAppointment != null)
{
previousAppointment.EndDate = appointment.StartDate;
}
// Add the new appointment
_db.Appointments.Add(appointment);
_db.SaveChanges();
}
}
One other comment: based on your explanation, it appears that EndDate should default to null, but you've used a non-nullable type. I would change it to the following:
public DateTime? EndDate { get; set; }

Is is possible to flatten parts of object when auto mapping

I'm new to using Elasticsearch and I am using search on a services where part of the result I get back is formatted like this(names translated from other language):
accounting: {
properties: {
accountingInterval: {
properties: {
endDate: {
type: "date",
format: "dateOptionalTime"
},
startDate: {
type: "date",
format: "dateOptionalTime"
}
}
}
}
}
I can auto map it to object like this without a problem:
class MyBaseObject
{
public Accounting Accounting { get; set; }
//...some other values on base object
}
class Accounting
{
public AccountingInterval AccountingInterval { get; set; }
}
class AccountingInterval
{
[Date(Format = "dateOptionalTime")]
public DateTime? StartDate { get; set; }
[Date(Format = "dateOptionalTime")]
public DateTime? EndDate { get; set; }
}
Is there an way to get it to map to a simple object like this:
class MyBaseObject
{
[Date(Format = "dateOptionalTime")]
public DateTime? AccountingStartDate { get; set; }
[Date(Format = "dateOptionalTime")]
public DateTime? AccountingEndDate { get; set; }
//...some other values on base object
}
I tried setting the name attribute but it did not seem to work
class MyBaseObject
{
[Date(Name ="accounting.accountingInterval.startDate", Format = "dateOptionalTime")]
public DateTime? AccountingStartDate { get; set; }
[Date(Name ="accounting.accountingInterval.endDate", Format = "dateOptionalTime")]
public DateTime? AccountingEndDate { get; set; }
//...some other values on base object
}
As panchicore said in the comments, it would be possible to perform this flattening at index time with Ingest node and pipelines, and the type mapping in the index would reflect this structure.
If you're not responsible for indexing, then this is trickier to do. The mapping in NEST is used for both input to and output of documents from Elasticsearch. It'd be possible to control how JSON is deserialized to MyBaseObject by hooking up the Nest.JsonSerializer nuget package, and using Json.NET as the serializer for the client, and defining a custom JsonConverter for the MyBaseObject type. If you'd only like to do it for type aesthetics though, the effort is probably more than the value!

Date format change upon update event

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"

How can I insert the current date/time into a datetime data type field using Entity Framework?

I am using Entity Framework to insert a row of data into my Application table.
Here's the class:
public partial class Application
{
public Application()
{
this.TestAccounts = new List<TestAccount>();
}
public int ApplicationId { get; set; }
public string Name { get; set; }
public byte[] RowVersion { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual ICollection<TestAccount> TestAccounts { get; set; }
}
Here's the C# code:
_Uow.Applications.Add(new Application { Name = name });
It's giving me an error saying
InnerException: System.Data.UpdateException
HResult=-2146233087
Message=An error occurred while updating the entries. See the inner exception for details.
Source=System.Data.Entity
InnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=The conversion of a datetime2 data type to a datetime data type
resulted in an out-of-range value.
How can I change my C# code to insert the current date in the ModifiedDate field?
You could specify a new constructor
public Application(string Name)
{
if(ModifiedDate == null)
//ModifiedDate = Convert.ToDateTime(01.01.2000);
ModifiedDate = DateTime.Now;
}
OR
public Application(string Name, System.Nullable<DateTime> modifiedDate)
{
if(modifiedDate != null)
//ModifiedDate = Convert.ToDateTime(01.01.2000);
ModifiedDate = DateTime.Now;
else
Do stuff
}
This isn't beatifull, but should do the trick.
Simply that way:
_Uow.Applications.Add(new Application
{
Name = name,
ModifiedDate = DateTime.Now
});
Or you may do it in the constructor:
public class Application
{
public Application()
{
ModifiedDate = DateTime.Now;
}
}
By the way: You got the exception because per default c# DateTime is more precise than datetime of SQL. Like the message says: Use datetime2 in SQL. DateTime has it's min value as initial value, which is too low for SQL datetime.
hi your ModifiedDate is initialized with minvalue as a result it gives an exception. Better to use either
public System.DateTime? ModifiedDate { get; set; } to make it nullable or
initialize in the construction
public Application()
{
this.TestAccounts = new List<TestAccount>();
ModifiedDate = DateTime.Now;
}

Categories

Resources