Date Time issue due to different Timezone of User in MS CRM

What is Issue?

Working with date time field is most challenging task in MS CRM. Many times user sees the Date time in record which is different on the email he received or on some where it is populated and he reports the issue. Date time on the records should be consistent through out the MS CRM application. If record is used on some other entity or web-resource with that date time field, It should be exactly matching to the actual date time on record which is visible to user.

The Reason-

The reason behind this issue on timezone of the user and CRM application. At the time of execution of some server side code, it will always give you the date time in CRM server timezone. And if CRM application is going to used wordwide or from multiple timezones these date time fields might show mismatch data.

Solution-

One solution for this is to make all the user time zone similar to the server timezone. But when any javascript populates some date time field this might cause wrong date time.

Example: User is working from India and the Crm Server is placed in UK. User sets his timezone to UK timezone. but if any javascript is populating current date and time in record it will be Indian time which will be +5.30 hrs ahead. This data created is wrong at this time.

Another solution is to handle this date time things in code itself.

In Code we can perform following steps-

  1. Get user’s personal setting like – date time form, timezone information, locale informtion.
  2. Convert date time according to user setting.
  3. format this date time in user setting format.
  4. Set this updated date time on your desired place.

Now in both server side and client side how to handle it?

Server side code

Server side you need only 2 requests –

  1. LocalTimeFromUtcTimeRequest
  2. UtcTimeFromLocalTimeRequest

And UserSettings entity for Timezone Code.

using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;

namespace TestConsoleApp
{
    class TestLogic
    {
        int? _timeZoneCode = 83;
        public void YourLogic(IOrganizationService service, Object parameter = null) {
            //get user's timezone-
            _timeZoneCode = RetrieveUsersTimeZoneCode(service);
            
            //datetime Value 
            DateTime scheduledStartDate=DateTime.Now;
            Console.WriteLine(scheduledStartDate.ToString());
         
            //Convert to User Local Time
            DateTime dt =scheduledStartDate.ToUniversalTime();
            DateTime UserLocalTime=RetrieveLocalTimeFromUTCTime(service, dt,_timeZoneCode);

            //convert to server Time
            DateTime serverTime =RetrieveUTCTimeFromLocalTime(service, UserLocalTime, _timeZoneCode);
            
            Console.Read();
        }

        public int? RetrieveUsersTimeZoneCode(IOrganizationService service, EntityReference user = null)
        {

            ConditionExpression cd = null;
            if (user == null)
                cd = new ConditionExpression("systemuserid", ConditionOperator.EqualUserId);
            else
                cd = new ConditionExpression("systemuserid", ConditionOperator.Equal, user.Id);

            var currentUserSettings = service.RetrieveMultiple(
            new QueryExpression("usersettings")
            {
                ColumnSet = new ColumnSet("localeid", "timezonecode"),
                Criteria = new FilterExpression
                {
                    Conditions = { cd }
                }
            }).Entities[0].ToEntity();
            return (int?)currentUserSettings.Attributes["timezonecode"];
        }

        public DateTime RetrieveLocalTimeFromUTCTime(IOrganizationService service, DateTime utcTime, int? timeZoneCode)
        {
            if (!timeZoneCode.HasValue)
                return utcTime;

            var request = new LocalTimeFromUtcTimeRequest
            {
                TimeZoneCode = timeZoneCode.Value,
                UtcTime = utcTime.ToUniversalTime()
            };

            var response = (LocalTimeFromUtcTimeResponse)service.Execute(request);

            return response.LocalTime;
        }

        public DateTime RetrieveUTCTimeFromLocalTime(IOrganizationService service, DateTime localTime, int? timeZoneCode)
        {
            if (!timeZoneCode.HasValue)
                return DateTime.Now;

            var request = new UtcTimeFromLocalTimeRequest
            {
                TimeZoneCode = timeZoneCode.Value,
                LocalTime = localTime
            };

            var response = (UtcTimeFromLocalTimeResponse)service.Execute(request);
            return response.UtcTime;
        }
    }
}

Client side code

In Javascripts, You need to play around UserSettings, LanguageLocale, TimezoneDefinition entities.

//array of all unique timezone offsets
var allTimeZones = { "(GMT+01:00)": "Africa/Algiers", "(GMT+02:00)": "Europe/Tirane", "(GMT+03:00)": "Europe/Mariehamn", "(GMT+04:00)": "Asia/Yerevan", "(GMT+04:30)": "Asia/Kabul", "(GMT+05:00)": "Antarctica/Mawson", "(GMT+05:30)": "Asia/Kolkata", "(GMT+05:45)": "Asia/Kathmandu", "(GMT+06:00)": "Antarctica/Vostok", "(GMT+06:30)": "Indian/Cocos", "(GMT+07:00)": "Antarctica/Davis", "(GMT+08:00)": "Antarctica/Casey", "(GMT+08:45)": "Australia/Eucla", "(GMT+09:00)": "Asia/Dili", "(GMT+09:30)": "Australia/Adelaide", "(GMT+10:00)": "Antarctica/DumontDUrville", "(GMT+10:00)": "Australia/Brisbane", "(GMT+10:30)": "Australia/Lord_Howe", "(GMT+11:00)": "Antarctica/Macquarie", "(GMT+12:00)": "Antarctica/McMurdo", "(GMT+12:45)": "Pacific/Chatham", "(GMT+13:00)": "Pacific/Enderbury", "(GMT+14:00)": "Pacific/Kiritimati", "(GMT-01:00)": "Atlantic/Cape_Verde", "(GMT-02:00)": "America/Noronha", "(GMT-02:30)": "America/St_Johns", "(GMT-03:00)": "Antarctica/Palmer", "(GMT-04:00)": "America/Anguilla", "(GMT-05:00)": "America/Eirunepe", "(GMT-06:00)": "America/Belize", "(GMT-07:00)": "America/Creston", "(GMT-08:00)": "Pacific/Pitcairn", "(GMT-09:00)": "Pacific/Gambier", "(GMT-09:30)": "Pacific/Marquesas", "(GMT-10:00)": "Pacific/Rarotonga", "(GMT-11:00)": "Pacific/Pago_Pago", "(GMT)": "Africa/Lome" };

//call this function to get user date in concerted format.
var GetTimeInCurrentUserZoneAndFormat=function(xrm,new_startdate) {
    //example : I have fetched new_startdate using REST Api and  new_startdate = 2019-08-30T10:00:00+01:00  
    var d = new Date(new_startdate);
    //step 1: Get user Date and Time format which he selected in Personal settings of CRM
    var dtFormat = GetDateandTimeSettingsOfUser(xrm);
    //step 2: convert Date to user local date.
    var userLocalDate = convertTimetoSpecificTimeZone(xrm, dtFormat.timeZone, dtFormat.localeId)
    // step 3: now convert date time format in user format 
    var convertedDateTime = ConvertDateInFormat(d, dtFormat.dateformat, dtFormat.dSeparator, dtFormat.timeFormat, dtFormat.tSeparator);

    return convertedDateTime; 
}

//get user's personal setting 
var GetDateandTimeSettingsOfUser = function (xrm) {
    var recordId = xrm.Page.context.getUserId();
    recordId = recordId.replace(/[{}]/g, "");
    var format = { dateformat: null, dSeparator: null, timeFormat: null, tSeparator: null, timeZone: null, localeId: null };
    var req = new XMLHttpRequest();
    req.open("GET", xrm.Page.context.getClientUrl() + "/api/data/v9.0/usersettingscollection?$select=dateformatstring,dateseparator,timeformatstring,timeseparator,localeid,timezonecode&$filter=systemuserid eq " + recordId, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var results = JSON.parse(this.response);
                for (var i = 0; i < results.value.length; i++) {
                    format.dateformat = results.value[i]["dateformatstring"];
                    format.dSeparator = results.value[i]["dateseparator"];
                    format.timeFormat = results.value[i]["timeformatstring"];
                    format.tSeparator = results.value[i]["timeseparator"];
                    format.localeId = results.value[i]["localeid"];
                    format.timeZone = results.value[i]["timezonecode"];
                }
            }
            else {
                Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
    return format;
}

//start converting time to user local time
var convertTimetoSpecificTimeZone = function (xrm, date, _timezonecode, _localeid) {
    var timeZoneName = getTimeZoneStandartName(xrm, _timezonecode);
    var localeCode = getLocaleCode(xrm, _localeid);

    var GMTCode = timeZoneName.uiName.substring(0, 11);


    var newDate = date.toLocaleString(localeCode, { timeZone: allTimeZones[GMTCode] });

    return newDate;

}
var getTimeZoneStandartName = function (xrm, _timezoneCode) {
    var TimeZoneName = { standardName: "Greenwich Standard Time", uiName: "(GMT+00:00) Monrovia, Reykjavik" }
    var req = new XMLHttpRequest();
    req.open("GET", xrm.Page.context.getClientUrl() + "/api/data/v9.0/timezonedefinitions?$select=standardname,timezonecode,userinterfacename&$filter=timezonecode eq " + _timezoneCode, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var results = JSON.parse(this.response);
                for (var i = 0; i < results.value.length; i++) {
                    TimeZoneName.standardName = results.value[i]["standardname"];
                    TimeZoneName.uiName = results.value[i]["userinterfacename"];
                }
            } else {
                xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
    return TimeZoneName;
}


var getLocaleCode = function (xrm, _localeId) {
    var code = "en-us";
    var req = new XMLHttpRequest();
    req.open("GET", xrm.Page.context.getClientUrl() + "/api/data/v9.0/languagelocale?$select=code,localeid&$filter=localeid eq " + _localeId, false);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var results = JSON.parse(this.response);
                for (var i = 0; i < results.value.length; i++) {
                    code = results.value[i]["code"];
                    var localeid = results.value[i]["localeid"];
                    var localeid_formatted = results.value[i]["localeid@OData.Community.Display.V1.FormattedValue"];
                }
            } else {
                xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
    return code;
}

//to convert date time in particular format use the below function
//You can add more cases if you required more formats.	
var ConvertDateInFormat = function (date, dateFormat, dateSeperator, timeformat, timeSeperator) {
    if (dateFormat === void 0) { dateFormat = "M/d/yyyy"; }
    if (dateSeperator === void 0) { dateSeperator = "/"; }
    if (timeformat === void 0) { timeformat = "HH:mm"; }
    if (timeSeperator === void 0) { timeSeperator = ":"; }
    var daysShortW = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    var daysLongW = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
    var monthLongW = ["January", "February", "March", "April", "May", "june", "July", "August", "September", "October", "November", "December"];
    var monthShortW = ["Jan", "Feb", "Mar", "Apr", "May", "jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"];
    var M = date.getMonth();
    var dd = date.getDate();
    var y = date.getFullYear();
    var day = date.getDay();
    var H = date.getHours();
    var m = date.getMinutes();
    var s = date.getSeconds();
    var ms = date.getMilliseconds();
    var formatedDateTime = date.toLocaleString();
    var formattedDateString = "";
    var formattedTimeString = "";
    switch (dateFormat) {
        case "MM/dd/yyyy":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            formattedDateString = [month, dt, "" + y].join(dateSeperator);
            break;
        case "dd/MM/yyyy":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            formattedDateString = [dt, month, "" + y].join(dateSeperator);
            break;
        case "M/d/yyyy":
            formattedDateString = ["" + (M + 1), "" + dd, "" + y].join(dateSeperator);
            break;
        case "dd/MMM/yy":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = [dt, monthShortW[M], year].join(dateSeperator);
            break;
        case "M/d/yy":
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = ["" + (M + 1), "" + dd, year].join(dateSeperator);
            break;
        case "d/M/yy":
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = ["" + dd, "" + (M + 1), year].join(dateSeperator);
            break;
        case "MM/dd/yy":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = [month, dt, year].join(dateSeperator);
            break;
        case "dd/MM/yy":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = [dt, month, year].join(dateSeperator);
            break;
        case "yy/MM/dd":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            var year = "" + y;
            year = year.substring(2, 4);
            formattedDateString = [year, month, dt].join(dateSeperator);
            break;
        case "yyyy/MM/dd":
            var dt = "" + dd;
            if (dt.length < 2)
                dt = '0' + dt;
            var month = "" + (M + 1);
            if (month.length < 2)
                month = '0' + month;
            formattedDateString = ["" + y, month, dt].join(dateSeperator);
            break;
    }
    var hours = "" + H;
    if (hours.length < 2)
        hours = "0" + hours;
    var minutes = "" + m;
    if (minutes.length < 2)
        minutes = "0" + minutes;
    switch (timeformat) {
        case "HH:mm":
            formattedTimeString = [hours, minutes].join(timeSeperator);
            break;
        case "H:mm":
            formattedTimeString = ["" + H, minutes].join(timeSeperator);
            break;
        case "hh:mm tt":
            var td = hours + ":" + minutes + ":00";
            formattedTimeString = this.timeConvertToAMPM(td, "hh:mm tt");
            break;
        case "h:mm tt":
            var td = hours + ":" + minutes + ":00";
            formattedTimeString = this.timeConvertToAMPM(td, "h:mm tt");
            break;
    }
    if (formattedDateString != "") {
        if (formattedTimeString != "")
            formatedDateTime = formattedDateString + " " + formattedTimeString;
        else
            formatedDateTime = formattedDateString;
    }
    else {
        if (formattedTimeString != "")
            formatedDateTime = formattedTimeString;
    }
    return formatedDateTime;
}

//convert time string to AMPM format
var timeConvertToAMPM = function (time, format) {
    var patt = new RegExp("^([01]?[0-9]|2[0-3]):?[0-5][0-9](:[0-5][0-9])?$");
    var res = patt.test(time);
    if (res) { // If time format correct
        var H = +time.substr(0, 2);
        var h = (H % 12) || 12;
        var hour = "" + h;
        var ampm = H < 12 ? "AM" : "PM";
        if (format == "hh:mm tt")
            if (hour.length < 2)
                hour = "0" + hour;
        time = hour + time.substr(2, 3) + " " + ampm;
    }
    return time;
};

Hope this will help…

Enjoy Ms CRM!!!

Follow on Facebook- FB: MSCRM16Tech

One thought on “Date Time issue due to different Timezone of User in MS CRM

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s