fix(platform): normalize legacy Vietnam timezone

This commit is contained in:
sechmachine
2026-08-15 22:07:27 +07:00
parent 7bac998866
commit a9fb948769
3 changed files with 184 additions and 2 deletions
@@ -1,23 +1,43 @@
package com.lab.labtimesheet;
import java.util.TimeZone;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import com.lab.labtimesheet.config.SecurityProperties;
/** Application entry point and root component-scan boundary for Lab Timesheet. */
/**
* Application entry point and root component-scan boundary for Lab Timesheet.
*
* <p>The entry point also canonicalizes the legacy Windows Vietnam timezone alias before database
* drivers inspect the JVM default timezone.
*/
@SpringBootApplication
@EnableConfigurationProperties(SecurityProperties.class)
public class LabtimesheetApplication {
private static final String LEGACY_VIETNAM_TIME_ZONE = "Asia/Saigon";
private static final String BUSINESS_TIME_ZONE = "Asia/Ho_Chi_Minh";
/**
* Starts the standalone Spring Boot process.
* Canonicalizes the process timezone and starts the standalone Spring Boot process.
*
* @param args command-line arguments forwarded to Spring Boot
*/
public static void main(String[] args) {
normalizeDefaultTimeZone();
SpringApplication.run(LabtimesheetApplication.class, args);
}
/**
* Replaces the legacy Windows Vietnam alias before pgJDBC sends it to PostgreSQL as a startup
* parameter. Other supported system timezones remain unchanged.
*/
static void normalizeDefaultTimeZone() {
if (LEGACY_VIETNAM_TIME_ZONE.equals(TimeZone.getDefault().getID())) {
TimeZone.setDefault(TimeZone.getTimeZone(BUSINESS_TIME_ZONE));
}
}
}
@@ -0,0 +1,80 @@
package com.lab.labtimesheet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mockStatic;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.TimeZone;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.SpringApplication;
import org.testcontainers.postgresql.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;
class ApplicationTimeZoneIntegrationTest {
@Test
void mainCanonicalizesLegacyAliasBeforeStartingSpring() {
TimeZone originalTimeZone = TimeZone.getDefault();
String[] args = {"--spring.profiles.active=test"};
try (MockedStatic<SpringApplication> springApplication = mockStatic(SpringApplication.class)) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Saigon"));
LabtimesheetApplication.main(args);
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Ho_Chi_Minh");
springApplication.verify(() -> SpringApplication.run(LabtimesheetApplication.class, args));
} finally {
TimeZone.setDefault(originalTimeZone);
}
}
@Test
void canonicalizesLegacyVietnamAliasBeforePostgresConnects() throws SQLException {
TimeZone originalTimeZone = TimeZone.getDefault();
try (PostgreSQLContainer postgres =
new PostgreSQLContainer(DockerImageName.parse("postgres:18.4"))) {
postgres.start();
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Saigon"));
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Saigon");
assertThatThrownBy(() -> openConnection(postgres))
.isInstanceOf(SQLException.class)
.hasMessageContaining("Asia/Saigon");
LabtimesheetApplication.normalizeDefaultTimeZone();
assertThat(TimeZone.getDefault().getID()).isEqualTo("Asia/Ho_Chi_Minh");
try (Connection connection = openConnection(postgres)) {
assertThat(connection.isValid(1)).isTrue();
}
} finally {
TimeZone.setDefault(originalTimeZone);
}
}
@Test
void leavesSupportedSystemTimeZoneUnchanged() {
TimeZone originalTimeZone = TimeZone.getDefault();
try {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
LabtimesheetApplication.normalizeDefaultTimeZone();
assertThat(TimeZone.getDefault().getID()).isEqualTo("Europe/Paris");
} finally {
TimeZone.setDefault(originalTimeZone);
}
}
private static Connection openConnection(PostgreSQLContainer postgres) throws SQLException {
return DriverManager.getConnection(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword());
}
}