Java 中文官方教程 2022 版(二十八)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

方法命名约定

原文:docs.oracle.com/javase/tutorial/datetime/overview/naming.html

日期时间 API 提供了丰富的方法集合,涵盖了丰富的类。在可能的情况下,方法名称在类之间保持一致。例如,许多类提供了一个now方法,用于捕获与该类相关的当前时刻的日期或时间值。还有from方法允许从一个类转换为另一个类。

方法名称前缀也有标准化。由于日期时间 API 中的大多数类都是不可变的,API 不包括set方法。(创建后,不可变对象的值不能更改。set方法的不可变等效方法是with。)以下表列出了常用的前缀:

前缀 方法类型 用途
of 静态工厂 创建一个实例,其中工厂主要验证输入参数,而不是转换它们。
from 静态工厂 将输入参数转换为目标类的实例,这可能涉及从输入中丢失信息。
parse 静态工厂 解析输入字符串以生成目标类的实例。
format 实例 使用指定的格式化程序格式化时间对象中的值以生成字符串。
get 实例 返回目标对象的状态的一部分。
is 实例 查询目标对象的状态。
with 实例 返回目标对象的副本,其中一个元素已更改;这相当于 JavaBean 上的set方法的不可变版本。
plus 实例 返回目标对象的副本,其中添加了一定数量的时间。
minus 实例 返回目标对象的副本,其中减去了一定数量的时间。
to 实例 将此对象转换为另一种类型。
at 实例 将此对象与另一个对象组合。

课程:标准日历

原文:docs.oracle.com/javase/tutorial/datetime/iso/index.html

日期时间 API 的核心是java.time包。java.time中定义的类基于 ISO 日历系统,这是代表日期和时间的世界标准。ISO 日历遵循普罗列普提克格里高利规则。格里高利日历于 1582 年引入;在普罗列普提克格里高利日历中,日期从那时开始向前延伸,以创建一致的、统一的时间线,并简化日期计算。

本课程涵盖以下主题:

概述

这一部分比较了人类时间和机器时间的概念,并提供了java.time包中主要基于时间的类的表格。

星期几和月份枚举

这一部分讨论了定义星期几(DayOfWeek)和定义月份(Month)的枚举。

日期类

这一部分展示了仅处理日期而不涉及时间或时区的基于时间的类。这四个类分别是LocalDateYearMonthMonthDayYear

日期和时间类

这一部分介绍了处理时间和日期时间的LocalTimeLocalDateTime类,但不涉及时区。

时区和偏移类

这一部分讨论了存储时区(或时区偏移)信息的基于时间的类,ZonedDateTimeOffsetDateTimeOffsetTime。还讨论了支持类ZoneIdZoneRulesZoneOffset

Instant 类

这一部分讨论了Instant类,它表示时间轴上的瞬时时刻。

解析和格式化

这一部分概述了如何使用预定义的格式化程序来格式化和解析日期和时间值。

时间包

这一部分概述了java.time.temporal包,支持时间类、字段(TemporalFieldChronoField)和单位(TemporalUnitChronoUnit)。这一部分还解释了如何使用时间调整器来检索调整后的时间值,比如“4 月 11 日后的第一个星期二”,以及如何执行时间查询。

周期和持续时间

这一部分解释了如何计算一段时间,使用了PeriodDuration类,以及ChronoUnit.between方法。

时钟

这一部分简要介绍了Clock类。您可以使用这个类来提供一个替代时钟来替代系统时钟。

非 ISO 日期转换

这一部分解释了如何将 ISO 日历系统中的日期转换为非 ISO 日历系统中的日期,比如JapaneseDateThaiBuddhistDate

遗留日期时间代码

本节提供了一些关于如何将旧的java.util.Datejava.util.Calendar代码转换为日期时间 API 的技巧。

摘要

本节提供了标准日历课程的摘要。

概述

原文:docs.oracle.com/javase/tutorial/datetime/iso/overview.html

有两种基本表示时间的方式。一种方式以人类术语表示时间,称为人类时间,例如年、月、日、小时、分钟和秒。另一种方式,机器时间,连续测量时间沿着时间线从一个起点,称为纪元,以纳秒分辨率。日期时间包提供了丰富的类来表示日期和时间。日期时间 API 中的一些类旨在表示机器时间,而另一些更适合表示人类时间。

首先确定您需要日期和时间的哪些方面,然后选择满足这些需求的类或类。在选择基于时间的类时,首先确定您需要表示人类时间还是机器时间。然后确定您需要表示时间的哪些方面。您需要时区吗?日期时间?仅日期?如果您需要日期,您需要月、日、年,还是一个子集?


术语: 在日期时间 API 中捕获和处理日期或时间值的类,如InstantLocalDateTimeZonedDateTime,在本教程中统称为基于时间的类(或类型)。不包括支持类型,如TemporalAdjuster接口或DayOfWeek枚举。


例如,您可能会使用LocalDate对象来表示出生日期,因为大多数人都在同一天庆祝生日,无论他们是在出生城市还是在国际日期变更线的另一边的地球上。如果您正在跟踪占星时间,那么您可能希望使用LocalDateTime对象来表示出生日期和时间,或者ZonedDateTime,它还包括时区。如果您正在创建时间戳,那么您很可能会使用Instant,它允许您比较时间线上的一个瞬时点和另一个。

以下表格总结了java.time包中存储日期和/或时间信息的基于时间的类,或者可用于测量时间量的类。列中的勾号表示该类使用该特定类型的数据,toString 输出列显示使用toString方法打印的实例。讨论位置列链接到教程中相关页面。

类或枚举 小时 分钟 秒* 时区偏移 时区 ID toString 输出 讨论位置
Instant checked 2013-08-20T15:16:26.355Z Instant 类
LocalDate 已选中 已选中 已选中 2013-08-20 日期类
LocalDateTime 已选中 已选中 已选中 已选中 已选中 已选中 2013-08-20T08:16:26.937 日期和时间类
ZonedDateTime 已选中 已选中 已选中 已选中 已选中 已选中 已选中 已选中 2013-08-21T00:16:26.941+09:00[亚洲/东京] 时区和偏移类
LocalTime 已选中 已选中 已选中 08:16:26.943 日期和时间类
MonthDay 已选中 已选中 --08-20 日期类
Year 已选中 2013 日期类
YearMonth 已选中 已选中 2013-08 日期类
Month 已选中 八月 星期几和月份枚举
OffsetDateTime 已选中 已选中 已选中 已选中 已选中 已选中 已选中 2013-08-20T08:16:26.954-07:00 时区和偏移类
OffsetTime checked checked checked checked 08:16:26.957-07:00 时区和偏移类
Duration ** ** ** checked PT20H (20 小时) 周期和持续时间
Period checked checked checked *** *** P10D (10 天) 周期和持续时间
* 秒被捕捉到纳秒精度。
** 这个类不存储这些信息,但有方法提供这些单位的时间。
*** Period添加到ZonedDateTime时,会考虑夏令时或其他本地时间差异。

DayOfWeek 和 Month 枚举

原文:docs.oracle.com/javase/tutorial/datetime/iso/enum.html

日期时间 API 提供了用于指定星期几和每年月份的枚举。

DayOfWeek

DayOfWeek枚举由描述一周中每天的七个常量组成:MONDAYSUNDAYDayOfWeek常量的整数值范围从 1(星期一)到 7(星期日)。使用定义的常量(DayOfWeek.FRIDAY)可以使您的代码更易读。

这个枚举还提供了许多方法,类似于时间为基础的类提供的方法。例如,以下代码将“Monday”加 3 天并打印结果。输出为“THURSDAY”:

System.out.printf("%s%n", DayOfWeek.MONDAY.plus(3));

使用getDisplayName(TextStyle, Locale)方法,您可以检索一个字符串来标识用户所在地区的星期几。TextStyle枚举允许您指定要显示的字符串类型:FULLNARROW(通常是一个字母)或SHORT(缩写)。STANDALONETextStyle常量在某些语言中使用,当作为日期的一部分时输出不同于单独使用时的输出。以下示例打印了“Monday”三种主要形式的TextStyle

DayOfWeek dow = DayOfWeek.MONDAY;
Locale locale = Locale.getDefault();
System.out.println(dow.getDisplayName(TextStyle.FULL, locale));
System.out.println(dow.getDisplayName(TextStyle.NARROW, locale));
System.out.println(dow.getDisplayName(TextStyle.SHORT, locale));

此代码在en区域设置下的输出如下:

Monday
M
Mon

Month枚举包括了十二个月的常量,从JANUARYDECEMBER。与DayOfWeek枚举一样,Month枚举是强类型的,每个常量的整数值对应 ISO 范围从 1(一月)到 12(十二月)。使用定义的常量(Month.SEPTEMBER)可以使您的代码更易读。

Month枚举还包括许多方法。以下代码使用maxLength方法打印二月份最大可能的天数。输出为“29”:

System.out.printf("%d%n", Month.FEBRUARY.maxLength());

Month枚举还实现了getDisplayName(TextStyle, Locale)方法,使用指定的TextStyle检索一个字符串来标识用户所在地区的月份。如果特定的TextStyle未定义,则返回表示常量数值的字符串。以下代码使用三种主要文本样式打印八月份的月份:

Month month = Month.AUGUST;
Locale locale = Locale.getDefault();
System.out.println(month.getDisplayName(TextStyle.FULL, locale));
System.out.println(month.getDisplayName(TextStyle.NARROW, locale));
System.out.println(month.getDisplayName(TextStyle.SHORT, locale));

此代码在en区域设置下的输出如下:

August
A
Aug

日期类

原文:docs.oracle.com/javase/tutorial/datetime/iso/date.html

日期时间 API 提供了四个专门处理日期信息的类,不考虑时间或时区。这些类的使用建议由类名决定:LocalDateYearMonthMonthDayYear

LocalDate

LocalDate表示 ISO 日历中的年-月-日,用于表示没有时间的日期。您可以使用LocalDate来跟踪重要事件,如出生日期或结婚日期。以下示例使用ofwith方法创建LocalDate实例:

LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
LocalDate nextWed = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));

有关TemporalAdjuster接口的更多信息,请参见时间调整器。

除了通常的方法外,LocalDate类还提供了用于获取有关给定日期信息的 getter 方法。getDayOfWeek方法返回特定日期所在的星期几。例如,以下代码返回"MONDAY":

DayOfWeek dotw = LocalDate.of(2012, Month.JULY, 9).getDayOfWeek();

以下示例使用TemporalAdjuster来获取特定日期后的第一个星期三。

LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20);
TemporalAdjuster adj = TemporalAdjusters.next(DayOfWeek.WEDNESDAY);
LocalDate nextWed = date.with(adj);
System.out.printf("For the date of %s, the next Wednesday is %s.%n",
                  date, nextWed);

运行代码会产生以下结果:

For the date of 2000-11-20, the next Wednesday is 2000-11-22.

Period and Duration 部分还有使用LocalDate类的示例。

YearMonth

YearMonth类表示特定年份的月份。以下示例使用YearMonth.lengthOfMonth()方法来确定几个年份和月份组合的天数。

YearMonth date = YearMonth.now();
System.out.printf("%s: %d%n", date, date.lengthOfMonth());

YearMonth date2 = YearMonth.of(2010, Month.FEBRUARY);
System.out.printf("%s: %d%n", date2, date2.lengthOfMonth());

YearMonth date3 = YearMonth.of(2012, Month.FEBRUARY);
System.out.printf("%s: %d%n", date3, date3.lengthOfMonth());

此代码的输出如下:

2013-06: 30
2010-02: 28
2012-02: 29

MonthDay

MonthDay类表示特定月份的某一天,例如 1 月 1 日的元旦。

以下示例使用MonthDay.isValidYear方法来确定 2010 年 2 月 29 日是否有效。调用返回false,确认 2010 年不是闰年。

MonthDay date = MonthDay.of(Month.FEBRUARY, 29);
boolean validLeapYear = date.isValidYear(2010);

Year

Year类表示一年。以下示例使用Year.isLeap方法来确定给定年份是否是闰年。调用返回true,确认 2012 年是闰年。

boolean validLeapYear = Year.of(2012).isLeap();

日期和时间类

原文:docs.oracle.com/javase/tutorial/datetime/iso/datetime.html

LocalTime

LocalTime类类似于其他以Local为前缀的类,但仅处理时间。这个类非常适用于表示人类时间,比如电影时间,或者当地图书馆的开放和关闭时间。它也可以用来创建数字时钟,就像下面的例子所示:

LocalTime thisSec;

for (;;) {
    thisSec = LocalTime.now();

    // implementation of display code is left to the reader
    display(thisSec.getHour(), thisSec.getMinute(), thisSec.getSecond());
}

LocalTime类不存储时区或夏令时信息。

LocalDateTime

处理日期和时间,不带时区的类是LocalDateTime,是日期时间 API 的核心类之一。这个类用于表示日期(月-日-年)和时间(时-分-秒-纳秒),实际上是LocalDateLocalTime的组合。这个类可以用来表示特定事件,比如 2013 年 8 月 17 日下午 1:10 开始的美洲杯挑战者系列赛首场路易威登杯决赛。请注意,这意味着当地时间下午 1:10。要包含时区,必须使用ZonedDateTimeOffsetDateTime,如时区和偏移类中所讨论的。

除了每个基于时间的类都提供的now方法外,LocalDateTime类还有各种of方法(或以of为前缀的方法)来创建LocalDateTime的实例。还有一个from方法,用于将另一种时间格式的实例转换为LocalDateTime实例。还有用于添加或减去小时、分钟、天、周和月份的方法。下面的例子展示了其中一些方法。日期时间表达式用粗体表示:

System.out.printf("now: %s%n", LocalDateTime.now());

System.out.printf("Apr 15, 1994 @ 11:30am: %s%n",
                  LocalDateTime.of(1994, Month.APRIL, 15, 11, 30));

System.out.printf("now (from Instant): %s%n",
                  LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));

System.out.printf("6 months from now: %s%n",
                  LocalDateTime.now().plusMonths(6));

System.out.printf("6 months ago: %s%n",
                  LocalDateTime.now().minusMonths(6));

这段代码产生的输出看起来类似于以下内容:

now: 2013-07-24T17:13:59.985
Apr 15, 1994 @ 11:30am: 1994-04-15T11:30
now (from Instant): 2013-07-24T17:14:00.479
6 months from now: 2014-01-24T17:14:00.480
6 months ago: 2013-01-24T17:14:00.481

时区和偏移类

docs.oracle.com/javase/tutorial/datetime/iso/timezones.html

时区是地球上使用相同标准时间的区域。每个时区由一个标识符描述,通常具有region/cityAsia/Tokyo)的格式和与格林威治/协调世界时的偏移。例如,东京的偏移是+09:00

ZoneId 和 ZoneOffset

日期时间 API 提供了两个用于指定时区或偏移的类:

  • ZoneId指定了一个时区标识符,并提供了在InstantLocalDateTime之间转换的规则。

  • ZoneOffset指定了与格林威治/协调世界时的时区偏移。

从格林威治/协调世界时的偏移通常以整小时定义,但也有例外情况。以下代码来自TimeZoneId示例,打印了所有使用非整小时定义的格林威治/协调世界时偏移的时区列表。

Set<String> allZones = ZoneId.getAvailableZoneIds();
LocalDateTime dt = LocalDateTime.now();

// Create a List using the set of zones and sort it.
List<String> zoneList = new ArrayList<String>(allZones);
Collections.sort(zoneList);

...

for (String s : zoneList) {
    ZoneId zone = ZoneId.of(s);
    ZonedDateTime zdt = dt.atZone(zone);
    ZoneOffset offset = zdt.getOffset();
    int secondsOfHour = offset.getTotalSeconds() % (60 * 60);
    String out = String.format("%35s %10s%n", zone, offset);

    // Write only time zones that do not have a whole hour offset
    // to standard out.
    if (secondsOfHour != 0) {
        System.out.printf(out);
    }
    ...
}

此示例将以下列表打印到标准输出:

      America/Caracas     -04:30
     America/St_Johns     -02:30
        Asia/Calcutta     +05:30
         Asia/Colombo     +05:30
           Asia/Kabul     +04:30
       Asia/Kathmandu     +05:45
        Asia/Katmandu     +05:45
         Asia/Kolkata     +05:30
         Asia/Rangoon     +06:30
          Asia/Tehran     +04:30
   Australia/Adelaide     +09:30
Australia/Broken_Hill     +09:30
     Australia/Darwin     +09:30
      Australia/Eucla     +08:45
        Australia/LHI     +10:30
  Australia/Lord_Howe     +10:30
      Australia/North     +09:30
      Australia/South     +09:30
 Australia/Yancowinna     +09:30
  Canada/Newfoundland     -02:30
         Indian/Cocos     +06:30
                 Iran     +04:30
              NZ-CHAT     +12:45
      Pacific/Chatham     +12:45
    Pacific/Marquesas     -09:30
      Pacific/Norfolk     +11:30

TimeZoneId示例还会将所有时区 ID 打印到名为timeZones的文件中。

日期时间类

日期时间 API 提供了三个基于时间的类,可以与时区一起使用:

  • ZonedDateTime处理具有与格林威治/协调世界时的相应时区偏移的日期和时间。

  • OffsetDateTime处理具有与格林威治/协调世界时的相应时区偏移的日期和时间,没有时区 ID。

  • OffsetTime处理具有与格林威治/协调世界时的相应时区偏移的时间,没有时区 ID。

何时使用OffsetDateTime而不是ZonedDateTime?如果您正在编写基于地理位置的自己的日期和时间计算规则的复杂软件,或者如果您正在存储仅跟踪与格林威治/协调世界时的绝对偏移的时间戳的数据库,那么您可能希望使用OffsetDateTime。此外,XML 和其他网络格式将日期时间传输定义为OffsetDateTimeOffsetTime

尽管所有三个类都保持与格林威治/协调世界时的偏移,但只有ZonedDateTime使用ZoneRules,这是java.time.zone包的一部分,用于确定特定时区的偏移如何变化。例如,大多数时区在将时钟向前调整到夏令时时会经历一个间隙(通常为 1 小时),在将时钟调回标准时间和过渡前最后一个小时重复时会发生时间重叠。ZonedDateTime类适应了这种情况,而没有访问ZoneRulesOffsetDateTimeOffsetTime类则没有。

ZonedDateTime

ZonedDateTime类实际上将LocalDateTime类与ZoneId类结合在一起。它用于表示完整的日期(年、月、日)和时间(时、分、秒、纳秒)以及时区(地区/城市,如Europe/Paris)。

以下代码来自Flight示例,定义了从旧金山飞往东京的航班的出发时间为美国/洛杉矶时区的ZonedDateTime。使用withZoneSameInstantplusMinutes方法创建一个代表东京预计到达时间的ZonedDateTime实例,经过 650 分钟的飞行。ZoneRules.isDaylightSavings方法确定航班到达东京时是否为夏令时。

DateTimeFormatter对象用于格式化ZonedDateTime实例以进行打印:

DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy  hh:mm a");

// Leaving from San Francisco on July 20, 2013, at 7:30 p.m.
LocalDateTime leaving = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneId leavingZone = ZoneId.of("America/Los_Angeles"); 
ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone);

try {
    String out1 = departure.format(format);
    System.out.printf("LEAVING:  %s (%s)%n", out1, leavingZone);
} catch (DateTimeException exc) {
    System.out.printf("%s can't be formatted!%n", departure);
    throw exc;
}

// Flight is 10 hours and 50 minutes, or 650 minutes
ZoneId arrivingZone = ZoneId.of("Asia/Tokyo"); 
ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone)
                                 .plusMinutes(650);

try {
    String out2 = arrival.format(format);
    System.out.printf("ARRIVING: %s (%s)%n", out2, arrivingZone);
} catch (DateTimeException exc) {
    System.out.printf("%s can't be formatted!%n", arrival);
    throw exc;
}

if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant())) 
    System.out.printf("  (%s daylight saving time will be in effect.)%n",
                      arrivingZone);
else
    System.out.printf("  (%s standard time will be in effect.)%n",
                      arrivingZone);

这将产生以下输出:

LEAVING:  Jul 20 2013  07:30 PM (America/Los_Angeles)
ARRIVING: Jul 21 2013  10:20 PM (Asia/Tokyo)
  (Asia/Tokyo standard time will be in effect.)

OffsetDateTime

OffsetDateTime类实际上将LocalDateTime类与ZoneOffset类结合在一起。它用于表示完整的日期(年、月、日)和时间(时、分、秒、纳秒)以及与格林威治/UTC 时间的偏移(+/-小时:分钟,如+06:00-08:00)。

以下示例使用OffsetDateTimeTemporalAdjuster.lastDay方法找到 2013 年 7 月最后一个星期四。

// Find the last Thursday in July 2013.
LocalDateTime localDate = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneOffset offset = ZoneOffset.of("-08:00");

OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset);
OffsetDateTime lastThursday =
        offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
System.out.printf("The last Thursday in July 2013 is the %sth.%n",
                   lastThursday.getDayOfMonth());

运行此代码的输出为:

The last Thursday in July 2013 is the 25th.

OffsetTime

OffsetTime类实际上将LocalTime类与ZoneOffset类结合在一起。它用于表示时间(时、分、秒、纳秒)以及与格林威治/UTC 时间的偏移(+/-小时:分钟,如+06:00-08:00)。

OffsetTime类在与OffsetDateTime类相同的情况下使用,但不需要跟踪日期时使用。

Instant 类

原文:docs.oracle.com/javase/tutorial/datetime/iso/instant.html

日期时间 API 的核心类之一是Instant类,它表示时间轴上纳秒的开始。此类对于生成时间戳以表示机器时间很有用。

import java.time.Instant;

Instant timestamp = Instant.now();

Instant类返回的值从 1970 年 1 月 1 日的第一秒开始计时(1970-01-01T00:00:00Z),也称为EPOCH。在纪元之前发生的瞬间具有负值,在纪元之后发生的瞬间具有正值。

Instant类提供的其他常量是MIN,表示最小可能的(遥远的过去)瞬间,以及MAX,表示最大(遥远的未来)瞬间。

调用Instant上的toString会产生以下输出:

2013-05-30T23:38:23.085Z

此格式遵循ISO-8601用于表示日期和时间的标准。

Instant类提供了各种方法来操作Instant。有用于添加或减去时间的plusminus方法。以下代码将当前时间加 1 小时:

Instant oneHourLater = Instant.now().plus(1, ChronoUnit.HOURS);

有用于比较瞬间的方法,例如isAfterisBeforeuntil方法返回两个Instant对象之间存在多少时间。以下代码报告自 Java 纪元开始以来经过了多少秒。

long secondsFromEpoch = Instant.ofEpochSecond(0L).until(Instant.now(),
                        ChronoUnit.SECONDS);

Instant类不适用于年、月或日等人类时间单位。如果要在这些单位中执行计算,可以通过将Instant与时区绑定将Instant转换为另一个类,例如LocalDateTimeZonedDateTime。然后可以访问所需单位的值。以下代码使用ofInstant方法和默认时区将Instant转换为LocalDateTime对象,然后以更可读的形式打印出日期和时间:

Instant timestamp;
...
LocalDateTime ldt = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());
System.out.printf("%s %d %d at %d:%d%n", ldt.getMonth(), ldt.getDayOfMonth(),
                  ldt.getYear(), ldt.getHour(), ldt.getMinute());

输出将类似于以下内容:

MAY 30 2013 at 18:21

ZonedDateTimeOffsetTimeZone 对象都可以转换为 Instant 对象,因为每个对象都映射到时间线上的确切时刻。然而,反过来却不成立。要将 Instant 对象转换为 ZonedDateTimeOffsetDateTime 对象,需要提供时区或时区偏移信息。

解析和格式化

原文:docs.oracle.com/javase/tutorial/datetime/iso/format.html

日期时间 API 中基于时间的类提供了用于解析包含日期和时间信息的字符串的parse方法。这些类还提供了用于为显示格式化基于时间的对象的format方法。在这两种情况下,过程是相似的:您提供一个模式给DateTimeFormatter来创建一个格式化程序对象。然后将此格式化程序传递给parseformat方法。

DateTimeFormatter 类提供了许多预定义的格式化程序,或者您可以定义自己的格式化程序。

如果在转换过程中出现问题,parseformat 方法会抛出异常。因此,您的解析代码应捕获DateTimeParseException错误,您的格式化代码应捕获DateTimeException错误。有关异常处理的更多信息,请参阅捕获和处理异常。

DateTimeFormatter 类既是不可变的又是线程安全的;在适当的情况下,应该将其分配给静态常量。


版本说明: java.time 日期时间对象可以直接与java.util.FormatterString.format一起使用,方法是使用与旧的java.util.Datejava.util.Calendar类一起使用的熟悉基于模式的格式化。


解析

LocalDate 类中的单参数parse(CharSequence)方法使用ISO_LOCAL_DATE格式化程序。要指定不同的格式化程序,可以使用两个参数的parse(CharSequence, DateTimeFormatter)方法。以下示例使用预定义的BASIC_ISO_DATE格式化程序,该格式使用19590709表示 1959 年 7 月 9 日。

String in = ...;
LocalDate date = LocalDate.parse(in, DateTimeFormatter.BASIC_ISO_DATE);

您还可以使用自己的模式定义格式化程序。以下代码来自Parse示例,创建了一个应用格式为"MMM d yyyy"的格式化程序。该格式指定三个字符表示月份,一个数字表示日期,四个数字表示年份。使用此模式创建的格式化程序将识别诸如"Jan 3 2003"或"Mar 23 1994"之类的字符串。但是,要将格式指定为"MMM dd yyyy",日期的两个字符,则您必须始终使用两个字符,对于一位数日期,用零填充:"Jun 03 2003"。

String input = ...;
try {
    DateTimeFormatter formatter =
                      DateTimeFormatter.ofPattern("MMM d yyyy");
    LocalDate date = LocalDate.parse(input, formatter);
    System.out.printf("%s%n", date);
}
catch (DateTimeParseException exc) {
    System.out.printf("%s is not parsable!%n", input);
    throw exc;      // Rethrow the exception.
}
// 'date' has been successfully parsed

DateTimeFormatter 类的文档指定了您可以使用的符号的完整列表来指定格式化或解析的模式。

非 ISO 日期转换页面上的StringConverter示例提供了另一个日期格式化的示例。

格式化

format(DateTimeFormatter) 方法使用指定的格式将基于时间的对象转换为字符串表示。以下代码来自 Flight 示例,使用格式"MMM d yyy hh:mm a"转换了一个ZonedDateTime实例。日期的定义方式与之前的解析示例相同,但此模式还包括小时、分钟以及上午和下午组件。

ZoneId leavingZone = ...;
ZonedDateTime departure = ...;

try {
    DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy  hh:mm a");
    String out = departure.format(format);
    System.out.printf("LEAVING:  %s (%s)%n", out, leavingZone);
}
catch (DateTimeException exc) {
    System.out.printf("%s can't be formatted!%n", departure);
    throw exc;
}

这个示例的输出,打印了到达和离开时间,如下所示:

LEAVING:  Jul 20 2013  07:30 PM (America/Los_Angeles)
ARRIVING: Jul 21 2013  10:20 PM (Asia/Tokyo)

时间包

原文:docs.oracle.com/javase/tutorial/datetime/iso/temporal.html

java.time.temporal包提供了支持日期和时间代码的一组接口、类和枚举,特别是日期和时间计算。

这些接口旨在在最低级别使用。典型的应用代码应该根据具体类型(如LocalDateZonedDateTime)声明变量和参数,而不是根据Temporal接口。这与声明类型为String而不是CharSequence的变量完全相同。

时间和 TemporalAccessor

Temporal接口提供了访问基于时间的对象的框架,并由基于时间的类(如InstantLocalDateTimeZonedDateTime)实现。该接口提供了添加或减去时间单位的方法,使得基于时间的算术在各种日期和时间类之间变得简单和一致。TemporalAccessor接口提供了Temporal接口的只读版本。

TemporalTemporalAccessor对象都是根据字段定义的,如TemporalField接口中所指定的。ChronoField枚举是TemporalField接口的具体实现,并提供了一组定义的常量,如DAY_OF_WEEKMINUTE_OF_HOURMONTH_OF_YEAR

这些字段的单位由TemporalUnit接口指定。ChronoUnit枚举实现了TemporalUnit接口。字段ChronoField.DAY_OF_WEEKChronoUnit.DAYSChronoUnit.WEEKS的组合。ChronoFieldChronoUnit枚举在以下部分讨论。

Temporal接口中基于算术的方法需要根据TemporalAmount值定义的参数。PeriodDuration类(在 Period and Duration 中讨论)实现了TemporalAmount接口。

ChronoField 和 IsoFields

实现了TemporalField接口的ChronoField枚举提供了一组丰富的常量,用于访问日期和时间值。一些示例包括CLOCK_HOUR_OF_DAYNANO_OF_DAYDAY_OF_YEAR。这个枚举可以用来表达时间的概念方面,比如一年中的第三周、一天中的第 11 小时或一个月中的第一个星期一。当你遇到一个未知类型的Temporal时,可以使用TemporalAccessor.isSupported(TemporalField)方法来确定Temporal是否支持特定字段。下面这行代码返回false,表示LocalDate不支持ChronoField.CLOCK_HOUR_OF_DAY

boolean isSupported = LocalDate.now().isSupported(ChronoField.CLOCK_HOUR_OF_DAY);

针对 ISO-8601 日历系统的特定字段在IsoFields类中定义。以下示例展示了如何使用ChronoFieldIsoFields获取字段的值:

time.get(ChronoField.MILLI_OF_SECOND)
int qoy = date.get(IsoFields.QUARTER_OF_YEAR);

另外两个类定义了可能有用的额外字段,WeekFieldsJulianFields

ChronoUnit

实现了TemporalUnit接口的ChronoUnit枚举提供了一组基于日期和时间的标准单位,从毫秒到千年。请注意,并非所有的ChronoUnit对象都被所有类支持。例如,Instant类不支持ChronoUnit.MONTHSChronoUnit.YEARS。日期时间 API 中的类包含isSupported(TemporalUnit)方法,可用于验证一个类是否支持特定的时间单位。下面对isSupported的调用返回false,确认Instant类不支持ChronoUnit.DAYS

Instant instant = Instant.now();
boolean isSupported = instant.isSupported(ChronoUnit.DAYS);

时间调整器

原文:docs.oracle.com/javase/tutorial/datetime/iso/adjusters.html

TemporalAdjuster接口,位于java.time.temporal包中,提供了接受Temporal值并返回调整后值的方法。这些调整器可以与任何基于时间的类型一起使用。

如果将调整器与ZonedDateTime一起使用,则会计算出一个新日期,保留原始的时间和时区值。

预定义调整器

TemporalAdjusters 类(注意复数形式)提供了一组预定义的调整器,用于查找月份的第一天或最后一天,年份的第一天或最后一天,月份的最后一个星期三,或特定日期后的第一个星期二等等。这些预定义的调整器被定义为静态方法,并设计用于与静态导入语句一起使用。

以下示例结合了几个TemporalAdjusters方法,与基于时间的类中定义的with方法一起,根据 2000 年 10 月 15 日的原始日期计算新日期:

LocalDate date = LocalDate.of(2000, Month.OCTOBER, 15);
DayOfWeek dotw = date.getDayOfWeek();
System.out.printf("%s is on a %s%n", date, dotw);

System.out.printf("first day of Month: %s%n",
                  date.with(TemporalAdjusters.firstDayOfMonth()));
System.out.printf("first Monday of Month: %s%n",
                  date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
System.out.printf("last day of Month: %s%n",
                  date.with(TemporalAdjusters.lastDayOfMonth()));
System.out.printf("first day of next Month: %s%n",
                  date.with(TemporalAdjusters.firstDayOfNextMonth()));
System.out.printf("first day of next Year: %s%n",
                  date.with(TemporalAdjusters.firstDayOfNextYear()));
System.out.printf("first day of Year: %s%n",
                  date.with(TemporalAdjusters.firstDayOfYear()));

这将产生以下输出:

2000-10-15 is on a SUNDAY
first day of Month: 2000-10-01
first Monday of Month: 2000-10-02
last day of Month: 2000-10-31
first day of next Month: 2000-11-01
first day of next Year: 2001-01-01
first day of Year: 2000-01-01

自定义调整器

您还可以创建自定义调整器。为此,您需要创建一个实现了TemporalAdjuster接口并带有adjustInto(Temporal)方法的类。来自NextPayday示例的PaydayAdjuster类是一个自定义调整器。PaydayAdjuster评估传入的日期并返回下一个发薪日,假设发薪日每月发生两次:在每月的 15 日和最后一天。如果计算出的日期落在周末,则使用前一个星期五。假定当前日历年。

/**
 * The adjustInto method accepts a Temporal instance 
 * and returns an adjusted LocalDate. If the passed in
 * parameter is not a LocalDate, then a DateTimeException is thrown.
 */
public Temporal adjustInto(Temporal input) {
    LocalDate date = LocalDate.from(input);
    int day;
    if (date.getDayOfMonth() < 15) {
        day = 15;
    } else {
        day = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
    }
    date = date.withDayOfMonth(day);
    if (date.getDayOfWeek() == DayOfWeek.SATURDAY ||
        date.getDayOfWeek() == DayOfWeek.SUNDAY) {
        date = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
    }

    return input.with(date);
}

调整器的调用方式与预定义调整器相同,使用with方法。以下代码行来自NextPayday示例:

LocalDate nextPayday = date.with(new PaydayAdjuster());

在 2013 年,6 月 15 日和 6 月 30 日都落在周末。使用 2013 年 6 月 3 日和 6 月 18 日的相应日期运行NextPayday示例,得到以下结果:

Given the date:  2013 Jun 3
the next payday: 2013 Jun 14

Given the date:  2013 Jun 18
the next payday: 2013 Jun 28

时间查询

原文:docs.oracle.com/javase/tutorial/datetime/iso/queries.html

TemporalQuery可用于从基于时间的对象中检索信息。

预定义查询

TemporalQueries类(注意是复数形式)提供了几个预定义查询,包括在应用程序无法识别基于时间的对象类型时有用的方法。与调整器一样,预定义查询被定义为静态方法,并设计用于与静态导入语句一起使用。

例如,precision查询返回特定基于时间的对象可以返回的最小ChronoUnit。以下示例在几种类型的基于时间的对象上使用了precision查询:

TemporalQuery<TemporalUnit> query = TemporalQueries.precision();
System.out.printf("LocalDate precision is %s%n",
                  LocalDate.now().query(query));
System.out.printf("LocalDateTime precision is %s%n",
                  LocalDateTime.now().query(query));
System.out.printf("Year precision is %s%n",
                  Year.now().query(query));
System.out.printf("YearMonth precision is %s%n",
                  YearMonth.now().query(query));
System.out.printf("Instant precision is %s%n",
                  Instant.now().query(query));

输出如下所示:

LocalDate precision is Days
LocalDateTime precision is Nanos
Year precision is Years
YearMonth precision is Months
Instant precision is Nanos

自定义查询

您还可以创建自定义查询。一种方法是创建一个实现了TemporalQuery接口的类,并使用queryFrom(TemporalAccessor)方法。CheckDate示例实现了两个自定义查询。第一个自定义查询可以在FamilyVacations类中找到,该类实现了TemporalQuery接口。queryFrom方法将传入的日期与计划的假期日期进行比较,并在日期范围内返回TRUE

// Returns true if the passed-in date occurs during one of the
// family vacations. Because the query compares the month and day only,
// the check succeeds even if the Temporal types are not the same.
public Boolean queryFrom(TemporalAccessor date) {
    int month = date.get(ChronoField.MONTH_OF_YEAR);
    int day   = date.get(ChronoField.DAY_OF_MONTH);

    // Disneyland over Spring Break
    if ((month == Month.APRIL.getValue()) && ((day >= 3) && (day <= 8)))
        return Boolean.TRUE;

    // Smith family reunion on Lake Saugatuck
    if ((month == Month.AUGUST.getValue()) && ((day >= 8) && (day <= 14)))
        return Boolean.TRUE;

    return Boolean.FALSE;
}

第二个自定义查询在FamilyBirthdays类中实现。该类提供了一个isFamilyBirthday方法,用于将传入的日期与几个生日进行比较,如果匹配则返回TRUE

// Returns true if the passed-in date is the same as one of the
// family birthdays. Because the query compares the month and day only,
// the check succeeds even if the Temporal types are not the same.
public static Boolean isFamilyBirthday(TemporalAccessor date) {
    int month = date.get(ChronoField.MONTH_OF_YEAR);
    int day   = date.get(ChronoField.DAY_OF_MONTH);

    // Angie's birthday is on April 3.
    if ((month == Month.APRIL.getValue()) && (day == 3))
        return Boolean.TRUE;

    // Sue's birthday is on June 18.
    if ((month == Month.JUNE.getValue()) && (day == 18))
        return Boolean.TRUE;

    // Joe's birthday is on May 29.
    if ((month == Month.MAY.getValue()) && (day == 29))
        return Boolean.TRUE;

    return Boolean.FALSE;
}

FamilyBirthday类未实现TemporalQuery接口,可作为 lambda 表达式的一部分使用。来自CheckDate示例的以下代码展示了如何调用两个自定义查询。

// Invoking the query without using a lambda expression.
Boolean isFamilyVacation = date.query(new FamilyVacations());

// Invoking the query using a lambda expression.
Boolean isFamilyBirthday = date.query(FamilyBirthdays::isFamilyBirthday);

if (isFamilyVacation.booleanValue() || isFamilyBirthday.booleanValue())
    System.out.printf("%s is an important date!%n", date);
else
    System.out.printf("%s is not an important date.%n", date);

Period 和 Duration

原文:docs.oracle.com/javase/tutorial/datetime/iso/period.html

当编写代码来指定一段时间时,请使用最符合您需求的类或方法:Duration类、Period类或ChronoUnit.between方法。Duration使用基于时间的值(秒、纳秒)来衡量一段时间。Period使用基于日期的值(年、月、日)。


注意: 一天的Duration确切地为 24 小时。将一天的Period添加到ZonedDateTime中时,根据时区可能会有所变化。例如,如果发生在夏令时的第一天或最后一天。


Duration

一个Duration在衡量机器时间的情况下最为适用,比如使用Instant对象的代码。Duration对象以秒或纳秒为单位进行衡量,不使用年、月和日等基于日期的构造,尽管该类提供了将时间转换为天、小时和分钟的方法。Duration可以具有负值,如果创建时结束点早于起始点。

以下代码计算两个瞬间之间的持续时间(以纳秒为单位):

Instant t1, t2;
...
long ns = Duration.between(t1, t2).toNanos();

以下代码将 10 秒添加到一个Instant中:

Instant start;
...
Duration gap = Duration.ofSeconds(10);
Instant later = start.plus(gap);

一个Duration与时间线无关,即它不跟踪时区或夏令时。将相当于 1 天的Duration添加到ZonedDateTime中,结果将确切地添加 24 小时,而不考虑夏令时或其他可能导致的时间差异。

ChronoUnit

讨论 Temporal Package 中的ChronoUnit枚举定义了用于衡量时间的单位。当您想要以单个时间单位(如天或秒)衡量一段时间时,ChronoUnit.between方法非常有用。between方法适用于所有基于时间的对象,但仅以单个单位返回时间量。以下代码计算两个时间戳之间的间隔,以毫秒为单位:

import java.time.Instant;
import java.time.temporal.Temporal;
import java.time.temporal.ChronoUnit;

Instant previous, current, gap;
...
current = Instant.now();
if (previous != null) {
    gap = ChronoUnit.MILLIS.between(previous,current);
}
...

Period

要使用基于日期的值(年、月、日)来定义一段时间,请使用Period类。Period类提供了各种get方法,比如getMonthsgetDaysgetYears,以便您可以从期间中提取时间量。

所有三个单位(月、日和年)一起表示的总时间段。要以单个时间单位(如天)表示测量的时间量,可以使用ChronoUnit.between方法。

以下代码报告了你的年龄,假设你出生于 1960 年 1 月 1 日。使用Period类来确定年、月和日的时间。使用ChronoUnit.between方法确定相同的时间段,以总天数显示在括号中:

LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);

Period p = Period.between(birthday, today);
long p2 = ChronoUnit.DAYS.between(birthday, today);
System.out.println("You are " + p.getYears() + " years, " + p.getMonths() +
                   " months, and " + p.getDays() +
                   " days old. (" + p2 + " days total)");

该代码生成类似以下的输出:

You are 53 years, 4 months, and 29 days old. (19508 days total)

要计算距离你下一个生日还有多长时间,可以使用来自Birthday示例的以下代码。使用Period类来确定月和日的值。ChronoUnit.between方法返回总天数的值,并显示在括号中。

LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);

LocalDate nextBDay = birthday.withYear(today.getYear());

//If your birthday has occurred this year already, add 1 to the year.
if (nextBDay.isBefore(today) || nextBDay.isEqual(today)) {
    nextBDay = nextBDay.plusYears(1);
}

Period p = Period.between(today, nextBDay);
long p2 = ChronoUnit.DAYS.between(today, nextBDay);
System.out.println("There are " + p.getMonths() + " months, and " +
                   p.getDays() + " days until your next birthday. (" +
                   p2 + " total)");

该代码生成类似以下的输出:

There are 7 months, and 2 days until your next birthday. (216 total)

这些计算没有考虑时区差异。例如,如果你出生在澳大利亚,但目前居住在班加罗尔,这会稍微影响你确切年龄的计算。在这种情况下,可以结合使用PeriodZonedDateTime类。当你将Period添加到ZonedDateTime时,会考虑时差。

时钟

原文:docs.oracle.com/javase/tutorial/datetime/iso/clock.html

大多数基于时间的对象提供一个无参数的now()方法,使用系统时钟和默认时区提供当前日期和时间。这些基于时间的对象还提供一个带有一个参数的now(Clock)方法,允许您传入一个替代的Clock

当前日期和时间取决于时区,对于全球化应用程序,需要一个Clock来确保日期/时间是使用正确的时区创建的。因此,虽然使用Clock类是可选的,但这个特性允许您测试您的代码是否适用于其他时区,或者通过使用一个固定的时钟,时间不会改变。

Clock类是抽象的,因此您不能创建它的实例。以下工厂方法对于测试很有用。

非 ISO 日期转换

原文:docs.oracle.com/javase/tutorial/datetime/iso/nonIso.html

本教程不会详细讨论java.time.chrono包。但是,了解到该包提供了几个不基于 ISO 的预定义年表,如日本、伊斯兰、民国和泰国佛教。您还可以使用此包创建自己的年表。

本节将向您展示如何在 ISO 日期和其他预定义年表日期之间进行转换。

转换为非 ISO 日期

您可以使用from(TemporalAccessor)方法将 ISO 日期转换为其他年表的日期,例如JapaneseDate.from(TemporalAccessor)。如果无法将日期转换为有效实例,则此方法会抛出DateTimeException。以下代码将LocalDateTime实例转换为几个预定义的非 ISO 日历日期:

LocalDateTime date = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
JapaneseDate jdate     = JapaneseDate.from(date);
HijrahDate hdate       = HijrahDate.from(date);
MinguoDate mdate       = MinguoDate.from(date);
ThaiBuddhistDate tdate = ThaiBuddhistDate.from(date);

StringConverter示例将从LocalDate转换为ChronoLocalDate再转换为String,然后再转回去。toString方法接受LocalDate实例和Chronology,并通过提供的Chronology返回转换后的字符串。DateTimeFormatterBuilder用于构建可用于打印日期的字符串:

/**
 * Converts a LocalDate (ISO) value to a ChronoLocalDate date
 * using the provided Chronology, and then formats the
 * ChronoLocalDate to a String using a DateTimeFormatter with a
 * SHORT pattern based on the Chronology and the current Locale.
 *
 * @param localDate - the ISO date to convert and format.
 * @param chrono - an optional Chronology. If null, then IsoChronology is used.
 */
public static String toString(LocalDate localDate, Chronology chrono) {
    if (localDate != null) {
        Locale locale = Locale.getDefault(Locale.Category.FORMAT);
        ChronoLocalDate cDate;
        if (chrono == null) {
            chrono = IsoChronology.INSTANCE;
        }
        try {
            cDate = chrono.date(localDate);
        } catch (DateTimeException ex) {
            System.err.println(ex);
            chrono = IsoChronology.INSTANCE;
            cDate = localDate;
        }
        DateTimeFormatter dateFormatter =
            DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
                             .withLocale(locale)
                             .withChronology(chrono)
                             .withDecimalStyle(DecimalStyle.of(locale));
        String pattern = "M/d/yyyy GGGGG";
        return dateFormatter.format(cDate);
    } else {
        return "";
    }
}

当使用以下日期调用预定义年表的方法时:

LocalDate date = LocalDate.of(1996, Month.OCTOBER, 29);
System.out.printf("%s%n",
     StringConverter.toString(date, JapaneseChronology.INSTANCE));
System.out.printf("%s%n",
     StringConverter.toString(date, MinguoChronology.INSTANCE));
System.out.printf("%s%n",
     StringConverter.toString(date, ThaiBuddhistChronology.INSTANCE));
System.out.printf("%s%n",
     StringConverter.toString(date, HijrahChronology.INSTANCE));

输出如下:

10/29/0008 H
10/29/0085 1
10/29/2539 B.E.
6/16/1417 1

转换为基于 ISO 的日期

您可以使用静态LocalDate.from方法将非 ISO 日期转换为LocalDate实例,如下例所示:

LocalDate date = LocalDate.from(JapaneseDate.now());

其他基于时间的类也提供此方法,如果日期无法转换,则会抛出DateTimeException

来自StringConverter示例的fromString方法解析包含非 ISO 日期的String并返回LocalDate实例。

/**
 * Parses a String to a ChronoLocalDate using a DateTimeFormatter
 * with a short pattern based on the current Locale and the
 * provided Chronology, then converts this to a LocalDate (ISO)
 * value.
 *
 * @param text   - the input date text in the SHORT format expected
 *                 for the Chronology and the current Locale.
 *
 * @param chrono - an optional Chronology. If null, then IsoChronology
 *                 is used.
 */
public static LocalDate fromString(String text, Chronology chrono) {
    if (text != null && !text.isEmpty()) {
        Locale locale = Locale.getDefault(Locale.Category.FORMAT);
        if (chrono == null) {
           chrono = IsoChronology.INSTANCE;
        }
        String pattern = "M/d/yyyy GGGGG";
        DateTimeFormatter df = new DateTimeFormatterBuilder().parseLenient()
                              .appendPattern(pattern)
                              .toFormatter()
                              .withChronology(chrono)
                              .withDecimalStyle(DecimalStyle.of(locale));
        TemporalAccessor temporal = df.parse(text);
        ChronoLocalDate cDate = chrono.date(temporal);
        return LocalDate.from(cDate);
    }
return null;
}

当使用以下字符串调用方法时:

System.out.printf("%s%n", StringConverter.fromString("10/29/0008 H",
    JapaneseChronology.INSTANCE));
System.out.printf("%s%n", StringConverter.fromString("10/29/0085 1",
    MinguoChronology.INSTANCE));
System.out.printf("%s%n", StringConverter.fromString("10/29/2539 B.E.",
    ThaiBuddhistChronology.INSTANCE));
System.out.printf("%s%n", StringConverter.fromString("6/16/1417 1",
    HijrahChronology.INSTANCE));

打印的字符串应该都能转换回 1996 年 10 月 29 日:

1996-10-29
1996-10-29
1996-10-29
1996-10-29

传统日期时间代码

原文:docs.oracle.com/javase/tutorial/datetime/iso/legacy.html

在 Java SE 8 发布之前,Java 日期和时间机制由 java.util.Datejava.util.Calendarjava.util.TimeZone 类以及它们的子类,如 java.util.GregorianCalendar 提供。这些类有几个缺点,包括:

  • Calendar 类不是类型安全的。

  • 由于这些类是可变的,它们不能在多线程应用程序中使用。

  • 应用程序代码中常见的错误是由于月份编号不寻常和缺乏类型安全性。

与传统代码的互操作性

也许您有使用 java.util 日期和时间类的传统代码,并且希望在最小更改代码的情况下利用 java.time 功能。

JDK 8 发布中添加了几种允许在 java.utiljava.time 对象之间进行转换的方法:

以下示例将 Calendar 实例转换为 ZonedDateTime 实例。请注意,必须提供一个时区来从 Instant 转换为 ZonedDateTime

Calendar now = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault()));

以下示例展示了 DateInstant 之间的转换:

Instant inst = date.toInstant();

Date newDate = Date.from(inst);

以下示例将从 GregorianCalendar 转换为 ZonedDateTime,然后从 ZonedDateTime 转换为 GregorianCalendar。其他基于时间的类是使用 ZonedDateTime 实例创建的:

GregorianCalendar cal = ...;

TimeZone tz = cal.getTimeZone();
int tzoffset = cal.get(Calendar.ZONE_OFFSET);

ZonedDateTime zdt = cal.toZonedDateTime();

GregorianCalendar newCal = GregorianCalendar.from(zdt);

LocalDateTime ldt = zdt.toLocalDateTime();
LocalDate date = zdt.toLocalDate();
LocalTime time = zdt.toLocalTime();

将 java.util 日期和时间功能映射到 java.time

由于在 Java SE 8 发布中完全重新设计了日期和时间的 Java 实现,您不能将一个方法替换为另一个方法。如果您想使用 java.time 包提供的丰富功能,最简单的解决方案是使用前一节中列出的 toInstanttoZonedDateTime 方法。但是,如果您不想使用该方法或该方法不满足您的需求,则必须重写您的日期时间代码。

在 概述 页面介绍的表格是评估哪些 java.time 类满足您需求的好地方。

两个 API 之间没有一对一的映射对应关系,但以下表格给出了 java.util 日期和时间类中的哪些功能映射到 java.time API 的一般想法。

java.util 功能 java.time 功能 注释

| java.util.Date | java.time.Instant | InstantDate 类相似。每个类:- 表示时间线上的瞬时时间点(UTC)

  • 持有独立于时区的时间

  • 表示为自 1970-01-01T00:00:00Z 以来的纪元秒数加上纳秒

Date.from(Instant)Date.toInstant() 方法允许在这些类之间进行转换。 |

| java.util.GregorianCalendar | java.time.ZonedDateTime | ZonedDateTime 类是 GregorianCalendar 的替代品。它提供以下类似功能。人类时间表示如下:

LocalDate:年,月,日

LocalTime:小时,分钟,秒,纳秒

ZoneId:时区

ZoneOffset:与 GMT 的当前偏移量

GregorianCalendar.from(ZonedDateTime)GregorianCalendar.to(ZonedDateTime) 方法促进这些类之间的转换。

java.util.TimeZone java.time.ZoneIdjava.time.ZoneOffset ZoneId 类指定时区标识符,并访问每个时区使用的规则。ZoneOffset 类仅指定与格林威治/UTC 的偏移量。更多信息,请参阅时区和偏移类。
将日期设置为 1970-01-01GregorianCalendar java.time.LocalTime 将日期设置为 1970-01-01GregorianCalendar 实例中的代码以使用时间组件可以替换为 LocalTime 实例。
GregorianCalendar 时间设置为 00:00. java.time.LocalDate 将时间设置为 GregorianCalendar 实例中的 00:00 以使用日期组件的代码可以替换为 LocalDate 实例。(这种 GregorianCalendar 方法存在缺陷,因为由于转换到夏令时,有些国家一年中不会发生午夜。)

日期和时间格式化

尽管java.time.format.DateTimeFormatter提供了一个强大的机制来格式化日期和时间值,但你也可以直接使用java.time基于时间的类与java.util.FormatterString.format一起使用,使用与java.util日期和时间类相同的基于模式的格式化。

摘要

原文:docs.oracle.com/javase/tutorial/datetime/iso/summary.html

java.time 包含许多类,您的程序可以使用这些类来表示时间和日期。这是一个非常丰富的 API。基于 ISO 的日期的关键入口点如下:

  • Instant 类提供了时间线的机器视图。

  • LocalDateLocalTimeLocalDateTime 类提供了日期和时间的人类视图,没有任何关于时区的参考。

  • ZoneIdZoneRulesZoneOffset 类描述时区、时区偏移和时区规则。

  • ZonedDateTime 类表示带有时区的日期和时间。OffsetDateTimeOffsetTime 类分别表示日期和时间,或仅时间。这些类考虑了时区偏移。

  • Duration 类以秒和纳秒来衡量时间量。

  • Period 类使用年、月和日来衡量时间量。

其他非 ISO 日历系统可以使用 java.time.chrono 包来表示。尽管本教程不涵盖此包,但 非 ISO 日期转换 页面提供了关于将基于 ISO 的日期转换为其他日历系统的信息。

日期时间 API 是作为 Java 社区流程的一部分开发的,其编号为 JSR 310。更多信息,请参阅 JSR 310: 日期和时间 API

问题和练习:日期时间 API

原文:docs.oracle.com/javase/tutorial/datetime/iso/QandE/questions.html

问题

1. 你会使用哪个类来存储你的生日,包括年、月、日、秒和纳秒?

2. 给定一个随机日期,如何找到前一个星期四的日期?

3. ZoneIdZoneOffset之间有什么区别?

4. 如何将Instant转换为ZonedDateTime?如何将ZonedDateTime转换为Instant

练习

1. 为给定年份编写一个示例,报告该年份内每个月的长度。

2. 为当前年份的给定月份编写一个示例,列出该月份内所有的星期一。

3. 编写一个示例,测试给定日期是否是一个星期五的 13 日。

检查你的答案。

Trail: 国际化

原文:docs.oracle.com/javase/tutorial/i18n/index.html

本教程中的课程将教您如何国际化 Java 应用程序。国际化的应用程序易于根据全球用户的习俗和语言进行定制。


注意: 本教程涵盖了核心国际化功能,这是桌面、企业和移动应用程序提供的附加功能所需的基础。有关更多信息,请参阅Java 国际化主页


定义了国际化这一术语,提供了一个快速的示例程序,并提供了一个检查表,您可以用来国际化现有程序。

解释了如何创建和使用Locale对象。

展示了如何动态访问随Locale变化的对象。

解释了如何根据Locale格式化数字、日期和文本消息,以及如何使用模式创建自定义格式。

提供了在与区域设置无关的方式下操作文本的技术。

解释了如何为 IDN 提供国际化支持。

解释了如何启用依赖于区域设置的数据和服务的插件。

教训:介绍

原文:docs.oracle.com/javase/tutorial/i18n/intro/index.html

国际化 是设计应用程序的过程,使其能够在不进行工程更改的情况下适应各种语言和地区。有时国际化一词被缩写为 i18n,因为在第一个 "i" 和最后一个 "n" 之间有 18 个字母。

一个国际化的程序具有以下特点:

  • 通过添加本地化数据,同一个可执行文件可以在全球范围内运行。

  • 文本元素,比如状态消息和 GUI 组件标签,不是硬编码在程序中的。而是存储在源代码之外,并动态检索。

  • 支持新语言不需要重新编译。

  • 依赖文化的数据,比如日期和货币,以符合最终用户的地区和语言的格式出现。

  • 它可以快速本地化。

本地化 是通过添加特定区域或语言的区域特定组件和翻译文本来使软件适应特定区域或语言的过程。本地化一词通常缩写为 l10n,因为在 "l" 和 "n" 之间有 10 个字母。

本地化的主要任务是翻译用户界面元素和文档。本地化不仅涉及改变语言交互,还涉及其他相关变化,比如数字、日期、货币的显示等。如果音频和图像等其他类型的数据在文化上敏感,可能需要本地化。应用程序国际化得越好,为特定语言和字符编码方案本地化就越容易。

一开始国际化可能看起来有点令人生畏。阅读以下章节将有助于您逐渐了解这个主题。

一个快速示例

本节向您展示如何逐步国际化一个简单的程序。

清单

所以你继承了一个需要国际化的程序,或者你正在计划确定新开发软件的需求。你可能不知道从哪里开始?查看这个清单。它总结了必要的国际化任务,并提供了本章相关课程的链接。

一个快速示例

原文:docs.oracle.com/javase/tutorial/i18n/intro/quick.html

如果你是国际化软件的新手,这节课适合你。这节课使用一个简单的示例来演示如何国际化程序,以便以适当的语言显示文本消息。你将学习如何LocaleResourceBundle对象一起工作,以及如何使用属性文件。

国际化前

源代码的第一个版本包含了硬编码的英文消息版本,这不是编写国际化软件的正确方式。

国际化后

这是我们的源代码在国际化后的 sneak preview。

运行示例程序

要运行示例程序,你需要在命令行上指定语言和国家。本节将向你展示一些示例。

国际化示例程序

国际化程序只需要几个步骤。你会惊讶于它是多么容易。

在国际化之前

原文:docs.oracle.com/javase/tutorial/i18n/intro/before.html

假设你已经编写了一个显示三条消息的程序,如下所示:

public class NotI18N {

    static public void main(String[] args) {

        System.out.println("Hello.");
        System.out.println("How are you?");
        System.out.println("Goodbye.");
    }
}

你已经决定这个程序需要为居住在法国和德国的人显示相同的消息。不幸的是,你的编程人员不懂多种语言,所以你需要帮助将这些消息翻译成法语和德语。由于翻译人员不是程序员,你需要将消息从源代码中移出,放入文本文件供翻译人员编辑。此外,程序必须足够灵活,以便能够显示其他语言的消息,但目前没有人知道那些语言会是什么。

看起来这个程序需要国际化。

国际化后

原文:docs.oracle.com/javase/tutorial/i18n/intro/after.html

国际化程序的源代码如下。请注意,消息的文本未包含在软件代码中。

import java.util.*;

public class I18NSample {

    static public void main(String[] args) {

        String language;
        String country;

        if (args.length != 2) {
            language = new String("en");
            country = new String("US");
        } else {
            language = new String(args[0]);
            country = new String(args[1]);
        }

        Locale currentLocale;
        ResourceBundle messages;

        currentLocale = new Locale(language, country);

        messages = ResourceBundle.getBundle("MessagesBundle", currentLocale);
        System.out.println(messages.getString("greetings"));
        System.out.println(messages.getString("inquiry"));
        System.out.println(messages.getString("farewell"));
    }
}

要编译和运行此程序,您需要这些源文件:

  • I18NSample.java

  • MessagesBundle.properties

  • MessagesBundle_de_DE.properties

  • MessagesBundle_en_US.properties

  • MessagesBundle_fr_FR.properties

运行示例程序

原文:docs.oracle.com/javase/tutorial/i18n/intro/run.html

国际化程序是灵活的;它允许最终用户在命令行上指定语言和国家。在下面的示例中,语言代码是fr(法语),国家代码是FR(法国),因此程序会以法语显示消息:

% java I18NSample fr FR
Bonjour.
Comment allez-vous?
Au revoir.

在下一个示例中,语言代码是en(英语),国家代码是US(美国),因此程序会以英语显示消息:

% java I18NSample en US
Hello.
How are you?
Goodbye.

国际化示例程序

原文:docs.oracle.com/javase/tutorial/i18n/intro/steps.html

如果你查看国际化的源代码,你会注意到硬编码的英文消息已经被移除。因为消息不再是硬编码的,而且语言代码在运行时指定,同一个可执行文件可以在全球范围内分发。本地化不需要重新编译。该程序已经国际化。

你可能想知道消息的文本发生了什么变化,或者语言和国家代码的含义是什么。别担心。当你逐步学习国际化示例程序的过程中,你会了解这些概念。

1. 创建属性文件

一个属性文件存储了关于程序或环境特性的信息。属性文件是纯文本格式。你可以用几乎任何文本编辑器创建这个文件。

在示例中,属性文件存储了要显示的可翻译消息的文本。在程序国际化之前,这些文本的英文版本是硬编码在System.out.println语句中的。默认的属性文件名为MessagesBundle.properties,包含以下内容:

greetings = Hello
farewell = Goodbye
inquiry = How are you?

现在消息已经在一个属性文件中,它们可以被翻译成各种语言。不需要对源代码进行任何更改。法语翻译者创建了一个名为MessagesBundle_fr_FR.properties的属性文件,其中包含以下内容:

greetings = Bonjour.
farewell = Au revoir.
inquiry = Comment allez-vous?

注意等号右侧的值已经被翻译,但左侧的键没有改变。这些键不能改变,因为当你的程序获取翻译后的文本时会引用这些键。

属性文件的名称很重要。例如,MessagesBundle_fr_FR.properties文件的名称包含了fr语言代码和FR国家代码。在创建Locale对象时也会使用这些代码。

2. 定义 Locale

Locale对象标识特定的语言和国家。以下语句定义了一个Locale,其中语言为英语,国家为美国:

aLocale = new Locale("en","US");

下一个示例创建了法语语言在加拿大和法国的Locale对象:

caLocale = new Locale("fr","CA");
frLocale = new Locale("fr","FR");

该程序是灵活的。程序不再使用硬编码的语言和国家代码,而是在运行时从命令行获取它们:

String language = new String(args[0]);
String country = new String(args[1]);
currentLocale = new Locale(language, country);

Locale对象只是标识符。在定义了一个Locale之后,你可以将它传递给执行有用任务的其他对象,比如格式化日期和数字。这些对象是区域敏感的,因为它们的行为根据Locale的不同而变化。ResourceBundle就是一个区域敏感的对象的例子。

3. 创建 ResourceBundle

ResourceBundle对象包含特定于区域设置的对象。你可以使用ResourceBundle对象来隔离与区域设置相关的数据,比如可翻译的文本。在示例程序中,ResourceBundle由包含我们想要显示的消息文本的属性文件支持。

ResourceBundle的创建方式如下:

messages = ResourceBundle.getBundle("MessagesBundle", currentLocale);

传递给getBundle方法的参数标识将要访问的属性文件。第一个参数MessagesBundle指的是这一系列属性文件:

MessagesBundle_en_US.properties
MessagesBundle_fr_FR.properties
MessagesBundle_de_DE.properties

LocalegetBundle的第二个参数,指定了选择哪个MessagesBundle文件。创建Locale时,语言代码和国家代码被传递给其构造函数。请注意,语言和国家代码在属性文件的名称中跟随MessagesBundle

现在你只需要从ResourceBundle中获取翻译后的消息即可。

4. 从 ResourceBundle 中获取文本

属性文件包含键值对。值包含了程序将显示的翻译文本。在从ResourceBundle中获取翻译后的消息时,你需要使用getString方法指定键。例如,要检索由greetings键标识的消息,你可以这样调用getString

String msg1 = messages.getString("greetings");

示例程序使用了键greetings,因为它反映了消息的内容,但也可以使用另一个String,比如s1msg1。只需记住,键是硬编码在程序中的,必须存在于属性文件中。如果你的翻译人员意外修改了属性文件中的键,getString将无法找到消息。

结论

这就是全部。正如你所看到的,国际化一个程序并不太困难。它需要一些规划和一点额外的编码,但好处是巨大的。为了让你了解国际化过程的概况,本课程中的示例程序被故意保持简单。在接下来的课程中,你将了解 Java 编程语言更高级的国际化特性。

检查表

原文:docs.oracle.com/javase/tutorial/i18n/intro/checklist.html

许多程序在最初编写时并未国际化。这些程序可能最初是原型,或者可能并非用于国际分发。如果你必须国际化现有程序,请执行以下步骤:

识别文化相关数据

文本消息是随文化变化最明显的数据形式。然而,其他类型的数据可能会随地区或语言而变化。以下列表包含了文化相关数据的例子:

  • 消息

  • GUI 组件上的标签

  • 在线帮助

  • 声音

  • 颜色

  • 图形

  • 图标

  • 日期

  • 时间

  • 数字

  • 货币

  • 测量

  • 电话号码

  • 敬语和个人头衔

  • 邮政地址

  • 页面布局

在资源包中隔离可翻译文本

翻译是昂贵的。你可以通过将必须翻译的文本隔离在ResourceBundle对象中来帮助降低成本。可翻译的文本包括状态消息、错误消息、日志文件条目和 GUI 组件标签。这些文本包含在尚未国际化的程序中。你需要找到所有显示给最终用户的包含文本的出现。例如,你应该清理这样的代码:

String buttonLabel = "OK";
// ...
JButton okButton = new JButton(buttonLabel);

详细信息请参见隔离特定区域数据部分。

处理复合消息

复合消息包含可变数据。在消息"The disk contains 1100 files."中,整数 1100 可能会变化。这个消息很难翻译,因为整数在句子中的位置在所有语言中都不相同。下面的消息是不可翻译的,因为句子元素的顺序是由串联固定的:

Integer fileCount;
// ...
String diskStatus = "The disk contains " + fileCount.toString() + " files";

尽可能避免构建复合消息,因为它们很难翻译。然而,如果你的应用程序需要复合消息,你可以使用消息部分中描述的技术来处理它们。

格式化数字和货币

如果你的应用程序显示数字和货币,你必须以与地区无关的方式格式化它们。以下代码尚未国际化,因为它在所有国家中都不会正确显示数字:

Double amount;
TextField amountField;
// ...
String displayAmount = amount.toString();
amountField.setText(displayAmount);

你应该用一个能正确格式化数字的例程替换前面的代码。Java 编程语言提供了几个格式化数字和货币的类。这些类在数字和货币部分有讨论。

格式化日期和时间

日期和时间格式因地区和语言而异。如果你的代码包含如下语句,你需要进行更改:

Date currentDate = new Date();
TextField dateField;
// ...
String dateString = currentDate.toString();
dateField.setText(dateString);

如果你使用日期格式化类,你的应用程序可以在全球范围内正确显示日期和时间。有关示例和说明,请参见日期和时间部分。

使用 Unicode 字符属性

以下代码尝试验证一个字符是否为字母:

char ch;
// This code is incorrect
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))

注意类似这样的代码,因为它在除英语以外的语言中无法正常工作。例如,if语句在德语单词 Grün 中漏掉了字符ü。

Character比较方法使用 Unicode 标准来识别字符属性。因此,您应该用以下代码替换先前的代码:

char ch;
// ...
if (Character.isLetter(ch))

有关Character比较方法的更多信息,请参阅检查字符属性部分。

正确比较字符串

在对文本进行排序时,通常需要比较字符串。如果文本是显示的,您不应该使用String类的比较方法。一个未国际化的程序可能会这样比较字符串:

String target;
String candidate;
// ...
if (target.equals(candidate)) {
// ...
if (target.compareTo(candidate) < 0) {
// ...

String.equalsString.compareTo方法执行二进制比较,在大多数语言中排序时效率低下。相反,您应该使用Collator类,该类在比较字符串部分有描述。

转换非 Unicode 文本

Java 编程语言中的字符是以 Unicode 编码的。如果您的应用程序处理非 Unicode 文本,您可能需要将其转换为 Unicode。有关更多信息,请参阅转换非 Unicode 文本部分。

课程:设置区域设置

原文:docs.oracle.com/javase/tutorial/i18n/locale/index.html

一个国际化的程序可以在世界各地以不同方式显示信息。例如,该程序将在巴黎、东京和纽约显示不同的消息。如果本地化过程已经经过精细调整,该程序将显示不同的消息在纽约和伦敦,以考虑美式英语和英式英语之间的差异。一个国际化的程序如何识别其最终用户的适当语言和地区?简单。它引用一个Locale对象。

一个Locale对象是特定语言和地区组合的标识符。如果一个类根据Locale变化其行为,那么它被称为区域敏感。例如,NumberFormat类是区域敏感的;它返回的数字格式取决于Locale。因此,NumberFormat可能会将数字返回为 902 300(法国)、902.300(德国)或 902,300(美国)。Locale对象只是标识符。真正的工作,比如格式化和检测单词边界,是由区域敏感类的方法执行的。

以下各节解释了如何使用Locale对象:

创建一个 Locale

在创建Locale对象时,通常会指定语言代码和国家代码。第三个参数,变体,是可选的。

BCP 47 扩展

本节向您展示如何向Locale添加 Unicode 区域扩展或私有使用扩展。

识别可用的 Locale

区域敏感类仅支持特定的Locale定义。本节将向您展示如何确定支持哪些Locale定义。

语言标签过滤和查找

本节描述了语言标签、语言标签过滤和语言标签查找的国际化支持。

Locale 的范围

在 Java 平台上,您不需要通过在运行应用程序之前设置环境变量来指定全局Locale。相反,您要么依赖于默认 Locale,要么为每个区域敏感对象分配一个Locale

区域敏感服务 SPI

本节解释了如何启用依赖于区域设置的数据和服务的插件。这些 SPI(服务提供者接口)提供了对当前可用区域设置之外更多区域设置的支持。

创建 Locale

原文:docs.oracle.com/javase/tutorial/i18n/locale/create.html

有几种方法可以创建 Locale 对象。无论使用哪种技术,创建可以简单地指定语言代码。但是,您还可以通过设置区域(也称为“国家”)和变体代码来进一步区分区域。如果您使用 JDK 7 发布或更高版本,还可以指定脚本代码和 Unicode 区域扩展。

创建 Locale 对象的四种方法是:

  • Locale.Builder

  • Locale 构造函数

  • Locale.forLanguageTag 工厂方法

  • Locale 常量


版本说明: Locale.Builder 类和 forLanguageTag 方法是在 Java SE 7 发布中添加的。


LocaleBuilder

Locale.Builder 实用类可用于构造符合 IETF BCP 47 语法的 Locale 对象。例如,要指定法语和加拿大国家,您可以调用 Locale.Builder 构造函数,然后链接设置器方法如下:

Locale aLocale = new Locale.Builder().setLanguage("fr").setRegion("CA").build();

下一个示例创建了英语在美国和英国的 Locale 对象:

Locale bLocale = new Locale.Builder().setLanguage("en").setRegion("US").build();
Locale cLocale = new Locale.Builder().setLanguage("en").setRegion("GB").build();

最后一个示例创建了俄语的 Locale 对象:

Locale dLocale = new Locale.Builder().setLanguage("ru").setScript("Cyrl").build();

Locale 构造函数

Locale 类有三个可用的构造函数用于创建 Locale 对象:

以下示例创建了法语在加拿大,英语在美国和英国,以及俄语的 Locale 对象。

aLocale = new Locale("fr", "CA");
bLocale = new Locale("en", "US");
cLocale = new Locale("en", "GB");
dLocale = new Locale("ru");

在 JDK 7 之前的版本中,无法在 Locale 对象上设置脚本代码。

forLanguageTag 工厂方法

如果您有符合 IETF BCP 47 标准的语言标记字符串,可以使用在 Java SE 7 发布中引入的 forLanguageTag(String) 工厂方法。例如:

Locale aLocale = Locale.forLanguageTag("en-US");
Locale bLocale = Locale.forLanguageTag("ja-JP-u-ca-japanese");

Locale 常量

为了方便起见,Locale 类为一些语言和国家提供了常量。例如:

cLocale = Locale.JAPAN;
dLocale = Locale.CANADA_FRENCH;

当您指定语言常量时,Locale 的区域部分是未定义的。下面的三个语句创建等效的 Locale 对象:

j1Locale = Locale.JAPANESE;
j2Locale = new Locale.Builder().setLanguage("ja").build();
j3Locale = new Locale("ja");

由以下三个语句创建的Locale对象也是等效的:

j4Locale = Locale.JAPAN;
j5Locale = new Locale.Builder().setLanguage("ja").setRegion("JP").build();
j6Locale = new Locale("ja", "JP");

代码

以下部分讨论语言代码和可选的脚本、地区和变体代码。

语言代码

语言代码是符合 ISO 639 标准的两个或三个小写字母。您可以在www.loc.gov/standards/iso639-2/php/code_list.php找到 ISO 639 代码的完整列表。

以下表格列出了一些语言代码。

示例语言代码

语言代码 描述
de 德语
en 英语
fr 法语
ru 俄语
ja 日语
jv 爪哇语
ko 韩语
zh 中文

脚本代码

脚本代码以大写字母开头,后跟三个小写字母,并符合 ISO 15924 标准。您可以在unicode.org/iso15924/iso15924-codes.html找到 ISO 15924 代码的完整列表。

以下表格列出了一些脚本代码。

示例脚本代码

脚本代码 描述
Arab 阿拉伯语
Cyrl 西里尔字母
Kana 片假名
Latn 拉丁字母

有三种方法可以检索Locale的脚本信息:

  • getScript() – 返回Locale对象的 4 字母脚本代码。如果未为区域设置定义脚本,则返回空字符串。

  • getDisplayScript() – 返回适合显示给用户的区域设置脚本的名称。如果可能,名称将针对默认区域设置进行本地化。因此,例如,如果脚本代码是"Latn",则返回的显示脚本名称将在英语语言区域设置下为"Latin"。

  • getDisplayScript(Locale) – 返回指定Locale的显示名称,如果可能的话进行本地化。

地区代码

地区(国家)代码由符合 ISO 3166 标准的两个或三个大写字母组成,或者符合 UN M.49 标准的三个数字。代码的副本可以在www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html找到。

以下表格包含几个示例国家和地区代码。

示例地区代码

A-2 代码 A-3 代码 数字代码 描述
AU AUS 036 澳大利亚
BR BRA 076 巴西
CA CAN 124 加拿大
CN CHN 156 中国
DE DEU 276 德国
FR FRA 250 法国
IN IND 356 印度
RU RUS 643 俄罗斯联邦
US USA 840 美国

变体代码

可选的variant代码可用于进一步区分您的Locale。例如,变体代码可用于指示区域代码未涵盖的方言差异。


版本说明: 在 Java SE 7 发布之前,变体代码有时用于标识不特定于语言或区域的差异。例如,它可能已用于标识计算平台之间的差异,如 Windows 或 UNIX。根据 IETF BCP 47 标准,不鼓励此用法。

要定义与您的环境相关的非语言特定变体,请使用扩展机制,如 BCP 47 扩展中所解释的那样。


自 Java SE 7 发布以来,符合 IETF BCP 47 标准的变体代码专门用于指示定义语言或其方言的附加变体。IETF BCP 47 标准对变体子标记施加了语法限制。您可以在www.iana.org/assignments/language-subtag-registry上查看变体代码列表(搜索变体)。

例如,Java SE 使用变体代码支持泰语。按照惯例,thth_TH 区域设置的NumberFormat对象将使用常见的阿拉伯数字形状或阿拉伯数字来格式化泰国数字。然而,th_TH_TH 区域设置的NumberFormat将使用泰国数字形状。ThaiDigits.java中的摘录演示了这一点:

String outputString = new String();
Locale[] thaiLocale = {
             new Locale("th"),
             new Locale("th", "TH"),
             new Locale("th", "TH", "TH")
         };

for (Locale locale : thaiLocale) {
    NumberFormat nf = NumberFormat.getNumberInstance(locale);
    outputString = outputString + locale.toString() + ": ";
    outputString = outputString + nf.format(573.34) + "\n";
}

以下是此示例的屏幕截图:

Sample ThaiDigits.java 的屏幕截图

posted @   绝不原创的飞龙  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2023-04-12 NumPy 初学者指南中文第三版:11~14
2023-04-12 NumPy 初学者指南中文第三版:6~10
2023-04-12 NumPy 初学者指南中文第三版:1~5
2023-04-12 NumPy 秘籍中文第二版:11~12
2023-04-12 NumPy 秘籍中文第二版:6~10
2023-04-12 NumPy 秘籍中文第二版:1~5
2023-04-12 NumPy 秘籍中文第二版:十二、使用 NumPy 进行探索性和预测性数据分析
点击右上角即可分享
微信分享提示