Inconsistent behaviour of DateTime values in plugins

At the moment we are developing a vertical solution on top of Dynamics CRM 2015. A fairly large project as we are developing in a team of 7 fulltime professionals to build the solution within a time frame of 6 months. An intriguing traject.
As with all large projects, a big part of the project is covered by custom software development.
The tester on the project found some bugs which were date related. As a background reference, in our solution we are working with numereous date ranges. One of the constraints in our solution is that date ranges are not allowed to have an overlap.
My collegue Henk was the lucky victim to repair a bug regarding a DateTime overlap.
The bug involved a validation of an entered Date value. In case a new record was added to CRM the insertion failed many times because there was an overlap. When he entered another start date the record could be inserted. When he changed the record afterwards and set the startdate to the initial date the system would accept it. * how bizar *

Time for some analysis

Below the date ranges he entered

Start Date End Date Comments

2015–01–01

2015–03–31

Success on Create (1st  record)
 Create second record

2015–04–01

2015–06–30

Validation error on Create

2015–04–02

2015–06–30

Succes on Create
 Update second record

2015–04–01

2015–06–30

Success On Update

In order to isolate the problem, he decided to write a test plugin. Within the plugin a couple of stages were handled for both inserting and updating records within CRM:

  • pre validation stage
  • pre operation stage
  • post operation stage

All the functions in the plugin did was displaying how the datetime was treated by CRM. The plugin is registred for: Pre-Validation, Pre-Operation and Post-Operation.

[code language=”javascript”]protected void ValidateCreate(Entity target,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = target.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("ValidateCreate {0} {1}", startDate,
startDate.Kind); } protected void PreCreate(Entity targetEntity,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = targetEntity.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("PreCreate {0} {1}", startDate,
startDate.Kind); } protected void PostCreate(Entity targetEntity, Guid objectId,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = targetEntity.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("PostCreate {0} {1}", startDate,
startDate.Kind); } protected void ValidateUpdate(Entity targetEntity,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = targetEntity.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("ValidateUpdate {0} {1}", startDate,
startDate.Kind); } protected void PreUpdate(Entity targetEntity,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = targetEntity.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("PreUpdate {0} {1}", startDate,
startDate.Kind); } protected void PostUpdate(Entity targetEntity,
PluginServiceContext context) { var commonExecutionContext =
context.DependencyResolver.GetService<CommonExecutionContext>(); var
startDate = targetEntity.GetAttributeValue<DateTime>("new_startdate");
commonExecutionContext.Tracer.TraceInfo("PostUpdate {0} {1}", startDate,
startDate.Kind); }[/code]

The code performed was the following:

[code language=”javascript”]var entity = new Entity("new_entity");
entity["new_startdate"] = DateTime.Parse("2015-01-01"); // DateTimeKind =
Unspecified service.Create(entity); // Retrieve created var retrievedEntity =
_userService.Retrieve("new_entity" , id, new ColumnSet("new_startdate"));
retrievedEntity["new_startdate"] = new DateTime(2015, 2, 1, 0, 0, 0,
DateTimeKind.Unspecified); _userService.Update(retrievedEntity);​[/code]

Simple code doing nothing more than creating a new record with a datetime value and updating a record with a datetime value.

Surprising results

Trace output for create events:

—>>> 2015-06-09 11:16:16.966 [Info] – ValidateCreate 1-1-2015 00:00:00 Unspecified
—>>> 2015-06-09 11:16:17.156 [Info] – PreCreate 31-12-2014 23:00:00 Utc
—>>> 2015-06-09 11:16:17.194 [Info] – PostCreate 31-12-2014 23:00:00 Utc​​

For update events, the datetime value is always in UTC kind, in Pre-Validation as well.

Trace output, when update a datetime value:

—>>> 2015-06-09 11:27:14.756 [Info] – ValidateUpdate 31-1-2015 23:00:00 Utc
—>>> 2015-06-09 11:27:14.880 [Info] – PreUpdate 31-1-2015 23:00:00 Utc
—>>> 2015-06-09 11:27:14.928 [Info] – PostUpdate 31-1-2015 23:00:00 Utc

Internally CRM is treating datetimes as UTC datetimes. Thus when you enter a date, the time is stored as a UTC datetime.
It turns out that there is a nasty exception which is the PreValidation stage when creating a new record.

Best practice

Avoid using DateTime values in the PreValidate stage when creating new records. Best practice is to validate the DateTime values during the PreCreate stage. Otherwise you might end up with sometimes failing validations.

Leave a Reply

Your email address will not be published. Required fields are marked *