Skip to content

Commit 7b2ac6d

Browse files
committed
LocaleContextHolder allows for independent setting of Locale and TimeZone
Issue: SPR-1528
1 parent 1dc7ff8 commit 7b2ac6d

File tree

2 files changed

+220
-8
lines changed

2 files changed

+220
-8
lines changed

spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* MessageSourceAccessor automatically use that Locale.
3636
*
3737
* @author Juergen Hoeller
38+
* @author Nicholas Williams
3839
* @since 1.2
3940
* @see LocaleContext
4041
* @see org.springframework.context.support.MessageSourceAccessor
@@ -62,7 +63,8 @@ public static void resetLocaleContext() {
6263
* <i>not</i> exposing it as inheritable for child threads.
6364
* <p>The given LocaleContext may be a {@link TimeZoneAwareLocaleContext},
6465
* containing a locale with associated time zone information.
65-
* @param localeContext the current LocaleContext
66+
* @param localeContext the current LocaleContext,
67+
* or {@code null} to reset the thread-bound context
6668
* @see SimpleLocaleContext
6769
* @see SimpleTimeZoneAwareLocaleContext
6870
*/
@@ -110,28 +112,43 @@ public static LocaleContext getLocaleContext() {
110112
}
111113

112114
/**
113-
* Associate the given Locale with the current thread.
115+
* Associate the given Locale with the current thread,
116+
* preserving any TimeZone that may have been set already.
114117
* <p>Will implicitly create a LocaleContext for the given Locale,
115118
* <i>not</i> exposing it as inheritable for child threads.
116119
* @param locale the current Locale, or {@code null} to reset
117-
* the thread-bound context
118-
* @see SimpleLocaleContext#SimpleLocaleContext(java.util.Locale)
120+
* the locale part of thread-bound context
121+
* @see #setTimeZone(TimeZone)
122+
* @see SimpleLocaleContext#SimpleLocaleContext(Locale)
119123
*/
120124
public static void setLocale(Locale locale) {
121125
setLocale(locale, false);
122126
}
123127

124128
/**
125-
* Associate the given Locale with the current thread.
129+
* Associate the given Locale with the current thread,
130+
* preserving any TimeZone that may have been set already.
126131
* <p>Will implicitly create a LocaleContext for the given Locale.
127132
* @param locale the current Locale, or {@code null} to reset
128-
* the thread-bound context
133+
* the locale part of thread-bound context
129134
* @param inheritable whether to expose the LocaleContext as inheritable
130135
* for child threads (using an {@link InheritableThreadLocal})
131-
* @see SimpleLocaleContext#SimpleLocaleContext(java.util.Locale)
136+
* @see #setTimeZone(TimeZone, boolean)
137+
* @see SimpleLocaleContext#SimpleLocaleContext(Locale)
132138
*/
133139
public static void setLocale(Locale locale, boolean inheritable) {
134-
LocaleContext localeContext = (locale != null ? new SimpleLocaleContext(locale) : null);
140+
LocaleContext localeContext = getLocaleContext();
141+
TimeZone timeZone = (localeContext instanceof TimeZoneAwareLocaleContext ?
142+
((TimeZoneAwareLocaleContext) localeContext).getTimeZone() : null);
143+
if (timeZone != null) {
144+
localeContext = new SimpleTimeZoneAwareLocaleContext(locale, timeZone);
145+
}
146+
else if (locale != null) {
147+
localeContext = new SimpleLocaleContext(locale);
148+
}
149+
else {
150+
localeContext = null;
151+
}
135152
setLocaleContext(localeContext, inheritable);
136153
}
137154

@@ -160,6 +177,46 @@ public static Locale getLocale() {
160177
return Locale.getDefault();
161178
}
162179

180+
/**
181+
* Associate the given TimeZone with the current thread,
182+
* preserving any Locale that may have been set already.
183+
* <p>Will implicitly create a LocaleContext for the given Locale,
184+
* <i>not</i> exposing it as inheritable for child threads.
185+
* @param timeZone the current TimeZone, or {@code null} to reset
186+
* the time zone part of the thread-bound context
187+
* @see #setLocale(Locale)
188+
* @see SimpleTimeZoneAwareLocaleContext#SimpleTimeZoneAwareLocaleContext(Locale, TimeZone)
189+
*/
190+
public static void setTimeZone(TimeZone timeZone) {
191+
setTimeZone(timeZone, false);
192+
}
193+
194+
/**
195+
* Associate the given TimeZone with the current thread,
196+
* preserving any Locale that may have been set already.
197+
* <p>Will implicitly create a LocaleContext for the given Locale.
198+
* @param timeZone the current TimeZone, or {@code null} to reset
199+
* the time zone part of the thread-bound context
200+
* @param inheritable whether to expose the LocaleContext as inheritable
201+
* for child threads (using an {@link InheritableThreadLocal})
202+
* @see #setLocale(Locale, boolean)
203+
* @see SimpleTimeZoneAwareLocaleContext#SimpleTimeZoneAwareLocaleContext(Locale, TimeZone)
204+
*/
205+
public static void setTimeZone(TimeZone timeZone, boolean inheritable) {
206+
LocaleContext localeContext = getLocaleContext();
207+
Locale locale = (localeContext != null ? localeContext.getLocale() : null);
208+
if (timeZone != null) {
209+
localeContext = new SimpleTimeZoneAwareLocaleContext(locale, timeZone);
210+
}
211+
else if (locale != null) {
212+
localeContext = new SimpleLocaleContext(locale);
213+
}
214+
else {
215+
localeContext = null;
216+
}
217+
setLocaleContext(localeContext, inheritable);
218+
}
219+
163220
/**
164221
* Return the TimeZone associated with the current thread, if any,
165222
* or the system default TimeZone else. This is effectively a
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.i18n;
18+
19+
import java.util.Locale;
20+
import java.util.TimeZone;
21+
22+
import org.junit.Test;
23+
24+
import static org.junit.Assert.*;
25+
26+
/**
27+
* @author Juergen Hoeller
28+
*/
29+
public class LocaleContextHolderTests {
30+
31+
@Test
32+
public void testSetLocaleContext() {
33+
LocaleContext lc = new SimpleLocaleContext(Locale.GERMAN);
34+
LocaleContextHolder.setLocaleContext(lc);
35+
assertSame(lc, LocaleContextHolder.getLocaleContext());
36+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale());
37+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
38+
39+
lc = new SimpleLocaleContext(Locale.GERMANY);
40+
LocaleContextHolder.setLocaleContext(lc);
41+
assertSame(lc, LocaleContextHolder.getLocaleContext());
42+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocale());
43+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
44+
45+
LocaleContextHolder.resetLocaleContext();
46+
assertNull(LocaleContextHolder.getLocaleContext());
47+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
48+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
49+
}
50+
51+
@Test
52+
public void testSetTimeZoneAwareLocaleContext() {
53+
LocaleContext lc = new SimpleTimeZoneAwareLocaleContext(Locale.GERMANY, TimeZone.getTimeZone("GMT+1"));
54+
LocaleContextHolder.setLocaleContext(lc);
55+
assertSame(lc, LocaleContextHolder.getLocaleContext());
56+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocale());
57+
assertEquals(TimeZone.getTimeZone("GMT+1"), LocaleContextHolder.getTimeZone());
58+
59+
LocaleContextHolder.resetLocaleContext();
60+
assertNull(LocaleContextHolder.getLocaleContext());
61+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
62+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
63+
}
64+
65+
@Test
66+
public void testSetLocale() {
67+
LocaleContextHolder.setLocale(Locale.GERMAN);
68+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale());
69+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
70+
assertFalse(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
71+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocaleContext().getLocale());
72+
73+
LocaleContextHolder.setLocale(Locale.GERMANY);
74+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocale());
75+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
76+
assertFalse(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
77+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocaleContext().getLocale());
78+
79+
LocaleContextHolder.setLocale(null);
80+
assertNull(LocaleContextHolder.getLocaleContext());
81+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
82+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
83+
}
84+
85+
@Test
86+
public void testSetTimeZone() {
87+
LocaleContextHolder.setTimeZone(TimeZone.getTimeZone("GMT+1"));
88+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
89+
assertEquals(TimeZone.getTimeZone("GMT+1"), LocaleContextHolder.getTimeZone());
90+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
91+
assertNull(LocaleContextHolder.getLocaleContext().getLocale());
92+
assertEquals(TimeZone.getTimeZone("GMT+1"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
93+
94+
LocaleContextHolder.setTimeZone(TimeZone.getTimeZone("GMT+2"));
95+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
96+
assertEquals(TimeZone.getTimeZone("GMT+2"), LocaleContextHolder.getTimeZone());
97+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
98+
assertNull(LocaleContextHolder.getLocaleContext().getLocale());
99+
assertEquals(TimeZone.getTimeZone("GMT+2"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
100+
101+
LocaleContextHolder.setTimeZone(null);
102+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
103+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
104+
assertNull(LocaleContextHolder.getLocaleContext());
105+
}
106+
107+
@Test
108+
public void testSetLocaleAndSetTimeZoneMixed() {
109+
LocaleContextHolder.setLocale(Locale.GERMANY);
110+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocale());
111+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
112+
assertFalse(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
113+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocaleContext().getLocale());
114+
115+
LocaleContextHolder.setTimeZone(TimeZone.getTimeZone("GMT+1"));
116+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocale());
117+
assertEquals(TimeZone.getTimeZone("GMT+1"), LocaleContextHolder.getTimeZone());
118+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
119+
assertEquals(Locale.GERMANY, LocaleContextHolder.getLocaleContext().getLocale());
120+
assertEquals(TimeZone.getTimeZone("GMT+1"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
121+
122+
LocaleContextHolder.setLocale(Locale.GERMAN);
123+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale());
124+
assertEquals(TimeZone.getTimeZone("GMT+1"), LocaleContextHolder.getTimeZone());
125+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
126+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocaleContext().getLocale());
127+
assertEquals(TimeZone.getTimeZone("GMT+1"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
128+
129+
LocaleContextHolder.setTimeZone(null);
130+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale());
131+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
132+
assertFalse(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
133+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocaleContext().getLocale());
134+
135+
LocaleContextHolder.setTimeZone(TimeZone.getTimeZone("GMT+2"));
136+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale());
137+
assertEquals(TimeZone.getTimeZone("GMT+2"), LocaleContextHolder.getTimeZone());
138+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
139+
assertEquals(Locale.GERMAN, LocaleContextHolder.getLocaleContext().getLocale());
140+
assertEquals(TimeZone.getTimeZone("GMT+2"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
141+
142+
LocaleContextHolder.setLocale(null);
143+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
144+
assertEquals(TimeZone.getTimeZone("GMT+2"), LocaleContextHolder.getTimeZone());
145+
assertTrue(LocaleContextHolder.getLocaleContext() instanceof TimeZoneAwareLocaleContext);
146+
assertNull(LocaleContextHolder.getLocaleContext().getLocale());
147+
assertEquals(TimeZone.getTimeZone("GMT+2"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone());
148+
149+
LocaleContextHolder.setTimeZone(null);
150+
assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale());
151+
assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone());
152+
assertNull(LocaleContextHolder.getLocaleContext());
153+
}
154+
155+
}

0 commit comments

Comments
 (0)