IDEMPIERE-5093 Scheduler cron pattern scheduling is always using serv… (#1270)

* IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone

* minor fix for oracle/202203240830_IDEMPIERE-5093.sql

* IDEMPIERE-5093 cheduler cron pattern scheduling is always using server time zone

- add time zone comment for cronpattern field
- use fix format for elapsed time (day'hour:minutes:seconds.millisecond)
- use time zone formatting at server monitor
- include etc/gmt* timezone id. fix handling of invalid user input
- fix wrong editor (date) use for timestamp with time zone field

* IDEMPIERE-5093 cheduler cron pattern scheduling is always using server time zone

- Fix MSchedule.getNextRunMS call.

* IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone

- add T_Timestamp to Test table and window.
- fix date time editor doesn't capture seconds for timestamp with time
zone.
- date time editor: use tenant time zone (if set), fallback to browser
time zone.
- date time editor: fix processing of timestamp with time zone value.
- time zone editor: drop the confusing etc/gmt* entries and support
entry of GMT(+/-)hh:mm custom zone id.

* IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone

Fix NPE

Co-authored-by: Carlos Ruiz <carg67@gmail.com>
This commit is contained in:
hengsin 2022-04-23 03:28:51 +08:00 committed by GitHub
parent 1928bba1b6
commit 13b37c4acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1528 additions and 64 deletions

View File

@ -0,0 +1,51 @@
SELECT register_migration_script('202203240830_IDEMPIERE-5093.sql') FROM dual
;
SET SQLBLANKLINES ON
SET DEFINE OFF
-- IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone
-- Mar 24, 2022 3:21:34 PM MYT
INSERT INTO AD_Reference (AD_Reference_ID,Name,AD_Reference_UU,IsOrderByValue,AD_Org_ID,Description,ValidationType,Updated,IsActive,CreatedBy,UpdatedBy,AD_Client_ID,Created,EntityType) VALUES (200133,'Timestamp With Time Zone','a4c9a3f1-ecbb-4beb-ac16-d1b66279f698','N',0,'Date and time with time zone','D',TO_DATE('2022-03-24 15:27:01','YYYY-MM-DD HH24:MI:SS'),'Y',100,100,0,TO_DATE('2022-03-24 15:27:01','YYYY-MM-DD HH24:MI:SS'),'D')
;
-- Mar 24, 2022 3:21:34 PM MYT
INSERT INTO AD_Reference (AD_Reference_ID,Name,AD_Reference_UU,IsOrderByValue,AD_Org_ID,Description,ValidationType,Updated,IsActive,CreatedBy,UpdatedBy,AD_Client_ID,Created,EntityType) VALUES (200135,'Time Zone','9420d498-a217-4c35-a638-b6fcf462538e','N',0,'Time Zone','D',TO_DATE('2022-03-24 15:21:27','YYYY-MM-DD HH24:MI:SS'),'Y',100,100,0,TO_DATE('2022-03-24 15:21:27','YYYY-MM-DD HH24:MI:SS'),'D')
;
-- Mar 24, 2022 3:27:34 PM MYT
UPDATE AD_Column SET AD_Reference_ID=200133,Updated=TO_DATE('2022-03-24 15:27:34','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=11264
;
-- Mar 24, 2022 3:27:42 PM MYT
ALTER TABLE AD_Scheduler MODIFY DateLastRun TIMESTAMP WITH TIME ZONE DEFAULT NULL
;
-- Mar 24, 2022 3:27:59 PM MYT
UPDATE AD_Column SET AD_Reference_ID=200133,Updated=TO_DATE('2022-03-24 15:27:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=11257
;
-- Mar 24, 2022 3:28:04 PM MYT
ALTER TABLE AD_Scheduler MODIFY DateNextRun TIMESTAMP WITH TIME ZONE DEFAULT NULL
;
-- Mar 24, 2022 3:39:53 PM MYT
INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203049,'TimeZone',TO_DATE('2022-03-24 15:39:47','YYYY-MM-DD HH24:MI:SS'),'Time Zone','Time zone name','Time Zone','90009634-4025-4850-a73e-0cdb0b06994b','Y',TO_DATE('2022-03-24 15:39:47','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D')
;
-- Mar 24, 2022 3:41:07 PM MYT
INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Table_ID) VALUES (212855,0,'N',1,'N','N','N',0,'N',60,'N','N','N','Y','17152afe-47ff-4610-8765-aec3a5d56b19',TO_DATE('2022-03-24 15:41:02','YYYY-MM-DD HH24:MI:SS'),'Y','TimeZone','Time zone name','Time Zone','Y','Y',100,100,'N','N',0,0,TO_DATE('2022-03-24 15:41:02','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203049,200135,227)
;
-- Mar 24, 2022 3:41:15 PM MYT
ALTER TABLE AD_ClientInfo ADD TimeZone VARCHAR2(60 CHAR) DEFAULT NULL
;
-- Mar 24, 2022 3:42:03 PM MYT
INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,AD_FieldGroup_ID,EntityType,AD_Tab_ID) VALUES (0,204287,'N',0,'N','N',1020,'Y','N',0,TO_DATE('2022-03-24 15:42:03','YYYY-MM-DD HH24:MI:SS'),'Time zone name','Time Zone','16686d03-b85e-4b58-a0a4-21e99fa4303c','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_DATE('2022-03-24 15:42:03','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212855,118,'D',169)
;
-- Mar 24, 2022 4:43:42 PM MYT
UPDATE AD_Field SET SeqNo=45,SeqNoGrid=45,Updated=TO_DATE('2022-03-24 16:43:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204287
;

View File

@ -0,0 +1,36 @@
-- IDEMPIERE-5093 cheduler cron pattern scheduling is always using server time zone
SELECT register_migration_script('202204041140_IDEMPIERE-5093.sql') FROM dual;
SET SQLBLANKLINES ON
SET DEFINE OFF
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Element SET Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.',Updated=TO_TIMESTAMP('2022-04-04 11:40:38','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Element_ID=54124
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Column SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Process_Para SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', AD_Element_ID=54124 WHERE UPPER(ColumnName)='CRONPATTERN' AND IsCentrallyMaintained='Y' AND AD_Element_ID IS NULL
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Process_Para SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124 AND IsCentrallyMaintained='Y'
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_InfoColumn SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124 AND IsCentrallyMaintained='Y'
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Field SET Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Column_ID IN (SELECT AD_Column_ID FROM AD_Column WHERE AD_Element_ID=54124) AND IsCentrallyMaintained='Y'
;

View File

@ -0,0 +1,186 @@
-- IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone
SELECT register_migration_script('202204111508_IDEMPIERE-5093.sql') FROM dual;
SET SQLBLANKLINES ON
SET DEFINE OFF
-- Apr 11, 2022, 3:09:15 PM MYT
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203573,0,0,'Y',TO_TIMESTAMP('2022-04-11 15:09:13','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:09:13','YYYY-MM-DD HH24:MI:SS'),100,'T_Timestamp','Timestamp','Timestamp with time zone','Timestamp','D','0e993a88-2c26-4946-9ca8-2eb4263d4323')
;
-- Apr 11, 2022, 3:16:46 PM MYT
INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214755,0,'Timestamp','Timestamp with time zone',135,'T_Timestamp',10,'N','N','N','N','N',0,'N',200133,0,0,'Y',TO_TIMESTAMP('2022-04-11 15:16:45','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:16:45','YYYY-MM-DD HH24:MI:SS'),100,203573,'Y','N','D','N','N','N','Y','ec1dd52b-d346-4ca3-ac50-6f87008ee99a','Y',0,'N','N','N','N')
;
-- Apr 11, 2022, 3:16:52 PM MYT
ALTER TABLE Test ADD T_Timestamp TIMESTAMP WITH TIME ZONE DEFAULT NULL
;
-- Apr 11, 2022, 3:17:54 PM MYT
INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField,IsQuickForm) VALUES (206945,'Timestamp','Timestamp with time zone',152,214755,'Y',0,270,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2022-04-11 15:17:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:17:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e95039ef-5430-473b-8f92-3caf99d6d914','Y',280,1,2,1,'N','N','N','N')
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=150,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=206945
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=160,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3209
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=170,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3902
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=180,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3210
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=190,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=4251
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=200,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3057
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=210,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3056
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=220,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=205590
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=230,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=8351
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=240,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=8352
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=250,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3060
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=260,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3061
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=270,IsDisplayed='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=206818
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=2024
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=415
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=416
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=10,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=417
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=20,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=418
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=30,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=419
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=40,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=420
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=50,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=421
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=60,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=422
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=70,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=423
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=80,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=424
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=90,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3059
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=100,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3062
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=110,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=425
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=120,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=426
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=130,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=206945
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=140,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3057
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=150,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3056
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=160,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3902
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=170,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=4251
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=180,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3209
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=190,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3210
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=200,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=8351
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=210,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=8352
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=220,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3060
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=230,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=3061
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=240,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=205590
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=250,IsDisplayedGrid='Y', Updated=getDate(), UpdatedBy=100 WHERE AD_Field_ID=206818
;

View File

@ -0,0 +1,48 @@
SELECT register_migration_script('202203240830_IDEMPIERE-5093.sql') FROM dual
;
-- IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone
-- Mar 24, 2022 3:21:34 PM MYT
INSERT INTO AD_Reference (AD_Reference_ID,Name,AD_Reference_UU,IsOrderByValue,AD_Org_ID,Description,ValidationType,Updated,IsActive,CreatedBy,UpdatedBy,AD_Client_ID,Created,EntityType) VALUES (200133,'Timestamp With Time Zone','a4c9a3f1-ecbb-4beb-ac16-d1b66279f698','N',0,'Date and time with time zone','D',TO_TIMESTAMP('2022-03-24 15:27:01','YYYY-MM-DD HH24:MI:SS'),'Y',100,100,0,TO_TIMESTAMP('2022-03-24 15:27:01','YYYY-MM-DD HH24:MI:SS'),'D')
;
-- Mar 24, 2022 3:21:34 PM MYT
INSERT INTO AD_Reference (AD_Reference_ID,Name,AD_Reference_UU,IsOrderByValue,AD_Org_ID,Description,ValidationType,Updated,IsActive,CreatedBy,UpdatedBy,AD_Client_ID,Created,EntityType) VALUES (200135,'Time Zone','9420d498-a217-4c35-a638-b6fcf462538e','N',0,'Time Zone','D',TO_TIMESTAMP('2022-03-24 15:21:27','YYYY-MM-DD HH24:MI:SS'),'Y',100,100,0,TO_TIMESTAMP('2022-03-24 15:21:27','YYYY-MM-DD HH24:MI:SS'),'D')
;
-- Mar 24, 2022 3:27:34 PM MYT
UPDATE AD_Column SET AD_Reference_ID=200133,Updated=TO_TIMESTAMP('2022-03-24 15:27:34','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=11264
;
-- Mar 24, 2022 3:27:42 PM MYT
INSERT INTO t_alter_column values('ad_scheduler','DateLastRun','TIMESTAMP WITH TIME ZONE','NULL',null)
;
-- Mar 24, 2022 3:27:59 PM MYT
UPDATE AD_Column SET AD_Reference_ID=200133,Updated=TO_TIMESTAMP('2022-03-24 15:27:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=11257
;
-- Mar 24, 2022 3:28:04 PM MYT
INSERT INTO t_alter_column values('ad_scheduler','DateNextRun','TIMESTAMP WITH TIME ZONE','NULL',null)
;
-- Mar 24, 2022 3:39:53 PM MYT
INSERT INTO AD_Element (AD_Element_ID,ColumnName,Updated,Name,Description,PrintName,AD_Element_UU,IsActive,Created,AD_Org_ID,CreatedBy,UpdatedBy,AD_Client_ID,EntityType) VALUES (203049,'TimeZone',TO_TIMESTAMP('2022-03-24 15:39:47','YYYY-MM-DD HH24:MI:SS'),'Time Zone','Time zone name','Time Zone','90009634-4025-4850-a73e-0cdb0b06994b','Y',TO_TIMESTAMP('2022-03-24 15:39:47','YYYY-MM-DD HH24:MI:SS'),0,100,100,0,'D')
;
-- Mar 24, 2022 3:41:07 PM MYT
INSERT INTO AD_Column (AD_Column_ID,SeqNoSelection,IsSyncDatabase,Version,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,Updated,IsUpdateable,ColumnName,Description,Name,IsAllowCopy,IsActive,CreatedBy,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,AD_Client_ID,AD_Org_ID,Created,EntityType,IsEncrypted,IsSecure,FKConstraintType,AD_Element_ID,AD_Reference_ID,AD_Table_ID) VALUES (212855,0,'N',1,'N','N','N',0,'N',60,'N','N','N','Y','17152afe-47ff-4610-8765-aec3a5d56b19',TO_TIMESTAMP('2022-03-24 15:41:02','YYYY-MM-DD HH24:MI:SS'),'Y','TimeZone','Time zone name','Time Zone','Y','Y',100,100,'N','N',0,0,TO_TIMESTAMP('2022-03-24 15:41:02','YYYY-MM-DD HH24:MI:SS'),'D','N','N','N',203049,200135,227)
;
-- Mar 24, 2022 3:41:15 PM MYT
ALTER TABLE AD_ClientInfo ADD COLUMN TimeZone VARCHAR(60) DEFAULT NULL
;
-- Mar 24, 2022 3:42:03 PM MYT
INSERT INTO AD_Field (SortNo,AD_Field_ID,IsEncrypted,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,IsReadOnly,AD_Org_ID,Updated,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,CreatedBy,UpdatedBy,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,Created,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,AD_FieldGroup_ID,EntityType,AD_Tab_ID) VALUES (0,204287,'N',0,'N','N',1020,'Y','N',0,TO_TIMESTAMP('2022-03-24 15:42:03','YYYY-MM-DD HH24:MI:SS'),'Time zone name','Time Zone','16686d03-b85e-4b58-a0a4-21e99fa4303c','Y','N',100,100,'Y','Y',1020,1,'N',0,TO_TIMESTAMP('2022-03-24 15:42:03','YYYY-MM-DD HH24:MI:SS'),2,1,'N','N',212855,118,'D',169)
;
-- Mar 24, 2022 4:43:43 PM MYT
UPDATE AD_Field SET SeqNo=45,SeqNoGrid=45,Updated=TO_TIMESTAMP('2022-03-24 16:43:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204287
;

View File

@ -0,0 +1,33 @@
-- IDEMPIERE-5093 cheduler cron pattern scheduling is always using server time zone
SELECT register_migration_script('202204041140_IDEMPIERE-5093.sql') FROM dual;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Element SET Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.',Updated=TO_TIMESTAMP('2022-04-04 11:40:38','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Element_ID=54124
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Column SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Process_Para SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', AD_Element_ID=54124 WHERE UPPER(ColumnName)='CRONPATTERN' AND IsCentrallyMaintained='Y' AND AD_Element_ID IS NULL
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Process_Para SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124 AND IsCentrallyMaintained='Y'
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_InfoColumn SET ColumnName='CronPattern', Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Element_ID=54124 AND IsCentrallyMaintained='Y'
;
-- Apr 4, 2022, 11:40:38 AM MYT
UPDATE AD_Field SET Name='Cron Scheduling Pattern', Description='Cron pattern to define when the process should be invoked.', Help='Cron pattern to define when the process should be invoked. See http://www.sauronsoftware.it/projects/cron4j/api/it/sauronsoftware/cron4j/SchedulingPattern.html
If set, the time zone from tenant info is used. Otherwise, use the default JVM time zone at the server.', Placeholder=NULL WHERE AD_Column_ID IN (SELECT AD_Column_ID FROM AD_Column WHERE AD_Element_ID=54124) AND IsCentrallyMaintained='Y'
;

View File

@ -0,0 +1,183 @@
-- IDEMPIERE-5093 Scheduler cron pattern scheduling is always using server time zone
SELECT register_migration_script('202204111508_IDEMPIERE-5093.sql') FROM dual;
-- Apr 11, 2022, 3:09:15 PM MYT
INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,PrintName,EntityType,AD_Element_UU) VALUES (203573,0,0,'Y',TO_TIMESTAMP('2022-04-11 15:09:13','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:09:13','YYYY-MM-DD HH24:MI:SS'),100,'T_Timestamp','Timestamp','Timestamp with time zone','Timestamp','D','0e993a88-2c26-4946-9ca8-2eb4263d4323')
;
-- Apr 11, 2022, 3:16:46 PM MYT
INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214755,0,'Timestamp','Timestamp with time zone',135,'T_Timestamp',10,'N','N','N','N','N',0,'N',200133,0,0,'Y',TO_TIMESTAMP('2022-04-11 15:16:45','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:16:45','YYYY-MM-DD HH24:MI:SS'),100,203573,'Y','N','D','N','N','N','Y','ec1dd52b-d346-4ca3-ac50-6f87008ee99a','Y',0,'N','N','N','N')
;
-- Apr 11, 2022, 3:16:52 PM MYT
ALTER TABLE Test ADD COLUMN T_Timestamp TIMESTAMP WITH TIME ZONE DEFAULT NULL
;
-- Apr 11, 2022, 3:17:54 PM MYT
INSERT INTO AD_Field (AD_Field_ID,Name,Description,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField,IsQuickForm) VALUES (206945,'Timestamp','Timestamp with time zone',152,214755,'Y',0,270,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2022-04-11 15:17:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2022-04-11 15:17:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','e95039ef-5430-473b-8f92-3caf99d6d914','Y',280,1,2,1,'N','N','N','N')
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=150,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=206945
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=160,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3209
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=170,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3902
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=180,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3210
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=190,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=4251
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=200,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3057
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=210,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3056
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=220,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=205590
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=230,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=8351
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=240,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=8352
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=250,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3060
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=260,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3061
;
-- Apr 11, 2022, 3:18:15 PM MYT
UPDATE AD_Field SET SeqNo=270,IsDisplayed='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=206818
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=2024
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=415
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=0,IsDisplayedGrid='N', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=416
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=10,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=417
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=20,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=418
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=30,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=419
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=40,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=420
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=50,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=421
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=60,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=422
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=70,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=423
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=80,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=424
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=90,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3059
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=100,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3062
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=110,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=425
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=120,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=426
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=130,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=206945
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=140,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3057
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=150,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3056
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=160,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3902
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=170,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=4251
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=180,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3209
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=190,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3210
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=200,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=8351
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=210,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=8352
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=220,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3060
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=230,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=3061
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=240,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=205590
;
-- Apr 11, 2022, 3:18:28 PM MYT
UPDATE AD_Field SET SeqNoGrid=250,IsDisplayedGrid='Y', Updated=statement_timestamp(), UpdatedBy=100 WHERE AD_Field_ID=206818
;

View File

@ -407,6 +407,11 @@ public interface AdempiereDatabase
*/ */
public String getTimestampDataType(); public String getTimestampDataType();
/**
*
* @return timestamp with time zone type name
*/
public String getTimestampWithTimezoneDataType();
/** /**
* Get SQL Create * Get SQL Create
* @param table * @param table

View File

@ -22,7 +22,7 @@ import org.compiere.util.KeyNamePair;
/** Generated Interface for AD_ClientInfo /** Generated Interface for AD_ClientInfo
* @author iDempiere (generated) * @author iDempiere (generated)
* @version Release 9 * @version Release 10
*/ */
public interface I_AD_ClientInfo public interface I_AD_ClientInfo
{ {
@ -44,8 +44,8 @@ public interface I_AD_ClientInfo
/** Column name AD_Client_ID */ /** Column name AD_Client_ID */
public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID"; public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID";
/** Get Client. /** Get Tenant.
* Client/Tenant for this installation. * Tenant for this installation.
*/ */
public int getAD_Client_ID(); public int getAD_Client_ID();
@ -62,12 +62,12 @@ public interface I_AD_ClientInfo
public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID"; public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID";
/** Set Organization. /** Set Organization.
* Organizational entity within client * Organizational entity within tenant
*/ */
public void setAD_Org_ID (int AD_Org_ID); public void setAD_Org_ID (int AD_Org_ID);
/** Get Organization. /** Get Organization.
* Organizational entity within client * Organizational entity within tenant
*/ */
public int getAD_Org_ID(); public int getAD_Org_ID();
@ -464,6 +464,19 @@ public interface I_AD_ClientInfo
public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException; public org.compiere.model.I_AD_StorageProvider getStorageImage() throws RuntimeException;
/** Column name TimeZone */
public static final String COLUMNNAME_TimeZone = "TimeZone";
/** Set Time Zone.
* Time zone name
*/
public void setTimeZone (String TimeZone);
/** Get Time Zone.
* Time zone name
*/
public String getTimeZone();
/** Column name Updated */ /** Column name Updated */
public static final String COLUMNNAME_Updated = "Updated"; public static final String COLUMNNAME_Updated = "Updated";

View File

@ -105,7 +105,8 @@ public class MAcctProcessor extends X_C_AcctProcessor
protected boolean beforeSave(boolean newRecord) protected boolean beforeSave(boolean newRecord)
{ {
if (newRecord || is_ValueChanged("AD_Schedule_ID")) { if (newRecord || is_ValueChanged("AD_Schedule_ID")) {
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern()); long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern(),
MClientInfo.get(getCtx(), getAD_Client_ID()).getTimeZone());
if (nextWork > 0) if (nextWork > 0)
setDateNextRun(new Timestamp(nextWork)); setDateNextRun(new Timestamp(nextWork));
} }

View File

@ -168,7 +168,8 @@ public class MAlertProcessor extends X_AD_AlertProcessor
protected boolean beforeSave(boolean newRecord) protected boolean beforeSave(boolean newRecord)
{ {
if (newRecord || is_ValueChanged("AD_Schedule_ID")) { if (newRecord || is_ValueChanged("AD_Schedule_ID")) {
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern()); long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern(),
MClientInfo.get(getCtx(), getAD_Client_ID()).getTimeZone());
if (nextWork > 0) if (nextWork > 0)
setDateNextRun(new Timestamp(nextWork)); setDateNextRun(new Timestamp(nextWork));
} }

View File

@ -242,7 +242,8 @@ public class MRequestProcessor extends X_R_RequestProcessor
protected boolean beforeSave(boolean newRecord) protected boolean beforeSave(boolean newRecord)
{ {
if (newRecord || is_ValueChanged("AD_Schedule_ID")) { if (newRecord || is_ValueChanged("AD_Schedule_ID")) {
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern()); long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern(),
MClientInfo.get(getCtx(), getAD_Client_ID()).getTimeZone());
if (nextWork > 0) if (nextWork > 0)
setDateNextRun(new Timestamp(nextWork)); setDateNextRun(new Timestamp(nextWork));
} }

View File

@ -26,12 +26,14 @@ import java.sql.ResultSet;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.Util;
import org.idempiere.cache.ImmutableIntPOCache; import org.idempiere.cache.ImmutableIntPOCache;
import org.idempiere.cache.ImmutablePOSupport; import org.idempiere.cache.ImmutablePOSupport;
@ -249,9 +251,29 @@ public class MSchedule extends X_AD_Schedule implements ImmutablePOSupport
/** /**
* Get Next Run * Get Next Run
* @param last in MS * @param last in MS
* @param scheduleType
* @param frequencyType
* @param frequency
* @param cronPattern
* @return next run in MS * @return next run in MS
* @deprecated
*/ */
public static long getNextRunMS (long last, String scheduleType, String frequencyType, int frequency, String cronPattern) public static long getNextRunMS (long last, String scheduleType, String frequencyType, int frequency, String cronPattern)
{
return getNextRunMS(last, scheduleType, frequencyType, frequency, cronPattern, null);
}
/**
* Get Next Run
* @param last in MS
* @param scheduleType
* @param frequencyType
* @param frequency
* @param cronPattern
* @param timeZone
* @return next run in MS
*/
public static long getNextRunMS (long last, String scheduleType, String frequencyType, int frequency, String cronPattern, String timeZone)
{ {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (MSchedule.SCHEDULETYPE_Frequency.equals(scheduleType)) if (MSchedule.SCHEDULETYPE_Frequency.equals(scheduleType))
@ -281,11 +303,22 @@ public class MSchedule extends X_AD_Schedule implements ImmutablePOSupport
{ {
if (cronPattern != null && cronPattern.trim().length() > 0 if (cronPattern != null && cronPattern.trim().length() > 0
&& SchedulingPattern.validate(cronPattern)) { && SchedulingPattern.validate(cronPattern)) {
TimeZone tz = null;
if (!Util.isEmpty(timeZone)) {
tz = TimeZone.getTimeZone(timeZone);
if (tz != null && !tz.getID().equals(timeZone)) {
tz = null;
}
}
Predictor predictor = new Predictor(cronPattern, last); Predictor predictor = new Predictor(cronPattern, last);
if (tz != null)
predictor.setTimeZone(tz);
long next = predictor.nextMatchingTime(); long next = predictor.nextMatchingTime();
while (next < now) while (next < now)
{ {
predictor = new Predictor(cronPattern, next); predictor = new Predictor(cronPattern, next);
if (tz != null)
predictor.setTimeZone(tz);
next = predictor.nextMatchingTime(); next = predictor.nextMatchingTime();
} }
return next; return next;

View File

@ -325,7 +325,9 @@ public class MScheduler extends X_AD_Scheduler
} }
if (newRecord || is_ValueChanged("AD_Schedule_ID")) { if (newRecord || is_ValueChanged("AD_Schedule_ID")) {
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern()); MClientInfo clientInfo = MClientInfo.get(getCtx(), getAD_Client_ID());
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern(),
clientInfo.getTimeZone());
if (nextWork > 0) if (nextWork > 0)
setDateNextRun(new Timestamp(nextWork)); setDateNextRun(new Timestamp(nextWork));
} }

View File

@ -144,6 +144,8 @@ public class SystemIDs
public final static int REFERENCE_DATATYPE_TEXT = 14; public final static int REFERENCE_DATATYPE_TEXT = 14;
public final static int REFERENCE_DATATYPE_TEXTLONG = 36; public final static int REFERENCE_DATATYPE_TEXTLONG = 36;
public final static int REFERENCE_DATATYPE_TIME = 24; public final static int REFERENCE_DATATYPE_TIME = 24;
public final static int REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE = 200133;
public final static int REFERENCE_DATATYPE_TIMEZONE = 200135;
public final static int REFERENCE_DATATYPE_URL = 40; public final static int REFERENCE_DATATYPE_URL = 40;
public final static int REFERENCE_DATATYPE_YES_NO = 20; public final static int REFERENCE_DATATYPE_YES_NO = 20;

View File

@ -23,7 +23,7 @@ import java.util.Properties;
/** Generated Model for AD_ClientInfo /** Generated Model for AD_ClientInfo
* @author iDempiere (generated) * @author iDempiere (generated)
* @version Release 9 - $Id$ */ * @version Release 10 - $Id$ */
@org.adempiere.base.Model(table="AD_ClientInfo") @org.adempiere.base.Model(table="AD_ClientInfo")
public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent
{ {
@ -31,7 +31,7 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent
/** /**
* *
*/ */
private static final long serialVersionUID = 20220116L; private static final long serialVersionUID = 20220325L;
/** Standard Constructor */ /** Standard Constructor */
public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName) public X_AD_ClientInfo (Properties ctx, int AD_ClientInfo_ID, String trxName)
@ -823,4 +823,20 @@ public class X_AD_ClientInfo extends PO implements I_AD_ClientInfo, I_Persistent
return 0; return 0;
return ii.intValue(); return ii.intValue();
} }
/** Set Time Zone.
@param TimeZone Time zone name
*/
public void setTimeZone (String TimeZone)
{
set_Value (COLUMNNAME_TimeZone, TimeZone);
}
/** Get Time Zone.
@return Time zone name
*/
public String getTimeZone()
{
return (String)get_Value(COLUMNNAME_TimeZone);
}
} }

View File

@ -55,6 +55,8 @@ import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TABLEDIR;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TEXT; import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TEXT;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TEXTLONG; import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TEXTLONG;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TIME; import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TIME;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_TIMEZONE;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_URL; import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_URL;
import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_YES_NO; import static org.compiere.model.SystemIDs.REFERENCE_DATATYPE_YES_NO;
@ -66,6 +68,7 @@ import java.util.Currency;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.TimeZone;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.base.IDisplayTypeFactory; import org.adempiere.base.IDisplayTypeFactory;
@ -172,6 +175,10 @@ public final class DisplayType
public static final int ChosenMultipleSelectionSearch = REFERENCE_DATATYPE_CHOSEN_MULTIPLE_SELECTION_SEARCH; public static final int ChosenMultipleSelectionSearch = REFERENCE_DATATYPE_CHOSEN_MULTIPLE_SELECTION_SEARCH;
public static final int TimestampWithTimeZone = REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE;
public static final int TimeZoneId = REFERENCE_DATATYPE_TIMEZONE;
/** /**
* - New Display Type * - New Display Type
INSERT INTO AD_REFERENCE INSERT INTO AD_REFERENCE
@ -317,7 +324,8 @@ public final class DisplayType
|| displayType == RadiogroupList || displayType == RadiogroupList
|| displayType == ChosenMultipleSelectionList || displayType == ChosenMultipleSelectionList
|| displayType == ChosenMultipleSelectionTable || displayType == ChosenMultipleSelectionTable
|| displayType == ChosenMultipleSelectionSearch) || displayType == ChosenMultipleSelectionSearch
|| displayType == TimeZoneId)
return true; return true;
IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType); IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType);
@ -347,6 +355,9 @@ public final class DisplayType
if (displayType == Date || displayType == DateTime || displayType == Time) if (displayType == Date || displayType == DateTime || displayType == Time)
return true; return true;
if (isTimestampWithTimeZone(displayType))
return true;
IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType); IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType);
if (cache != null) { if (cache != null) {
IDisplayTypeFactory service = cache.getService(); IDisplayTypeFactory service = cache.getService();
@ -455,6 +466,19 @@ public final class DisplayType
return false; return false;
} // isLOB } // isLOB
/**
*
* @param displayType
* @return true if displayType == TimestampWithTimeZone
*/
public static boolean isTimestampWithTimeZone(int displayType)
{
if (displayType == TimestampWithTimeZone)
return true;
else
return false;
}
/************************************************************************** /**************************************************************************
* Return Format for numeric DisplayType * Return Format for numeric DisplayType
* @param displayType Display Type (default Number) * @param displayType Display Type (default Number)
@ -611,8 +635,8 @@ public final class DisplayType
{ {
SimpleDateFormat format = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, language.getLocale()); SimpleDateFormat format = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, language.getLocale());
try { try {
format.applyPattern(pattern); format.applyPattern(pattern);
return format; return displayType==TimeZoneId ? setTimeZone(format) : format;
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
s_log.log(Level.WARNING, "Invalid date pattern: " + pattern); s_log.log(Level.WARNING, "Invalid date pattern: " + pattern);
@ -631,7 +655,14 @@ public final class DisplayType
return new SimpleDateFormat(lang.getTimePattern()); return new SimpleDateFormat(lang.getTimePattern());
return myLanguage.getTimeFormat(); return myLanguage.getTimeFormat();
} }
else if ( displayType == TimestampWithTimeZone) {
SimpleDateFormat format = null;
if (!Util.isEmpty(lang.getDatePattern()) && !Util.isEmpty(lang.getTimePattern()))
format = new SimpleDateFormat(lang.getDatePattern() + " " + lang.getTimePattern());
else
format = myLanguage.getDateTimeFormat();
return setTimeZone(format);
}
else { else {
IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType); IServiceReferenceHolder<IDisplayTypeFactory> cache = s_displayTypeFactoryCache.get(displayType);
if (cache != null) { if (cache != null) {
@ -658,6 +689,20 @@ public final class DisplayType
return myLanguage.getDateFormat(); // default return myLanguage.getDateFormat(); // default
} // getDateFormat } // getDateFormat
private static SimpleDateFormat setTimeZone(SimpleDateFormat dateFormat) {
String timezoneId = Env.getContext(Env.getCtx(), Env.CLIENT_INFO_TIME_ZONE);
if (!Util.isEmpty(timezoneId, true))
{
TimeZone tz = TimeZone.getTimeZone(timezoneId);
if (tz != null && timezoneId.equals(tz.getID()))
{
dateFormat = new SimpleDateFormat(dateFormat.toPattern());
dateFormat.setTimeZone(tz);
}
}
return dateFormat;
}
/** /**
* JDBC Date Format YYYY-MM-DD * JDBC Date Format YYYY-MM-DD
* @return date format * @return date format
@ -768,6 +813,8 @@ public final class DisplayType
// //
if (displayType == DisplayType.Integer) if (displayType == DisplayType.Integer)
return getDatabase().getNumericDataType()+"(10)"; return getDatabase().getNumericDataType()+"(10)";
if (DisplayType.isTimestampWithTimeZone(displayType))
return getDatabase().getTimestampWithTimezoneDataType();
if (DisplayType.isDate(displayType)) if (DisplayType.isDate(displayType))
return getDatabase().getTimestampDataType(); return getDatabase().getTimestampDataType();
if (DisplayType.isNumeric(displayType)) if (DisplayType.isNumeric(displayType))

View File

@ -102,6 +102,11 @@ public final class Env
public static final String C_TAXCATEGORY_ID = "#C_TaxCategory_ID"; public static final String C_TAXCATEGORY_ID = "#C_TaxCategory_ID";
public static final String C_TAX_ID = "#C_Tax_ID"; public static final String C_TAX_ID = "#C_Tax_ID";
public static final String C_UOM_ID = "#C_UOM_ID"; public static final String C_UOM_ID = "#C_UOM_ID";
public static final String CLIENT_INFO_DESKTOP_HEIGHT = "#clientInfo_desktopHeight";
public static final String CLIENT_INFO_DESKTOP_WIDTH = "#clientInfo_desktopWidth";
public static final String CLIENT_INFO_MOBILE = "#clientInfo_mobile";
public static final String CLIENT_INFO_ORIENTATION = "#clientInfo_orientation";
public static final String CLIENT_INFO_TIME_ZONE = "#clientInfo_timeZone";
public static final String DATE = "#Date"; public static final String DATE = "#Date";
public static final String DB_TYPE = "#DBType"; public static final String DB_TYPE = "#DBType";
public static final String GL_CATEGORY_ID = "#GL_Category_ID"; public static final String GL_CATEGORY_ID = "#GL_Category_ID";

View File

@ -543,17 +543,16 @@ public class TimeUtil
long hours = elapsedMS%24; long hours = elapsedMS%24;
long days = elapsedMS / 24; long days = elapsedMS / 24;
// //
if (days != 0) sb.append(days).append("'");
sb.append(days).append("'");
// hh // hh
if (hours != 0) if (hours != 0)
sb.append(get2digits(hours)).append(":"); sb.append(get2digits(hours)).append(":");
else if (days != 0) else
sb.append("00:"); sb.append("00:");
// mm // mm
if (minutes != 0) if (minutes != 0)
sb.append(get2digits(minutes)).append(":"); sb.append(get2digits(minutes)).append(":");
else if (hours != 0 || days != 0) else
sb.append("00:"); sb.append("00:");
// ss // ss
sb.append(get2digits(seconds)) sb.append(get2digits(seconds))

View File

@ -24,6 +24,7 @@ import java.util.Properties;
import org.compiere.model.AdempiereProcessor; import org.compiere.model.AdempiereProcessor;
import org.compiere.model.AdempiereProcessor2; import org.compiere.model.AdempiereProcessor2;
import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.AdempiereProcessorLog;
import org.compiere.model.MClientInfo;
import org.compiere.model.MSchedule; import org.compiere.model.MSchedule;
import org.compiere.model.Query; import org.compiere.model.Query;
import org.compiere.model.X_AD_WorkflowProcessor; import org.compiere.model.X_AD_WorkflowProcessor;
@ -142,7 +143,8 @@ public class MWorkflowProcessor extends X_AD_WorkflowProcessor
protected boolean beforeSave(boolean newRecord) protected boolean beforeSave(boolean newRecord)
{ {
if (newRecord || is_ValueChanged("AD_Schedule_ID")) { if (newRecord || is_ValueChanged("AD_Schedule_ID")) {
long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern()); long nextWork = MSchedule.getNextRunMS(System.currentTimeMillis(), getScheduleType(), getFrequencyType(), getFrequency(), getCronPattern(),
MClientInfo.get(getCtx(), getAD_Client_ID()).getTimeZone());
if (nextWork > 0) if (nextWork > 0)
setDateNextRun(new Timestamp(nextWork)); setDateNextRun(new Timestamp(nextWork));
} }

View File

@ -26,6 +26,7 @@ import org.compiere.model.AdempiereProcessor;
import org.compiere.model.AdempiereProcessor2; import org.compiere.model.AdempiereProcessor2;
import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.AdempiereProcessorLog;
import org.compiere.model.MClient; import org.compiere.model.MClient;
import org.compiere.model.MClientInfo;
import org.compiere.model.MOrgInfo; import org.compiere.model.MOrgInfo;
import org.compiere.model.MRole; import org.compiere.model.MRole;
import org.compiere.model.MSchedule; import org.compiere.model.MSchedule;
@ -127,7 +128,7 @@ public abstract class AdempiereServer implements Runnable
{ {
m_nextWork = MSchedule.getNextRunMS(now.getTime(), m_nextWork = MSchedule.getNextRunMS(now.getTime(),
p_model.getScheduleType(), p_model.getFrequencyType(), p_model.getScheduleType(), p_model.getFrequencyType(),
p_model.getFrequency(), p_model.getCronPattern()); p_model.getFrequency(), p_model.getCronPattern(), MClientInfo.get(getCtx(), p_model.getAD_Client_ID()).getTimeZone());
} }
if (m_nextWork > now.getTime()) if (m_nextWork > now.getTime())
@ -294,7 +295,7 @@ public abstract class AdempiereServer implements Runnable
m_nextWork = MSchedule.getNextRunMS(lastRun.getTime(), m_nextWork = MSchedule.getNextRunMS(lastRun.getTime(),
p_model.getScheduleType(), p_model.getFrequencyType(), p_model.getScheduleType(), p_model.getFrequencyType(),
p_model.getFrequency(), p_model.getCronPattern()); p_model.getFrequency(), p_model.getCronPattern(), MClientInfo.get(getCtx(), p_model.getAD_Client_ID()).getTimeZone());
m_sleepMS = m_nextWork - now; m_sleepMS = m_nextWork - now;
if (m_nextWork == 0) { if (m_nextWork == 0) {

View File

@ -24,10 +24,14 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.InetAddress; import java.net.InetAddress;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
@ -65,6 +69,7 @@ import org.apache.ecs.xhtml.tr;
import org.compiere.Adempiere; import org.compiere.Adempiere;
import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.AdempiereProcessorLog;
import org.compiere.model.MClient; import org.compiere.model.MClient;
import org.compiere.model.MClientInfo;
import org.compiere.model.MSession; import org.compiere.model.MSession;
import org.compiere.model.MSysConfig; import org.compiere.model.MSysConfig;
import org.compiere.model.MSystem; import org.compiere.model.MSystem;
@ -687,7 +692,7 @@ public class AdempiereMonitor extends HttpServlet
table.addElement(line); table.addElement(line);
line = new tr(); line = new tr();
line.addElement(new th().addElement("Start - Elapsed")); line.addElement(new th().addElement("Start - Elapsed"));
line.addElement(new td().addElement(WebEnv.getCellContent(getServerManager().getStartTime()) line.addElement(new td().addElement(WebEnv.getCellContent(formatTimestampWithTimeZone(0, getServerManager().getStartTime()))
+ " - " + TimeUtil.formatElapsed(getServerManager().getStartTime()))); + " - " + TimeUtil.formatElapsed(getServerManager().getStartTime())));
table.addElement(line); table.addElement(line);
line = new tr(); line = new tr();
@ -696,7 +701,7 @@ public class AdempiereMonitor extends HttpServlet
table.addElement(line); table.addElement(line);
line = new tr(); line = new tr();
line.addElement(new th().addElement("Last Updated")); line.addElement(new th().addElement("Last Updated"));
line.addElement(new td().addElement(new Timestamp(System.currentTimeMillis()).toString())); line.addElement(new td().addElement(formatTimestampWithTimeZone(0, new Timestamp(System.currentTimeMillis()))));
table.addElement(line); table.addElement(line);
bb.addElement(table); bb.addElement(table);
@ -834,7 +839,7 @@ public class AdempiereMonitor extends HttpServlet
table.addElement(line); table.addElement(line);
line = new tr(); line = new tr();
line.addElement(new th().addElement("Start - Elapsed")); line.addElement(new th().addElement("Start - Elapsed"));
line.addElement(new td().addElement(WebEnv.getCellContent(server.getStartTime()) line.addElement(new td().addElement(WebEnv.getCellContent(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getStartTime()))
+ " - " + TimeUtil.formatElapsed(server.getStartTime()))); + " - " + TimeUtil.formatElapsed(server.getStartTime())));
} }
else else
@ -853,7 +858,7 @@ public class AdempiereMonitor extends HttpServlet
// //
line = new tr(); line = new tr();
line.addElement(new th().addElement("Last Run")); line.addElement(new th().addElement("Last Run"));
line.addElement(new td().addElement(WebEnv.getCellContent(server.getModel().getDateLastRun()))); line.addElement(new td().addElement(WebEnv.getCellContent(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateLastRun()))));
table.addElement(line); table.addElement(line);
line = new tr(); line = new tr();
line.addElement(new th().addElement("Info")); line.addElement(new th().addElement("Info"));
@ -863,7 +868,7 @@ public class AdempiereMonitor extends HttpServlet
line = new tr(); line = new tr();
line.addElement(new th().addElement("Next Run")); line.addElement(new th().addElement("Next Run"));
td td = new td(); td td = new td();
td.addElement(WebEnv.getCellContent(server.getModel().getDateNextRun(false))); td.addElement(WebEnv.getCellContent(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateNextRun(false))));
td.addElement(" - "); td.addElement(" - ");
link = new a ("idempiereMonitor?RunNow=" + server.getServerId(), "(Run Now)"); link = new a ("idempiereMonitor?RunNow=" + server.getServerId(), "(Run Now)");
td.addElement(link); td.addElement(link);
@ -912,6 +917,22 @@ public class AdempiereMonitor extends HttpServlet
WebUtil.createResponse (request, response, this, null, doc, false); WebUtil.createResponse (request, response, this, null, doc, false);
} // createSummaryPage } // createSummaryPage
private String formatTimestampWithTimeZone(int AD_Client_ID, Timestamp ts) {
return formatTimestampWithTimeZone(AD_Client_ID, (Date)ts);
}
private String formatTimestampWithTimeZone(int AD_Client_ID, Date date) {
if (date == null)
return "";
DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
MClientInfo clientInfo = MClientInfo.get(AD_Client_ID);
if (!Util.isEmpty(clientInfo.getTimeZone()))
formatter = formatter.withZone(ZoneId.of(clientInfo.getTimeZone()));
else
formatter = formatter.withZone(ZoneId.systemDefault());
return formatter.format(date.toInstant().truncatedTo(ChronoUnit.SECONDS));
}
private String createServerCountMessage(ServerCount serverCount) { private String createServerCountMessage(ServerCount serverCount) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
@ -971,7 +992,7 @@ public class AdempiereMonitor extends HttpServlet
writer.print(getServerManager().getDescription()); writer.print(getServerManager().getDescription());
writer.println("</description>"); writer.println("</description>");
writer.print("\t\t<start-time>"); writer.print("\t\t<start-time>");
writer.print(getServerManager().getStartTime()); writer.print(formatTimestampWithTimeZone(0, getServerManager().getStartTime()));
writer.println("</start-time>"); writer.println("</start-time>");
writer.print("\t\t<server-count>"); writer.print("\t\t<server-count>");
writer.print(getServerManager().getServerCount()); writer.print(getServerManager().getServerCount());
@ -1008,13 +1029,13 @@ public class AdempiereMonitor extends HttpServlet
writer.print("Stopped"); writer.print("Stopped");
writer.println("</status>"); writer.println("</status>");
writer.print("\t\t\t<start-time>"); writer.print("\t\t\t<start-time>");
writer.print(server.getStartTime()); writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getStartTime()));
writer.println("</start-time>"); writer.println("</start-time>");
writer.print("\t\t\t<last-run>"); writer.print("\t\t\t<last-run>");
writer.print(server.getModel().getDateLastRun()); writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateLastRun()));
writer.println("</last-run>"); writer.println("</last-run>");
writer.print("\t\t\t<next-run>"); writer.print("\t\t\t<next-run>");
writer.print(server.getModel().getDateNextRun(false)); writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateNextRun(false)));
writer.println("</next-run>"); writer.println("</next-run>");
writer.print("\t\t\t<statistics>"); writer.print("\t\t\t<statistics>");
writer.print(server.getStatistics()); writer.print(server.getStatistics());
@ -1115,7 +1136,7 @@ public class AdempiereMonitor extends HttpServlet
td td = new td(); td td = new td();
td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('<title>" + escapeEcmaScript(trx.getDisplayName()) +"</title>" td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('<title>" + escapeEcmaScript(trx.getDisplayName()) +"</title>"
+ "<pre>" + escapeEcmaScript(trx.getStackTrace()) + "</pre>')"); + "<pre>" + escapeEcmaScript(trx.getStackTrace()) + "</pre>')");
td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + trx.getStartTime()); td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + formatTimestampWithTimeZone(0,trx.getStartTime()));
td.setTitle("Click to see stack trace"); td.setTitle("Click to see stack trace");
td.setStyle("text-decoration: underline; color: blue"); td.setStyle("text-decoration: underline; color: blue");
line.addElement(td); line.addElement(td);
@ -1692,7 +1713,7 @@ public class AdempiereMonitor extends HttpServlet
td td = new td(); td td = new td();
td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('<title>" + escapeEcmaScript(trx.getDisplayName()) +"</title>" td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('<title>" + escapeEcmaScript(trx.getDisplayName()) +"</title>"
+ "<pre>" + escapeEcmaScript(trx.getStackTrace()) + "</pre>')"); + "<pre>" + escapeEcmaScript(trx.getStackTrace()) + "</pre>')");
td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + trx.getStartTime()); td.addElement("Name="+trx.getDisplayName() + ", StartTime=" + formatTimestampWithTimeZone(0, trx.getStartTime()));
td.setTitle("Click to see stack trace"); td.setTitle("Click to see stack trace");
td.setStyle("text-decoration: underline; color: blue"); td.setStyle("text-decoration: underline; color: blue");
line.addElement(td); line.addElement(td);

View File

@ -535,15 +535,16 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
Executions.getCurrent().getSession().setAttribute(CLIENT_INFO, clientInfo); Executions.getCurrent().getSession().setAttribute(CLIENT_INFO, clientInfo);
} }
Env.setContext(Env.getCtx(), "#clientInfo_desktopWidth", clientInfo.desktopWidth); Env.setContext(Env.getCtx(), Env.CLIENT_INFO_DESKTOP_WIDTH, clientInfo.desktopWidth);
Env.setContext(Env.getCtx(), "#clientInfo_desktopHeight", clientInfo.desktopHeight); Env.setContext(Env.getCtx(), Env.CLIENT_INFO_DESKTOP_HEIGHT, clientInfo.desktopHeight);
Env.setContext(Env.getCtx(), "#clientInfo_orientation", clientInfo.orientation); Env.setContext(Env.getCtx(), Env.CLIENT_INFO_ORIENTATION, clientInfo.orientation);
Env.setContext(Env.getCtx(), "#clientInfo_mobile", clientInfo.tablet); Env.setContext(Env.getCtx(), Env.CLIENT_INFO_MOBILE, clientInfo.tablet);
if (clientInfo.timeZone != null)
Env.setContext(Env.getCtx(), Env.CLIENT_INFO_TIME_ZONE, clientInfo.timeZone.getID());
IDesktop appDesktop = getAppDeskop(); IDesktop appDesktop = getAppDeskop();
if (appDesktop != null) if (appDesktop != null)
appDesktop.setClientInfo(clientInfo); appDesktop.setClientInfo(clientInfo);
} else if (event.getName().equals(ON_LOGIN_COMPLETED)) { } else if (event.getName().equals(ON_LOGIN_COMPLETED)) {
loginCompleted(); loginCompleted();
} }
@ -601,10 +602,11 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
Env.setContext(properties, ITheme.ZK_TOOLBAR_BUTTON_SIZE, Env.getContext(Env.getCtx(), ITheme.ZK_TOOLBAR_BUTTON_SIZE)); Env.setContext(properties, ITheme.ZK_TOOLBAR_BUTTON_SIZE, Env.getContext(Env.getCtx(), ITheme.ZK_TOOLBAR_BUTTON_SIZE));
Env.setContext(properties, ITheme.USE_CSS_FOR_WINDOW_SIZE, Env.getContext(Env.getCtx(), ITheme.USE_CSS_FOR_WINDOW_SIZE)); Env.setContext(properties, ITheme.USE_CSS_FOR_WINDOW_SIZE, Env.getContext(Env.getCtx(), ITheme.USE_CSS_FOR_WINDOW_SIZE));
Env.setContext(properties, ITheme.USE_FONT_ICON_FOR_IMAGE, Env.getContext(Env.getCtx(), ITheme.USE_FONT_ICON_FOR_IMAGE)); Env.setContext(properties, ITheme.USE_FONT_ICON_FOR_IMAGE, Env.getContext(Env.getCtx(), ITheme.USE_FONT_ICON_FOR_IMAGE));
Env.setContext(properties, "#clientInfo_desktopWidth", clientInfo.desktopWidth); Env.setContext(properties, Env.CLIENT_INFO_DESKTOP_WIDTH, clientInfo.desktopWidth);
Env.setContext(properties, "#clientInfo_desktopHeight", clientInfo.desktopHeight); Env.setContext(properties, Env.CLIENT_INFO_DESKTOP_HEIGHT, clientInfo.desktopHeight);
Env.setContext(properties, "#clientInfo_orientation", clientInfo.orientation); Env.setContext(properties, Env.CLIENT_INFO_ORIENTATION, clientInfo.orientation);
Env.setContext(properties, "#clientInfo_mobile", clientInfo.tablet); Env.setContext(properties, Env.CLIENT_INFO_MOBILE, clientInfo.tablet);
Env.setContext(properties, Env.CLIENT_INFO_TIME_ZONE, clientInfo.timeZone.getID());
Desktop desktop = Executions.getCurrent().getDesktop(); Desktop desktop = Executions.getCurrent().getDesktop();
Locale locale = (Locale) desktop.getSession().getAttribute(Attributes.PREFERRED_LOCALE); Locale locale = (Locale) desktop.getSession().getAttribute(Attributes.PREFERRED_LOCALE);

View File

@ -96,7 +96,7 @@ public class ClientInfo implements Serializable {
* @return true if mobile browser * @return true if mobile browser
*/ */
public static boolean isMobile() { public static boolean isMobile() {
return "Y".equals(Env.getContext(Env.getCtx(), "#clientInfo_mobile")); return "Y".equals(Env.getContext(Env.getCtx(), Env.CLIENT_INFO_MOBILE));
} }
/** /**

View File

@ -13,8 +13,11 @@
package org.adempiere.webui.component; package org.adempiere.webui.component;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
@ -31,13 +34,13 @@ public class DatetimeBox extends Panel {
private static final long serialVersionUID = -1075410511739601354L; private static final long serialVersionUID = -1075410511739601354L;
private Datebox dateBox; private Datebox dateBox;
private Timebox timeBox; private Timebox timeBox;
private TimeZone timeZone;
public DatetimeBox() { public DatetimeBox() {
dateBox = new Datebox(); dateBox = new Datebox();
dateBox.setCols(10); dateBox.setCols(10);
timeBox = new Timebox(); timeBox = new Timebox();
timeBox.setCols(10); timeBox.setCols(10);
//timeBox.setButtonVisible(false);
appendChild(dateBox); appendChild(dateBox);
appendChild(timeBox); appendChild(timeBox);
@ -107,17 +110,40 @@ public class DatetimeBox extends Panel {
* @return date * @return date
*/ */
public Date getValue() { public Date getValue() {
Date d = dateBox.getValue(); Date d = null;
Date t = timeBox.getValue(); if (timeZone != null) {
ZonedDateTime zonedDate = dateBox.getValueInZonedDateTime();
if (zonedDate != null)
d = Date.from(zonedDate.toInstant().atZone(timeZone.toZoneId()).toInstant());
Date t = null;
ZonedDateTime zonedTime = timeBox.getValueInZonedDateTime();
if (zonedTime != null)
t = Date.from(zonedTime.toInstant().atZone(timeZone.toZoneId()).toInstant());
if (d != null && t != null) { if (d != null && t != null) {
Calendar cd = Calendar.getInstance(); Calendar cd = Calendar.getInstance();
cd.setTime(d); cd.setTimeZone(dateBox.getTimeZone());
Calendar ct = Calendar.getInstance(); cd.setTime(d);
ct.setTime(t); Calendar ct = Calendar.getInstance();
cd.set(cd.get(Calendar.YEAR), cd.get(Calendar.MONTH), cd.get(Calendar.DAY_OF_MONTH), ct.setTimeZone(timeBox.getTimeZone());
ct.get(Calendar.HOUR_OF_DAY), ct.get(Calendar.MINUTE), ct.get(Calendar.SECOND)); ct.setTime(t);
d = cd.getTime(); cd.set(cd.get(Calendar.YEAR), cd.get(Calendar.MONTH), cd.get(Calendar.DAY_OF_MONTH),
ct.get(Calendar.HOUR_OF_DAY), ct.get(Calendar.MINUTE), ct.get(Calendar.SECOND));
d = cd.getTime();
}
} else {
d = dateBox.getValue();
Date t = timeBox.getValue();
if (d != null && t != null) {
Calendar cd = Calendar.getInstance();
cd.setTime(d);
Calendar ct = Calendar.getInstance();
ct.setTime(t);
cd.set(cd.get(Calendar.YEAR), cd.get(Calendar.MONTH), cd.get(Calendar.DAY_OF_MONTH),
ct.get(Calendar.HOUR_OF_DAY), ct.get(Calendar.MINUTE), ct.get(Calendar.SECOND));
d = cd.getTime();
}
} }
return d; return d;
@ -146,4 +172,36 @@ public class DatetimeBox extends Panel {
{ {
return timeBox; return timeBox;
} }
/**
*
* @param tz
*/
public void setTimeZone(TimeZone tz) {
this.timeZone = tz;
dateBox.setTimeZone(tz);
timeBox.setTimeZone(tz);
if (tz == null)
timeBox.setCols(10);
else
timeBox.setCols(14);
}
/**
*
* @param localTime
*/
public void setValueInLocalDateTime(LocalDateTime localTime) {
dateBox.setValueInLocalDateTime(localTime);
timeBox.setValueInLocalDateTime(localTime);
}
/**
*
* @param zdt
*/
public void setValueInZonedDateTime(ZonedDateTime zdt) {
dateBox.setValueInZonedDateTime(zdt);
timeBox.setValueInZonedDateTime(zdt);
}
} }

View File

@ -15,7 +15,9 @@ package org.adempiere.webui.editor;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.adempiere.webui.ValuePreference; import org.adempiere.webui.ValuePreference;
import org.adempiere.webui.component.DatetimeBox; import org.adempiere.webui.component.DatetimeBox;
@ -24,9 +26,12 @@ import org.adempiere.webui.event.ContextMenuListener;
import org.adempiere.webui.event.ValueChangeEvent; import org.adempiere.webui.event.ValueChangeEvent;
import org.adempiere.webui.window.WFieldRecordInfo; import org.adempiere.webui.window.WFieldRecordInfo;
import org.compiere.model.GridField; import org.compiere.model.GridField;
import org.compiere.model.MClientInfo;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DisplayType;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.Msg; import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
@ -115,8 +120,37 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener
addChangeLogMenu(popupMenu); addChangeLogMenu(popupMenu);
if (gridField != null) if (gridField != null)
getComponent().getDatebox().setPlaceholder(gridField.getPlaceholder()); getComponent().getDatebox().setPlaceholder(gridField.getPlaceholder());
if (isTimestampWithTimeZone())
{
MClientInfo clientInfo = MClientInfo.get();
String timezoneId = clientInfo.getTimeZone();
if (Util.isEmpty(timezoneId, true))
timezoneId = Env.getContext(Env.getCtx(), Env.CLIENT_INFO_TIME_ZONE);
if (!Util.isEmpty(timezoneId, true))
{
TimeZone tz = TimeZone.getTimeZone(timezoneId);
if (tz != null && timezoneId.equals(tz.getID()))
{
getComponent().setTimeZone(tz);
getComponent().getTimebox().setFormat("hh:mm:ss a z");
}
}
}
} }
private boolean isTimestampWithTimeZone()
{
if (gridField != null)
{
int displayType = gridField.getDisplayType();
return DisplayType.isTimestampWithTimeZone(displayType);
}
return false;
}
@Override
public void onEvent(Event event) public void onEvent(Event event)
{ {
if (Events.ON_CHANGE.equalsIgnoreCase(event.getName()) || Events.ON_OK.equalsIgnoreCase(event.getName())) if (Events.ON_CHANGE.equalsIgnoreCase(event.getName()) || Events.ON_OK.equalsIgnoreCase(event.getName()))
@ -126,7 +160,14 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener
if (date != null) if (date != null)
{ {
newValue = Timestamp.valueOf(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); if (isTimestampWithTimeZone())
{
newValue = Timestamp.from(date.toInstant());
}
else
{
newValue = Timestamp.valueOf(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
}
} }
if (oldValue != null && newValue != null && oldValue.equals(newValue)) { if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
return; return;
@ -167,10 +208,18 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener
} }
else if (value instanceof Timestamp) else if (value instanceof Timestamp)
{ {
LocalDateTime localTime =((Timestamp)value).toLocalDateTime(); Timestamp ts = (Timestamp) value;
getComponent().getDatebox().setValueInLocalDateTime(localTime); if (isTimestampWithTimeZone())
getComponent().getTimebox().setValueInLocalDateTime(localTime); {
oldValue = (Timestamp)value; ZonedDateTime zdt = ts.toInstant().atZone(getComponent().getDatebox().getTimeZone().toZoneId());
getComponent().setValueInZonedDateTime(zdt);
}
else
{
LocalDateTime localTime = ts.toLocalDateTime();
getComponent().setValueInLocalDateTime(localTime);
}
oldValue = ts;
} }
else else
{ {
@ -179,9 +228,16 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener
getComponent().setText(value.toString()); getComponent().setText(value.toString());
} catch (Exception e) {} } catch (Exception e) {}
if (getComponent().getValue() != null) if (getComponent().getValue() != null)
oldValue = Timestamp.valueOf(getComponent().getDatebox().getValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()); {
if (isTimestampWithTimeZone())
oldValue = Timestamp.from(getComponent().getDatebox().getValue().toInstant());
else
oldValue = Timestamp.valueOf(getComponent().getDatebox().getValue().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
}
else else
{
oldValue = null; oldValue = null;
}
} }
} }

View File

@ -288,7 +288,7 @@ public class WEditorPopupMenu extends Menupopup implements EventListener<Event>
if (evt != null) if (evt != null)
{ {
ContextMenuEvent menuEvent = new ContextMenuEvent(evt); ContextMenuEvent menuEvent = new ContextMenuEvent(evt);
menuEvent.setTarget(event.getTarget());
ContextMenuListener[] listeners = new ContextMenuListener[0]; ContextMenuListener[] listeners = new ContextMenuListener[0];
listeners = menuListeners.toArray(listeners); listeners = menuListeners.toArray(listeners);
for (ContextMenuListener listener : listeners) for (ContextMenuListener listener : listeners)

View File

@ -0,0 +1,467 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.editor;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.ValuePreference;
import org.adempiere.webui.component.Combobox;
import org.adempiere.webui.component.Menupopup;
import org.adempiere.webui.event.ContextMenuEvent;
import org.adempiere.webui.event.ContextMenuListener;
import org.adempiere.webui.event.ValueChangeEvent;
import org.adempiere.webui.session.SessionManager;
import org.compiere.model.GridField;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.NamePair;
import org.compiere.util.Util;
import org.compiere.util.ValueNamePair;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Menu;
import org.zkoss.zul.Menuitem;
/**
* @author hengsin
*
*/
public class WTimeZoneEditor extends WEditor implements ContextMenuListener {
private static final String[] LISTENER_EVENTS = {Events.ON_CHANGE};
private static final String TIME_ZONE = "TIME_ZONE";
private static final String TIME_ZONE_ADDED = "TIME_ZONE_ADDED";
private static final String TIME_ZONE_ITEM_ATTR = "TIME_ZONE_ITEM";
private static final String TIME_ZONE_ID_ATTR = "TIME_ZONE_ID";
private String oldValue;
/**
* @param gridField
*/
public WTimeZoneEditor(GridField gridField) {
this(gridField, false);
}
/**
* @param gridField
* @param rowIndex
*/
public WTimeZoneEditor(GridField gridField, boolean tableEditor) {
super(new Combobox(), gridField);
this.tableEditor = tableEditor;
init();
}
/**
* @param label
* @param description
* @param mandatory
* @param readonly
* @param updateable
*/
public WTimeZoneEditor(String label, String description,
boolean mandatory, boolean readonly, boolean updateable) {
super(new Combobox(), label, description, mandatory, readonly, updateable);
init();
}
/**
* @param columnName
* @param label
* @param description
* @param mandatory
* @param readonly
* @param updateable
*/
public WTimeZoneEditor(String columnName, String label,
String description, boolean mandatory, boolean readonly,
boolean updateable) {
super(new Combobox(), columnName, label, description, mandatory, readonly,
updateable);
init();
}
private void init() {
popupMenu = new WEditorPopupMenu(false, false, isShowPreference());
popupMenu.addMenuListener(this);
popupMenu.addEventListener(Events.ON_OPEN, this);
Set<String> ids = ZoneId.getAvailableZoneIds();
List<Integer> rawOffsets = new ArrayList<Integer>();
Map<Integer, List<NamePair>> map = new HashMap<Integer, List<NamePair>>();
for(String id : ids) {
if (id.startsWith("Etc/") || id.startsWith("SystemV/") || id.indexOf("/") < 0)
continue;
TimeZone tz = TimeZone.getTimeZone(ZoneId.of(id));
String label = getLabel(tz);
int rawOffset = tz.getRawOffset();
if (!rawOffsets.contains(rawOffset))
rawOffsets.add(rawOffset);
List<NamePair> timezones = map.get(rawOffset);
if (timezones == null) {
timezones = new ArrayList<NamePair>();
map.put(rawOffset, timezones);
}
ValueNamePair valueNamePair = new ValueNamePair(id, label);
timezones.add(valueNamePair);
}
Collections.sort(rawOffsets);
for(int rawOffset : rawOffsets) {
List<NamePair> namePairs = map.get(rawOffset);
Collections.sort(namePairs);
for(NamePair namePair : namePairs) {
getComponent().appendItem(namePair.getName(), namePair.getID());
}
}
getComponent().addEventListener(Events.ON_BLUR, e -> onBlur());
}
private void onBlur() {
Comboitem item = getComponent().getSelectedItem();
if (item == null)
{
setValue(oldValue);
}
else
{
//on select not fire for empty label item
if (Util.isEmpty(item.getLabel(),true))
{
String newValue = getValue();
if (isValueChange(newValue)) {
try {
if (gridField != null)
gridField.setLookupEditorSettingValue(true);
ValueChangeEvent changeEvent = new ValueChangeEvent(this, this.getColumnName(), oldValue, newValue);
super.fireValueChange(changeEvent);
oldValue = newValue;
} finally {
if (gridField != null)
gridField.setLookupEditorSettingValue(false);
}
}
}
}
}
private boolean isValueChange(String newValue) {
return (oldValue == null && newValue != null) || (oldValue != null && newValue == null)
|| ((oldValue != null && newValue != null) && !oldValue.equals(newValue));
}
/* (non-Javadoc)
* @see org.zkoss.zk.ui.event.EventListener#onEvent(org.zkoss.zk.ui.event.Event)
*/
@Override
public void onEvent(Event event) throws Exception {
if (Events.ON_CHANGE.equalsIgnoreCase(event.getName())) {
String id = getComponent().getSelectedItem() != null ? (String)getComponent().getSelectedItem().getValue() : (String)null;
String newValue = null;
if (id != null) {
newValue = id;
} else if (!Util.isEmpty(getComponent().getText(), true)) {
String customId = getComponent().getText();
if (processCustomZoneId(customId))
newValue = customId;
}
if (oldValue != null && newValue != null && oldValue.equals(newValue)) {
return;
}
if (oldValue == null && newValue == null) {
return;
}
ValueChangeEvent changeEvent = new ValueChangeEvent(this, this.getColumnName(), oldValue, newValue);
super.fireValueChange(changeEvent);
oldValue = newValue;
} else if (Events.ON_OPEN.equals(event.getName())) {
addTimeZoneMenu(popupMenu);
}
}
private boolean processCustomZoneId(String customId) {
TimeZone timeZone = TimeZone.getTimeZone(customId);
if (timeZone != null && timeZone.getID().equals(customId)) {
getComponent().appendItem(customId, customId);
getComponent().setSelectedIndex(getComponent().getItemCount()-1);
return true;
}
return false;
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#setReadWrite(boolean)
*/
@Override
public void setReadWrite(boolean readWrite) {
getComponent().setReadonly(!readWrite);
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#isReadWrite()
*/
@Override
public boolean isReadWrite() {
return !getComponent().isReadonly();
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#setValue(java.lang.Object)
*/
@Override
public void setValue(Object value) {
if (value == null || value.toString().trim().length() == 0) {
oldValue = null;
getComponent().setValue((Object)null);
getComponent().setSelectedItem(null);
getComponent().setText("");
} else if (value instanceof TimeZone) {
TimeZone tz = (TimeZone)value;
getComponent().setValue((Object)tz.getID());
oldValue = tz.getID();
} else {
getComponent().setValue(value);
if (getComponent().getSelectedItem() != null) {
oldValue = getComponent().getSelectedItem().getValue();
} else {
if (processCustomZoneId(value.toString()))
oldValue = getComponent().getSelectedItem().getValue();
else
oldValue = null;
}
}
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#getValue()
*/
@Override
public String getValue() {
return getComponent().getValue();
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#getDisplay()
*/
@Override
public String getDisplay() {
return getComponent().getName();
}
@Override
public String getDisplayTextForGridView(Object value) {
if (value == null)
return "";
if (value instanceof TimeZone) {
return getLabel((TimeZone) value);
} else if (value instanceof String) {
String id = (String) value;
if (Util.isEmpty(id, true))
return (String) value;
TimeZone tz = TimeZone.getTimeZone((String) value);
if (tz != null && tz.getID().equals((String)value))
return getLabel(tz);
else
return (String) value;
}
return value.toString();
}
/* (non-Javadoc)
* @see org.adempiere.webui.editor.WEditor#getComponent()
*/
@Override
public Combobox getComponent() {
return (Combobox) super.getComponent();
}
@Override
public void onMenu(ContextMenuEvent evt) {
if (TIME_ZONE.equals(evt.getContextEvent())) {
if (isReadWrite()) {
Component target = evt.getTarget();
if (target != null && target.getAttribute(TIME_ZONE_ID_ATTR) != null) {
String id = (String) target.getAttribute(TIME_ZONE_ID_ATTR);
setTimeZoneFromContextMenu(id);
}
}
} else if (WEditorPopupMenu.PREFERENCE_EVENT.equals(evt.getContextEvent()) && gridField != null) {
if (isShowPreference())
ValuePreference.start (getComponent(), this.getGridField(), getValue());
}
}
private void setTimeZoneFromContextMenu(String id) {
String newValue = id;
String currentValue = oldValue;
setValue(id);
if (getComponent().getSelectedItem() == null) {
newValue = null;
}
if (newValue != null) {
ValueChangeEvent changeEvent = new ValueChangeEvent(this, this.getColumnName(), currentValue, newValue);
super.fireValueChange(changeEvent);
oldValue = newValue;
} else if (currentValue != null) {
setValue(currentValue);
}
}
@Override
public String[] getEvents() {
return LISTENER_EVENTS;
}
/**
* @param popupMenu
*/
private void addTimeZoneMenu(WEditorPopupMenu popupMenu) {
if (popupMenu != null && isReadWrite() && popupMenu.getAttribute(TIME_ZONE_ADDED) == null) {
ClientInfo clientInfo = SessionManager.getAppDesktop().getClientInfo();
if (clientInfo != null && clientInfo.timeZone != null) {
TimeZone firstSameRule = null;
TimeZone found = null;
for(int i = 0; i < getComponent().getItemCount(); i++) {
String id = getComponent().getItemAtIndex(i).getValue();
TimeZone tz = TimeZone.getTimeZone(id);
if (tz.getID().equals(clientInfo.timeZone.getID())) {
found = tz;
break;
} else if (tz.getRawOffset() == clientInfo.timeZone.getRawOffset() &&
tz.getDSTSavings() == clientInfo.timeZone.getDSTSavings() &&
tz.useDaylightTime() == clientInfo.timeZone.useDaylightTime()) {
if (firstSameRule == null)
firstSameRule = tz;
}
}
TimeZone tz = found != null ? found : firstSameRule;
if (tz != null) {
Menuitem item = new Menuitem();
item.setLabel(getLabel(clientInfo.timeZone));
item.addEventListener(Events.ON_CLICK, popupMenu);
item.setAttribute(WEditorPopupMenu.EVENT_ATTRIBUTE, TIME_ZONE);
item.setAttribute(TIME_ZONE_ID_ATTR, tz.getID());
item.setAttribute(TIME_ZONE_ITEM_ATTR, Boolean.TRUE);
popupMenu.appendChild(item);
popupMenu.setAttribute(TIME_ZONE_ADDED, Boolean.TRUE);
}
if (popupMenu.getAttribute(TIME_ZONE_ADDED) == null) {
List<String> labels = new ArrayList<String>();
List<String> ids = new ArrayList<String>();
for(int i = 0; i < getComponent().getItemCount(); i++) {
String id = getComponent().getItemAtIndex(i).getValue();
tz = TimeZone.getTimeZone(id);
if (tz.getRawOffset() == clientInfo.timeZone.getRawOffset()) {
labels.add(getLabel(tz));
ids.add(id);
}
}
if (labels.size() == 1) {
Menuitem item = new Menuitem();
item.setLabel(labels.get(0));
item.addEventListener(Events.ON_CLICK, popupMenu);
item.setAttribute(WEditorPopupMenu.EVENT_ATTRIBUTE, TIME_ZONE);
item.setAttribute(TIME_ZONE_ID_ATTR, ids.get(0));
item.setAttribute(TIME_ZONE_ITEM_ATTR, Boolean.TRUE);
popupMenu.appendChild(item);
} else {
Menu menu = new Menu();
menu.setLabel(Msg.getElement(Env.getCtx(), "TimeZone"));
menu.setAttribute(TIME_ZONE_ITEM_ATTR, Boolean.TRUE);
popupMenu.appendChild(menu);
Menupopup subPopup = new Menupopup();
menu.appendChild(subPopup);
for(int i = 0; i < labels.size(); i++) {
String label = labels.get(i);
String id = ids.get(i);
Menuitem item = new Menuitem();
item.setLabel(label);
item.addEventListener(Events.ON_CLICK, popupMenu);
item.setAttribute(WEditorPopupMenu.EVENT_ATTRIBUTE, TIME_ZONE);
item.setAttribute(TIME_ZONE_ID_ATTR, id);
subPopup.appendChild(item);
}
}
popupMenu.setAttribute(TIME_ZONE_ADDED, Boolean.TRUE);
}
}
} else if (popupMenu != null) {
List<Component> childs = popupMenu.getChildren();
for(Component c : childs) {
if (c.getAttribute(TIME_ZONE_ITEM_ATTR) != null) {
if (isReadWrite()) {
if (!c.isVisible())
c.setVisible(true);
} else {
if (c.isVisible())
c.setVisible(false);
}
}
}
}
}
/**
*
* @param tz
* @return UTC offset + time zone id
*/
private String getLabel(TimeZone tz) {
String name = tz.getID();
int rawOffset = tz.getRawOffset();
int offsetMinutes = rawOffset / (60 * 1000);
boolean negative = false;
if (offsetMinutes < 0) {
negative = true;
offsetMinutes = -offsetMinutes;
}
final int h = offsetMinutes / 60;
final int m = offsetMinutes - h * 60;
StringBuilder fullName = new StringBuilder("UTC")
.append(((negative) ? "-" : "+"))
.append(((h < 10) ? "0" : "")).append(h).append(":")
.append(((m < 10) ? "0" : "")).append(m).append(" ")
.append(name);
return fullName.toString();
}
}

View File

@ -17,6 +17,8 @@
package org.adempiere.webui.event; package org.adempiere.webui.event;
import org.zkoss.zk.ui.Component;
/** /**
* *
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
@ -26,14 +28,39 @@ package org.adempiere.webui.event;
public class ContextMenuEvent public class ContextMenuEvent
{ {
private String contextEvent; private String contextEvent;
private Component target;
/**
*
* @param event
*/
public ContextMenuEvent(String event) public ContextMenuEvent(String event)
{ {
this.contextEvent = event; this.contextEvent = event;
} }
/**
*
* @return event name
*/
public String getContextEvent() public String getContextEvent()
{ {
return contextEvent; return contextEvent;
} }
/**
*
* @param target
*/
public void setTarget(Component target) {
this.target = target;
}
/**
*
* @return target component
*/
public Component getTarget() {
return this.target;
}
} }

View File

@ -41,6 +41,7 @@ import org.adempiere.webui.editor.WSearchEditor;
import org.adempiere.webui.editor.WStringEditor; import org.adempiere.webui.editor.WStringEditor;
import org.adempiere.webui.editor.WTableDirEditor; import org.adempiere.webui.editor.WTableDirEditor;
import org.adempiere.webui.editor.WTimeEditor; import org.adempiere.webui.editor.WTimeEditor;
import org.adempiere.webui.editor.WTimeZoneEditor;
import org.adempiere.webui.editor.WUnknownEditor; import org.adempiere.webui.editor.WUnknownEditor;
import org.adempiere.webui.editor.WUrlEditor; import org.adempiere.webui.editor.WUrlEditor;
import org.adempiere.webui.editor.WYesNoEditor; import org.adempiere.webui.editor.WYesNoEditor;
@ -131,7 +132,7 @@ public class DefaultEditorFactory implements IEditorFactory {
{ {
if (displayType == DisplayType.Time) if (displayType == DisplayType.Time)
editor = new WTimeEditor(gridField, tableEditor, editorConfiguration); editor = new WTimeEditor(gridField, tableEditor, editorConfiguration);
else if (displayType == DisplayType.DateTime) else if (displayType == DisplayType.DateTime || displayType == DisplayType.TimestampWithTimeZone)
editor = new WDatetimeEditor(gridField, tableEditor, editorConfiguration); editor = new WDatetimeEditor(gridField, tableEditor, editorConfiguration);
else else
editor = new WDateEditor(gridField, tableEditor, editorConfiguration); editor = new WDateEditor(gridField, tableEditor, editorConfiguration);
@ -225,6 +226,10 @@ public class DefaultEditorFactory implements IEditorFactory {
{ {
editor = new WRadioGroupEditor(gridField, tableEditor, editorConfiguration); editor = new WRadioGroupEditor(gridField, tableEditor, editorConfiguration);
} }
else if (displayType == DisplayType.TimeZoneId)
{
editor = new WTimeZoneEditor(gridField, tableEditor);
}
else else
{ {
editor = new WUnknownEditor(gridField, tableEditor, editorConfiguration); editor = new WUnknownEditor(gridField, tableEditor, editorConfiguration);

View File

@ -1406,6 +1406,11 @@ public class DB_Oracle implements AdempiereDatabase
return "DATE"; return "DATE";
} }
@Override
public String getTimestampWithTimezoneDataType() {
return "TIMESTAMP WITH TIME ZONE";
}
@Override @Override
public String getSQLDDL(MColumn column) { public String getSQLDDL(MColumn column) {
StringBuilder sql = new StringBuilder ().append(column.getColumnName()) StringBuilder sql = new StringBuilder ().append(column.getColumnName())

View File

@ -1296,6 +1296,11 @@ public class DB_PostgreSQL implements AdempiereDatabase
return "TIMESTAMP"; return "TIMESTAMP";
} }
@Override
public String getTimestampWithTimezoneDataType() {
return "TIMESTAMP WITH TIME ZONE";
}
@Override @Override
public String getSQLDDL(MColumn column) { public String getSQLDDL(MColumn column) {
StringBuilder sql = new StringBuilder ().append(column.getColumnName()) StringBuilder sql = new StringBuilder ().append(column.getColumnName())

View File

@ -0,0 +1,153 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.model;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import java.sql.Timestamp;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.TimeZone;
import org.compiere.model.MClientInfo;
import org.compiere.model.MSchedule;
import org.compiere.model.MScheduler;
import org.compiere.model.PO;
import org.compiere.util.CacheMgt;
import org.compiere.util.Env;
import org.compiere.util.Util;
import org.idempiere.test.AbstractTestCase;
import org.junit.jupiter.api.Test;
/**
* @author hengsin
*
*/
public class MSchedulerTest extends AbstractTestCase {
/**
*
*/
public MSchedulerTest() {
}
@Test
public void testCronSchedulingPatternWithTimeZone() {
MSchedule schedule = null;
MClientInfo clientInfo = MClientInfo.getCopy(Env.getCtx(), getAD_Client_ID(), null);
String currentTimeZone = clientInfo.getTimeZone();
try {
schedule = new MSchedule(Env.getCtx(), 0, null);
schedule.setName("Every Day at 5 pm Test");
schedule.setScheduleType(MSchedule.SCHEDULETYPE_CronSchedulingPattern);
schedule.setIsSystemSchedule(false);
schedule.setCronPattern("0 17 * * *");
try {
PO.setCrossTenantSafe();
schedule.saveEx();
} finally {
PO.clearCrossTenantSafe();
}
//get jvm timezone
//this test assume jvm and db server is using the same default time zone
TimeZone tz1 = TimeZone.getDefault();
Calendar cal1 = Calendar.getInstance();
cal1.setTimeZone(tz1);
cal1.setTimeInMillis(System.currentTimeMillis());
int hour = cal1.get(Calendar.HOUR_OF_DAY);
if (hour > 17) {
cal1.add(Calendar.DAY_OF_MONTH, 1);
}
cal1.set(Calendar.HOUR_OF_DAY, 17);
cal1.set(Calendar.MINUTE, 0);
cal1.set(Calendar.SECOND, 0);
cal1.set(Calendar.MILLISECOND, 0);
//get timezone with +2 hour offset
String[] ids = TimeZone.getAvailableIDs(tz1.getRawOffset()+(2*60*60*1000));
TimeZone tz2 = TimeZone.getTimeZone(ids[0]);
Calendar cal2 = Calendar.getInstance();
cal2.setTimeZone(tz2);
cal2.setTimeInMillis(System.currentTimeMillis());
hour = cal2.get(Calendar.HOUR_OF_DAY);
if (hour > 17) {
cal2.add(Calendar.DAY_OF_MONTH, 1);
}
cal2.set(Calendar.HOUR_OF_DAY, 17);
cal2.set(Calendar.MINUTE, 0);
cal2.set(Calendar.SECOND, 0);
cal2.set(Calendar.MILLISECOND, 0);
//formatter for comparison
DateTimeFormatter formatter1 = DateTimeFormatter.ISO_ZONED_DATE_TIME;
formatter1 = formatter1.withZone(tz1.toZoneId());
DateTimeFormatter formatter2 = DateTimeFormatter.ISO_ZONED_DATE_TIME;
formatter2 = formatter2.withZone(tz2.toZoneId());
//test with default time zone
if (!Util.isEmpty(currentTimeZone, true)) {
clientInfo.setTimeZone(null);
clientInfo.saveEx();
CacheMgt.get().reset();
}
MScheduler scheduler1 = new MScheduler(Env.getCtx(), 0, getTrxName());
scheduler1.setAD_Process_ID(121); //Open Orders Process Id
scheduler1.setAD_Schedule_ID(schedule.get_ID());
scheduler1.setName("Cron Scheduler Test 1");
scheduler1.setSupervisor_ID(100);
scheduler1.saveEx();
Timestamp ts1 = scheduler1.getDateNextRun();
assertEquals(formatter1.format(cal1.getTime().toInstant()), formatter1.format(ts1.toInstant()), "Un-expected date next run");
assertFalse(cal2.getTimeInMillis() == ts1.getTime(), "Un-expected date next run");
//test with default + 2hour time zone
clientInfo.setTimeZone(tz2.getID());
clientInfo.saveEx();
CacheMgt.get().reset();
MScheduler scheduler2 = new MScheduler(Env.getCtx(), 0, getTrxName());
scheduler2.setAD_Process_ID(121);
scheduler2.setAD_Schedule_ID(schedule.get_ID());
scheduler2.setName("Cron Scheduler Test 2");
scheduler2.setSupervisor_ID(100);
scheduler2.saveEx();
Timestamp ts2 = scheduler2.getDateNextRun();
assertEquals(formatter2.format(cal2.getTime().toInstant()), formatter2.format(ts2.toInstant()), "Un-expected date next run");
assertFalse(cal1.getTimeInMillis() == ts2.getTime(), "Un-expected date next run");
} finally {
if (schedule != null && schedule.get_ID() > 0)
schedule.deleteEx(true);
if (!Util.isEmpty(currentTimeZone, true)) {
clientInfo.setTimeZone(currentTimeZone);
clientInfo.saveEx();
CacheMgt.get().reset();
}
}
}
}