代码之家  ›  专栏  ›  技术社区  ›  Bozho

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

  •  15
  • Bozho  · 技术社区  · 15 年前

    Java正在从安装的系统中读取区域、时区和编码信息(或许更多)。

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

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

    那么,为什么Java没有它自己的默认值——UTF-8、GMT、EnUMUS(是的,我在非EnSUS地区,但拥有它作为默认值)是很好的。如果需要,应用程序可以通过一些API读取系统设置。

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

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

    6 回复  |  直到 7 年前
        1
  •  8
  •   cletus    15 年前

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

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

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

        2
  •  7
  •   Jon Skeet    15 年前

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

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

    java -Duser.timezone=Europe/London
    

    等。

    我个人认为问题不在于 选择 默认情况下-事实上,它使用起来非常容易。即使是Joda Time(我在很多方面都很喜欢)也很容易意外地使用默认时区。编码等也是如此。

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

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

    同样,对于其他特定于系统的默认值。

        3
  •  3
  •   Donal Fellows    15 年前

    我认为这是因为它对更多的人来说不那么令人惊讶。

    大多数程序(包括其他语言的程序)使用本地部署的时区。这种情况已经发生很久了。如果你想要别的东西,你可以覆盖它。想象一下如果是另一种方式:我们会有 相同的 反方向的问题,但被更多人问到。

    (在时间戳中使用UTC,不能只使用从epoch偏移的时间戳。)

        4
  •  2
  •   BalusC    15 年前

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

        5
  •  0
  •   David Z    15 年前

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

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

        6
  •  0
  •   Basil Bourque    7 年前

    DR

    _为什么_是不相关的,因为提供此类默认值的框架和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将隐式应用其当前默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/预期的时区显式指定为参数。

    指定一个 proper time zone name 格式为 continent/region ,如 America/Montreal , Africa/Casablanca Pacific/Auckland . 切勿使用3-4字母缩写,如 EST IST 因为它们是 真正的时区,不标准,甚至不独特!.

    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.
    

    现场

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

    笔记

    另外,请注意,在日期时间处理中,时区和区域设置的问题是不同的、正交的。第一个定义日期时间值的含义或内容,第二个控制其显示格式。您可以使用日文格式的qu_)BEC时刻显示,也可以使用挪威格式的印度时刻显示。

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

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

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

    现在您可以更轻松地使用 Java.时间 通过传递 Clock 班级。这个类本身提供了其他的行为,比如设置一个固定的时间,或者将时钟设置为比实际时间提前或落后那么多小时/分钟。

    您可能需要重写一些代码,以便通过某种类型的 dependency-injection . 这可能和将参数传递给代码的一部分一样简单。

    尽管可以将时区传递给日历,但API可能正在使用默认时区实例化日历(或日期)。

    似乎是一个 非常 程序员忽略时区和区域设置问题的常见问题。可能更多的原因是无知,而不是懒惰。

    这里没有魔法子弹,除了:

    • 向程序员介绍日期时间处理、本地化和 character encoding .
    • 为您的团队设置一个坚持区域、区域设置和字符编码的标准 总是 要明确。如果需要默认值,则显式调用以获取默认值。

    我同意你问题背后的感情。 框架和API不应为时区、区域设置和字符编码提供默认值。 在我看来。依赖这样的默认值而不被程序员考虑是一些最麻烦的、难以捉摸的运行时错误的来源。但是, C_E st la vie 我们必须面对现实。