Коллеги, 

а почему в справочнике "Часовые пояса" значения недоступны для добавления, редактирования и удаления? 



Клиент хочет использовать другой список, но есть подозрение, что это где-то зашито в коде и вызовет проблемы

Нравится

3 комментария

Владимир, здравствуйте!

В базовый справочник "Часовые пояса" добавлены все часовые пояса из справочника Windows. Это системный справочник и он не доступен для редактирования с помощью базового функционала, так как при добавлении кастомного часового пояса и выборе его в личном профиле, пользователь не сможет зайти в систему. Для выбора часового пояса мы рекомендуем использовать только базовое наполнение.

Кстати, а как в системе отслеживается переход (и не переход) на летнее-зимнее время? Летом с Москвой одна разница, а зимой другая.

Владимир Соколов,

для понимания того, как bpm'online работает с датами, нужно помнить следующее:

  • В базе даты хранятся в UTC, т.е. "по Гринвичу"
  • При создании соединения приоритет использования часового пояса следующий (по убыванию):
  1. Часовой пояс из профиля пользователя.

    При этом используется колонка "TimeZoneId" из таблицы "SysAdminUnit".

     
  2. Системная настройка «DefaultTimeZone».

    По умолчанию заполняется значением "(GMT) Coordinated Universal Time" (в пакете Base есть соотв. привязка данных), т.е. с нулевым смещением.

     
  3. Клиентский часовой пояс.

    Определяется по передаваемому с клиентского приложения UTC смещению.

     
  4. Часовой пояс сервера приложений.

Учитывая вышеизложенное, время будет отображаться правильно и при переходах на летнее\зимнее, так как в базе дата хранится в UTC и для ее отображения она приводится к "правильному" часовому поясу (В том случае, когда в системной настройке или в профиле пользователей установлены корректные часовые пояса).

 

Показать все комментарии

Добрый день.

Настраиваю мобильное приложение и столкнулся со следующей ситуацией.

Настройки часовых поясов: 

В профиле пользователя, от которого произведен логин в мобильное приложение +3 часа.

На мобильном устройстве +3 часа.

Добавляю запись через мобильное приложение со следующими значениями:

Изображение удалено.

После сохранения записи, выхода в основное меню и повторного открытия этой записи значения оказываются следующими:

Изображение удалено.

В основном приложении значения:

Дата подачи: 20.12.2017 9:28

Дата начала командировки: 19.12.2017

Дата окончания командировки: 19.12.2017

Что можно с этим сделать?

Нравится

2 комментария

Искандер, здравствуйте!

Уточните, пожалуйста:

- версию мобильного приложения;

- версию приложения bpm'online;

- какой конкретно (название) часовой пояс стоит в мобильном устройстве и в приложении;

- в каком разделе создаете запись;

- какой режим (offline/online) мобильного приложения используется;

- какую версию платформы Вы используете (UIv1/UIv2).

Вильшанский Дмитрий,

Версия мобильного приложения: 7.11.7

Версия приложения  7.11.0.3122

Часовой пояс в мобильном приложении определяется автоматически (GMT+03:00 Москва, стандартное время)

Запись создаем в новом разделе (раздел создан в рамках проекта)

Режим приложения - Онлайн

Использовать мобильный интерфейс V2 : да

Показать все комментарии

bpm 5.4 on-site

В базе данных хранится, например, 11 ч 41 мин, а в карточке отображается 18 ч 41, т.е. не учитывает мой часовой пояс. Как это исправить?

Нравится

18 комментариев

о да)
это вечная проблема всех версий ТС
Даже тут, на форуме, часовой пояс сообщений не учитывается
я пишу по московскому времени, а отображается на форуме киевское время ...)
простите за флуд

мы у себя в ТС 3.х решали это тем, что просто использовали везде серверное время
плюс у SQL есть удобный формат даты с UTC

но у нас еще и проблема была в том, что у террасофт 3.х не разделено, где какое время будет использоваться с часовым поясом, а где без
и в результате, если использовать часовые пояса, то у нас, например, даты рождения контактов у некоторых людей становились не, допустим, 09.01.01 (00:00), а 08.01.01 22:00 )))
мило было)

Версия 5.4, а не 3.х

Здравствуйте, Илья.

По умолчанию на клиенте время берется из браузера, который, в свою очередь, берет время из системных настроек.

В любом случае, Вы всегда можете установить конкретному пользователю определенный часовой пояс, тогда время для данного пользователя будет всегда устанавливаться согласно определенному часовому поясу:

В таблице SysAdminUnit у пользователя есть колонка TimeZoneId, куда Вы можете установить определенный часовой пояс из таблицы TimeZone, где хранятся все часовые пояса.

Например, следующим скриптом мы принудительно установим часовой пояс GMT+06:00 Новосибирск пользователю Supervisor:

update SysAdminUnit
set TimeZoneId = 
(select Id from TimeZone where Name = '(GMT+06:00) Новосибирск')
where Name = 'Supervisor'

"Соколов Илья Андреевич" написал:Версия 5.4, а не 3.х

"Калинин Иван" написал:мы у себя в ТС 3.х

я лишь привел пример и поделился опытом. я прекрасно видел, что ваша версия не 3.х

Так же отображает другой часовой пояс.

Здравствуйте, Илья.

Если проблема справедлива для раздела "Активности", прошу применить исправления ниже, предварительно выполнив резервную копию базы данных (!).

1. В схеме «ActivityUtils» найти статический метод «TryGetActivityPeriod» и заменить его содержимое, чтобы получилось так:

public static bool TryGetActivityPeriod(UserConnection userConnection, NameValueCollection queryString, HttpServerUtility server, out DateTime startDate, out DateTime dueDate) {
bool result = false;
                var currentDateTime = userConnection.CurrentUser.GetCurrentDateTime();
                startDate = currentDateTime;
                dueDate = currentDateTime;
                string startDateString = queryString[StartDateKey];                                     
                string dueDateString = queryString[DueDateKey];
                if (!string.IsNullOrEmpty(startDateString) && !string.IsNullOrEmpty(dueDateString)) {
                                result = DateTime.TryParse(server.UrlDecode(startDateString), CultureInfo.InvariantCulture, DateTimeStyles.None, out startDate) &&
                                DateTime.TryParse(server.UrlDecode(dueDateString), CultureInfo.InvariantCulture, DateTimeStyles.None, out dueDate);
                }
                return result;
}

2. Сохранить изменения (НЕ ПУБЛИКОВАТЬ)
3. В процессе схемы «CallEditPage» найти ScriptTask «ChildPageLoadCompleteAfterBaseScript» (событие «PageLoadComplete» вызов после базового). В этой схеме заменить содержимое ScriptTask на:

if (IsNew) {
                var startDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var dueDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var periodFromQueryString = Terrasoft.Configuration.ActivityUtils.TryGetActivityPeriod(UserConnection, Page.Request.QueryString, Page.Server, out startDate, out dueDate);
 
                var defValues = Terrasoft.Configuration.ActivityUtils.GetActivityDefValues(UserConnection);
 
                var dataSource = Page.DataSource;
                dataSource.ActiveRow.SetColumnValue("StartDate", !periodFromQueryString ? startDate : defValues.StartDate);
                dataSource.ActiveRow.SetColumnValue("DueDate", !periodFromQueryString ? dueDate : defValues.DueDate);
                dataSource.ActiveRow.SetColumnValue("OwnerId", defValues.OwnerId);
                dataSource.ActiveRow.SetColumnValue("AuthorId", defValues.AuthorId);
                Page.StartDateEdit.SetValue(!periodFromQueryString ? startDate : defValues.StartDate);
                Page.DueDateEdit.SetValue(!periodFromQueryString ? dueDate : defValues.DueDate);
                Page.OwnerEdit.SetValueAndText(defValues.OwnerId, defValues.OwnerName);
 
                if (Page.Request.QueryString[Terrasoft.Configuration.ActivityUtils.ShowInScheduleKey] == bool.TrueString) {
                               dataSource.ActiveRow.SetColumnValue("ShowInScheduler", true);
                               Page.ShowInShedulerEdit.Checked = true;
                }              
}
 
if (!(IsNew || IsCopy) && Page.DataSource.Rows[0].GetTypedColumnValue<bool>("Status_Finish")) {
                Page.ResultEdit.Enabled = true;
                Page.DetailedResultEdit.Enabled = true;
}
var allowedResult = Page.DataSource.ActiveRow.GetTypedColumnValue<string>("AllowedResult");
AllowedResults = string.IsNullOrEmpty(allowedResult) ? new Dictionary<Guid, string>() : 
                JsonConvert.DeserializeObject<Dictionary<Guid, string>>(allowedResult);
 
if (Page.DataSource.Rows.Count > 0) {
                Page.RemindToAuthorDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToAuthor");
                Page.RemindToOwnerDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToOwner");
}
return true;

4. Сохраняем изменения и сохраняем (НЕ ПУБЛИКУЕМ) схему процесса.
5. В процессе схемы «TaskEditPage» найти ScriptTask «ChildPageLoadCompleteAfterBaseScript» (событие «PageLoadComplete» вызов после базового). В этой схеме заменить содержимое ScriptTask на:

if (IsNew) {
                var startDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var dueDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var periodFromQueryString = Terrasoft.Configuration.ActivityUtils.TryGetActivityPeriod(UserConnection, Page.Request.QueryString, Page.Server, out startDate, out dueDate);
 
                var defValues = Terrasoft.Configuration.ActivityUtils.GetActivityDefValues(UserConnection);
 
                var dataSource = Page.DataSource;
                dataSource.ActiveRow.SetColumnValue("StartDate", !periodFromQueryString ? startDate : defValues.StartDate);
                dataSource.ActiveRow.SetColumnValue("DueDate", !periodFromQueryString ? dueDate : defValues.DueDate);
                dataSource.ActiveRow.SetColumnValue("OwnerId", defValues.OwnerId);
                dataSource.ActiveRow.SetColumnValue("AuthorId", defValues.AuthorId);
                Page.StartDateEdit.SetValue(!periodFromQueryString ? startDate : defValues.StartDate);
                Page.DueDateEdit.SetValue(!periodFromQueryString ? dueDate : defValues.DueDate);
                Page.OwnerEdit.SetValueAndText(defValues.OwnerId, defValues.OwnerName);
 
                if (Page.Request.QueryString[Terrasoft.Configuration.ActivityUtils.ShowInScheduleKey] == bool.TrueString) {
                               dataSource.ActiveRow.SetColumnValue("ShowInScheduler", true);
                               Page.ShowInSchedulerEdit.Checked = true;
                }
}
 
if (!(IsNew || IsCopy) && Page.DataSource.Rows[0].GetTypedColumnValue<bool>("Status_Finish")) {
                Page.ResultEdit.Enabled = true;
                Page.DetailedResultEdit.Enabled = true;
}
var allowedResult = Page.DataSource.ActiveRow.GetTypedColumnValue<string>("AllowedResult");
AllowedResults = string.IsNullOrEmpty(allowedResult) ? new Dictionary<Guid, string>() : 
                JsonConvert.DeserializeObject<Dictionary<Guid, string>>(allowedResult);
Page.CategoryEdit.Enabled = (AllowedResults as Dictionary<Guid, string>).Count == 0;
if (Page.DataSource.Rows.Count > 0) {
                Page.RemindToAuthorDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToAuthor");
                Page.RemaindToOwnerDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToOwner");
}
return true;

6. Сохраняем изменения и сохраняем (НЕ ПУБЛИКУЕМ) схему процесса.
7. В процессе схемы «EmailEditPage» найти ScriptTask «EnableSendButton»/« Set Default Values, Activate Mail Send Button» (событие «PageLoadComplete» вызов после базового). В этой схеме заменить содержимое ScriptTask на:

if (IsNew) {
                var startDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var dueDate = UserConnection.CurrentUser.GetCurrentDateTime();
                var periodFromQueryString = Terrasoft.Configuration.ActivityUtils.TryGetActivityPeriod(UserConnection, Page.Request.QueryString, Page.Server, out startDate, out dueDate);
 
                var defValues = Terrasoft.Configuration.ActivityUtils.GetActivityDefValues(UserConnection);
 
                var dataSource = Page.DataSource;
                dataSource.ActiveRow.SetColumnValue("StartDate", !periodFromQueryString ? startDate : defValues.StartDate);
                dataSource.ActiveRow.SetColumnValue("DueDate", !periodFromQueryString ? dueDate : defValues.DueDate);
                dataSource.ActiveRow.SetColumnValue("OwnerId", defValues.OwnerId);
                dataSource.ActiveRow.SetColumnValue("AuthorId", defValues.AuthorId);
 
                Page.StartDateEdit.SetValue(!periodFromQueryString ? startDate : defValues.StartDate);
                Page.DueDateEdit.SetValue(!periodFromQueryString ? dueDate : defValues.DueDate);
                Page.OwnerEdit.SetValueAndText(defValues.OwnerId, defValues.OwnerName);
 
                if (Page.Request.QueryString[Terrasoft.Configuration.ActivityUtils.ShowInScheduleKey] == bool.TrueString) {
                               dataSource.ActiveRow.SetColumnValue("ShowInScheduler", true);
                               Page.ShowInSchedulerEdit.Checked = true;
                }
 
                var parameterContactId = GetIdFromParameter("ContactId");
                if (!parameterContactId.Equals(Guid.Empty)) {
                               dataSource.ActiveRow.SetColumnValue("Recepient", GetRecipientByContact(parameterContactId));
                }
 
                var parameterTemplateId = GetIdFromParameter("TemplateId");
                if (!parameterTemplateId.Equals(Guid.Empty)) {
                /*
                               Activity activity = GetActivity();
                               var condition = new Dictionary<string, object>() {
                                                                                                                             { "Activity", activity.Id },
                                                                                                                             { "EmailTemplate", parameterTemplateId }
                                                                                                              };
 
                               var link = new EmailTemplateActivity(UserConnection);
 
                               bool isFetched = false;
                               try {
                                               isFetched = link.FetchFromDB(condition);
                               } catch { continue; }
 
                               if (!isFetched) {
                                               link.SetDefColumnValues();
 
                                               link.ActivityId = activity.Id;
                                               link.EmailTemplateId = parameterTemplateId;
 
                                               link.Save();
                               }
                               */
                               AddTemplateToEmailBody(parameterTemplateId, true);                                                           
                }
}
 
if (!IsNew && !IsCopy) {
                var emailStatusId = Page.DataSource.Rows[0].GetTypedColumnValue<Guid>("EmailSendStatusId");
                var emailStatus_Sended = new Guid("8074FFC0-6107-E011-A646-16D83CAB0980");
                var emailStatus_InProgress = new Guid("603BA6AF-6107-E011-A646-16D83CAB0980"); 
                if(emailStatusId.Equals(emailStatus_Sended) || emailStatusId.Equals(emailStatus_InProgress)) {
                               Page.EmailButton.Enabled = false;
                }
 
                if(emailStatusId.Equals(emailStatus_Sended)) {
                               Page.SenderEdit.Enabled = false;
                               Page.RecepientEdit.Enabled = false;
                               Page.CopyRecepientEdit.Enabled = false;
                               Page.BlindCopyRecepientEdit.Enabled = false;
                               Page.TitleEdit.Enabled = false;
                               Page.OwnerEdit.Enabled = false;
                               Page.StatusEdit.Enabled = false;
                               Page.BodyEdit.Enabled = false;
                }
}
 
Page.AddTemplateButton.Enabled = !IsNew && !IsCopy;
Page.CreateMessageFromTemplatesButton.Enabled = !IsNew && !IsCopy;
if (Page.DataSource.Rows.Count > 0) {
                Page.RemindToAuthorDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToAuthor");
                Page.RemindToOwnerDateEdit.Enabled = Page.DataSource.Rows[0].GetTypedColumnValue<bool>("RemindToOwner");
}
return true;

8. Сохраняем изменения, и ПУБЛИКУЕМ схему. (Подтянется публикация измененных схем при компилировании конфигурации).
9. Проверяем.
Все.

Нет, это не раздел активности. Я создал свой раздел.

Нет, это не раздел активности. Я создал свой раздел.

Нет, это не раздел активности. Я создал свой раздел.

Илья, проблемы со временем только в Вашем разделе или во всех разделах системы?

Только в моём разделе.

Илья,

проблема в том, что в базе данные хранятся в UTC формате, а затем у каждого пользователя отображаются в соответствии с часовым поясом его браузера.

В Ваш раздел данные попадают напрямую SQL-запросом и уже в базу они сохраняются со смещением согласно Вашему часовому поясу.

Решением является писать записи в Ваш раздел в формате UTC. Для этого существуют функция SQL:

GETUTCDATE()

http://msdn.microsoft.com/en-us/library/ms178635.aspx

Працюю з версією 5.4

Намагаюсь розібратися з часовим поясом.
Формую в SQL вибірку для формування звіту.
В базі дати зберігаються як UTC.
Витягую значення

var offSet=UserConnection.CurrentUser.TimeZone.BaseUtcOffset.TotalMinutes

яке в SQL-запиті підставляю в дати:

DATEADD(MINUTES,offSet,<дата>)

Все добре, окрім одного:
UserConnection.CurrentUser.TimeZone.DisplayName =>
displayName : (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius
але по карточці звернення бачу, що зміщення не +2:00, а +3:00

Поки що не знайшов, як вийти на +3:00

Видимо, ещё один час даёт летнее время.

"Зверев Александр" написал:

Видимо, ещё один час даёт летнее время.


Може й так, але яким чином та година враховується ?
В одному скрипті я отримую значення:

var respondedOn = activeRow.GetTypedColumnValue<DateTime>("RespondedOn");

та

var	offSet = UserConnection.CurrentUser.TimeZone.BaseUtcOffset.TotalMinutes;

respondedOn враховує +3:00
а
offSet = 120

Тут пишут, что для часового пояса можно получить, переводят ли часы:


Use TimeZoneInfo.IsDaylightSavingTime Method (DateTimeOffset) to find if it is currently Daylight saving for your Timezone.

var info = TimeZoneInfo.FindSystemTimeZoneById("Greenwich Standard Time");
DateTimeOffset localServerTime = DateTimeOffset.Now;
bool isDaylightSaving = info.IsDaylightSavingTime(localServerTime);

There are further examples here

А тут упоминают такое свойтво:

DateTimeOffset.Now.Offset.TotalMinutes

Оно даёт 2 или 3?

offSet : 180

Те, що треба. Дякую

Показать все комментарии