This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 986103395b [feature] System time zone optimization. (#3588)
986103395b is described below

commit 986103395b7d3e846fff5842dbe78f22337d6a9b
Author: Duansg <siguod...@gmail.com>
AuthorDate: Sat Jul 19 18:18:25 2025 +0800

    [feature] System time zone optimization. (#3588)
    
    Co-authored-by: Sherlock Yin <sherlock.yin1...@gmail.com>
    Co-authored-by: tomsun28 <tomsu...@outlook.com>
---
 .../controller/GeneralConfigController.java        | 42 +++++++++-
 .../system-config/system-config.component.html     | 97 ++++++++++------------
 .../system-config/system-config.component.ts       | 19 +++++
 web-app/src/app/service/general-config.service.ts  |  4 +
 4 files changed, 106 insertions(+), 56 deletions(-)

diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
index 4a89e69c12..f88444e7bd 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
@@ -17,7 +17,6 @@
 
 package org.apache.hertzbeat.manager.controller;
 
-import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -25,6 +24,7 @@ import jakarta.annotation.Resource;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.common.entity.dto.Message;
+import org.apache.hertzbeat.common.util.CommonUtil;
 import org.apache.hertzbeat.common.util.ResponseUtil;
 import org.apache.hertzbeat.manager.pojo.dto.TemplateConfig;
 import org.apache.hertzbeat.manager.service.ConfigService;
@@ -37,6 +37,19 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.TextStyle;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+
 /**
  * Alert sender Configuration API
  */
@@ -45,6 +58,9 @@ import org.springframework.web.bind.annotation.RestController;
 @Tag(name = "Alert sender Configuration API")
 @Slf4j
 public class GeneralConfigController {
+
+    private static final Set<String> ZONE_IDS = ZoneId.getAvailableZoneIds();
+
     @Resource
     private ConfigService configService;
 
@@ -74,4 +90,28 @@ public class GeneralConfigController {
             @RequestBody TemplateConfig.AppTemplate template) {
         return ResponseUtil.handle(() -> 
configService.updateTemplateAppConfig(app, template));
     }
+
+    @GetMapping(path = "/timezones")
+    @Operation(summary = "Get all available timezones and their current UTC 
offset", description = "Get all available timezones and their current UTC 
offset")
+    public ResponseEntity<Message<List<Map<String, String>>>> getTimezones() {
+        List<Map<String, String>> timezones = ZONE_IDS.stream()
+                .map(id -> {
+                    try {
+                        ZoneId zoneId = ZoneId.of(id);
+                        ZonedDateTime now = ZonedDateTime.now(zoneId);
+                        int totalSeconds = now.getOffset().getTotalSeconds();
+                        String offset = String.format("UTC%+03d:%02d", 
totalSeconds / 3600, Math.abs((totalSeconds / 60) % 60));
+                        String displayName = 
zoneId.getDisplayName(TextStyle.FULL, Locale.getDefault());
+                        return Map.of("zoneId", id, "offset", offset, 
"displayName", displayName);
+                    } catch (Exception e) {
+                        String errorMsg = 
CommonUtil.getMessageFromThrowable(e);
+                        log.warn("Query Timezone failed. {} ", errorMsg);
+                        return null;
+                    }
+                })
+                .filter(t -> Objects.nonNull(t) && 
Objects.nonNull(t.get("zoneId")))
+                .sorted(Comparator.comparing(m -> m.get("zoneId")))
+                .collect(Collectors.toList());
+        return ResponseEntity.ok(Message.success(timezones));
+    }
 }
diff --git 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.html
 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.html
index 947ddad97b..c05b0422b2 100644
--- 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.html
+++ 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.html
@@ -22,65 +22,52 @@
     <div class="left">
       <form nz-form nzLayout="vertical" #f="ngForm" 
(submit)="onSaveSystemConfig()" se-container="1">
         <se [label]="'settings.system-config.locale' | i18n" 
[error]="'validation.required' | i18n">
-          <nz-select
-            [(ngModel)]="config.locale"
-            [ngModelOptions]="{ standalone: true }"
-            style="text-align: center; font-weight: bolder"
-            [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
-          >
-            <nz-option [nzValue]="'en_US'" 
[nzLabel]="'settings.system-config.locale.en_US' | i18n"></nz-option>
-            <nz-option [nzValue]="'zh_CN'" 
[nzLabel]="'settings.system-config.locale.zh_CN' | i18n"></nz-option>
-            <nz-option [nzValue]="'zh_TW'" 
[nzLabel]="'settings.system-config.locale.zh_TW' | i18n"></nz-option>
-            <nz-option [nzValue]="'ja_JP'" 
[nzLabel]="'settings.system-config.locale.ja-JP' | i18n"></nz-option>
-            <nz-option [nzValue]="'pt_BR'" 
[nzLabel]="'settings.system-config.locale.pt_BR' | i18n"></nz-option>
-          </nz-select>
+          <div style="width: 400px">
+            <nz-select
+              [(ngModel)]="config.locale"
+              [ngModelOptions]="{ standalone: true }"
+              style="text-align: center; font-weight: bolder"
+              [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
+            >
+              <nz-option [nzValue]="'en_US'" 
[nzLabel]="'settings.system-config.locale.en_US' | i18n"></nz-option>
+              <nz-option [nzValue]="'zh_CN'" 
[nzLabel]="'settings.system-config.locale.zh_CN' | i18n"></nz-option>
+              <nz-option [nzValue]="'zh_TW'" 
[nzLabel]="'settings.system-config.locale.zh_TW' | i18n"></nz-option>
+              <nz-option [nzValue]="'ja_JP'" 
[nzLabel]="'settings.system-config.locale.ja-JP' | i18n"></nz-option>
+              <nz-option [nzValue]="'pt_BR'" 
[nzLabel]="'settings.system-config.locale.pt_BR' | i18n"></nz-option>
+            </nz-select>
+          </div>
         </se>
         <se [label]="'settings.system-config.timezone' | i18n" 
[error]="'validation.required' | i18n">
-          <nz-select
-            [(ngModel)]="config.timeZoneId"
-            [ngModelOptions]="{ standalone: true }"
-            [nzDropdownMatchSelectWidth]="false"
-            style="text-align: center; font-weight: bolder"
-            [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
-          >
-            <nz-option [nzValue]="'Pacific/Pago_Pago'" 
[nzLabel]="'Pacific/Pago_Pago(UTC-11:00)'"></nz-option>
-            <nz-option [nzValue]="'Pacific/Tahiti'" 
[nzLabel]="'Pacific/Tahiti(UTC-10:00)'"></nz-option>
-            <nz-option [nzValue]="'Pacific/Gambier'" 
[nzLabel]="'Pacific/Gambier(UTC-09:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Nome'" 
[nzLabel]="'America/Nome(UTC-08:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Phoenix'" 
[nzLabel]="'America/Phoenix(UTC-07:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Boise'" 
[nzLabel]="'America/Boise(UTC-06:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Lima'" 
[nzLabel]="'America/Lima(UTC-05:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Tortola'" 
[nzLabel]="'America/Tortola(UTC-04:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Maceio'" 
[nzLabel]="'America/Maceio(UTC-03:00)'"></nz-option>
-            <nz-option [nzValue]="'America/Miquelon'" 
[nzLabel]="'America/Miquelon(UTC-02:00)'"></nz-option>
-            <nz-option [nzValue]="'Atlantic/Cape_Verde'" 
[nzLabel]="'Atlantic/Cape_Verde(UTC-01:00)'"></nz-option>
-            <nz-option [nzValue]="'Africa/Lome'" 
[nzLabel]="'Africa/Lome(UTC+00:00)'"></nz-option>
-            <nz-option [nzValue]="'Europe/Dublin'" 
[nzLabel]="'Europe/Dublin(UTC+01:00)'"></nz-option>
-            <nz-option [nzValue]="'Europe/Madrid'" 
[nzLabel]="'Europe/Madrid(UTC+02:00)'"></nz-option>
-            <nz-option [nzValue]="'Europe/Athens'" 
[nzLabel]="'Europe/Athens(UTC+03:00)'"></nz-option>
-            <nz-option [nzValue]="'Indian/Reunion'" 
[nzLabel]="'Indian/Reunion(UTC+04:00)'"></nz-option>
-            <nz-option [nzValue]="'Asia/Samarkand'" 
[nzLabel]="'Asia/Samarkand(UTC+05:00)'"></nz-option>
-            <nz-option [nzValue]="'Asia/Bishkek'" 
[nzLabel]="'Asia/Bishkek(UTC+06:00)'"></nz-option>
-            <nz-option [nzValue]="'Asia/Jakarta'" 
[nzLabel]="'Asia/Jakarta(UTC+07:00)'"></nz-option>
-            <nz-option [nzValue]="'Asia/Shanghai'" 
[nzLabel]="'Asia/Shanghai(UTC+08:00)'"></nz-option>
-            <nz-option [nzValue]="'Asia/Tokyo'" 
[nzLabel]="'Asia/Tokyo(UTC+09:00)'"></nz-option>
-            <nz-option [nzValue]="'Australia/Sydney'" 
[nzLabel]="'Australia/Sydney(UTC+10:00)'"></nz-option>
-            <nz-option [nzValue]="'Pacific/Noumea'" 
[nzLabel]="'Pacific/Noumea(UTC+12:00)'"></nz-option>
-            <nz-option [nzValue]="'Pacific/Apia'" 
[nzLabel]="'Pacific/Apia(UTC+13:00)'"></nz-option>
-            <nz-option [nzValue]="'Pacific/Kiritimati'" 
[nzLabel]="'Pacific/Kiritimati(UTC+14:00)'"></nz-option>
-          </nz-select>
+          <div style="width: 400px">
+            <nz-select
+              [(ngModel)]="config.timeZoneId"
+              [ngModelOptions]="{ standalone: true }"
+              [nzDropdownMatchSelectWidth]="false"
+              style="text-align: center; font-weight: bolder"
+              [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
+              nzShowSearch
+            >
+              <nz-option
+                *ngFor="let tz of timezones"
+                [nzValue]="tz.zoneId"
+                [nzLabel]="tz.zoneId + ' (' + tz.offset + ') ' + 
tz.displayName"
+              ></nz-option>
+            </nz-select>
+          </div>
         </se>
         <se [label]="'settings.system-config.theme' | i18n" 
[error]="'validation.required' | i18n">
-          <nz-select
-            [(ngModel)]="config.theme"
-            [ngModelOptions]="{ standalone: true }"
-            style="text-align: center; font-weight: bolder"
-            [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
-          >
-            <nz-option [nzValue]="'default'" 
[nzLabel]="'settings.system-config.theme.default' | i18n"></nz-option>
-            <nz-option [nzValue]="'dark'" 
[nzLabel]="'settings.system-config.theme.dark' | i18n"></nz-option>
-            <nz-option [nzValue]="'compact'" 
[nzLabel]="'settings.system-config.theme.compact' | i18n"></nz-option>
-          </nz-select>
+          <div style="width: 400px">
+            <nz-select
+              [(ngModel)]="config.theme"
+              [ngModelOptions]="{ standalone: true }"
+              style="text-align: center; font-weight: bolder"
+              [nzDropdownStyle]="{ 'font-weight': 'bolder', 'font-size': 
'larger' }"
+            >
+              <nz-option [nzValue]="'default'" 
[nzLabel]="'settings.system-config.theme.default' | i18n"></nz-option>
+              <nz-option [nzValue]="'dark'" 
[nzLabel]="'settings.system-config.theme.dark' | i18n"></nz-option>
+              <nz-option [nzValue]="'compact'" 
[nzLabel]="'settings.system-config.theme.compact' | i18n"></nz-option>
+            </nz-select>
+          </div>
         </se>
         <se>
           <button nz-button nzType="primary" [disabled]="f.invalid">{{ 
'settings.system-config.ok' | i18n }}</button>
diff --git 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.ts
 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.ts
index e40afd8f00..f77f82ddea 100644
--- 
a/web-app/src/app/routes/setting/settings/system-config/system-config.component.ts
+++ 
b/web-app/src/app/routes/setting/settings/system-config/system-config.component.ts
@@ -46,9 +46,11 @@ export class SystemConfigComponent implements OnInit {
 
   loading = true;
   config!: SystemConfig;
+  timezones: Array<{ zoneId: string; offset: string; displayName: string }> = 
[];
 
   ngOnInit(): void {
     this.loadSystemConfig();
+    this.loadTimezones();
   }
 
   loadSystemConfig() {
@@ -76,6 +78,23 @@ export class SystemConfigComponent implements OnInit {
     );
   }
 
+  loadTimezones() {
+    this.configService.getTimezones().subscribe(
+      message => {
+        if (message.code === 0 && Array.isArray(message.data)) {
+          this.timezones = message.data;
+        } else {
+          this.timezones = [];
+        }
+        this.cdr.markForCheck();
+      },
+      error => {
+        this.timezones = [];
+        this.cdr.markForCheck();
+      }
+    );
+  }
+
   onSaveSystemConfig() {
     this.loading = true;
     const configOk$ = this.configService
diff --git a/web-app/src/app/service/general-config.service.ts 
b/web-app/src/app/service/general-config.service.ts
index 1fcb400cb9..02141ee5bd 100644
--- a/web-app/src/app/service/general-config.service.ts
+++ b/web-app/src/app/service/general-config.service.ts
@@ -42,4 +42,8 @@ export class GeneralConfigService {
   public updateAppTemplateConfig(body: any, app: string): 
Observable<Message<void>> {
     return 
this.http.put<Message<void>>(`${general_config_uri}/template/${app}`, body);
   }
+
+  public getTimezones(): Observable<Message<any>> {
+    return this.http.get<Message<any>>(`${general_config_uri}/timezones`);
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@hertzbeat.apache.org
For additional commands, e-mail: notifications-h...@hertzbeat.apache.org

Reply via email to