From 3c6026c04f0a5df46a450191e2e7d6e162abfd9c Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Sat, 25 Apr 2026 23:58:30 +0300 Subject: [PATCH 01/30] #2716 Main changes to client-side Entity Centre / Master conversions for properties marked with 'isDependentTimeZoneMode()'. --- .../impl/DateTimePickerWidget.java | 6 ++-- .../platform/web/components/tg-calendar.js | 14 ++++----- .../web/components/tg-month-selector.js | 22 +++++++------- .../web/editors/tg-datetime-picker.js | 28 ++++++++++------- .../platform/web/reflection/tg-date-utils.js | 30 +++++++++++++++---- .../platform/web/reflection/tg-reflector.js | 11 ++++++- 6 files changed, 74 insertions(+), 37 deletions(-) diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java index b4c405a9912..b1a499a707f 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java @@ -1,10 +1,10 @@ package ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl; -import java.util.Map; - import ua.com.fielden.platform.utils.Pair; import ua.com.fielden.platform.web.view.master.api.widgets.impl.AbstractWidget; +import java.util.Map; + /** * The implementation for web date-time picker widgets. * @@ -34,7 +34,7 @@ protected Map createCustomAttributes() { final Map customAttr = super.createCustomAttributes(); customAttr.put("time-portion-to-become-end-of-day", timePortionToBecomeEndOfDay); if (timeZone != null) { - customAttr.put("time-zone", timeZone); + customAttr.put("prop", null); // TODO } if (datePortion != null) { customAttr.put("date-portion", datePortion); diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js index 4ed3f4fce02..9f1d2ec4fe0 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js @@ -82,7 +82,7 @@ const template = html`
- + @@ -109,8 +109,8 @@ template.setAttribute('strip-whitespace', ''); * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. */ - timeZone: { - type: String + prop: { + type: Object }, selectedDate: { @@ -162,7 +162,7 @@ template.setAttribute('strip-whitespace', ''); }, attached: function () { - var todayDate = _momentTz(this.timeZone).startOf('minute'); + var todayDate = _momentTz(this.prop).startOf('minute'); this.selectedDate = this.selectedDate || todayDate.valueOf(); this.selectedHour = (typeof this.selectedHour !== 'undefined' && this.selectedHour !== null) ? this.selectedHour : todayDate.hour(); this.selectedMinute = (typeof this.selectedMinute !== 'undefined' && this.selectedMinute !== null) ? this.selectedMinute : todayDate.minutes(); @@ -190,11 +190,11 @@ template.setAttribute('strip-whitespace', ''); _selectYearMonthDay: function (newYear, newMonth, newDay) { if (newYear !== null && newMonth !== null && newDay !== null) { const yearMonth = { year: newYear, month: newMonth, day: 1 }; - const lastDayOfTheMonth = _momentTz(yearMonth, this.timeZone).daysInMonth(); + const lastDayOfTheMonth = _momentTz(yearMonth, this.prop).daysInMonth(); const computedDay = newDay > lastDayOfTheMonth ? lastDayOfTheMonth : newDay; const yearMonthDay = { year: newYear, month: newMonth, day: computedDay }; - this.selectedDate = _momentTz(yearMonthDay, this.timeZone).valueOf(); + this.selectedDate = _momentTz(yearMonthDay, this.prop).valueOf(); } }, @@ -203,7 +203,7 @@ template.setAttribute('strip-whitespace', ''); */ _selectedDateChanged: function (newValue, oldValue) { if (newValue !== null) { - var newMoment = _momentTz(newValue, this.timeZone); + var newMoment = _momentTz(newValue, this.prop); this._adjustDate(newMoment); } }, diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js index d8200b5dcd0..ff60010c8ed 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js @@ -97,11 +97,11 @@ const template = html` (function () { const getDaysInMonth = function (year, month) { const yearMonth = { year: year, month: month, day: 1 }; - return _momentTz(yearMonth, this.timeZone).daysInMonth(); + return _momentTz(yearMonth, this.prop).daysInMonth(); }; const getDaysInPreviousMonth = function (year, month) { const yearMonth = { year: year, month: month, day: 1 }; - return _momentTz(yearMonth, this.timeZone).add(-1, 'M').daysInMonth(); + return _momentTz(yearMonth, this.prop).add(-1, 'M').daysInMonth(); }; const createMonth = function (year, month, firstDayOfWeek) { const weeks = []; @@ -117,7 +117,7 @@ const template = html` }; const createWeek = function (year, month, day, firstDayOfWeek) { const yearMonthDay = { year: year, month: month, day: day }; - const firstDay = (7 - firstDayOfWeek + _momentTz(yearMonthDay, this.timeZone).day()) % 7;//Maps week day where 0 - Sunday into week where 0 - custom first day of week + const firstDay = (7 - firstDayOfWeek + _momentTz(yearMonthDay, this.prop).day()) % 7;//Maps week day where 0 - Sunday into week where 0 - custom first day of week const week = []; const previousMonthDays = getDaysInPreviousMonth.bind(this)(year, month); let _i = 0; @@ -149,8 +149,8 @@ const template = html` * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. */ - timeZone: { - type: String + prop: { + type: Object }, selectedDate: { @@ -176,7 +176,7 @@ const template = html` }, ready: function () { - var today = _momentTz(this.timeZone); + var today = _momentTz(this.prop); this.todayDay = today.date(); this.todayMonth = today.month(); this.todayYear = today.year(); @@ -198,8 +198,8 @@ const template = html` */ _firstDayOfWeekChanged: function (newFirstDayOfWeek) { const momentToShow = this.selectedDate ? - _momentTz(this.selectedDate, this.timeZone) : - _momentTz(this.timeZone); + _momentTz(this.selectedDate, this.prop) : + _momentTz(this.prop); this._adjustMonth(momentToShow, newFirstDayOfWeek); }, @@ -222,7 +222,7 @@ const template = html` */ _selectMonth: function (inc) { var yearMonth = { year: this.year, month: this.month, day: 1 }; - var momentToShow = _momentTz(yearMonth, this.timeZone).add(inc, 'M'); + var momentToShow = _momentTz(yearMonth, this.prop).add(inc, 'M'); this._adjustMonth(momentToShow, this._firstDayOfWeek); }, @@ -243,7 +243,7 @@ const template = html` */ _selectDay: function (event, detail, el) { const yearMonthDay = { year: this.year, month: this.month, day: 1 }; - this.selectedDate = _momentTz(yearMonthDay, this.timeZone).add(event.model.day.monthIncrementor, 'M').date(Math.abs(event.model.day.day)).valueOf(); + this.selectedDate = _momentTz(yearMonthDay, this.prop).add(event.model.day.monthIncrementor, 'M').date(Math.abs(event.model.day.day)).valueOf(); }, _acceptDate: function (e) { @@ -255,7 +255,7 @@ const template = html` */ _selectedDateChanged: function (newValue, oldValue) { if (newValue !== null) { - var newMoment = _momentTz(newValue, this.timeZone); + var newMoment = _momentTz(newValue, this.prop); if (newMoment.isValid()) { this.selectedYear = newMoment.year(); this.selectedMonth = newMoment.month(); diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js index 00b7087dacc..9f38fe9d38e 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js @@ -88,8 +88,8 @@ export class TgDatetimePicker extends TgEditor { * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. */ - timeZone: { - type: String, + prop: { + type: Object, value: null }, @@ -227,7 +227,7 @@ export class TgDatetimePicker extends TgEditor { domBind._acceptDateBind = function () { self._isAcceptingDateFromPicker = true; - const acceptedMoment = _momentTz(this.$.datePicker.selectedDate, self.timeZone) + const acceptedMoment = _momentTz(this.$.datePicker.selectedDate, self.prop) .hour(this.$.datePicker.selectedHour) .minute(this.$.datePicker.selectedMinute) .seconds(this.$.datePicker.seconds) @@ -281,12 +281,12 @@ export class TgDatetimePicker extends TgEditor { }.bind(domBind)); }.bind(domBind); - domBind.timeZone = self.timeZone; + domBind.prop = self.prop; const dialogTemplate = document.createElement('template'); dialogTemplate.innerHTML = '' + - '' + + '' + '
' + 'Cancel' + 'Ok' + @@ -334,7 +334,7 @@ export class TgDatetimePicker extends TgEditor { return this.convertToString(this._validMoment.valueOf()); } // determine current date portion format for this editor ... - const datePortionFormat = this.timeZone ? timeZoneFormats[this.timeZone]['L'] : moment.localeData().longDateFormat('L'); + const datePortionFormat = this?.prop.timeZone() ? timeZoneFormats[this?.prop.timeZone()]['L'] : moment.localeData().longDateFormat('L'); // ... and its separator; const separator = datePortionFormat.includes('/') ? '/' : datePortionFormat.includes('-') ? '-' : null; // validate separator and ... @@ -364,7 +364,7 @@ export class TgDatetimePicker extends TgEditor { let adjustedMoment = validMoment; if (indexOfY > -1 && format.indexOf('Y', indexOfY + 1) === -1) { // only one Y in the format const strWithDoubleDigitYear = this._convertToDoubleDigitYear(stringValue, indexOfY === 0 ? 0 : this._findSecondSeparatorIndex(stringValue, separator) + 1); - adjustedMoment = _momentTz(strWithDoubleDigitYear, format.replace('Y', 'YY'), true, this.timeZone); + adjustedMoment = _momentTz(strWithDoubleDigitYear, format.replace('Y', 'YY'), true, this.prop); } if (datePortionFormats.indexOf(format) !== -1 && this.timePortionToBecomeEndOfDay === true) { adjustedMoment.add(1, 'days').subtract(1, 'milliseconds'); // even though original validMoment can be mutated here, it will not be used anywhere else; so it is safe to do this mutation @@ -423,7 +423,7 @@ export class TgDatetimePicker extends TgEditor { const upperCasedValue = editingValue[0].toUpperCase(); // In concrete time-zone (e.g. UTC) just use standard method _momentTz for creating 'now' in that time-zone. // Otherwise use standard now() function. - const convertedMoment = this.timeZone ? _momentTz(this.timeZone) : now(); + const convertedMoment = this?.prop.timeZone() ? _momentTz(this?.prop.timeZone()) : now(this?.prop); if ('T' === upperCasedValue) { const todayMoment = convertedMoment.startOf("day"); @@ -445,7 +445,7 @@ export class TgDatetimePicker extends TgEditor { return null; } else { const firstFormat = formats[0]; - let tryingMoment = _momentTz(stringValue, firstFormat, true, this.timeZone); + let tryingMoment = _momentTz(stringValue, firstFormat, true, this.prop); if (tryingMoment.isValid()) { if (adjustValidMoment) { return adjustValidMoment(stringValue, tryingMoment, firstFormat); @@ -512,7 +512,7 @@ export class TgDatetimePicker extends TgEditor { return strWithoutSpaces; } else { const insertionPoint = firstSeparatorIndex + numberOfDigitsAfterLastSeparator + 1; - const currYearStr = _momentTz(this.timeZone).format('YYYY'); + const currYearStr = _momentTz(this.prop).format('YYYY'); return yearsOnEnding // years are only supported on beginning and ending of datePortionFormat (not in the middle, like MM-YYYY-DD) ? strWithoutSpaces.slice(0, insertionPoint) + separator + currYearStr + ' ' + strWithoutSpaces.slice(insertionPoint) : currYearStr + separator + strWithoutSpaces.slice(0, insertionPoint) + ' ' + strWithoutSpaces.slice(insertionPoint) @@ -522,6 +522,14 @@ export class TgDatetimePicker extends TgEditor { return strWithoutSpaces.slice(0, insertionPoint) + ' ' + strWithoutSpaces.slice(insertionPoint); } } + + _entityChanged (newValue, oldValue) { + if (this.reflector().isEntity(newValue)) { + this.prop = newValue.type().prop(this.propertyName); + } + super._entityChanged (newValue, oldValue); + } + } customElements.define('tg-datetime-picker', TgDatetimePicker); \ No newline at end of file diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js index acb258d8058..fd1202e69ee 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js @@ -23,7 +23,8 @@ export function _millis(fullDateString) { // using strict 'DD/MM/YYYY HH:mm:ss.S return moment(fullDateString, 'DD/MM/YYYY HH:mm:ss.SSS', true).valueOf(); }; -export function _millisDateRepresentation(dateMillis, timeZone, portionToDisplay) { +export function _millisDateRepresentation(dateMillis, prop, portionToDisplay) { + const timeZone = prop?.timeZone(); const millisecondsExist = dateMillis % 1000 !== 0; const secondsExist = !millisecondsExist ? (dateMillis / 1000) % 60 !== 0 : true; const timeFormat = () => { @@ -43,7 +44,7 @@ export function _millisDateRepresentation(dateMillis, timeZone, portionToDisplay } else { format = "L " + timeFormat(); } - return _momentTz(dateMillis, timeZone).format(timeZone ? timeZoneFormats[timeZone][format] : format); + return _momentTz(dateMillis, prop).format(timeZone ? timeZoneFormats[timeZone][format] : format); }; /** @@ -54,8 +55,21 @@ export function _millisDateRepresentation(dateMillis, timeZone, portionToDisplay */ export function _momentTz(input) { const args = Array.prototype.slice.call(arguments, 0, -1); - const timeZone = arguments[arguments.length - 1]; - return timeZone ? moment.tz.apply(null, arguments) : moment.apply(null, args); + const prop = arguments[arguments.length - 1]; + const timeZone = prop?.timeZone(); + if (timeZone) { + args.push(timeZone); + return moment.tz.apply(null, args); + } + if (window.TG_APP && window.TG_APP.timeZone && prop.isDependentTimeZoneMode()) { + moment.tz.setDefault(moment.tz.guess(true)); + const result = moment.apply(null, args); + moment.tz.setDefault(window.TG_APP.timeZone); + return result; + } + else { + return moment.apply(null, args); + } }; export function _timeZoneHeader () { @@ -65,7 +79,13 @@ export function _timeZoneHeader () { /** * Returns time-zone mode-specific 'now' moment. */ -export function now() { +export function now(prop) { + if (window.TG_APP && window.TG_APP.timeZone && prop && prop.isDependentTimeZoneMode()) { + moment.tz.setDefault(moment.tz.guess(true)); + const result = moment(moment().tz(moment.tz.guess(true)).format('YYYY-MM-DD HH:mm:ss.SSS')); + moment.tz.setDefault(window.TG_APP.timeZone); + return result; + } // In independent time-zone mode we do the following trick: // 1. create 'now' moment in current surrogate (equal to server one) time-zone; // 2. convert it to real time-zone to be able to format it into our 'real' string; diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js index 6b137d6a61f..3318f6f2970 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js @@ -110,6 +110,15 @@ var _createEntityTypePropPrototype = function () { return typeof this._timeZone === 'undefined' ? null : this._timeZone; } + /** + * Returns specific time-zone for the property of type date. + * + * IMPORTANT: do not use '_timeZone' field directly! + */ + EntityTypeProp.prototype.isDependentTimeZoneMode = function () { // TODO + return ['startDate', 'lastExecutionDate', 'nextExecutionDate'].includes(this._name); + } + /** * Returns 'true' when the type property is secrete, false otherwise. * @@ -1381,7 +1390,7 @@ const _toString = function (bindingValue, rootEntityType, property) { } else if (typeof bindingValue === 'number') { if (propertyType === 'Date') { const prop = rootEntityType.prop(property); - return _millisDateRepresentation(bindingValue, prop.timeZone(), prop.datePortion()); + return _millisDateRepresentation(bindingValue, prop, prop.datePortion()); } else { return '' + bindingValue; // Integer value (or Long, but very rare) and BigDecimal value } From 615392d14b61694f0dddd4ae6d42ae8c91fedc08 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Mon, 27 Apr 2026 23:50:24 +0300 Subject: [PATCH 02/30] #2716 Remove unused imports. --- .../chart-decker/tg-chart-decker-template-behavior.js | 2 +- .../web/ua/com/fielden/platform/web/components/tg-calendar.js | 2 +- .../fielden/platform/web/egi/tg-egi-data-retrieval-behavior.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js index dca048d2317..ef6fb28d67b 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js @@ -2,7 +2,7 @@ import { TgAppConfig } from '/app/tg-app-config.js'; import { TgEntityMasterTemplateBehavior} from '/resources/master/tg-entity-master-template-behavior.js'; import { tearDownEvent } from '/resources/reflection/tg-polymer-utils.js'; -import {_momentTz, _millisDateRepresentation} from '/resources/reflection/tg-date-utils.js'; +import { _millisDateRepresentation } from '/resources/reflection/tg-date-utils.js'; const generateActionTooltip = function (action) { const shortDesc = "" + action.shortDesc + ""; diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js index 9f1d2ec4fe0..c0a62952650 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js @@ -13,7 +13,7 @@ import '/resources/components/tg-month-selector.js'; import {Polymer} from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn.js'; import {html} from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js'; -import moment from '/resources/polymer/lib/moment-lib.js'; // used for moment.localeData(). ... +import moment from '/resources/polymer/lib/moment-lib.js'; // used for moment.localeData() and moment.monthsShort(). import {_momentTz} from '/resources/reflection/tg-date-utils.js'; const template = html` diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/egi/tg-egi-data-retrieval-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/egi/tg-egi-data-retrieval-behavior.js index d11d91990e6..93b4c4cda10 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/egi/tg-egi-data-retrieval-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/egi/tg-egi-data-retrieval-behavior.js @@ -1,4 +1,3 @@ -import { _millisDateRepresentation } from '/resources/reflection/tg-date-utils.js'; import { TgReflector } from '/app/tg-reflector.js'; export const TgEgiDataRetrievalBehavior = { From a7ea864c3a2389ae9ae751747e6f090bf5efd543 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Mon, 27 Apr 2026 23:53:01 +0300 Subject: [PATCH 03/30] #2716 Correct computation of moment for concrete-time-zone date properties. --- .../ua/com/fielden/platform/web/editors/tg-datetime-picker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js index 9f38fe9d38e..04ecb8ea028 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js @@ -423,7 +423,7 @@ export class TgDatetimePicker extends TgEditor { const upperCasedValue = editingValue[0].toUpperCase(); // In concrete time-zone (e.g. UTC) just use standard method _momentTz for creating 'now' in that time-zone. // Otherwise use standard now() function. - const convertedMoment = this?.prop.timeZone() ? _momentTz(this?.prop.timeZone()) : now(this?.prop); + const convertedMoment = this.prop?.timeZone() ? _momentTz(this.prop) : now(this.prop); if ('T' === upperCasedValue) { const todayMoment = convertedMoment.startOf("day"); From 5bd4840672d74a2e2592f5ca5245a3e706a47fe6 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Mon, 27 Apr 2026 23:58:01 +0300 Subject: [PATCH 04/30] #2716 Adjust chart decker / scatter plot date value accessors as per updated tg-date-utils API methods. Remove incorrect _momentTz parameter (date portion) in scatter plot date value accessor. --- .../chart-decker/tg-chart-decker-template-behavior.js | 2 +- .../master/scatter-plot/tg-scatter-plot-master-behavior.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js index ef6fb28d67b..8e805b07e50 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js @@ -79,7 +79,7 @@ const TgChartDeckerTemplateBehaviorImpl = { return function (entity, value) { if (!value) { const splitedType = dateType.split(':'); - return _millisDateRepresentation(entity.get(propertyName), splitedType[1] || null, splitedType[2] || null); + return _millisDateRepresentation(entity.get(propertyName), entity.type().prop(propertyName), splitedType[2] || null); } } }, diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js index a5b2de84eb1..a45425b9030 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js @@ -1,5 +1,5 @@ import { TgEntityMasterTemplateBehavior} from '/resources/master/tg-entity-master-template-behavior.js' -import {_momentTz} from '/resources/reflection/tg-date-utils.js'; +import { _momentTz } from '/resources/reflection/tg-date-utils.js'; const getTooltipValueForEntity = function (entity) { const keyValue = entity.toString(); @@ -67,7 +67,7 @@ const TgScatterPlotMasterBehaviorImpl = { return function (entity, value) { if (!value) { const splitedType = dateType.split(':'); - return _momentTz(entity.get(propertyName), splitedType[1] || null, splitedType[2] || null).toDate(); + return _momentTz(entity.get(propertyName), entity.type().prop(propertyName)).toDate(); } } }, From 757ddce0fc60638c46d3688a659a97b3abf1520e Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 00:30:17 +0300 Subject: [PATCH 05/30] #2716 Allow surrogate prop objects without timeZone / isDependentTimeZoneMode functions. Simplify checks for independent time-zone mode ('window.TG_APP?.timeZone'). --- .../fielden/platform/web/editors/tg-datetime-picker.js | 2 +- .../com/fielden/platform/web/reflection/tg-date-utils.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js index 04ecb8ea028..e63e74148bf 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js @@ -423,7 +423,7 @@ export class TgDatetimePicker extends TgEditor { const upperCasedValue = editingValue[0].toUpperCase(); // In concrete time-zone (e.g. UTC) just use standard method _momentTz for creating 'now' in that time-zone. // Otherwise use standard now() function. - const convertedMoment = this.prop?.timeZone() ? _momentTz(this.prop) : now(this.prop); + const convertedMoment = this.prop?.timeZone?.() ? _momentTz(this.prop) : now(this.prop); if ('T' === upperCasedValue) { const todayMoment = convertedMoment.startOf("day"); diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js index fd1202e69ee..3e45eb2f6ac 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-date-utils.js @@ -24,7 +24,7 @@ export function _millis(fullDateString) { // using strict 'DD/MM/YYYY HH:mm:ss.S }; export function _millisDateRepresentation(dateMillis, prop, portionToDisplay) { - const timeZone = prop?.timeZone(); + const timeZone = prop?.timeZone?.(); const millisecondsExist = dateMillis % 1000 !== 0; const secondsExist = !millisecondsExist ? (dateMillis / 1000) % 60 !== 0 : true; const timeFormat = () => { @@ -56,12 +56,12 @@ export function _millisDateRepresentation(dateMillis, prop, portionToDisplay) { export function _momentTz(input) { const args = Array.prototype.slice.call(arguments, 0, -1); const prop = arguments[arguments.length - 1]; - const timeZone = prop?.timeZone(); + const timeZone = prop?.timeZone?.(); if (timeZone) { args.push(timeZone); return moment.tz.apply(null, args); } - if (window.TG_APP && window.TG_APP.timeZone && prop.isDependentTimeZoneMode()) { + if (window.TG_APP?.timeZone && prop?.isDependentTimeZoneMode?.()) { moment.tz.setDefault(moment.tz.guess(true)); const result = moment.apply(null, args); moment.tz.setDefault(window.TG_APP.timeZone); @@ -80,7 +80,7 @@ export function _timeZoneHeader () { * Returns time-zone mode-specific 'now' moment. */ export function now(prop) { - if (window.TG_APP && window.TG_APP.timeZone && prop && prop.isDependentTimeZoneMode()) { + if (window.TG_APP?.timeZone && prop?.isDependentTimeZoneMode?.()) { moment.tz.setDefault(moment.tz.guess(true)); const result = moment(moment().tz(moment.tz.guess(true)).format('YYYY-MM-DD HH:mm:ss.SSS')); moment.tz.setDefault(window.TG_APP.timeZone); From 0c3b1580ead85d706dffc98931b31aa796d91a55 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 00:30:45 +0300 Subject: [PATCH 06/30] #2716 Adjust web tests. --- .../web/editors/test/tg-datetime-picker-time-zones.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/test/tg-datetime-picker-time-zones.html b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/test/tg-datetime-picker-time-zones.html index d1461eeb31c..3e5027ea6a0 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/test/tg-datetime-picker-time-zones.html +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/test/tg-datetime-picker-time-zones.html @@ -49,7 +49,7 @@ const edUtc = master.$.editor_4_datePropUtc; master.postRetrieved = function (entity, bindingEntity, customObject) { - const utcMoment = _millisDateRepresentation(expectedMillis, 'UTC'); /* 05/09/2016 06:33:24.000 in UTC (UTC+00:00) */ + const utcMoment = _millisDateRepresentation(expectedMillis, {timeZone: () => 'UTC'}); /* 05/09/2016 06:33:24.000 in UTC (UTC+00:00) */ assert.strictEqual(utcMoment, expectedText, "Date representation should not have millis but should have seconds."); assert.strictEqual(edUtc._editingValue, expectedText, "_editingValue should not have millis but should have seconds."); assert.strictEqual(edUtc._commValue, expectedText, "_commValue should not have millis but should have seconds."); @@ -167,7 +167,7 @@ assert.strictEqual(ed._editingValue, localisedMomentOf3609999, "_editingValue should be non-empty on start."); assert.strictEqual(ed._commValue, localisedMomentOf3609999, "_commValue should be non-empty on start."); assert.strictEqual(ed._acceptedValue, 3609999, "_acceptedValue should be non-empty on start."); - const utcMomentOf3609999 = _millisDateRepresentation(3609999, 'UTC'); /* 01/01/1970 01:00:09.999 in UTC (UTC+00:00) */ + const utcMomentOf3609999 = _millisDateRepresentation(3609999, {timeZone: () => 'UTC'}); /* 01/01/1970 01:00:09.999 in UTC (UTC+00:00) */ assert.strictEqual(edUtc._editingValue, utcMomentOf3609999, "_editingValue should be non-empty on start."); assert.strictEqual(edUtc._commValue, utcMomentOf3609999, "_commValue should be non-empty on start."); assert.strictEqual(edUtc._acceptedValue, 3609999, "_acceptedValue should be non-empty on start."); @@ -303,7 +303,7 @@ assert.strictEqual(ed._editingValue, localisedMomentOf3609999, "_editingValue should be non-empty on start."); assert.strictEqual(ed._commValue, localisedMomentOf3609999, "_commValue should be non-empty on start."); assert.strictEqual(ed._acceptedValue, 3609999, "_acceptedValue should be non-empty on start."); - const utcMomentOf3609999 = _millisDateRepresentation(3609999, 'UTC'); /* 01/01/1970 01:00:09.999 in UTC (UTC+00:00) */ + const utcMomentOf3609999 = _millisDateRepresentation(3609999, {timeZone: () => 'UTC'}); /* 01/01/1970 01:00:09.999 in UTC (UTC+00:00) */ assert.strictEqual(edUtc._editingValue, utcMomentOf3609999, "_editingValue should be non-empty on start."); assert.strictEqual(edUtc._commValue, utcMomentOf3609999, "_commValue should be non-empty on start."); assert.strictEqual(edUtc._acceptedValue, 3609999, "_acceptedValue should be non-empty on start."); From 7352823edb7ba041dca5e35957989da2837eb427 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 01:22:45 +0300 Subject: [PATCH 07/30] #2716 Add property-level marker annotation. --- .../annotation/DependentTimeZoneMode.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java new file mode 100644 index 00000000000..adf8d56d108 --- /dev/null +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java @@ -0,0 +1,35 @@ +package ua.com.fielden.platform.entity.annotation; + +import ua.com.fielden.platform.utils.IDates; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Date; + +/// An annotation for properties of type [java.util.Date] to enforce dependent time-zone mode in Web UI logic. +/// +/// Please note, that appropriate [IDates#now()] and [IDates#zoned(Date)] method variations are required in server-side logic. +/// I.e. it may not be sufficient to annotate the properties with this annotation. +/// +/// ```java +///static DateTime now(final IDates dates) { +/// return new DateTime(getTimeZone(dates)); +///} +///static DateTime zoned(final Date date, fina IDates dates) { +/// return new DateTime(date, getTimeZone(dates)); +///} +///private static DateTimeZone getTimeZone(final IDates dates) { +/// if (dates.requestTimeZone().isEmpty()) { +/// throw failure(ERR_TIME_ZONE_IS_MISSING); +/// } +/// return dates.requestTimeZone().get(); +///} +/// ``` +/// +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD }) +public @interface DependentTimeZoneMode { + +} From ed2bc6119b64ffa6b33558e7d6935b4ec7050203 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 01:25:18 +0300 Subject: [PATCH 08/30] #2716 Proliferate property-level marker annotation to criteria entity type 'from / to' criteria properties. This is necessary for correct UI logic in Entity Centre selection criteria. ----- Changes from merge during cherry picking from Issue-#2716 to Issue-#2716-independent-from-#2224. --- .../generator/impl/CriteriaGenerator.java | 15 +++--- .../annotation/factory/DateAnnotations.java | 47 +++++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java index b953600a19c..16656fd0d49 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java @@ -215,15 +215,10 @@ private static List generateCriteriaProperties(final Class copyDateAnnotations(final Class managedType, final String propertyName) { - return of(DateOnly.class, TimeOnly.class, PersistentType.class) + return of(DateOnly.class, TimeOnly.class, PersistentType.class, DependentTimeZoneMode.class) .map(annotationType -> getPropertyAnnotationOptionally(annotationType, managedType, propertyName)) .flatMap(annotation -> annotation.isPresent() ? of(annotation.get()) : empty()) .map(annotation -> { @@ -231,8 +226,10 @@ private static List copyDateAnnotations(final Class managedType, return newDateOnlyAnnotation(); } else if (annotation instanceof TimeOnly) { return newTimeOnlyAnnotation(); - } else { + } else if (annotation instanceof PersistentType) { return newUtcAnnotation(); + } else { + return newDependentTimeZoneModeAnnotation(); } }) .collect(toList()); diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java index 5aa37d51693..4396a9a2ea0 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java @@ -1,25 +1,19 @@ package ua.com.fielden.platform.entity.annotation.factory; -import java.lang.annotation.Annotation; - import ua.com.fielden.platform.entity.annotation.DateOnly; +import ua.com.fielden.platform.entity.annotation.DependentTimeZoneMode; import ua.com.fielden.platform.entity.annotation.PersistentType; import ua.com.fielden.platform.entity.annotation.TimeOnly; import ua.com.fielden.platform.types.markers.IUtcDateTimeType; -/** - * Factory for date annotations. - * - * @author TG Team - * - */ +import java.lang.annotation.Annotation; + +/// Factory for date annotations. +/// public class DateAnnotations { - /** - * Instantiates {@link DateOnly} annotation. - * - * @return - */ + /// Instantiates [DateOnly] annotation. + /// public static DateOnly newDateOnlyAnnotation() { return new DateOnly() { @Override @@ -29,11 +23,8 @@ public Class annotationType() { }; } - /** - * Instantiates {@link TimeOnly} annotation. - * - * @return - */ + /// Instantiates [TimeOnly] annotation. + /// public static TimeOnly newTimeOnlyAnnotation() { return new TimeOnly() { @Override @@ -43,11 +34,8 @@ public Class annotationType() { }; } - /** - * Instantiates marker annotation for UTC date properties. - * - * @return - */ + /// Instantiates marker annotation for UTC date properties. + /// public static PersistentType newUtcAnnotation() { return new PersistentType() { @Override @@ -66,5 +54,16 @@ public Class userType() { } }; } - + + /// Instantiates [DependentTimeZoneMode] annotation. + /// + public static DependentTimeZoneMode newDependentTimeZoneModeAnnotation() { + return new DependentTimeZoneMode() { + @Override + public Class annotationType() { + return DependentTimeZoneMode.class; + } + }; + } + } \ No newline at end of file From 80ac20707f0d8a16afd1ed3ebc2be8a040bcf9ac Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 01:27:03 +0300 Subject: [PATCH 09/30] #2716 Ship marker annotation to UI entity type properties ('isDependentTimeZoneMode' API). --- .../serialisation/jackson/EntitySerialiser.java | 3 +++ .../serialisation/jackson/EntityTypeProp.java | 14 ++++++++++++++ .../platform/web/reflection/tg-reflector.js | 8 ++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java index bc8ea007c92..0057209837a 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java @@ -205,6 +205,9 @@ private static > EntityType createEntityTypeInfo(fin if (isPropertyAnnotationPresent(TimeOnly.class, type, name)) { entityTypeProp.set_time(TRUE); } + if (isPropertyAnnotationPresent(DependentTimeZoneMode.class, type, name)) { + entityTypeProp.set_dependentTimeZoneMode(TRUE); + } final IsProperty isPropertyAnnotation = AnnotationReflector.getPropertyAnnotation(IsProperty.class, type, name); if (isPropertyAnnotation != null) { final Long length = Long.valueOf(isPropertyAnnotation.length()); diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java index 9e882f50900..74f9dbf5c33 100644 --- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java +++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java @@ -58,6 +58,10 @@ public class EntityTypeProp extends AbstractEntity { @Title(value = "TimeZone", desc = "TimeZone of date-typed property.") private String _timeZone; + @IsProperty + @Title(value = "Dependent Time-zone Mode", desc = "Dependent Time-zone Mode of date-typed property.") + private Boolean _dependentTimeZoneMode; + @IsProperty @Title(value = "Is Date Only?", desc = "Should display only date portion?") private Boolean _date; @@ -156,6 +160,16 @@ public Boolean get_date() { return _date; } + @Observable + public EntityTypeProp set_dependentTimeZoneMode(final Boolean _dependentTimeZoneMode) { + this._dependentTimeZoneMode = _dependentTimeZoneMode; + return this; + } + + public Boolean get_dependentTimeZoneMode() { + return _dependentTimeZoneMode; + } + @Observable public EntityTypeProp set_timeZone(final String _timeZone) { this._timeZone = _timeZone; diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js index 3318f6f2970..44b292f5938 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/reflection/tg-reflector.js @@ -111,12 +111,12 @@ var _createEntityTypePropPrototype = function () { } /** - * Returns specific time-zone for the property of type date. + * Indicates whether dependent time-zone mode is enforced for the property of date type. * - * IMPORTANT: do not use '_timeZone' field directly! + * IMPORTANT: do not use '_dependentTimeZoneMode' field directly! */ - EntityTypeProp.prototype.isDependentTimeZoneMode = function () { // TODO - return ['startDate', 'lastExecutionDate', 'nextExecutionDate'].includes(this._name); + EntityTypeProp.prototype.isDependentTimeZoneMode = function () { + return typeof this._dependentTimeZoneMode === 'undefined' ? false : this._dependentTimeZoneMode; } /** From 4aa4eda51dbf3604610913b0a6bda7abac39d708 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 01:43:21 +0300 Subject: [PATCH 10/30] #2716 Simplify date widgets generation by removing time-zone. The time-zone are now taken from entity type property on the client. --- .../api/crit/impl/DateCriterionWidget.java | 22 +++++-------------- .../crit/impl/DateSingleCriterionWidget.java | 17 ++++---------- .../web/centre/api/impl/ResultSetBuilder.java | 1 - .../api/helpers/impl/WidgetSelector.java | 4 ---- .../impl/DateTimePickerWidget.java | 15 +++---------- 5 files changed, 13 insertions(+), 46 deletions(-) diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java index 3e0bdb6174c..89c215226e6 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java @@ -1,39 +1,29 @@ package ua.com.fielden.platform.web.centre.api.crit.impl; -import java.util.Map; - import ua.com.fielden.platform.entity.AbstractEntity; import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract; import ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl.DateTimePickerWidget; -/** - * An implementation for date double-editor criterion. - * - * @author TG Team - * - */ +import java.util.Map; + +/// An implementation for date double-editor criterion. +/// public class DateCriterionWidget extends AbstractRangeCriterionWidget { - /** - * Creates an instance of {@link DateCriterionWidget} for specified entity type and property name. - * - * @param criteriaType - * @param propertyName - */ + /// Creates an instance of [DateCriterionWidget] for specified entity type and property name. + /// public DateCriterionWidget(final Class> root, final Class managedType, final String propertyName) { super(root, "centre/criterion/multi/range/tg-date-range-criterion", propertyName, new DateTimePickerWidget( AbstractCriterionWidget.generateTitleDesc(root, managedType, propertyName).getKey(), AbstractCriterionWidget.generateNames(root, managedType, propertyName).getKey(), false, - DefaultValueContract.getTimeZone(managedType, propertyName), DefaultValueContract.getTimePortionToDisplay(managedType, propertyName) ), new DateTimePickerWidget( AbstractCriterionWidget.generateTitleDesc(root, managedType, propertyName).getValue(), AbstractCriterionWidget.generateNames(root, managedType, propertyName).getValue(), true, - DefaultValueContract.getTimeZone(managedType, propertyName), DefaultValueContract.getTimePortionToDisplay(managedType, propertyName) )); } diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java index 7a88e0a974d..92bbe27b6ef 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java @@ -4,27 +4,18 @@ import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract; import ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl.DateTimePickerWidget; -/** - * An implementation for date single-editor criterion. - * - * @author TG Team - * - */ +/// An implementation for date single-editor criterion. +/// public class DateSingleCriterionWidget extends AbstractSingleCriterionWidget { - /** - * Creates an instance of {@link DateSingleCriterionWidget} for specified entity type and property name. - * - * @param criteriaType - * @param propertyName - */ + /// Creates an instance of [DateSingleCriterionWidget] for specified entity type and property name. + /// public DateSingleCriterionWidget(final Class> root, final Class managedType, final String propertyName) { super(root, propertyName, new DateTimePickerWidget( AbstractCriterionWidget.generateSingleTitleDesc(root, managedType, propertyName), AbstractCriterionWidget.generateSingleName(root, managedType, propertyName), false, - DefaultValueContract.getTimeZone(managedType, propertyName), DefaultValueContract.getTimePortionToDisplay(managedType, propertyName) )); } diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java index f46e88c4f37..ac05ec243e1 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java @@ -162,7 +162,6 @@ private Optional createWidget(final String propName) { return of(new CheckboxWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()), propName)); } else if (isDate(propertyType)) { return of(new DateTimePickerWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()), propName, false, - DefaultValueContract.getTimeZone(root, propName), DefaultValueContract.getTimePortionToDisplay(root, propName))); } else if (isCollectional(propertyType)) { return of(new CollectionalRepresentorWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()),propName)); diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java index 8044b2caa04..4ad863ce5d5 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java @@ -6,7 +6,6 @@ import ua.com.fielden.platform.entity.annotation.TimeOnly; import ua.com.fielden.platform.reflection.PropertyTypeDeterminator; import ua.com.fielden.platform.reflection.TitlesDescsGetter; -import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract; import ua.com.fielden.platform.web.view.master.api.helpers.IWidgetSelector; import ua.com.fielden.platform.web.view.master.api.impl.SimpleMasterBuilder; import ua.com.fielden.platform.web.view.master.api.widgets.*; @@ -142,7 +141,6 @@ public IDateTimePickerConfig asDateTimePicker() { TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false, - DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName), null ); return new DateTimePickerConfig<>((DateTimePickerWidget) widget, smBuilder); @@ -159,7 +157,6 @@ public IDatePickerConfig asDatePicker() { TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false, - DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName), DATE_ONLY ); return new DatePickerConfig<>((DateTimePickerWidget) widget, smBuilder); @@ -175,7 +172,6 @@ public ITimePickerConfig asTimePicker() { TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false, - DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName), TIME_ONLY ); return new TimePickerConfig<>((DateTimePickerWidget) widget, smBuilder); diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java index b1a499a707f..20e5b5114ee 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java @@ -13,19 +13,13 @@ */ public class DateTimePickerWidget extends AbstractWidget { private final boolean timePortionToBecomeEndOfDay; - private final String timeZone; private final String datePortion; - /** - * Creates an instance of {@link DateTimePickerWidget} for specified entity type and property name. - * - * @param titleDesc - * @param propertyName - */ - public DateTimePickerWidget(final Pair titleDesc, final String propertyName, final boolean timePortionToBecomeEndOfDay, final String timeZone, final String datePortion) { + /// Creates an instance of [DateTimePickerWidget] for specified entity type and property name. + /// + public DateTimePickerWidget(final Pair titleDesc, final String propertyName, final boolean timePortionToBecomeEndOfDay, final String datePortion) { super("editors/tg-datetime-picker", titleDesc, propertyName); this.timePortionToBecomeEndOfDay = timePortionToBecomeEndOfDay; - this.timeZone = timeZone; this.datePortion = datePortion; } @@ -33,9 +27,6 @@ public DateTimePickerWidget(final Pair titleDesc, final String p protected Map createCustomAttributes() { final Map customAttr = super.createCustomAttributes(); customAttr.put("time-portion-to-become-end-of-day", timePortionToBecomeEndOfDay); - if (timeZone != null) { - customAttr.put("prop", null); // TODO - } if (datePortion != null) { customAttr.put("date-portion", datePortion); } From b6e8c96633f1d9ba38d79bf3d78164e5f55e22b5 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 01:58:06 +0300 Subject: [PATCH 11/30] #2716 Further simplify chart decker / scatter plot by removing unused time-zone. The time-zone is now taken from entity type property on the client. refs 7d0f278ff4fc70f7c5de3200b72ab4c12092db39 --- .../master/chart/decker/api/impl/ChartDeckerMaster.java | 4 +--- .../scatterplot/api/implementation/ScatterPlotMaster.java | 7 +++---- .../chart-decker/tg-chart-decker-template-behavior.js | 2 +- .../master/scatter-plot/tg-scatter-plot-master-behavior.js | 1 - 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java index ca901e70895..e07536df341 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java @@ -25,7 +25,6 @@ import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.join; import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimePortionToDisplay; -import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimeZone; import static ua.com.fielden.platform.web.centre.EntityCentre.IMPORTS; import static ua.com.fielden.platform.web.centre.api.resultset.impl.FunctionalActionKind.PRIMARY_RESULT_SET; import static ua.com.fielden.platform.web.view.master.EntityMaster.ENTITY_TYPE; @@ -171,9 +170,8 @@ private String generateValueAccessor(final Class deckType, final Class pro if (Money.class.isAssignableFrom(propertyType)) { return "this._moneyPropAccessor('" + aggregationProperty + "')"; } else if (EntityUtils.isDate(propertyType)) { - final Optional timeZone = ofNullable(getTimeZone(deckType, aggregationProperty)); final Optional timePortion = ofNullable(getTimePortionToDisplay(deckType, aggregationProperty)); - final String typeSpec = "Date:" + timeZone.orElse(":") + timePortion.orElse(""); + final String typeSpec = "Date:" + timePortion.orElse(""); return "this._datePropAccessor('" + aggregationProperty + "', '" + typeSpec + "')"; } return "'" + aggregationProperty + "'"; diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java index b928407155f..8724abb6bfe 100644 --- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java +++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java @@ -24,9 +24,9 @@ import static java.util.Optional.*; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.join; -import static ua.com.fielden.platform.reflection.TitlesDescsGetter.*; +import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getEntityTitleAndDesc; +import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getTitleAndDesc; import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimePortionToDisplay; -import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimeZone; import static ua.com.fielden.platform.web.centre.EntityCentre.IMPORTS; import static ua.com.fielden.platform.web.centre.api.resultset.impl.FunctionalActionKind.PRIMARY_RESULT_SET; import static ua.com.fielden.platform.web.view.master.EntityMaster.ENTITY_TYPE; @@ -154,9 +154,8 @@ private String generateValueAccessor(Class> entityTy if (Money.class.isAssignableFrom(propertyType)) { return "this._moneyPropAccessor('" + propertyName + "')"; } else if (EntityUtils.isDate(propertyType)) { - final Optional timeZone = ofNullable(getTimeZone(entityType, propertyName)); final Optional timePortion = ofNullable(getTimePortionToDisplay(entityType, propertyName)); - final String typeSpec = "Date:" + timeZone.orElse(":") + timePortion.orElse(""); + final String typeSpec = "Date:" + timePortion.orElse(""); return "this._datePropAccessor('" + propertyName + "', '" + typeSpec + "')"; } return "'" + propertyName + "'"; diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js index 8e805b07e50..6da92c2f248 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/chart-decker/tg-chart-decker-template-behavior.js @@ -79,7 +79,7 @@ const TgChartDeckerTemplateBehaviorImpl = { return function (entity, value) { if (!value) { const splitedType = dateType.split(':'); - return _millisDateRepresentation(entity.get(propertyName), entity.type().prop(propertyName), splitedType[2] || null); + return _millisDateRepresentation(entity.get(propertyName), entity.type().prop(propertyName), splitedType[1] || null); } } }, diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js index a45425b9030..21e4e004a50 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/master/scatter-plot/tg-scatter-plot-master-behavior.js @@ -66,7 +66,6 @@ const TgScatterPlotMasterBehaviorImpl = { _datePropAccessor: function (propertyName, dateType) { return function (entity, value) { if (!value) { - const splitedType = dateType.split(':'); return _momentTz(entity.get(propertyName), entity.type().prop(propertyName)).toDate(); } } From 3dca6ed99931fdb62dfc17eebc938c96d6187bef Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 02:19:40 +0300 Subject: [PATCH 12/30] #2716 Adjust / extend documentation. --- .../com/fielden/platform/web/components/tg-calendar.js | 6 ++++-- .../platform/web/components/tg-month-selector.js | 6 ++++-- .../fielden/platform/web/editors/tg-datetime-picker.js | 10 ++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js index c0a62952650..60c62bddd36 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js @@ -106,8 +106,10 @@ template.setAttribute('strip-whitespace', ''); properties: { /** - * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. - * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. + * Holds an instance of `tg-reflector.EntityTypeProp` corresponding to the property being edited. + * Must be used as the last parameter for `_momentTz(...)` invocations. + * + * It is used for moment conversions for a) fixed time-zone properties b) props with enforced dependent time-zone mode. */ prop: { type: Object diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js index ff60010c8ed..441d79ab6b6 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-month-selector.js @@ -146,8 +146,10 @@ const template = html` properties: { /** - * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. - * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. + * Holds an instance of `tg-reflector.EntityTypeProp` corresponding to the property being edited. + * Must be used as the last parameter for `_momentTz(...)` invocations. + * + * It is used for moment conversions for a) fixed time-zone properties b) props with enforced dependent time-zone mode. */ prop: { type: Object diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js index e63e74148bf..9eb50f0707e 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/editors/tg-datetime-picker.js @@ -85,8 +85,10 @@ export class TgDatetimePicker extends TgEditor { static get properties () { return { /** - * If empty then default timezone should be used for toString and fromString conversions in 'moment()' and 'moment(...)' methods. - * Otherwise -- the specified timezone should be used in 'moment.tz(timeZone)' and 'moment.tz(..., timeZone)' methods. + * Holds an instance of `tg-reflector.EntityTypeProp` corresponding to the property being edited. + * Must be used as the last parameter for `_momentTz(...)` invocations. + * + * It is used for moment conversions for a) fixed time-zone properties b) props with enforced dependent time-zone mode. */ prop: { type: Object, @@ -523,6 +525,10 @@ export class TgDatetimePicker extends TgEditor { } } + /** + * An overridden 'entity' observer to provide 'EntityTypeProp' computed instance once the entity has been provided. + * That instance would be bound uni-directionally to child `tg-calendar` and `tg-month-selector` elements. + */ _entityChanged (newValue, oldValue) { if (this.reflector().isEntity(newValue)) { this.prop = newValue.type().prop(this.propertyName); From 327d38c1328ddabace747f3c8e2fb4bb10542f48 Mon Sep 17 00:00:00 2001 From: jhou-pro Date: Tue, 28 Apr 2026 02:20:29 +0300 Subject: [PATCH 13/30] #2716 Use constants instead of variables. --- .../ua/com/fielden/platform/web/components/tg-calendar.js | 6 +++--- .../fielden/platform/web/components/tg-month-selector.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js index 60c62bddd36..06a74b1317d 100644 --- a/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js +++ b/platform-web-ui/src/main/web/ua/com/fielden/platform/web/components/tg-calendar.js @@ -14,7 +14,7 @@ import {Polymer} from '/resources/polymer/@polymer/polymer/lib/legacy/polymer-fn import {html} from '/resources/polymer/@polymer/polymer/lib/utils/html-tag.js'; import moment from '/resources/polymer/lib/moment-lib.js'; // used for moment.localeData() and moment.monthsShort(). -import {_momentTz} from '/resources/reflection/tg-date-utils.js'; +import { _momentTz } from '/resources/reflection/tg-date-utils.js'; const template = html`