Разработка
24
Сен
0

Дата без времени

В CRM для полей типа дата/время можно задать свойство «Только дата». В результате чего на форме и в Представление поле будет отображаться «без времени» (только дата). Но ехническая часть CRM в этом плане реализована так, что в БД для полей дата/время всегда хранится полное время – содержащее и дату и время. Причем хранится она в формате UTC. И когда пользователь запрашивает поле дата/время – оно приводится к локальному времени текущего пользователя. По этой причине для полей «без времени» в фоне всегда добавляется временная часть в виде нулей (00:00:00.000). Т.е. полная дата будет выглядеть, например, так: 23.12.2014 00:00:00.000.

В связи с этим при использовании полей «без времени» возникает одна проблема (достаточно серьезная для территориально распределённых компаний)…

Допустим у текущего пользователя задан часовой пояс +4. Он сохраняет в поле new_date дату 10.01.2014. Тогда мы получим следующее:

  • В локальном часовом поясе полная дата будет равна 10.01.2014 00:00:00.000;
  • В формате UTC (в котором значение будет сохранено в БД) полная дата будет равна 09.01.2014 20:00:00.000. Т.е. минус 4 часа.

Далее, предположим, данную дату запрашивает пользователь в часовом поясе +2. Т.е. при преобразовании к локальному времени для него ко времени UTC прибавиться 2 часа. И он увидит не 10.01.2014, а 09.01.2014 (09.01.2014 20:00:00.000 + 2:00:00.000 = 09.01.2014 22:00:00.000). Аналогичная ситуация может возникнуть с «прибавлением» часов к дате UTC (в результате чего пользователи «увидят» следующий день).

Полностью решить эту не получится, но можно попробовать максимально сгладить ее негативный эффект. Для этого нужно задать такое время для даты в формате UTC, чтобы при любом прибавлении/вычитании дата не перескакивала на следующий/предыдущий день. Часовых поясов у нас 24: от -12 до +12. Т.е. к полной дате UTC может быть либо прибавлено 12 часов, либо вычтено 12 часов. Соответственно всегда будет такое время, при котором мы можем «перешагнуть» на следующий день или «вернуться» в предыдущий.



Подхода в этом случае два:

  • Универсальный (более/менее): задаем время в районе 12 часов дня: 11:59 или 12:01 – в зависимости от того, какой («верхний» или «нижний») часовой пояс вы считаете не важным. В результате только либо +12, либо -12 отобразит некорректную дату;
  • Частный: предположим, что компания ведет бизнес только в России. Тогда варианты часовых поясов для нас колеблются от +3 до +12. Соответственно нам необходимо задать такое время для UTC даты, чтобы ни прибавление 3 часов, ни прибавление 12 не изменили дату. Например, 06:00:00.

А теперь посмотрим, как с помощью плагина решить эту задачу:

  • Создайте плагин с таким кодом:
    using System;
    using System.Collections.Generic;
    using Microsoft.Xrm.Sdk;
    
    
    namespace TrueDate
    {
        public class Plugin : IPlugin
        {
            private List<string> dateAttributes;
            public Plugin(string unsecure, string secure)
            {
                // Если в параметрах плагина передан список атрибутов, то разбиваем его и помещаем в массив
                dateAttributes = unsecure == null ? new List<string>() : new List<string>(
                    unsecure.Split(
                        new char[] { ',', ';', ' ', '\r', '\n' },
                        StringSplitOptions.RemoveEmptyEntries
                    )
                );
            }
    
            public void Execute(IServiceProvider serviceProvider)
            {
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                Entity entity = (Entity)context.InputParameters["Target"];
                
                // Просматриваем все переданные атрибуты
                foreach (var attr in dateAttributes)
                {
                    // Если какой-либо из них был обновлен (или заполнен при создании)
                    if (entity.Contains(attr) && entity[attr] is DateTime)
                    {
                        //Получаем его уже в формате UTC
                        DateTime utc = (DateTime)entity[attr];
    
                        // Если время в формате UTC больше 12 часов, то значит дата была уменьшена на один день, при переводе в формат UTC
                        if (utc.TimeOfDay >= new TimeSpan(12, 0, 0))
                        {
                            // Поэтому прибавляем к ней 1 день
                            utc = utc.AddDays(1);
                        }
                        // Прибавляем к (только) UTC дате 11 часов 59 минут и записываем в тот же атрибут во входных апарметрах
                        entity[attr] = utc.Date.AddMinutes(719);
                    }
                }
            }
        }
    }
    

    Что мы тут делаем:

    • В параметрах плагина мы передаем список атрибутов (полей дата/время), для которых необходимо модифицировать время. Названия атрибута могут быть разделены запятыми, точкой с запятой, пробелом, новой строкой, абзацом. Полученные атрибуты помещаем в массив;
    • Просматриваем каждый принятый атрибут и проверяем имеется ли он на входе, т.е. был ли обновлен или заполнен при создании записи;
    • Затем проверяем, была ли изменена сама дата в атрибуте при конвертации в формат UTC. Если была, то возвращаем дату в прежнее состояние. Затем к только дате добавляем 11 часов и 59 минут;
    • И все что получилось записываем в тот же атрибут во входных параметрах.
  • Зарегистрируйте плагин в CRM и создайте для него один шаг:
    • Сообщения: Create и Update;
    • Стадия: Пре;
    • Режим: Синхронно;
    • В небезопасных параметрах передайте названия нужных полей дата/время;
    • Для шага на Update задайте фильтр по этим же атрибутам.

На этом все… почти настоящая дата без времени готова 🙂




Комментарии (0)

*

code