Gotcha: fn_UTCToLocalTime and fn_LocalTimeToUTC

There are not many scenarios where you would directly operate against an entity base table in CRM. There two scenarios that I can think of are:

  1. Reporting
  2. Data Migration/Integration

Filtered view is the recommended approach when it comes to writing reports for OnPremise CRM server. But if the performance is not really upto the mark, you are forced to either optimise the query by creating indexes/statistics against the correct tables, or directly operate against the base tables.

CRM 2015 Update 1 has introduced the option to create a datetime field as Timezone independent, but in all versions prior to this one, you’ll have to take care of the UTC/Local and Local/UTC conversion, when it comes to datetime fields. CRM does this using fn_UTCToLocalTime and fn_LocalTimeToUTC UDFs that are defined in the MSCRM database.

When you are connected into the SQL database as an Active Directory user, who is not a user in CRM, both these functions will return null. The reason CRM needs to know who the user is to find out the Timezone preferences that are stored again the user’s profile. This is needed to perform the UTC/Local conversion.

In order to understand why this happens, you’ll have to understand how CRM finds out who the current user is.

CRM first tries to match the Active Directory login name of the user against the DomainName column of SystemUser table in MSCRM database. If it can’t find a match, it then tries to match the SystemUserId in the SystemUser table with the context_info on the connection.

Query to understand the behaviour

--user has access to mscrm db, but not a user in CRM
EXECUTE AS user ='CONTOSO\Max.Power'
select dbo.fn_UTCToLocalTime(CreatedOn) from dbo.contact

DECLARE @systemuserid varbinary(128);
select @systemuserid=SystemUserId FROM dbo.systemuser where DomainName='CONTOSO\Alan.Smith'

--impersonate the context of an user who in CRM
SET context_info @systemuserid
select dbo.fn_UTCToLocalTime(CreatedOn) from dbo.contact

--reset context
SET CONTEXT_INFO 0x

When you run this query in SSMS, you’ll get something similar to this.

Query Result
Query Result

As you can see the first fn_UTCToLocalTime returned null, but the second one doesn’t, as a different user is impersonated using the context_info. Please be mindful of this behaviour when you are directly working against the base tables.