1. 名词解释
1.1 UTC
Coordinated Universal Time协调世界时, 是个世界标准时间。
1.2 GMT
GMT(Greenwich Mean Time,格林威治时间)是时区时间。
GMT = UTC +0, 和 UTC 的值是一样的。
Tue Mar 15 03:25:32 GMT 2022
1.3 CST
北京时间,CST = GMT + 8
Tue Mar 15 11:27:12 CST 2022
1.4 T
连接 date 和 time 的字符,没有意义。
2022-03-15T03:27:12.556410
1.5 Z
ISO 8601中规定:对 UTC 时间最后加一个大写字母 Z
1970-01-01T00:00:00Z
2. 时区问题
使用 Java 处理时间时,我们可能会经常发现时间不对,比如相差 8 个小时等等,其真实原因便是 TimeZone。只有正确合理的运用 TimeZone,才能保证系统时间无论何时都是准确的。
上文提到的 gmt 和 cst 就是时区。
2.1 timestamp
我们先说下时间戳
Date date = new Date();
Date date2 = Calendar.getInstance().getTime();
long time = date.getTime();
Date 对象本身所存储的毫秒数可以通过 date.getTime() 方法得到;该函数返回自 1970年1月1日 00:00:00 GMT 以来此对象表示的毫秒数。它与时区和地域没有关系(可以认为是GMT时间)
同样的
long currentTimeMillis = System.currentTimeMillis();
该函数返回自 1970年1月1日 00:00:00 GMT 以来此对象表示的毫秒数。它与时区和地域没有关系(可以认为是GMT时间)
2.2 TimeZone
旧版 API, 新版使用 ZoneId
TimeZone 对象给我们的是原始的偏移量,也就是与 GMT 相差的微秒数,即 TimeZone 表示时区偏移量,本质上以毫秒数保存与 GMT 的差值。
获取 TimeZone 可以通过时区 ID,如 “America/New_York”,也可以通过 GMT+/-hh:mm 来设定,例如北京时间可以表示为 GMT+8:00。
TimeZone.getRawOffset() 方法可以用来得到当前时区的标准时间到 GMT 的偏移量。上段提到的 “America/New_York” 和 “GMT+8:00” 两个时区的偏移量分别为 -18000000 和 28800000。
TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
TimeZone timeZone1 = TimeZone.getTimeZone("GMT+8:00");
TimeZone timeZone2 = TimeZone.getTimeZone("Asia/Shanghai");
System.out.println(timeZone2.getRawOffset());
2.3 Locale
一个 Locale 对象代表一个特定的地理、政治或文化区域。需要指定一个 Locale 对象来执行其任务的操作称为区域敏感的操作,并使用 Locale 对象为用户提供特定区域的定制信息。例如,打印日期是一个区分区域设置的操作,该文本应该根据用户所在国家、地区或文化的习俗和惯例进行格式化。
一个 Locale 对象由客户端给出的 language & country & script(比如四川话) & varient(变体语种) 等信息确定。
Locale.getDefault();
静态方法,会给出根据服务器操作系统中设置的 language / country 等信息,得到系统默认的 Locale 对象。
// 调用默认的locale
Locale locale = Locale.getDefault();
// zh-CN 华-中国 zh-HK 华-香港 SARzh-MO 华-澳门
System.out.println("locale:"+locale);
Locale 类中定义了以下常量实例:
static public final Locale CHINESE = createConstant("zh", "");
static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
static public final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");
static public final Locale CHINA = SIMPLIFIED_CHINESE;
static public final Locale PRC = SIMPLIFIED_CHINESE;
static public final Locale ENGLISH = createConstant("en", "");
static public final Locale UK = createConstant("en", "GB");
static public final Locale US = createConstant("en", "US");
static public final Locale FRENCH = createConstant("fr", "");
static public final Locale FRANCE = createConstant("fr", "FR");
static public final Locale CANADA = createConstant("en", "CA");
static public final Locale CANADA_FRENCH = createConstant("fr", "CA");
...
Locale类中还提供了以下工具方法:
public static Locale[] getAvailableLocales() { ...... }
public static synchronized void setDefault(Locale newLocale) { ...... }
public static String[] getISOCountries() { ...... }
public static String[] getISOLanguages() { ...... }
2.4 Calendar
新版不建议使用
Calendar 是个和时区相关的东西,Calendar 的 getInstance() 方法有参数为 TimeZone 和 Locale 的重载,可以使用指定时区和语言环境获得一个日历。无参则使用默认时区和语言环境(系统默认,从系统获取)获得日历。
2.4.1 Locale 对 Calendar 的影响
Locale 对 calendar 时间没什么影响,我们一般用不到,影响体现在下面两个地方。
- Locale 使 DateFormat 按所配置的地区特性来输出文字(例如中国,美国,法国不同地区对日期的表示格式不一样, 中国可能是2001年10月5日)
- Local 对 calendar.getFirstDayOfWeek() 有影响,不用地区的每周第一天是不相同的,美国是周末,法国是周一。
2.4.2 TimeZone 对 Calendar 的影响
TimeZone 对 Calendar 获取到的时间有直接的影响,最常见的就是 8 小时问题。
Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
System.out.println(date); // Fri Jan 31 21:20:50 CST 2014
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE)); // 13:20
可以看到,首先我们使用 系统时区来获取时间,获取到 CST 时间,然后使用 GMT 获取时间,两个时间差了 8 个 小时。
我们可以在项目中设置统一时区,来防止不同操作系统造成时区差。
设置完时区后,我们不能用 calendar.getTime() 来直接获取 Date 日期,因为此时的日期与一开始 setTime 时是相同值,要想获取某时区的时间,正确的做法是用 calendar.get() 方法。
2.5 Date
上文我们好几处用到了 Date 对象,Date 本身是没有时区概念的,就是一个时间戳。
但我们将 Date 格式化字符串和将字符串解析为 Date 对象的时候,是涉及到时区概念的。
格式化 Date 为字符串
Date date = new Date();
// 默认是系统时区
System.out.println(date); // Tue Mar 15 14:04:01 CST 2022
// 修改默认时区
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
System.out.println(date); // Tue Mar 15 06:04:01 GMT 2022
Date date1 = new Date();
// 默认是系统时区
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(date1)); // 2022-03-15 06:05:08
// 设置时区
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
System.out.println(dateFormat.format(date1)); // 2022-03-15 07:05:08
解析字符串为 Date
String dateStr = "2019-12-10 08:00:00";
// 默认是系统时区
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date1 = dateFormat.parse(dateStr);
System.out.println(date1.getTime()); // 1575936000000
// 设置时区
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
Date date2 = dateFormat.parse(dateStr);
System.out.println(date2.getTime()); // 1575961200000
可见时区不同,解析出的时间戳是不相同的
3. java8 改动
java.time
是对 java.util.Date
强有力的补充,解决了 Date 类的大部分痛点:
- 非线程安全
- 时区处理麻烦
- 各种格式化、和时间计算繁琐
- 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。
java.util.Date
既包含日期又包含时间,而 java.time
把它们进行了分离
LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss
总结:
- Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代
System.currentTimeMillis()
来获取当前的微秒数。 - 某一个特定的时间点可以使用
Instant
类来表示,Instant
类也可以用来创建旧版本的java.util.Date
对象。
- 在新 API 中时区使用 ZoneId 来表示,代替旧版的 TimeZone。时区可以很方便的使用静态方法 of 来获取到。 抽象类
ZoneId
(在java.time
包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds
的静态方法,它返回所有区域标识符。 - jdk1.8 中新增了 LocalDate 与 LocalDateTime 等类来解决日期处理方法。可以使用 LocalDateTime 代替 Calendar。
- 同时引入了一个新的类 DateTimeFormatter 来解决日期格式化问题,可以使用 DateTimeFormatter 代替 SimpleDateFormat。
4. DateTimeFormatter
参考:【Java 8 新特性】Java DateTimeFormatter 日期时间格式化器
我们之前使用 SimpleDateFormat 来格式化日期,java 8 之后,可使用 DateTimeFormatter。它是不可变的,且线程安全。
SimpleDateFormat 线程不安全。
Java 8 之前 转换都需要借助 SimpleDateFormat
类,而 Java 8 之后只需要 LocalDate
、LocalTime
、LocalDateTime
的 of
或 parse
方法。
4.1 自定义格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化
String dateTime = formatter.format(LocalDateTime.now());
System.out.println(dateTime); //2018-Dec-21 11:14:12
// 解析
LocalDateTime ldt = LocalDateTime.parse("2018-12-20 08:25:30", formatter);
System.out.println(ldt); //2018-12-20T08:25:30
ldt = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
System.out.println(ldt); //2021-01-26T12:12:22
4.2 预定义的格式器
ZonedDateTime zDateTime = ZonedDateTime.now();
System.out.println( zDateTime.format(DateTimeFormatter.BASIC_ISO_DATE)); // 20220511+0800
System.out.println( zDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE)); // 2022-05-11
System.out.println( zDateTime.format(DateTimeFormatter.ISO_DATE)); // 2022-05-11+08:00
System.out.println( zDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE)); // 2022-05-11+08:00
System.out.println( zDateTime.format(DateTimeFormatter.ISO_INSTANT)); // 2022-05-11T12:36:36.575Z
System.out.println( zDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); // 2022-05-11T20:36:36.575+08:00[Asia/Shanghai]
System.out.println( zDateTime.format(DateTimeFormatter.ISO_ORDINAL_DATE)); // 2022-131+08:00
System.out.println( zDateTime.format(DateTimeFormatter.ISO_WEEK_DATE)); // 2022-W19-3+08:00
4.3 与 Local 相关的格式器
对于日期时间,有4种相关的格式化风格:
- FormatStyle.SHORT
- FormatStyle.MEDIUM
- FormatStyle.LONG
- FormatStyle.FULL
DateTimeFormatter 类中提供了获取这些风格的实例的静态方法:
- ofLocalizedDate(FormatStyle)
- ofLocalizedTime(FormatStyle)
- ofLocalizedDateTime(FormatStyle)
可以使用DateTimeFormatter的静态方法来获取这些风格的实例,并且这些风格随着Locale改变而不同,如:
ZonedDateTime zDateTime = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
System.out.println(formatter.format(zDateTime)); // 默认的Locale是根据当前服务器操作系统设置的language、country而获得的Locale。即Locale.CHINESE 22-5-11
System.out.println(formatter.withLocale(Locale.CANADA).format(zDateTime)); // 将Locale设置为CANADA 11/05/22
System.out.println(formatter.withLocale(Locale.ENGLISH).format(zDateTime)); // 5/11/22
5. LocalDateTime
参考:
再也不为日期烦恼——LocalDate的使用
LocalDateTime总结
5.1 各类介绍
- LocalDate: 只对年月日处理,不涉及时区。
- LocalTime:只对时分秒纳秒处理,不涉及时区。
- LocalDateTime:同时处理年月日时分秒,不涉及时区。
- ZonedDateTime: 包含时区的完整的日期时间,偏移量是以 UTC 时间为基准的。
5.2 LocalDateTime 与时区无关
证明 LocalDateTime 和时区无关:
LocalDateTime now = LocalDateTime.now();
long ms1 = now.toEpochSecond(ZoneOffset.of("+8"));
long ms2 = now.toEpochSecond(ZoneOffset.UTC);
long ms3 = System.currentTimeMillis();
System.out.println(ms1);
System.out.println(ms2);
System.out.println(ms3 / 1000);
// 1664422021
// 1664450821
// 1664422021
我们可以看到我们用 “+8” 时区去获取时间戳时和 GMT 时间戳一致。
这是因为我们获取 LocalDateTime 时使用的是 “GMT+8” 时区,获取到了一个和时区无关的时间。然后我们把这个时间转换为时间戳,ZoneOffset.of("+8")
声明了此时间所用时区,才得到了与 System.currentTimeMillis()
相同的值。
而 now.toEpochSecond(ZoneOffset.UTC)
, 相当于我们用 “GMT+8” 获取的时间,本来就多了 8 小时,我们又把它当成 “GMT+0” 时区的值去获取时间戳,就会多 8 个小时。
5.3 基础用法
public static void main(String[] args) {
//------------------------------【获取当前时间】---------------------------------------------------
LocalDateTime nowTime = LocalDateTime.now();
//结果:2021-02-20T09:45:44.527
System.out.println(nowTime);
//------------------------------【获取年月日】-----------------------------------------------------
//【方法1】
String a = nowTime.format(DateTimeFormatter.ISO_DATE);
//结果:2021-02-20
System.out.println(a);
//【方法2】
String b = nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//结果:2021-02-20
System.out.println(b);
//------------------------------【获取时分秒】-----------------------------------------------------
String c = nowTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
//结果:09:45:44
System.out.println(c);
//------------------------------【获取年月日时分秒毫秒】--------------------------------------------
String d = nowTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS"));
//结果:2021-02-20 09:45:44:527
System.out.println(d);
//------------------------------【获取秒数】-------------------------------------------------------
Long second = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
//结果:1613785755
System.out.println(second);
//------------------------------【获取毫秒数】-----------------------------------------------------
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
//结果:1613785815633
System.out.println(milliSecond);
//------------------------------【毫秒转LocalDateTime】--------------------------------------------
Long longTime = 1613786449976L;
LocalDateTime ldt = Instant.ofEpochMilli(longTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
//结果:2021-02-20T10:00:49.976
System.out.println(ldt);
//------------------------------【String转LocalDateTime】------------------------------------------
String dateTimeStr = "2021-02-20 09:50:15";
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, df);
//结果:2021-02-20T09:50:15
System.out.println(dateTime);
//------------------------------【LocalDateTime转String】------------------------------------------
//除了下面的方法之外,上面类型转换的都可以看成是LocalDateTime转String ZoneOffset.of("+8")意思为:默认时区为东8区
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateTime2 = LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
//结果:2021-02-20 09:55:52
System.out.println(dateTime2);
//------------------------------【LocalDateTime转Date】--------------------------------------------
Date date = new Date();
LocalDateTime localDateTime = date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
//结果:2021-02-20T09:59:14.719
System.out.println(localDateTime);
//------------------------------【Date转LocalDateTime】--------------------------------------------
LocalDateTime localDateTime1 = LocalDateTime.now();
Date date1 = Date.from(localDateTime1.toInstant(ZoneOffset.of("+8")));
//结果:Sat Feb 20 10:00:49 CST 2021
System.out.println(date1);
//------------------------------【LocalDateTime获取年】--------------------------------------------
System.out.println("当前时间为:" + LocalDateTime.now() + " == " + "2021-02-20T10:23:45.041");
int year = LocalDateTime.now().getYear();
//结果:2021
System.out.println(year);
//------------------------------【LocalDateTime获取月】--------------------------------------------
Month month = LocalDateTime.now().getMonth();
//结果:FEBRUARY
System.out.println(month);
int monthValue = LocalDateTime.now().getMonthValue();
//结果:2
System.out.println(monthValue);
//------------------------------【LocalDateTime获取日】--------------------------------------------
int dayOfMonth = LocalDateTime.now().getDayOfMonth();
//结果:20
System.out.println(dayOfMonth);
DayOfWeek dayOfWeek = LocalDateTime.now().getDayOfWeek();
//结果:SATURDAY
System.out.println(dayOfWeek);
int dayOfYear = LocalDateTime.now().getDayOfYear();
//结果:51
System.out.println(dayOfYear);
//------------------------------【LocalDateTime获取时】--------------------------------------------
int hour = LocalDateTime.now().getHour();
//结果:10
System.out.println(hour);
//------------------------------【LocalDateTime获取分】--------------------------------------------
int minute = LocalDateTime.now().getMinute();
//结果:23
System.out.println(minute);
//------------------------------【LocalDateTime获取秒】--------------------------------------------
int second1 = LocalDateTime.now().getSecond();
//结果:45
System.out.println(second1);
//------------------------------【LocalDateTime获取特定日期】---------------------------------------
LocalDateTime of = LocalDateTime.of(2021, 2, 20, 10, 37, 20);
//结果:2021-02-20T10:37:20
System.out.println(of);
//------------------------------【LocalDateTime时间对比】-------------------------------------------
LocalDateTime of1 = LocalDateTime.of(2021, 2, 20, 10, 37, 20);
LocalDateTime of2 = LocalDateTime.of(2021, 2, 20, 10, 37, 22);
LocalDateTime of3 = LocalDateTime.of(2021, 2, 20, 10, 37, 22);
//【方法1】
//结果:false
System.out.println(of1.equals(of2));
//结果:true
System.out.println(of2.equals(of3));
//结果:-1 of1 < of2
System.out.println(of1.compareTo(of2));
//结果:0 of2 = of3
System.out.println(of2.compareTo(of3));
//结果:1 of3 > of1
System.out.println(of3.compareTo(of1));
//【方法2】
//结果:false
System.out.println(of1.isAfter(of2));
//结果:true
System.out.println(of1.isBefore(of2));
//结果:true
System.out.println(of2.isEqual(of3));
//------------------------------【LocalDateTime获取一天前/后日期】-----------------------------------
LocalDateTime localDateTime2 = LocalDateTime.now().plusDays(1);
//结果:2021-02-21T10:48:26.911
System.out.println(localDateTime2);
LocalDateTime localDateTime3 = LocalDateTime.now().plusDays(-1);
//结果:2021-02-19T10:48:26.911
System.out.println(localDateTime3);
//------------------------------【LocalDateTime获取一小时前/后日期】-----------------------------------
LocalDateTime localDateTime4 = LocalDateTime.now().plusHours(1);
//结果:2021-02-20T11:49:32.320
System.out.println(localDateTime4);
LocalDateTime localDateTime5 = LocalDateTime.now().plusHours(-1);
//结果:2021-02-20T09:49:32.320
System.out.println(localDateTime5);
//------------------------------【LocalDateTime获取一年前/后日期】-------------------------------------
LocalDateTime localDateTime6 = LocalDateTime.now().plusYears(1);
//结果:2022-02-20T10:51:47.585
System.out.println(localDateTime6);
LocalDateTime localDateTime7 = LocalDateTime.now().plusYears(-1);
//结果:2020-02-20T10:51:47.585
System.out.println(localDateTime7);
//------------------------------【LocalDateTime获取一分钟前/后日期】-----------------------------------
LocalDateTime localDateTime8 = LocalDateTime.now().plusMinutes(1);
//结果:2021-02-20T13:54:39.209
System.out.println(localDateTime8);
LocalDateTime localDateTime9 = LocalDateTime.now().plusMinutes(-1);
//结果:2021-02-20T13:52:39.209
System.out.println(localDateTime9);
//------------------------------【LocalDateTime获取一月前/后日期】-------------------------------------
LocalDateTime localDateTime10 = LocalDateTime.now().plusMonths(1);
//结果:2021-03-20T13:55:12.718
System.out.println(localDateTime10);
LocalDateTime localDateTime11 = LocalDateTime.now().plusMonths(-1);
//结果:2021-01-20T13:55:12.718
System.out.println(localDateTime11);
//------------------------------【LocalDateTime获取一秒前/后日期】-------------------------------------
LocalDateTime localDateTime12 = LocalDateTime.now().plusSeconds(1);
//结果:2021-02-20T13:57:51.966
System.out.println(localDateTime12);
LocalDateTime localDateTime13 = LocalDateTime.now().plusSeconds(-1);
//结果:2021-02-20T13:57:49.966
System.out.println(localDateTime13);
//------------------------------【LocalDateTime获取一周前/后日期】-------------------------------------
LocalDateTime localDateTime14 = LocalDateTime.now().plusWeeks(1);
//结果:2021-02-27T13:59:46.615
System.out.println(localDateTime14);
LocalDateTime localDateTime15 = LocalDateTime.now().plusWeeks(-1);
//结果:2021-02-13T13:59:46.615
System.out.println(localDateTime15);
}
6. Clock
Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis()
来获取当前的微秒数。某一个特定的时间点也可以使用 Instant
类来表示,Instant
类也可以用来创建旧版本的java.util.Date
对象。
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
System.out.println(millis);//1552379579043
Instant instant = clock.instant();
System.out.println(instant);
Date legacyDate = Date.from(instant); //2019-03-12T08:46:42.588Z
System.out.println(legacyDate);//Tue Mar 12 16:32:59 CST 2019
7. ZoneId
在新 API 中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法 of 来获取到。 抽象类ZoneId
(在java.time
包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds
的静态方法,它返回所有区域标识符。
//输出所有区域标识符
System.out.println(ZoneId.getAvailableZoneIds());
ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());// ZoneRules[currentStandardOffset=+01:00]
System.out.println(zone2.getRules());// ZoneRules[currentStandardOffset=-03:00]
8. ZoneOffset
时区偏移量,是时区相对于格林威治时间精确到秒。
有三个常量实例:
public static final ZoneOffset UTC = ZoneOffset.ofTotalSeconds(0); // 0时区,即UTC时间。偏移量为0秒。
public static final ZoneOffset MIN = ZoneOffset.ofTotalSeconds(-MAX_SECONDS); // -18:00。偏移量为-18*60*60秒
public static final ZoneOffset MAX = ZoneOffset.ofTotalSeconds(MAX_SECONDS); // +18:00。偏移量为+18*60*60秒
可以通过静态方法ZoneOffset.of( String offsetId )
获取一个ZoneOffset的实例。
ZoneOffset offset = ZoneOffset.of("+08:00");
System.out.println(offset); // +08:00
System.out.println(ZoneOffset.ofHours(8)); // +08:00
System.out.println(ZoneOffset.ofHoursMinutes(-8, -23)); // 时、分、秒的正负号必须完全一致 -08:23
System.out.println(offset.getTotalSeconds()); // 28800
System.out.println(ZoneOffset.ofTotalSeconds(28800)); // +08:00
9. ChronoUnit
计时单位的封装类
LocalTime time1 = LocalTime.of(1, 1, 0);
LocalTime time2 = LocalTime.of(2, 2, 0);
System.out.println(ChronoUnit.HOURS.between(time1, time2)); //1
System.out.println(ChronoUnit.MINUTES.between(time1, time2)); //61
10. Duration
TemporalAmount 接口是定义时间量的框架级接口,如“6小时”、“8天”或“2年零3个月”。
Duration 类是 TemporalAmout 接口的实现类,类中定义了一些生成 Duration 实例的静态方法:
System.out.println( Duration.ofDays(2)); // 2天
System.out.println( Duration.ofHours(2)); // 2小时
System.out.println( Duration.ofMinutes(2));
System.out.println( Duration.ofSeconds(2));
System.out.println( Duration.ofMillis(2));
System.out.println( Duration.ofNanos(2)); // 2纳秒,注意毫秒和纳秒中间没有微秒:micro
11. Instant
Instant 表示一个时间戳,和旧版的 Date 可以互转。
记录着自 1970年1月1日 00:00:00 UTC 到当前时间的刻度。
Instant instant = Instant.now();
System.out.println(instant.toEpochMilli()); // 1684676392921
System.out.println(instant.getEpochSecond()); // 1684676392
// Date 与 Instant 互转
Date date = new Date();
System.out.println(date.toInstant()); // Date 转 Instant
System.out.println(Date.from(instant)); // Instant 转 Date
12. ZonedDateTime
ZonedDateTime 与 LocalDateTime 的区别就是: LocalDateTime 总是表示本地日期和时间,而 ZonedDateTime 表示一个带时区的日期和时间。
常用方法:
//从默认时区中的系统时钟中获取当前日期时间
static ZonedDateTime now()
//从指定的时钟中获取当前日期时间
static ZonedDateTime now(Clock clock)
//从指定时区中的系统时钟中获得当前日期时间
static ZonedDateTime now(ZoneId zone)
//获得 ZonedDateTime实例从年,月,日,小时,分钟,秒,纳秒和时区
static ZonedDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone)
//获得 ZonedDateTime实例从本地日期和时间
static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone)
//获得 ZonedDateTime实例从本地日期时间
static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone)
//获得 ZonedDateTime实例从一个文本字符串,如 2007-12-03T10:15:30+01:00[Europe/Paris]
static ZonedDateTime parse(CharSequence text)
//获得 ZonedDateTime实例从使用特定格式的文本字符串
static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter)
//返回此日期时间的副本,以不同的时区,保留即时
ZonedDateTime withZoneSameInstant(ZoneId zone)
//结合时间与时区来创建一个 ZonedDateTime
ZonedDateTime atZone(ZoneId zone)
转换为 ms:
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now.toInstant().toEpochMilli());
例子:
//当前时区时间
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("当前时区时间: " + zonedDateTime);
//东京时间
ZoneId zoneId = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
ZonedDateTime tokyoTime = zonedDateTime.withZoneSameInstant(zoneId);
System.out.println("东京时间: " + tokyoTime);
// ZonedDateTime 转 LocalDateTime
LocalDateTime localDateTime = tokyoTime.toLocalDateTime();
System.out.println("东京时间转当地时间: " + localDateTime);
//LocalDateTime 转 ZonedDateTime
ZonedDateTime localZoned = localDateTime.atZone(ZoneId.systemDefault());
System.out.println("本地时区时间: " + localZoned);
//打印结果
当前时区时间: 2021-01-27T14:43:58.735+08:00[Asia/Shanghai]
东京时间: 2021-01-27T15:43:58.735+09:00[Asia/Tokyo]
东京时间转当地时间: 2021-01-27T15:43:58.735
当地时区时间: 2021-01-27T15:53:35.618+08:00[Asia/Shanghai]
13. OffsetDateTime
带有时区偏移量信息的日期时间对象。
与 ZoneDateTime 的区别是,只有时区偏移量信息,没有时区ID。即仅记录时区偏移量,而不记录时区ID。
System.out.println(OffsetDateTime.now());
System.out.println(OffsetDateTime.of(2011, 5, 11, 15, 43, 23, 0, ZoneOffset.of("+08:00")));
// 2022-05-11T15:35:20.837+08:00
// 2011-05-11T15:43:23+08:00
14. TemporalAdjusters 时间调整器
所有实现了 Temporal 接口的类的实例,都可以调用自身的 with( ajuster )
方法,来调整自身的日期时间。
工具类 TemporalAdjusters 提供了许多生成 adjuster 实例的静态方法,这些 adjuster 可以实现复杂的日期调整。
需要注意的是,TemporalAdjusters 只提供了调整日期、星期的 adjuster 生成器,没有年、月、时、分、秒相关的生成器。
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.with( TemporalAdjusters.firstDayOfMonth())); // 将instant的日期调整为本月第一天,注意只是day变了,年月、时间都没变。
System.out.println(localDateTime.with( TemporalAdjusters.firstDayOfYear()));
System.out.println(localDateTime.with( TemporalAdjusters.firstDayOfNextMonth()));
System.out.println(localDateTime.with( TemporalAdjusters.firstDayOfNextYear()));
System.out.println(localDateTime.with( TemporalAdjusters.lastDayOfMonth()));
System.out.println(localDateTime.with( TemporalAdjusters.lastDayOfYear()));
System.out.println(localDateTime.with( TemporalAdjusters.ofDateAdjuster(localDate->localDate.plusDays(2))));
System.out.println(localDateTime.with( TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY))); // 当月的第一个周五
System.out.println(localDateTime.with( TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY))); // 当月的最后一个周五
System.out.println(localDateTime.with( TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY))); // 当月的第2个周五
System.out.println(localDateTime.with( TemporalAdjusters.previous( DayOfWeek.FRIDAY))); // 今天之前、距离今天最近的周五
System.out.println(localDateTime.with( TemporalAdjusters.next( DayOfWeek.FRIDAY))); // 今天之后、距离今天最近的周五
System.out.println(localDateTime.with( TemporalAdjusters.previousOrSame( DayOfWeek.FRIDAY))); // 今天或之前的最近的一个周五
System.out.println(localDateTime.with( TemporalAdjusters.nextOrSame( DayOfWeek.FRIDAY))); // 今天或之后最近的一个周五