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