From 13b37c4acc2764f6bf4c2d4e26a77c9de85777ba Mon Sep 17 00:00:00 2001 From: hengsin Date: Sat, 23 Apr 2022 03:28:51 +0800 Subject: [PATCH] =?UTF-8?q?IDEMPIERE-5093=20Scheduler=20cron=20pattern=20s?= =?UTF-8?q?cheduling=20is=20always=20using=20serv=E2=80=A6=20(#1270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .../oracle/202203240830_IDEMPIERE-5093.sql | 51 ++ .../oracle/202204041140_IDEMPIERE-5093.sql | 36 ++ .../oracle/202204111508_IDEMPIERE-5093.sql | 186 +++++++ .../202203240830_IDEMPIERE-5093.sql | 48 ++ .../202204041140_IDEMPIERE-5093.sql | 33 ++ .../202204111508_IDEMPIERE-5093.sql | 183 +++++++ .../org/compiere/db/AdempiereDatabase.java | 5 + .../org/compiere/model/I_AD_ClientInfo.java | 23 +- .../org/compiere/model/MAcctProcessor.java | 3 +- .../org/compiere/model/MAlertProcessor.java | 3 +- .../org/compiere/model/MRequestProcessor.java | 3 +- .../src/org/compiere/model/MSchedule.java | 33 ++ .../src/org/compiere/model/MScheduler.java | 4 +- .../src/org/compiere/model/SystemIDs.java | 2 + .../org/compiere/model/X_AD_ClientInfo.java | 20 +- .../src/org/compiere/util/DisplayType.java | 57 ++- .../src/org/compiere/util/Env.java | 5 + .../src/org/compiere/util/TimeUtil.java | 7 +- .../org/compiere/wf/MWorkflowProcessor.java | 4 +- .../org/compiere/server/AdempiereServer.java | 5 +- .../org/compiere/web/AdempiereMonitor.java | 43 +- .../org/adempiere/webui/AdempiereWebUI.java | 20 +- .../src/org/adempiere/webui/ClientInfo.java | 2 +- .../webui/component/DatetimeBox.java | 82 ++- .../webui/editor/WDatetimeEditor.java | 68 ++- .../webui/editor/WEditorPopupMenu.java | 2 +- .../webui/editor/WTimeZoneEditor.java | 467 ++++++++++++++++++ .../webui/event/ContextMenuEvent.java | 27 + .../webui/factory/DefaultEditorFactory.java | 7 +- .../src/org/compiere/db/DB_Oracle.java | 5 + .../src/org/compiere/db/DB_PostgreSQL.java | 5 + .../idempiere/test/model/MSchedulerTest.java | 153 ++++++ 32 files changed, 1528 insertions(+), 64 deletions(-) create mode 100644 migration/iD10/oracle/202203240830_IDEMPIERE-5093.sql create mode 100644 migration/iD10/oracle/202204041140_IDEMPIERE-5093.sql create mode 100644 migration/iD10/oracle/202204111508_IDEMPIERE-5093.sql create mode 100644 migration/iD10/postgresql/202203240830_IDEMPIERE-5093.sql create mode 100644 migration/iD10/postgresql/202204041140_IDEMPIERE-5093.sql create mode 100644 migration/iD10/postgresql/202204111508_IDEMPIERE-5093.sql create mode 100644 org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTimeZoneEditor.java create mode 100644 org.idempiere.test/src/org/idempiere/test/model/MSchedulerTest.java diff --git a/migration/iD10/oracle/202203240830_IDEMPIERE-5093.sql b/migration/iD10/oracle/202203240830_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..ec7fefc982 --- /dev/null +++ b/migration/iD10/oracle/202203240830_IDEMPIERE-5093.sql @@ -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 +; + diff --git a/migration/iD10/oracle/202204041140_IDEMPIERE-5093.sql b/migration/iD10/oracle/202204041140_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..d4ce30775a --- /dev/null +++ b/migration/iD10/oracle/202204041140_IDEMPIERE-5093.sql @@ -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' +; + diff --git a/migration/iD10/oracle/202204111508_IDEMPIERE-5093.sql b/migration/iD10/oracle/202204111508_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..f90b24a0a7 --- /dev/null +++ b/migration/iD10/oracle/202204111508_IDEMPIERE-5093.sql @@ -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 +; + diff --git a/migration/iD10/postgresql/202203240830_IDEMPIERE-5093.sql b/migration/iD10/postgresql/202203240830_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..d3f884c04f --- /dev/null +++ b/migration/iD10/postgresql/202203240830_IDEMPIERE-5093.sql @@ -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 +; + diff --git a/migration/iD10/postgresql/202204041140_IDEMPIERE-5093.sql b/migration/iD10/postgresql/202204041140_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..b4e5277d8a --- /dev/null +++ b/migration/iD10/postgresql/202204041140_IDEMPIERE-5093.sql @@ -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' +; + diff --git a/migration/iD10/postgresql/202204111508_IDEMPIERE-5093.sql b/migration/iD10/postgresql/202204111508_IDEMPIERE-5093.sql new file mode 100644 index 0000000000..f8b1dd2e48 --- /dev/null +++ b/migration/iD10/postgresql/202204111508_IDEMPIERE-5093.sql @@ -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 +; + diff --git a/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java b/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java index ce577c47d2..64b3309c19 100644 --- a/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java +++ b/org.adempiere.base/src/org/compiere/db/AdempiereDatabase.java @@ -407,6 +407,11 @@ public interface AdempiereDatabase */ public String getTimestampDataType(); + /** + * + * @return timestamp with time zone type name + */ + public String getTimestampWithTimezoneDataType(); /** * Get SQL Create * @param table diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java b/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java index 5d616b94ac..c2315cc400 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_ClientInfo.java @@ -22,7 +22,7 @@ import org.compiere.util.KeyNamePair; /** Generated Interface for AD_ClientInfo * @author iDempiere (generated) - * @version Release 9 + * @version Release 10 */ public interface I_AD_ClientInfo { @@ -44,8 +44,8 @@ public interface I_AD_ClientInfo /** Column name AD_Client_ID */ public static final String COLUMNNAME_AD_Client_ID = "AD_Client_ID"; - /** Get Client. - * Client/Tenant for this installation. + /** Get Tenant. + * Tenant for this installation. */ 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"; /** Set Organization. - * Organizational entity within client + * Organizational entity within tenant */ public void setAD_Org_ID (int AD_Org_ID); /** Get Organization. - * Organizational entity within client + * Organizational entity within tenant */ public int getAD_Org_ID(); @@ -464,6 +464,19 @@ public interface I_AD_ClientInfo 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 */ public static final String COLUMNNAME_Updated = "Updated"; diff --git a/org.adempiere.base/src/org/compiere/model/MAcctProcessor.java b/org.adempiere.base/src/org/compiere/model/MAcctProcessor.java index 0563022e83..a90eb62da9 100644 --- a/org.adempiere.base/src/org/compiere/model/MAcctProcessor.java +++ b/org.adempiere.base/src/org/compiere/model/MAcctProcessor.java @@ -105,7 +105,8 @@ public class MAcctProcessor extends X_C_AcctProcessor protected boolean beforeSave(boolean newRecord) { 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) setDateNextRun(new Timestamp(nextWork)); } diff --git a/org.adempiere.base/src/org/compiere/model/MAlertProcessor.java b/org.adempiere.base/src/org/compiere/model/MAlertProcessor.java index f538ca73d6..d399577782 100644 --- a/org.adempiere.base/src/org/compiere/model/MAlertProcessor.java +++ b/org.adempiere.base/src/org/compiere/model/MAlertProcessor.java @@ -168,7 +168,8 @@ public class MAlertProcessor extends X_AD_AlertProcessor protected boolean beforeSave(boolean newRecord) { 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) setDateNextRun(new Timestamp(nextWork)); } diff --git a/org.adempiere.base/src/org/compiere/model/MRequestProcessor.java b/org.adempiere.base/src/org/compiere/model/MRequestProcessor.java index 011c400aff..201535644e 100644 --- a/org.adempiere.base/src/org/compiere/model/MRequestProcessor.java +++ b/org.adempiere.base/src/org/compiere/model/MRequestProcessor.java @@ -242,7 +242,8 @@ public class MRequestProcessor extends X_R_RequestProcessor protected boolean beforeSave(boolean newRecord) { 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) setDateNextRun(new Timestamp(nextWork)); } diff --git a/org.adempiere.base/src/org/compiere/model/MSchedule.java b/org.adempiere.base/src/org/compiere/model/MSchedule.java index 53d7208335..472cc80f22 100644 --- a/org.adempiere.base/src/org/compiere/model/MSchedule.java +++ b/org.adempiere.base/src/org/compiere/model/MSchedule.java @@ -26,12 +26,14 @@ import java.sql.ResultSet; import java.util.Enumeration; import java.util.Properties; import java.util.StringTokenizer; +import java.util.TimeZone; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.compiere.util.Env; +import org.compiere.util.Util; import org.idempiere.cache.ImmutableIntPOCache; import org.idempiere.cache.ImmutablePOSupport; @@ -249,9 +251,29 @@ public class MSchedule extends X_AD_Schedule implements ImmutablePOSupport /** * Get Next Run * @param last in MS + * @param scheduleType + * @param frequencyType + * @param frequency + * @param cronPattern * @return next run in MS + * @deprecated */ 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(); 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 && 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); + if (tz != null) + predictor.setTimeZone(tz); long next = predictor.nextMatchingTime(); while (next < now) { predictor = new Predictor(cronPattern, next); + if (tz != null) + predictor.setTimeZone(tz); next = predictor.nextMatchingTime(); } return next; diff --git a/org.adempiere.base/src/org/compiere/model/MScheduler.java b/org.adempiere.base/src/org/compiere/model/MScheduler.java index 7548b49ba0..9a09645b8a 100644 --- a/org.adempiere.base/src/org/compiere/model/MScheduler.java +++ b/org.adempiere.base/src/org/compiere/model/MScheduler.java @@ -325,7 +325,9 @@ public class MScheduler extends X_AD_Scheduler } 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) setDateNextRun(new Timestamp(nextWork)); } diff --git a/org.adempiere.base/src/org/compiere/model/SystemIDs.java b/org.adempiere.base/src/org/compiere/model/SystemIDs.java index bd27da3705..712907e2a4 100644 --- a/org.adempiere.base/src/org/compiere/model/SystemIDs.java +++ b/org.adempiere.base/src/org/compiere/model/SystemIDs.java @@ -144,6 +144,8 @@ public class SystemIDs public final static int REFERENCE_DATATYPE_TEXT = 14; public final static int REFERENCE_DATATYPE_TEXTLONG = 36; 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_YES_NO = 20; diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java b/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java index 5ebae6c627..697d17d38b 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_ClientInfo.java @@ -23,7 +23,7 @@ import java.util.Properties; /** Generated Model for AD_ClientInfo * @author iDempiere (generated) - * @version Release 9 - $Id$ */ + * @version Release 10 - $Id$ */ @org.adempiere.base.Model(table="AD_ClientInfo") 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 */ 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 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); + } } \ No newline at end of file diff --git a/org.adempiere.base/src/org/compiere/util/DisplayType.java b/org.adempiere.base/src/org/compiere/util/DisplayType.java index d502f7551d..b78557d791 100644 --- a/org.adempiere.base/src/org/compiere/util/DisplayType.java +++ b/org.adempiere.base/src/org/compiere/util/DisplayType.java @@ -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_TEXTLONG; 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_YES_NO; @@ -66,6 +68,7 @@ import java.util.Currency; import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.TimeZone; import java.util.logging.Level; 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 TimestampWithTimeZone = REFERENCE_DATATYPE_TIMESTAMP_WITH_TIMEZONE; + + public static final int TimeZoneId = REFERENCE_DATATYPE_TIMEZONE; + /** * - New Display Type INSERT INTO AD_REFERENCE @@ -317,7 +324,8 @@ public final class DisplayType || displayType == RadiogroupList || displayType == ChosenMultipleSelectionList || displayType == ChosenMultipleSelectionTable - || displayType == ChosenMultipleSelectionSearch) + || displayType == ChosenMultipleSelectionSearch + || displayType == TimeZoneId) return true; IServiceReferenceHolder cache = s_displayTypeFactoryCache.get(displayType); @@ -347,6 +355,9 @@ public final class DisplayType if (displayType == Date || displayType == DateTime || displayType == Time) return true; + if (isTimestampWithTimeZone(displayType)) + return true; + IServiceReferenceHolder cache = s_displayTypeFactoryCache.get(displayType); if (cache != null) { IDisplayTypeFactory service = cache.getService(); @@ -455,6 +466,19 @@ public final class DisplayType return false; } // 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 * @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()); try { - format.applyPattern(pattern); - return format; + format.applyPattern(pattern); + return displayType==TimeZoneId ? setTimeZone(format) : format; } catch (IllegalArgumentException e) { s_log.log(Level.WARNING, "Invalid date pattern: " + pattern); @@ -631,7 +655,14 @@ public final class DisplayType return new SimpleDateFormat(lang.getTimePattern()); 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 { IServiceReferenceHolder cache = s_displayTypeFactoryCache.get(displayType); if (cache != null) { @@ -658,6 +689,20 @@ public final class DisplayType return myLanguage.getDateFormat(); // default } // 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 * @return date format @@ -768,6 +813,8 @@ public final class DisplayType // if (displayType == DisplayType.Integer) return getDatabase().getNumericDataType()+"(10)"; + if (DisplayType.isTimestampWithTimeZone(displayType)) + return getDatabase().getTimestampWithTimezoneDataType(); if (DisplayType.isDate(displayType)) return getDatabase().getTimestampDataType(); if (DisplayType.isNumeric(displayType)) @@ -793,7 +840,7 @@ public final class DisplayType return getDatabase().getNumericDataType()+"(10)"; else return getDatabase().getCharacterDataType()+"(" + fieldLength + ")"; - } + } IServiceReferenceHolder cache = s_displayTypeFactoryCache.get(displayType); if (cache != null) { diff --git a/org.adempiere.base/src/org/compiere/util/Env.java b/org.adempiere.base/src/org/compiere/util/Env.java index a72e999fdf..7d05fbcdc2 100644 --- a/org.adempiere.base/src/org/compiere/util/Env.java +++ b/org.adempiere.base/src/org/compiere/util/Env.java @@ -102,6 +102,11 @@ public final class Env 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_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 DB_TYPE = "#DBType"; public static final String GL_CATEGORY_ID = "#GL_Category_ID"; diff --git a/org.adempiere.base/src/org/compiere/util/TimeUtil.java b/org.adempiere.base/src/org/compiere/util/TimeUtil.java index f12bc19e8b..edee31b475 100644 --- a/org.adempiere.base/src/org/compiere/util/TimeUtil.java +++ b/org.adempiere.base/src/org/compiere/util/TimeUtil.java @@ -543,17 +543,16 @@ public class TimeUtil long hours = elapsedMS%24; long days = elapsedMS / 24; // - if (days != 0) - sb.append(days).append("'"); + sb.append(days).append("'"); // hh if (hours != 0) sb.append(get2digits(hours)).append(":"); - else if (days != 0) + else sb.append("00:"); // mm if (minutes != 0) sb.append(get2digits(minutes)).append(":"); - else if (hours != 0 || days != 0) + else sb.append("00:"); // ss sb.append(get2digits(seconds)) diff --git a/org.adempiere.base/src/org/compiere/wf/MWorkflowProcessor.java b/org.adempiere.base/src/org/compiere/wf/MWorkflowProcessor.java index 45aff7b88a..6934bf588c 100644 --- a/org.adempiere.base/src/org/compiere/wf/MWorkflowProcessor.java +++ b/org.adempiere.base/src/org/compiere/wf/MWorkflowProcessor.java @@ -24,6 +24,7 @@ import java.util.Properties; import org.compiere.model.AdempiereProcessor; import org.compiere.model.AdempiereProcessor2; import org.compiere.model.AdempiereProcessorLog; +import org.compiere.model.MClientInfo; import org.compiere.model.MSchedule; import org.compiere.model.Query; import org.compiere.model.X_AD_WorkflowProcessor; @@ -142,7 +143,8 @@ public class MWorkflowProcessor extends X_AD_WorkflowProcessor protected boolean beforeSave(boolean newRecord) { 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) setDateNextRun(new Timestamp(nextWork)); } diff --git a/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServer.java b/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServer.java index 71a115fc05..8bdaff0a62 100644 --- a/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServer.java +++ b/org.adempiere.server/src/main/server/org/compiere/server/AdempiereServer.java @@ -26,6 +26,7 @@ import org.compiere.model.AdempiereProcessor; import org.compiere.model.AdempiereProcessor2; import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.MClient; +import org.compiere.model.MClientInfo; import org.compiere.model.MOrgInfo; import org.compiere.model.MRole; import org.compiere.model.MSchedule; @@ -127,7 +128,7 @@ public abstract class AdempiereServer implements Runnable { m_nextWork = MSchedule.getNextRunMS(now.getTime(), 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()) @@ -294,7 +295,7 @@ public abstract class AdempiereServer implements Runnable m_nextWork = MSchedule.getNextRunMS(lastRun.getTime(), 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; if (m_nextWork == 0) { diff --git a/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java b/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java index ebc0c71db6..3c3cc1a2bd 100644 --- a/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java +++ b/org.adempiere.server/src/main/servlet/org/compiere/web/AdempiereMonitor.java @@ -24,10 +24,14 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; 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.Arrays; import java.util.Comparator; +import java.util.Date; import java.util.Collection; import java.util.List; import java.util.Properties; @@ -65,6 +69,7 @@ import org.apache.ecs.xhtml.tr; import org.compiere.Adempiere; import org.compiere.model.AdempiereProcessorLog; import org.compiere.model.MClient; +import org.compiere.model.MClientInfo; import org.compiere.model.MSession; import org.compiere.model.MSysConfig; import org.compiere.model.MSystem; @@ -687,7 +692,7 @@ public class AdempiereMonitor extends HttpServlet table.addElement(line); line = new tr(); 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()))); table.addElement(line); line = new tr(); @@ -696,7 +701,7 @@ public class AdempiereMonitor extends HttpServlet table.addElement(line); line = new tr(); 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); bb.addElement(table); @@ -834,7 +839,7 @@ public class AdempiereMonitor extends HttpServlet table.addElement(line); line = new tr(); 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()))); } else @@ -853,7 +858,7 @@ public class AdempiereMonitor extends HttpServlet // line = new tr(); 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); line = new tr(); line.addElement(new th().addElement("Info")); @@ -863,7 +868,7 @@ public class AdempiereMonitor extends HttpServlet line = new tr(); line.addElement(new th().addElement("Next Run")); 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(" - "); link = new a ("idempiereMonitor?RunNow=" + server.getServerId(), "(Run Now)"); td.addElement(link); @@ -912,6 +917,22 @@ public class AdempiereMonitor extends HttpServlet WebUtil.createResponse (request, response, this, null, doc, false); } // 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) { StringBuilder builder = new StringBuilder(); @@ -971,7 +992,7 @@ public class AdempiereMonitor extends HttpServlet writer.print(getServerManager().getDescription()); writer.println(""); writer.print("\t\t"); - writer.print(getServerManager().getStartTime()); + writer.print(formatTimestampWithTimeZone(0, getServerManager().getStartTime())); writer.println(""); writer.print("\t\t"); writer.print(getServerManager().getServerCount()); @@ -1008,13 +1029,13 @@ public class AdempiereMonitor extends HttpServlet writer.print("Stopped"); writer.println(""); writer.print("\t\t\t"); - writer.print(server.getStartTime()); + writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getStartTime())); writer.println(""); writer.print("\t\t\t"); - writer.print(server.getModel().getDateLastRun()); + writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateLastRun())); writer.println(""); writer.print("\t\t\t"); - writer.print(server.getModel().getDateNextRun(false)); + writer.print(formatTimestampWithTimeZone(server.getModel().getAD_Client_ID(), server.getModel().getDateNextRun(false))); writer.println(""); writer.print("\t\t\t"); writer.print(server.getStatistics()); @@ -1115,7 +1136,7 @@ public class AdempiereMonitor extends HttpServlet td td = new td(); td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('" + escapeEcmaScript(trx.getDisplayName()) +"" + "
" + escapeEcmaScript(trx.getStackTrace()) + "
')"); - 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.setStyle("text-decoration: underline; color: blue"); line.addElement(td); @@ -1692,7 +1713,7 @@ public class AdempiereMonitor extends HttpServlet td td = new td(); td.setOnClick("var newwindow=window.open('','Popup', 'width=800,height=600');newwindow.document.write('" + escapeEcmaScript(trx.getDisplayName()) +"" + "
" + escapeEcmaScript(trx.getStackTrace()) + "
')"); - 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.setStyle("text-decoration: underline; color: blue"); line.addElement(td); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java index 103fa0f007..1375617014 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java @@ -535,15 +535,16 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb Executions.getCurrent().getSession().setAttribute(CLIENT_INFO, clientInfo); } - Env.setContext(Env.getCtx(), "#clientInfo_desktopWidth", clientInfo.desktopWidth); - Env.setContext(Env.getCtx(), "#clientInfo_desktopHeight", clientInfo.desktopHeight); - Env.setContext(Env.getCtx(), "#clientInfo_orientation", clientInfo.orientation); - Env.setContext(Env.getCtx(), "#clientInfo_mobile", clientInfo.tablet); + Env.setContext(Env.getCtx(), Env.CLIENT_INFO_DESKTOP_WIDTH, clientInfo.desktopWidth); + Env.setContext(Env.getCtx(), Env.CLIENT_INFO_DESKTOP_HEIGHT, clientInfo.desktopHeight); + Env.setContext(Env.getCtx(), Env.CLIENT_INFO_ORIENTATION, clientInfo.orientation); + 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(); if (appDesktop != null) appDesktop.setClientInfo(clientInfo); - } else if (event.getName().equals(ON_LOGIN_COMPLETED)) { loginCompleted(); } @@ -601,10 +602,11 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb 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_FONT_ICON_FOR_IMAGE, Env.getContext(Env.getCtx(), ITheme.USE_FONT_ICON_FOR_IMAGE)); - Env.setContext(properties, "#clientInfo_desktopWidth", clientInfo.desktopWidth); - Env.setContext(properties, "#clientInfo_desktopHeight", clientInfo.desktopHeight); - Env.setContext(properties, "#clientInfo_orientation", clientInfo.orientation); - Env.setContext(properties, "#clientInfo_mobile", clientInfo.tablet); + Env.setContext(properties, Env.CLIENT_INFO_DESKTOP_WIDTH, clientInfo.desktopWidth); + Env.setContext(properties, Env.CLIENT_INFO_DESKTOP_HEIGHT, clientInfo.desktopHeight); + Env.setContext(properties, Env.CLIENT_INFO_ORIENTATION, clientInfo.orientation); + 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(); Locale locale = (Locale) desktop.getSession().getAttribute(Attributes.PREFERRED_LOCALE); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ClientInfo.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ClientInfo.java index a2f30c139b..7a88ded8f1 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ClientInfo.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ClientInfo.java @@ -96,7 +96,7 @@ public class ClientInfo implements Serializable { * @return true if mobile browser */ public static boolean isMobile() { - return "Y".equals(Env.getContext(Env.getCtx(), "#clientInfo_mobile")); + return "Y".equals(Env.getContext(Env.getCtx(), Env.CLIENT_INFO_MOBILE)); } /** diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/DatetimeBox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/DatetimeBox.java index 4a2377f2dd..a50dfad606 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/DatetimeBox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/DatetimeBox.java @@ -13,8 +13,11 @@ package org.adempiere.webui.component; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Date; +import java.util.TimeZone; import org.zkoss.zk.ui.event.EventListener; @@ -31,13 +34,13 @@ public class DatetimeBox extends Panel { private static final long serialVersionUID = -1075410511739601354L; private Datebox dateBox; private Timebox timeBox; + private TimeZone timeZone; public DatetimeBox() { dateBox = new Datebox(); dateBox.setCols(10); timeBox = new Timebox(); timeBox.setCols(10); - //timeBox.setButtonVisible(false); appendChild(dateBox); appendChild(timeBox); @@ -107,17 +110,40 @@ public class DatetimeBox extends Panel { * @return date */ public Date getValue() { - Date 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(); + Date d = null; + 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) { + Calendar cd = Calendar.getInstance(); + cd.setTimeZone(dateBox.getTimeZone()); + cd.setTime(d); + Calendar ct = Calendar.getInstance(); + ct.setTimeZone(timeBox.getTimeZone()); + 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(); + } + } 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; @@ -146,4 +172,36 @@ public class DatetimeBox extends Panel { { 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); + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WDatetimeEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WDatetimeEditor.java index 4e32e3d613..033dcb92f9 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WDatetimeEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WDatetimeEditor.java @@ -15,7 +15,9 @@ package org.adempiere.webui.editor; import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Date; +import java.util.TimeZone; import org.adempiere.webui.ValuePreference; 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.window.WFieldRecordInfo; import org.compiere.model.GridField; +import org.compiere.model.MClientInfo; import org.compiere.util.CLogger; +import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Msg; +import org.compiere.util.Util; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Events; @@ -115,8 +120,37 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener addChangeLogMenu(popupMenu); if (gridField != null) 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) { 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) { - 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)) { return; @@ -167,10 +208,18 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener } else if (value instanceof Timestamp) { - LocalDateTime localTime =((Timestamp)value).toLocalDateTime(); - getComponent().getDatebox().setValueInLocalDateTime(localTime); - getComponent().getTimebox().setValueInLocalDateTime(localTime); - oldValue = (Timestamp)value; + Timestamp ts = (Timestamp) value; + if (isTimestampWithTimeZone()) + { + ZonedDateTime zdt = ts.toInstant().atZone(getComponent().getDatebox().getTimeZone().toZoneId()); + getComponent().setValueInZonedDateTime(zdt); + } + else + { + LocalDateTime localTime = ts.toLocalDateTime(); + getComponent().setValueInLocalDateTime(localTime); + } + oldValue = ts; } else { @@ -179,9 +228,16 @@ public class WDatetimeEditor extends WEditor implements ContextMenuListener getComponent().setText(value.toString()); } catch (Exception e) {} 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 + { oldValue = null; + } } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditorPopupMenu.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditorPopupMenu.java index 2f95bf5e77..232a2d5219 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditorPopupMenu.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditorPopupMenu.java @@ -288,7 +288,7 @@ public class WEditorPopupMenu extends Menupopup implements EventListener if (evt != null) { ContextMenuEvent menuEvent = new ContextMenuEvent(evt); - + menuEvent.setTarget(event.getTarget()); ContextMenuListener[] listeners = new ContextMenuListener[0]; listeners = menuListeners.toArray(listeners); for (ContextMenuListener listener : listeners) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTimeZoneEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTimeZoneEditor.java new file mode 100644 index 0000000000..07ec1ebdaf --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTimeZoneEditor.java @@ -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 ids = ZoneId.getAvailableZoneIds(); + List rawOffsets = new ArrayList(); + Map> map = new HashMap>(); + 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 timezones = map.get(rawOffset); + if (timezones == null) { + timezones = new ArrayList(); + map.put(rawOffset, timezones); + } + ValueNamePair valueNamePair = new ValueNamePair(id, label); + timezones.add(valueNamePair); + } + Collections.sort(rawOffsets); + for(int rawOffset : rawOffsets) { + List 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 labels = new ArrayList(); + List ids = new ArrayList(); + 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 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(); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/event/ContextMenuEvent.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/event/ContextMenuEvent.java index 5845ba1f9b..ff1a34ede6 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/event/ContextMenuEvent.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/event/ContextMenuEvent.java @@ -17,6 +17,8 @@ package org.adempiere.webui.event; +import org.zkoss.zk.ui.Component; + /** * * @author Ashley G Ramdass @@ -26,14 +28,39 @@ package org.adempiere.webui.event; public class ContextMenuEvent { private String contextEvent; + private Component target; + /** + * + * @param event + */ public ContextMenuEvent(String event) { this.contextEvent = event; } + /** + * + * @return event name + */ public String getContextEvent() { return contextEvent; } + + /** + * + * @param target + */ + public void setTarget(Component target) { + this.target = target; + } + + /** + * + * @return target component + */ + public Component getTarget() { + return this.target; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultEditorFactory.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultEditorFactory.java index efbbbed6d8..0c39b8d33f 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultEditorFactory.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/factory/DefaultEditorFactory.java @@ -41,6 +41,7 @@ import org.adempiere.webui.editor.WSearchEditor; import org.adempiere.webui.editor.WStringEditor; import org.adempiere.webui.editor.WTableDirEditor; import org.adempiere.webui.editor.WTimeEditor; +import org.adempiere.webui.editor.WTimeZoneEditor; import org.adempiere.webui.editor.WUnknownEditor; import org.adempiere.webui.editor.WUrlEditor; import org.adempiere.webui.editor.WYesNoEditor; @@ -131,7 +132,7 @@ public class DefaultEditorFactory implements IEditorFactory { { if (displayType == DisplayType.Time) 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); else editor = new WDateEditor(gridField, tableEditor, editorConfiguration); @@ -225,6 +226,10 @@ public class DefaultEditorFactory implements IEditorFactory { { editor = new WRadioGroupEditor(gridField, tableEditor, editorConfiguration); } + else if (displayType == DisplayType.TimeZoneId) + { + editor = new WTimeZoneEditor(gridField, tableEditor); + } else { editor = new WUnknownEditor(gridField, tableEditor, editorConfiguration); diff --git a/org.compiere.db.oracle.provider/src/org/compiere/db/DB_Oracle.java b/org.compiere.db.oracle.provider/src/org/compiere/db/DB_Oracle.java index 7acd798ba6..a9de8458cd 100644 --- a/org.compiere.db.oracle.provider/src/org/compiere/db/DB_Oracle.java +++ b/org.compiere.db.oracle.provider/src/org/compiere/db/DB_Oracle.java @@ -1406,6 +1406,11 @@ public class DB_Oracle implements AdempiereDatabase return "DATE"; } + @Override + public String getTimestampWithTimezoneDataType() { + return "TIMESTAMP WITH TIME ZONE"; + } + @Override public String getSQLDDL(MColumn column) { StringBuilder sql = new StringBuilder ().append(column.getColumnName()) diff --git a/org.compiere.db.postgresql.provider/src/org/compiere/db/DB_PostgreSQL.java b/org.compiere.db.postgresql.provider/src/org/compiere/db/DB_PostgreSQL.java index aa4035cf59..887c03b150 100755 --- a/org.compiere.db.postgresql.provider/src/org/compiere/db/DB_PostgreSQL.java +++ b/org.compiere.db.postgresql.provider/src/org/compiere/db/DB_PostgreSQL.java @@ -1296,6 +1296,11 @@ public class DB_PostgreSQL implements AdempiereDatabase return "TIMESTAMP"; } + @Override + public String getTimestampWithTimezoneDataType() { + return "TIMESTAMP WITH TIME ZONE"; + } + @Override public String getSQLDDL(MColumn column) { StringBuilder sql = new StringBuilder ().append(column.getColumnName()) diff --git a/org.idempiere.test/src/org/idempiere/test/model/MSchedulerTest.java b/org.idempiere.test/src/org/idempiere/test/model/MSchedulerTest.java new file mode 100644 index 0000000000..5cf069b6b1 --- /dev/null +++ b/org.idempiere.test/src/org/idempiere/test/model/MSchedulerTest.java @@ -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(); + } + } + } +}