Comprehensive update to the framework's TimeZone handling, including a new TimeZoneAwareLocaleContext and a LocaleContextResolver for Spring MVC
A few noteworthy minor changes: LocaleContext.getLocale() may return null in special cases (not by default), which our own accessing classes are able to handle now. If there is a non-null TimeZone user setting, we're exposing it to all collaborating libraries, in particular to JSTL, Velocity and JasperReports. Our JSR-310 and Joda-Time support falls back to checking the general LocaleContext TimeZone now, adapting it to their time zone types, if no more specific setting has been provided. Our DefaultConversionService has TimeZone<->ZoneId converters registered. And finally, we're using a custom parseTimeZoneString method now that doesn't accept the TimeZone.getTimeZone(String) GMT fallback for an invalid time zone id anymore. Issue: SPR-1528
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,14 +26,15 @@ import java.util.Locale;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see LocaleContextHolder
|
||||
* @see java.util.Locale
|
||||
* @see LocaleContextHolder#getLocale()
|
||||
* @see TimeZoneAwareLocaleContext
|
||||
*/
|
||||
public interface LocaleContext {
|
||||
|
||||
/**
|
||||
* Return the current Locale, which can be fixed or determined dynamically,
|
||||
* depending on the implementation strategy.
|
||||
* @return the current Locale, or {@code null} if no specific Locale associated
|
||||
*/
|
||||
Locale getLocale();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.context.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.core.NamedInheritableThreadLocal;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
@@ -59,7 +60,11 @@ public abstract class LocaleContextHolder {
|
||||
/**
|
||||
* Associate the given LocaleContext with the current thread,
|
||||
* <i>not</i> exposing it as inheritable for child threads.
|
||||
* <p>The given LocaleContext may be a {@link TimeZoneAwareLocaleContext},
|
||||
* containing a locale with associated time zone information.
|
||||
* @param localeContext the current LocaleContext
|
||||
* @see SimpleLocaleContext
|
||||
* @see SimpleTimeZoneAwareLocaleContext
|
||||
*/
|
||||
public static void setLocaleContext(LocaleContext localeContext) {
|
||||
setLocaleContext(localeContext, false);
|
||||
@@ -67,10 +72,14 @@ public abstract class LocaleContextHolder {
|
||||
|
||||
/**
|
||||
* Associate the given LocaleContext with the current thread.
|
||||
* <p>The given LocaleContext may be a {@link TimeZoneAwareLocaleContext},
|
||||
* containing a locale with associated time zone information.
|
||||
* @param localeContext the current LocaleContext,
|
||||
* or {@code null} to reset the thread-bound context
|
||||
* @param inheritable whether to expose the LocaleContext as inheritable
|
||||
* for child threads (using an {@link InheritableThreadLocal})
|
||||
* @see SimpleLocaleContext
|
||||
* @see SimpleTimeZoneAwareLocaleContext
|
||||
*/
|
||||
public static void setLocaleContext(LocaleContext localeContext, boolean inheritable) {
|
||||
if (localeContext == null) {
|
||||
@@ -128,7 +137,13 @@ public abstract class LocaleContextHolder {
|
||||
|
||||
/**
|
||||
* Return the Locale associated with the current thread, if any,
|
||||
* or the system default Locale else.
|
||||
* or the system default Locale else. This is effectively a
|
||||
* replacement for {@link java.util.Locale#getDefault()},
|
||||
* able to optionally respect a user-level Locale setting.
|
||||
* <p>Note: This method has a fallback to the system default Locale.
|
||||
* If you'd like to check for the raw LocaleContext content
|
||||
* (which may indicate no specific locale through {@code null}, use
|
||||
* {@link #getLocaleContext()} and call {@link LocaleContext#getLocale()}
|
||||
* @return the current Locale, or the system default Locale if no
|
||||
* specific Locale has been associated with the current thread
|
||||
* @see LocaleContext#getLocale()
|
||||
@@ -136,7 +151,39 @@ public abstract class LocaleContextHolder {
|
||||
*/
|
||||
public static Locale getLocale() {
|
||||
LocaleContext localeContext = getLocaleContext();
|
||||
return (localeContext != null ? localeContext.getLocale() : Locale.getDefault());
|
||||
if (localeContext != null) {
|
||||
Locale locale = localeContext.getLocale();
|
||||
if (locale != null) {
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TimeZone associated with the current thread, if any,
|
||||
* or the system default TimeZone else. This is effectively a
|
||||
* replacement for {@link java.util.TimeZone#getDefault()},
|
||||
* able to optionally respect a user-level TimeZone setting.
|
||||
* <p>Note: This method has a fallback to the system default Locale.
|
||||
* If you'd like to check for the raw LocaleContext content
|
||||
* (which may indicate no specific time zone through {@code null}, use
|
||||
* {@link #getLocaleContext()} and call {@link TimeZoneAwareLocaleContext#getTimeZone()}
|
||||
* after downcasting to {@link TimeZoneAwareLocaleContext}.
|
||||
* @return the current TimeZone, or the system default TimeZone if no
|
||||
* specific TimeZone has been associated with the current thread
|
||||
* @see TimeZoneAwareLocaleContext#getTimeZone()
|
||||
* @see java.util.TimeZone#getDefault()
|
||||
*/
|
||||
public static TimeZone getTimeZone() {
|
||||
LocaleContext localeContext = getLocaleContext();
|
||||
if (localeContext instanceof TimeZoneAwareLocaleContext) {
|
||||
TimeZone timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
|
||||
if (timeZone != null) {
|
||||
return timeZone;
|
||||
}
|
||||
}
|
||||
return TimeZone.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,14 +18,15 @@ package org.springframework.context.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link LocaleContext} interface,
|
||||
* always returning a specified {@code Locale}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see LocaleContextHolder#setLocaleContext
|
||||
* @see LocaleContextHolder#getLocale()
|
||||
* @see SimpleTimeZoneAwareLocaleContext
|
||||
*/
|
||||
public class SimpleLocaleContext implements LocaleContext {
|
||||
|
||||
@@ -34,11 +35,10 @@ public class SimpleLocaleContext implements LocaleContext {
|
||||
|
||||
/**
|
||||
* Create a new SimpleLocaleContext that exposes the specified Locale.
|
||||
* Every {@code getLocale()} will return this Locale.
|
||||
* Every {@link #getLocale()} call will return this Locale.
|
||||
* @param locale the Locale to expose
|
||||
*/
|
||||
public SimpleLocaleContext(Locale locale) {
|
||||
Assert.notNull(locale, "Locale must not be null");
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.i18n;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link TimeZoneAwareLocaleContext} interface,
|
||||
* always returning a specified {@code Locale} and {@code TimeZone}.
|
||||
*
|
||||
* <p>Note: Prefer the use of {@link SimpleLocaleContext} when only setting
|
||||
* a Locale but no TimeZone.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see LocaleContextHolder#setLocaleContext
|
||||
* @see LocaleContextHolder#getTimeZone()
|
||||
*/
|
||||
public class SimpleTimeZoneAwareLocaleContext extends SimpleLocaleContext implements TimeZoneAwareLocaleContext {
|
||||
|
||||
private final TimeZone timeZone;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleTimeZoneAwareLocaleContext that exposes the specified
|
||||
* Locale and TimeZone. Every {@link #getLocale()} call will return the given
|
||||
* Locale, and every {@link #getTimeZone()} call will return the given TimeZone.
|
||||
* @param locale the Locale to expose
|
||||
* @param timeZone the TimeZone to expose
|
||||
*/
|
||||
public SimpleTimeZoneAwareLocaleContext(Locale locale, TimeZone timeZone) {
|
||||
super(locale);
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return this.timeZone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + this.timeZone.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.i18n;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Extension of {@link LocaleContext}, adding awareness of the current time zone.
|
||||
*
|
||||
* <p>Having this variant of LocaleContext set to {@link LocaleContextHolder} means
|
||||
* that some TimeZone-aware infrastructure has been configured, even if it may not
|
||||
* be able to produce a non-null TimeZone at the moment.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see LocaleContextHolder#getTimeZone()
|
||||
*/
|
||||
public interface TimeZoneAwareLocaleContext extends LocaleContext {
|
||||
|
||||
/**
|
||||
* Return the current TimeZone, which can be fixed or determined dynamically,
|
||||
* depending on the implementation strategy.
|
||||
* @return the current TimeZone, or {@code null} if no specific TimeZone associated
|
||||
*/
|
||||
TimeZone getTimeZone();
|
||||
|
||||
}
|
||||
@@ -16,10 +16,16 @@
|
||||
|
||||
package org.springframework.format.datetime.joda;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.joda.time.Chronology;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
|
||||
|
||||
/**
|
||||
* A context that holds user-specific Joda-Time settings such as the user's
|
||||
* Chronology (calendar system) and time zone.
|
||||
@@ -53,6 +59,11 @@ public class JodaTimeContext {
|
||||
|
||||
/**
|
||||
* Set the user's time zone.
|
||||
* <p>Alternatively, set a {@link TimeZoneAwareLocaleContext} on
|
||||
* {@link LocaleContextHolder}. This context class will fall back to
|
||||
* checking the locale context if no setting has been provided here.
|
||||
* @see org.springframework.context.i18n.LocaleContextHolder#getTimeZone()
|
||||
* @see org.springframework.context.i18n.LocaleContextHolder#setLocaleContext
|
||||
*/
|
||||
public void setTimeZone(DateTimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
@@ -80,6 +91,15 @@ public class JodaTimeContext {
|
||||
if (this.timeZone != null) {
|
||||
formatter = formatter.withZone(this.timeZone);
|
||||
}
|
||||
else {
|
||||
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
|
||||
if (localeContext instanceof TimeZoneAwareLocaleContext) {
|
||||
TimeZone timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
|
||||
if (timeZone != null) {
|
||||
formatter = formatter.withZone(DateTimeZone.forTimeZone(timeZone));
|
||||
}
|
||||
}
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ package org.springframework.format.datetime.standard;
|
||||
import java.time.ZoneId;
|
||||
import java.time.chrono.Chronology;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContext;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
|
||||
|
||||
/**
|
||||
* A context that holds user-specific <code>java.time</code> (JSR-310) settings
|
||||
@@ -52,6 +57,11 @@ public class DateTimeContext {
|
||||
|
||||
/**
|
||||
* Set the user's time zone.
|
||||
* <p>Alternatively, set a {@link TimeZoneAwareLocaleContext} on
|
||||
* {@link LocaleContextHolder}. This context class will fall back to
|
||||
* checking the locale context if no setting has been provided here.
|
||||
* @see org.springframework.context.i18n.LocaleContextHolder#getTimeZone()
|
||||
* @see org.springframework.context.i18n.LocaleContextHolder#setLocaleContext
|
||||
*/
|
||||
public void setTimeZone(ZoneId timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
@@ -79,6 +89,15 @@ public class DateTimeContext {
|
||||
if (this.timeZone != null) {
|
||||
formatter = formatter.withZone(this.timeZone);
|
||||
}
|
||||
else {
|
||||
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();
|
||||
if (localeContext instanceof TimeZoneAwareLocaleContext) {
|
||||
TimeZone timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone();
|
||||
if (timeZone != null) {
|
||||
formatter = formatter.withZone(timeZone.toZoneId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.scheduling.support.ScheduledMethodRunnable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
@@ -192,11 +193,7 @@ public class ScheduledAnnotationBeanPostProcessor
|
||||
}
|
||||
TimeZone timeZone;
|
||||
if (!"".equals(zone)) {
|
||||
timeZone = TimeZone.getTimeZone(zone);
|
||||
// Check for that silly TimeZone fallback...
|
||||
if ("GMT".equals(timeZone.getID()) && !zone.startsWith("GMT")) {
|
||||
throw new IllegalArgumentException("Invalid time zone id '" + zone + "'");
|
||||
}
|
||||
timeZone = StringUtils.parseTimeZoneString(zone);
|
||||
}
|
||||
else {
|
||||
timeZone = TimeZone.getDefault();
|
||||
|
||||
Reference in New Issue
Block a user