首页 > 编程语言 >理解 Java8 的时间API(一)时区

理解 Java8 的时间API(一)时区

时间:2023-04-22 17:46:34浏览次数:55  
标签:UTC code 理解 时区 ZoneId API time ID Java8

理解 Java8 的时间API:java.time

由于Java的时间API:java.util.Datejava.util.Calendarjava.util.TimeZone使用起来非常混乱,因此 Java8 重新设计了一套时间API,放在java.time.* 包下。

如果需要熟练使用新的LocalDateTimeLocalDateLocalTime类,最好是先了解时区的概念。因此本文先梳理一下 Java8 里新的时区API。

一、时区与时间

首先是时区time zone,定义这里不多介绍,只需要了解地球上划分了很多时区,不同时区当前的日期时间是不一样的。比如北京时间、伦敦时间、洛杉矶时间等。常见的格式是:

其次是时间,这个太熟悉了,甚至不需要定义。时间通常有两种表示法:

  • 时间戳(timestamp):计算机常用的表示方法。定义是以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。也就是说表示某一个“瞬间”,并且不受时区影响,在任何一个时区获取当前的时间戳都是一个相同的值。在类Unix系统里是一个有正负号的32位整数(signed int32),因此存在2038年问题。在Java里则通常用一个long类型的整数表示。
  • 日期时间(date time):日常生活中最常见的表示方法。例如“2023年4月22日15时23分08秒”,格式有很多种,但是通常由两部分组成:date(年+月+日)与 time(时+分+秒)组成。受时区影响,在当前这个“瞬间”,不同时区的日期时间是不同的。

由上面的定义可以知道:时间戳与日期时间之间相互转化是需要时区信息的。这个作为大前提。

二、Java8中的时区

2.1 介绍

对时区和时间有了一个大概的概念之后,这里介绍一下Java中怎么描述这两种概念。

首先是Java中时区类java.time.ZoneId,这里贴一下注释:

/**
 * A time-zone ID, such as {@code Europe/Paris}.
 * <p>
 * A {@code ZoneId} is used to identify the rules used to convert between
 * an {@link Instant} and a {@link LocalDateTime}.
 * There are two distinct types of ID:
 * <ul>
 * <li>Fixed offsets - a fully resolved offset from UTC/Greenwich, that uses
 *  the same offset for all local date-times
 * <li>Geographical regions - an area where a specific set of rules for finding
 *  the offset from UTC/Greenwich apply
 * </ul>
 * Most fixed offsets are represented by {@link ZoneOffset}.
 * Calling {@link #normalized()} on any {@code ZoneId} will ensure that a
 * fixed offset ID will be represented as a {@code ZoneOffset}.
 * <p>
 * The actual rules, describing when and how the offset changes, are defined by {@link ZoneRules}.
 * This class is simply an ID used to obtain the underlying rules.
 * This approach is taken because rules are defined by governments and change
 * frequently, whereas the ID is stable.
 * <p>
 * The distinction has other effects. Serializing the {@code ZoneId} will only send
 * the ID, whereas serializing the rules sends the entire data set.
 * Similarly, a comparison of two IDs only examines the ID, whereas
 * a comparison of two rules examines the entire data set.
 *
 * <h3>Time-zone IDs</h3>
 * The ID is unique within the system.
 * There are three types of ID.
 * <p>
 * The simplest type of ID is that from {@code ZoneOffset}.
 * This consists of 'Z' and IDs starting with '+' or '-'.
 * <p>
 * The next type of ID are offset-style IDs with some form of prefix,
 * such as 'GMT+2' or 'UTC+01:00'.
 * The recognised prefixes are 'UTC', 'GMT' and 'UT'.
 * The offset is the suffix and will be normalized during creation.
 * These IDs can be normalized to a {@code ZoneOffset} using {@code normalized()}.
 * <p>
 * The third type of ID are region-based IDs. A region-based ID must be of
 * two or more characters, and not start with 'UTC', 'GMT', 'UT' '+' or '-'.
 * Region-based IDs are defined by configuration, see {@link ZoneRulesProvider}.
 * The configuration focuses on providing the lookup from the ID to the
 * underlying {@code ZoneRules}.
 * <p>
 * Time-zone rules are defined by governments and change frequently.
 * There are a number of organizations, known here as groups, that monitor
 * time-zone changes and collate them.
 * The default group is the IANA Time Zone Database (TZDB).
 * Other organizations include IATA (the airline industry body) and Microsoft.
 * <p>
 * Each group defines its own format for the region ID it provides.
 * The TZDB group defines IDs such as 'Europe/London' or 'America/New_York'.
 * TZDB IDs take precedence over other groups.
 * <p>
 * It is strongly recommended that the group name is included in all IDs supplied by
 * groups other than TZDB to avoid conflicts. For example, IATA airline time-zone
 * region IDs are typically the same as the three letter airport code.
 * However, the airport of Utrecht has the code 'UTC', which is obviously a conflict.
 * The recommended format for region IDs from groups other than TZDB is 'group~region'.
 * Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'.
 *
 * <h3>Serialization</h3>
 * This class can be serialized and stores the string zone ID in the external form.
 * The {@code ZoneOffset} subclass uses a dedicated format that only stores the
 * offset from UTC/Greenwich.
 * <p>
 * A {@code ZoneId} can be deserialized in a Java Runtime where the ID is unknown.
 * For example, if a server-side Java Runtime has been updated with a new zone ID, but
 * the client-side Java Runtime has not been updated. In this case, the {@code ZoneId}
 * object will exist, and can be queried using {@code getId}, {@code equals},
 * {@code hashCode}, {@code toString}, {@code getDisplayName} and {@code normalized}.
 * However, any call to {@code getRules} will fail with {@code ZoneRulesException}.
 * This approach is designed to allow a {@link ZonedDateTime} to be loaded and
 * queried, but not modified, on a Java Runtime with incomplete time-zone information.
 *
 * <p>
 * This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>
 * class; use of identity-sensitive operations (including reference equality
 * ({@code ==}), identity hash code, or synchronization) on instances of
 * {@code ZoneId} may have unpredictable results and should be avoided.
 * The {@code equals} method should be used for comparisons.
 *
 * @implSpec
 * This abstract class has two implementations, both of which are immutable and thread-safe.
 * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling
 * offset-based IDs. This difference is visible in serialization.
 *
 * @since 1.8
 */
public abstract class ZoneId implements Serializable {
}

从注释中可以总结一下要点:

  1. 时区ID 定义了InstantLocalDateTime之间相互转化的规则;
  2. 时区ID 有两种不同的类型:
    • 固定偏移:以与某个标准时间(如UTC、GMT、UT等)的偏移量来定义时区;例如“UTC+08:00”、“UTC+8”、“GMT+8”等。(UTC、GMT、UT的定义可以自行搜索,这里只需要知道UTC和GMT可以简单理解成一个东西。)
    • 地理区域:以具体的地理位置来表示当地的时区。通常是定义在TZDB中的,例如“America/Los_Angeles”、“Asia/Shanghai”等。

此外,还有时区缩写(time-zone abbreviations),也叫短时区ID:例如"PST"(太平洋标准时间,Pacific Standard Time,代表城市洛杉矶,因此等效于America/Los_Angeles),但是由于很多时区的缩写相同,容易引起歧义,因此不太建议用这种。

固定偏移类型的时区ID通常是根据地理经度来划分的,日常生活中使用这种类型的时区会带来诸多不便,例如中国在地理上横跨很多时区,但是全国使用同一的北京时间("UTC+8",国际上定义"Asia/Shanghai")。并且,欧美很多国家会实行夏时制(又称夏令时、日光节约时间),例如2023年,美国在3月12日启用夏时制,11月5日结束夏时制(这里日期用美国东部时间,"EST"),因此洛杉矶时区"America/Los_Angeles"在夏时制等效于UTC-7,不在夏时制等效于UTC-8

2.2 ZoneId使用

经过前面的介绍,正式介绍Java里的时区类:java.time.ZoneId,这是一个抽象类,有两个具体子类:public的java.time.ZoneOffset 和 package的java.time.ZoneRegion

ZoneId可以使用其静态方法来构造:public static ZoneId of(String zoneId),参数中"zoneId"合法的格式:

  1. 只包含与UTC/GMT的偏移量:"Z"(即UTC/GMT),"+8","+08:00",之所以精确到分,是因为还有一些"+04:30"之类的时区;
  2. 包含前缀以及与其的偏移量:例如 "UTC" / "UTC+0","UTC+8","UTC+08:00";(合法的前缀只有 'UTC', 'GMT' and 'UT')
  3. 包含定义在TZDB中的地区格式:例如:"Asia/Shanghai","America/Los_Angeles"。

其中,格式1初始化出来的时区类是ZoneOffset,格式2和格式3初始化出来的是ZoneRegion

几个额外的点:

  1. 如果需要使用缩写:"EST","PST",只能使用重载的public static ZoneId of(String zoneId, Map<String, String> aliasMap)方法,为了消除歧义,需要指定缩写与全称的映射关系。
  2. public ZoneId normalized() 方法:如果一个ZoneId实例实质上是一个UTC/GMT的固定偏移,那么调用其normalized()方法会转化为ZoneOffset实例返回;否则返回自身。
  3. public abstract ZoneRules getRules()方法:获取当前ZoneId实例对应的java.time.zone.ZoneRulesZoneRules包含了InstantLocalDateTime互相转化的时区规则,并且已经考虑了夏时制等其他规则,了解即可。并且可以使用java.time.zone.ZoneRules#getOffset(java.time.Instant)java.time.zone.ZoneRules#getOffset(java.time.LocalDateTime)方法获取当前时间的偏移量(因为夏时制的存在,不同时间的偏移量可能不同)。

代码验证:

public static void main(String[] args) {
    System.out.println(ZoneId.of("Z").getClass().getSimpleName()); // ZoneOffset
    System.out.println(ZoneId.of("+8").getClass().getSimpleName()); // ZoneOffset
    System.out.println(ZoneId.of("UTC").getClass().getSimpleName()); // ZoneRegion
    System.out.println(ZoneId.of("UTC+8").getClass().getSimpleName()); // ZoneRegion
    System.out.println(ZoneId.of("Asia/Shanghai").getClass().getSimpleName()); // ZoneRegion
    System.out.println(ZoneId.of("PST", ZoneId.SHORT_IDS).getClass().getSimpleName()); // ZoneRegion
    System.out.println(ZoneId.of("UTC+8").normalized().getClass().getSimpleName()); // ZoneOffset

    ZoneId los = ZoneId.of("America/Los_Angeles");
    ZoneRules rules = los.getRules();
    ZoneOffset offset1 = rules.getOffset(Instant.now()); // 现在是2023/4/22 17:29
    ZoneOffset offset2 = rules.getOffset(LocalDateTime.of(2023, 11, 10, 0, 0, 0));
    System.out.println(offset1); // -07:00
    System.out.println(offset2); // -08:00
}

标签:UTC,code,理解,时区,ZoneId,API,time,ID,Java8
From: https://www.cnblogs.com/recycer/p/17343564.html

相关文章

  • Java基础知识点API之Object
    一:概述Object是java中的顶级父类,所有的父类都直接或间接地继承于Object类。Object类中的方法可以被所有子类访问,所以我们要学习Object类和其中的方法。二·:OBject的构造方法及其常用的成员方法publicObject()//空参构造classStudent(){privateStringname;private......
  • Java获取拼多多搜索词推荐 API接口(item_search_suggest-获得搜索词推荐)
    搜索词推荐的作用1.可以精准把控流量2.可以测试产品款式测试产品图片3.提升类目在平台的排名4.提升销量,加速报名参加平台的活动5.提升ROI,日常平销item_search_suggest-获得搜索词推荐公共参数名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥(接......
  • bootloader 理解
    bootloader有2个概念,一个是由芯片厂固化到芯片flash中指定区域的代码(stm32称这段为系统启动区,tc397称这段空间为BROM,这片区域的代码执行需要硬件boot-pin的配合,以stm23为例boot1-pin=0;boot0-pin=1之后在芯片复位后就不会flash开始执行,而是从系统启动区执行这段代码,这段代码具体......
  • Java根据ID获取拼多多商品详情 API接口(商品ID、商品标题、价格、原价、昵称、库存、销
    ​ 根据小编对新零售的核心诉求的理解,拼多多其实并不属于新零售实体。而在我国经济红红火火的这个时代,与消费升级并存的对立方向消费降级现象、小镇的中青年的消费偏好、以及一线的消费者复杂甚至相对矛盾的消费特征,和背后由这些消费特征与诉求来决定的电商大格局。Java它是一......
  • 理解Java程序的执行
    main方法publicclassSolution{publicstaticvoidmain(String[]args){Personperson=newPerson();person.hello();}}classPerson{publicvoidhello(){System.out.println("hello");}}源文件名是Solu......
  • 理解Java程序的执行
    main方法publicclassSolution{publicstaticvoidmain(String[]args){Personperson=newPerson();person.hello();}}classPerson{publicvoidhello(){System.out.println("hello");}}源文件名是Solu......
  • Java基础知识点API之Runtime
    一:Runtime的介绍Runtime表示当前虚拟机的运行状态二:Runtime的常用方法方法名说明publicstaticRuntimegetRuntime()当前系统的运行环境publicvoidexit(intstatus)停止虚拟机publicintavailableProcessors()获取cpu线程数publiclongmaxMemoryJVM能从系统中获取总内存大小(单......
  • JDBC--API--PreparedStatement
     案例:  代码实现:  packageJDbc;importjava.sql.*;importjava.util.Scanner;publicclassjdbcdome_PreparedStatement{publicstaticvoidmain(String[]args)throwsClassNotFoundException,SQLException{Class.forName("com.mysql.jdb......
  • JDBC--API --ResultSet
        importjava.sql.*;publicclassjdbcdome_ResultSet{publicstaticvoidmain(String[]args)throwsClassNotFoundException,SQLException{Class.forName("com.mysql.jdbc.Driver");Stringurl="jdbc:mysql://127.0.......
  • FastAPI.2
    目录FastAPI.2一、简单的编写基于fastapi的接口二、请求路径FastAPI.2一、简单的编写基于fastapi的接口创建main.py文件导入fastapifromfastapiimportFastAPI实例化出FastAPI的对象app=FastAPI()通过装饰器添加路径,@app.get("/")'''@app.get("/")的作......