Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
return false;
}
request.setAttribute(Constants.SESSION_USER, user);
ThreadLocalContext.getTimezoneThreadLocal().set(user.getTimeZone());
ThreadLocalContext.setTimezone(user.getTimeZone());
return true;
}

Expand All @@ -100,7 +100,6 @@ public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
ThreadLocalContext.getTimezoneThreadLocal().remove();

int code = response.getStatus();
if (code >= 200 && code < 300) {
Expand All @@ -113,4 +112,12 @@ public void postHandle(HttpServletRequest request,
ApiServerMetrics.incApiResponse5xxCount();
}
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
ThreadLocalContext.removeTimezone();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,47 @@
package org.apache.dolphinscheduler.common.thread;

/**
* thread local context
* A utility class to manage timezone context using ThreadLocal.
* This allows each thread to have its own timezone value, which is useful in
* multi-threaded environments such as web applications where each request
* may need to operate in a different timezone.
*
* Note: Always call {@link #removeTimezone()} at the end of a request or task
* to prevent memory leaks and context pollution in thread pool environments.
*/
public class ThreadLocalContext {

public static final ThreadLocal<String> timezoneThreadLocal = new ThreadLocal<>();
/**
* ThreadLocal variable to hold the timezone string for the current thread.
* Each thread will have its own copy of the timezone value.
*/
private static final ThreadLocal<String> TIMEZONE_THREAD_LOCAL = new ThreadLocal<>();

/**
* Sets the timezone for the current thread.
*
* @param timezone the timezone ID (e.g., "UTC", "Asia/Shanghai", "America/New_York")
*/
public static void setTimezone(String timezone) {
TIMEZONE_THREAD_LOCAL.set(timezone);
}

/**
* Retrieves the timezone for the current thread.
*
* @return the timezone string set for the current thread, or null if not set
*/
public static String getTimezone() {
return TIMEZONE_THREAD_LOCAL.get();
}

public static ThreadLocal<String> getTimezoneThreadLocal() {
return timezoneThreadLocal;
/**
* Removes the timezone value for the current thread.
* This method should be called to clean up the thread-local value, especially
* when using thread pools (e.g., in web servers), to prevent memory leaks
* and unintended data leakage between requests.
*/
public static void removeTimezone() {
TIMEZONE_THREAD_LOCAL.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ private DateUtils() {
* @return local datetime
*/
private static LocalDateTime date2LocalDateTime(Date date) {
String timezone = ThreadLocalContext.getTimezoneThreadLocal().get();
String timezone = ThreadLocalContext.getTimezone();
ZoneId zoneId = StringUtils.isNotEmpty(timezone) ? ZoneId.of(timezone) : ZoneId.systemDefault();
return date2LocalDateTime(date, zoneId);
}

public static String getTimezone() {
String timezone = ThreadLocalContext.getTimezoneThreadLocal().get();
String timezone = ThreadLocalContext.getTimezone();
return StringUtils.isNotEmpty(timezone) ? timezone : ZoneId.systemDefault().getId();
}

Expand All @@ -90,7 +90,7 @@ private static LocalDateTime date2LocalDateTime(Date date, ZoneId zoneId) {
* @return date
*/
private static Date localDateTime2Date(LocalDateTime localDateTime) {
String timezone = ThreadLocalContext.getTimezoneThreadLocal().get();
String timezone = ThreadLocalContext.getTimezone();
ZoneId zoneId = StringUtils.isNotEmpty(timezone) ? ZoneId.of(timezone) : ZoneId.systemDefault();
return localDateTime2Date(localDateTime, zoneId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public class DateUtilsTest {

@BeforeEach
public void before() {
ThreadLocalContext.getTimezoneThreadLocal().remove();
ThreadLocalContext.removeTimezone();
}

@AfterEach
public void after() {
ThreadLocalContext.getTimezoneThreadLocal().remove();
ThreadLocalContext.removeTimezone();
}

@Test
Expand Down Expand Up @@ -242,11 +242,11 @@ public void testGetTimezone() {
public void testTimezone() {

String time = "2019-01-28 00:00:00";
ThreadLocalContext.timezoneThreadLocal.set("UTC");
ThreadLocalContext.setTimezone("UTC");
Date utcDate = DateUtils.stringToDate(time);
Assertions.assertEquals(time, DateUtils.dateToString(utcDate));

ThreadLocalContext.timezoneThreadLocal.set("Asia/Shanghai");
ThreadLocalContext.setTimezone("Asia/Shanghai");
Date shanghaiDate = DateUtils.stringToDate(time);
Assertions.assertEquals(time, DateUtils.dateToString(shanghaiDate));

Expand Down
Loading