Java 8u51应用中如何将泰国地区日历替换为公历
解决Java 8u51中泰国Locale使用公历而非佛历的问题
我之前也碰到过类似的场景,Java 8里泰国Locale默认绑定佛历(Buddhist Calendar),想要替换成公历的话,SPI方案没成功通常是Provider优先级不够或者服务注册不规范导致的。下面给你几个靠谱的解决思路:
方案一:通过SPI正确替换CalendarProvider(推荐)
这个方案能全局生效,不用修改各处的Calendar调用代码,步骤如下:
1. 实现自定义CalendarProvider
创建一个继承自sun.util.spi.CalendarProvider的类,针对泰国Locale返回GregorianCalendar实例:
import sun.util.spi.CalendarProvider; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; public class ThaiGregorianCalendarProvider extends CalendarProvider { @Override public Calendar getInstance(Locale locale) { // 仅对泰国Locale返回公历 if (Locale.THAI.equals(locale)) { return new GregorianCalendar(locale); } // 其他Locale沿用默认实现 return Calendar.getInstance(locale); } @Override public Locale[] getAvailableLocales() { // 声明仅支持泰国Locale return new Locale[]{Locale.THAI}; } }
2. 注册SPI服务
在项目的META-INF/services目录下创建名为sun.util.spi.CalendarProvider的文件,文件内容是你的自定义Provider的全类名,比如:
com.yourpackage.ThaiGregorianCalendarProvider
(注意路径和文件名必须完全匹配,否则JVM找不到你的Provider)
3. 调整LocaleProviderAdapter优先级
Java 8默认优先使用JRE自带的Locale Provider,会覆盖你的SPI实现,所以需要通过系统属性调整优先级:
- 启动时设置JVM参数:
-Dsun.locale.provider=SPI,JRE - 或者在代码中提前设置(必须在第一次调用
Calendar.getInstance()之前执行,且需要权限包裹):import java.security.AccessController; import java.security.PrivilegedAction; public class Main { public static void main(String[] args) { // 先设置系统属性,确保SPI优先加载 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { System.setProperty("sun.locale.provider", "SPI,JRE"); return null; }); // 测试验证 Calendar cal = Calendar.getInstance(Locale.THAI); System.out.println("当前年份(公历):" + cal.get(Calendar.YEAR)); } }
方案二:手动创建GregorianCalendar(快速临时方案)
如果不想折腾SPI,也可以直接在需要的地方创建GregorianCalendar实例并指定泰国Locale,这样能快速生效,但需要修改所有调用Calendar的代码:
// 直接创建泰国Locale的公历实例 Calendar thaiGregorian = new GregorianCalendar(Locale.THAI); // 验证年份(佛历会比公历多543年,比如2023对应佛历2566) System.out.println("公历年份:" + thaiGregorian.get(Calendar.YEAR));
为什么你的SPI替换没成功?
大概率是这两个原因:
- 没有正确创建
META-INF/services/sun.util.spi.CalendarProvider服务文件,导致JVM无法发现你的Provider; - 没有调整
sun.locale.provider属性,JRE自带的ThaiCalendarProvider优先级更高,被优先加载了。
内容的提问来源于stack exchange,提问作者nikai
相关产品推荐
相关产品推荐

