为什么Java从系统中读取默认设置

为什么Java从系统中读取默认设置,java,Java,Java正在从安装它的系统中读取语言环境、时区和编码信息(可能还有更多) 这经常带来不好的惊喜(就在昨天给我带来了一个)。假设您的开发和生产服务器设置为时区GMT+2。然后部署在设置为GMT的生产服务器上。2小时轮班可能不容易立即观察到。尽管您可以将时区传递给日历,但API可能正在使用默认时区实例化日历(或日期) 现在,我知道人们应该小心这些设置,但它们很容易丢失,因此使程序更容易出错 那么,为什么Java没有自己的默认值——UTF-8、GMT、en_-US(是的,我使用的是非en_-US语言环

Java正在从安装它的系统中读取语言环境、时区和编码信息(可能还有更多)

这经常带来不好的惊喜(就在昨天给我带来了一个)。假设您的开发和生产服务器设置为时区GMT+2。然后部署在设置为GMT的生产服务器上。2小时轮班可能不容易立即观察到。尽管您可以将
时区
传递给日历,但API可能正在使用默认时区实例化日历(或日期)

现在,我知道人们应该小心这些设置,但它们很容易丢失,因此使程序更容易出错

那么,为什么Java没有自己的默认值——UTF-8、GMT、en_-US(是的,我使用的是非en_-US语言环境,但将其作为默认值是可以的)。如果需要,应用程序可以通过一些API读取系统设置

因此,项目将更具可预测性


那么,这一决定背后的原因是什么呢?

我可以想象,每当您开发一个运行在服务器上的企业(web)应用程序时,这种需求将被全世界的所有人访问,但对于普通的桌面应用程序来说,这并不是必需的。试着在他们的背景下思考。您不希望让编程不知情的客户机应用程序用户只配置他们的默认系统设置,因为Java有自己的默认设置。作为一名企业(web)应用程序开发人员,您确实需要考虑这些问题。

这不是Java独有的。许多系统默认为系统时区。毕竟,他们还能做什么

时区是一个棘手的问题,特别是当应用程序需要处理多个时区时。这就是为什么像这样的网站都使用UTC


至于你的情况,很难评论,因为描述相当模糊,但听起来这是你的错误。如果您在GMT+2时将日期(不带时区)保存在一个位置,然后在GMT+2时将其加载到另一个位置,则表明您做错了什么。

我不明白为什么有一组额外的默认值会使这更容易。当然,这些默认值仍然需要从某个地方读取——因此它们可能是操作系统的默认值

如果您想影响默认设置,通常可以在启动应用程序时设置系统属性,例如

java -Duser.timezone=Europe/London
等等

就我个人而言,我认为问题不在于违约的选择,而在于它的使用如此容易。即使是Joda Time(我在很多方面都很喜欢它)也很容易意外地使用默认时区。编码等也是如此

编辑:另一个选项是使用
main
方法(或其他早期初始化模块),该方法调用

TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));

对于其他特定于系统的默认设置也是如此。

如果在设置为GMT的生产服务器上部署,明智的默认行为是使用GMT作为默认值。Java应该是一种编程语言和框架,而不是一个系统内的整个操作系统。。维护自己的一组默认设置不是标准库工作的一部分


考虑一下另一种情况:如果java确实为时区之类的东西保持自己的设置,那么每个想要使用不同设置的人(例如GMT中的每个人,大概90%的计算机用户)都想使用java程序,必须手动改变java的时区。对于那些使用Java与其他语言相结合的系统的人来说,这将更加复杂,因为在同一台计算机上,你会有不同的程序,使用不同的(可能不兼容的)设置。

我认为这是因为这对更多的人来说并不令人惊讶

大多数程序(包括其他语言的程序)使用本地部署的时区。多年来一直如此。如果你想要别的东西,你可以覆盖它。想象一下,如果是另一种方式:我们会有同样的问题,相反的方向,但更多的人问

(在时间戳中使用UTC,您不能只使用与历元的偏移量。)

tl;博士 “为什么”与此无关,因为提供此类默认值的框架和API都很常见

始终显式传递
ZoneId
&
Locale
,即使是可选的

LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
……还有

DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( Locale.JAPAN )
指定可选参数 JVM当前的默认时区和区域设置不仅超出了程序员的控制范围(来自主机操作系统或JVM启动参数),而且在运行时随时都可能发生变化。JVM中任何应用程序的任何线程中的任何代码都可以更改默认区域或区域设置

最好始终通过传递可选参数显式指定所需/预期的区域或语言环境,而不是隐式依赖JVM的当前默认值

时区 时区对于确定日期至关重要。在任何一个特定的时刻,世界各地的日期都因地区而异。例如,中午夜后几分钟是新的一天,而中仍然是“昨天”

如果未指定时区,JVM将隐式应用其当前默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/预期时区明确指定为参数

大陆/地区
的格式指定,例如,或
太平洋/奥克兰
。切勿使用3-4个字母的缩写,如
EST
IST
,因为它们不是真正的时区,也不是标准化的,甚至不是唯一的(!)

如果要使用JVM的当前默认时区,请请求它并将其作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好是显式的,因为JVM中任何应用程序的任何线程中的任何代码都可能在运行时的任何时刻更改默认值

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.
场所 生成表示日期时间值的字符串时,该类可以自动本地化

Instant instant = Instant.now();  // Current moment in UTC with a resolution of up to nanoseconds.
ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime zdt = instant.atZone( z );  // Adjust from UTC to a specific time zone. Same moment, different wall-clock time.
要本地化,请指定:

  • 阻止
    Instant instant = Instant.now();  // Current moment in UTC with a resolution of up to nanoseconds.
    ZoneId z = ZoneId.of( "America/Montreal" ); 
    ZonedDateTime zdt = instant.atZone( z );  // Adjust from UTC to a specific time zone. Same moment, different wall-clock time.
    
    ZoneId z = ZoneId.of( "Pacific/Auckland" ); 
    ZonedDateTime zdt = ZonedDateTime.now( z ) ;
    
    Locale l = Locale.CANADA_FRENCH ; 
    DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
    String output = zdt.format( f );