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

时间:2010-04-18 06:44:14

标签: java

Java正在从安装它的系统中读取区域设置,时区和编码信息(可能还有更多信息)。

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

现在,我知道应该小心这些设置,但容易错过,因此使程序更容易出错。

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

因此,程序将更具可预测性。

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

6 个答案:

答案 0 :(得分:8)

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

时区是一个棘手的问题,特别是当应用程序需要处理多个时区时。这就是为什么像这样的网站将所有内容都放在UTC中。

至于你的情况,很难评论,因为描述相当模糊,但听起来这是你的错误。如果您在GMT + 2的某个地方保存一个日期(没有时区),然后在格林威治标准时加载另一个,那么您就做错了。

答案 1 :(得分:7)

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

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

java -Duser.timezone=Europe/London

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

编辑:另一种选择是使用调用

main方法(或其他早期初始化模块)
TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));

同样适用于其他系统特定的默认值。

答案 2 :(得分:3)

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

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

(使用UTC表示时间戳,你不能只有偏​​离历元的时间。)

答案 3 :(得分:2)

每当您开发在服务器上运行的企业(Web)应用程序时,我可以想象这种需求,该应用程序将由世界上的每个人访问,但普通桌面应用程序不需要这样做。尝试在他们的背景下思考。您不希望让编程不知道的客户端应用程序用户仅配置其默认系统设置,因为Java有自己的默认设置。作为企业(Web)应用程序开发人员,您必须自己考虑这些因素。

答案 4 :(得分:0)

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

考虑另一种情况:如果Java确实为时区等事情保留了自己的设置,那么每个想要使用不同设置的人(例如,不是GMT的人,可能是约90%的计算机用户)想要使用Java程序必须手动更改Java的时区。对于使用Java与其他语言相结合的系统的人来说会更复杂,因为在同一台计算机上使用不同(可能不兼容)的设置会有不同的程序。

答案 5 :(得分:0)

TL;博士

“为什么”是无关紧要的,因为提供此类默认值的框架和API是通用的。

始终通过ZoneId&明确Locale,即使是可选的。

LocalDate.now( ZoneId.of( "Africa/Tunis" ) )

...和...

DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( Locale.JAPAN )

指定可选参数

作为程序员(来自主机操作系统或来自JVM启动参数),JVM当前的默认时区和语言环境不仅在您的控件之外,它们可以在运行时期间随时更改。 JVM中任何应用程序的任何线程中的任何代码都可以更改默认区域或区域设置。

最好始终通过传递可选参数明确指定所需/预期的区域或区域设置,而不是隐式依赖JVM的当前默认值。

时区

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

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

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  
LocalDate today = LocalDate.now( z ) ;

如果要使用JVM的当前默认时区,请求它并作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好是明确的,因为默认情况下可以在运行时期间由JVM中任何应用程序的任何线程中的任何代码随时更改

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

区域设置

生成表示日期时间值的String时,DateTimeFormatter类可以自动进行本地化。

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.

要进行本地化,请指定:

  • FormatStyle确定字符串的长度或缩写。
  • Locale确定(a)翻译日期名称,月份名称等的人类语言,以及(b)决定缩写,大写,标点符号,分隔符等问题的文化规范

示例:

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 );

备注

顺便提一下,请注意,在日期时间处理中,时区和区域设置的问题是不同的,正交的。第一个定义日期时间值的含义或内容,第二个控制其格式以供显示。您可以使用日语格式的魁北克时刻显示,或以挪威格式显示印度时刻。

  

假设您的开发和生产服务器设置为TimeZone GMT + 2。

一般来说,服务器应设置为UTC。但如上所述,永远不要依赖于此。将您期望/预期的时区作为参数传递,即使您希望是UTC。

  

2小时的班次可能不容易立即观察。

现在,您可以通过传递Clock类的替代实现,使用 java.time 更轻松地进行测试。该类本身提供了其他行为,例如设置固定时间,或将时钟设置为真实时间之前或之后的数小时/分钟。

您可能需要重写一些代码,以通过某种dependency-injection来传达区域,区域设置或编码。这可能就像将参数传递给一段代码一样简单。

  

虽然您可以将TimeZone传递到日历,但API可能会使用默认时区实例化日历(或日期)。

似乎是程序员忽略时区和语言环境问题的非常常见问题。可能更多的是因为无知而不是懒惰。

除了:

之外没有灵丹妙药
  • 让您的程序员了解日期时间处理,本地化和character encoding
  • 为您的团队设置标准,坚持区域,区域设置和字符编码始终是明确的。如果需要默认值,请进行显式调用以获取默认值。

我同意你的问题背后的情绪。 我认为框架和API永远不应该为时区,区域设置和字符编码提供默认值。在没有程序员思考的情况下依赖这样的默认值是一些最令人难以捉摸的难以捉摸的运行时错误的根源。但是, c'est la vie ,我们必须按原样处理现实。