万字博文教你搞懂java源码的日期和时间相关用法(2)

举报
小虚竹 发表于 2021/10/19 20:25:06 2021/10/19
【摘要】 万字博文教你搞懂java源码的日期和时间相关用法:旧API的Date和Calendar等和新API 放在**java.time** 这个包下的:LocalDateTime,ZonedDateTime,DateTimeFormatter和Instant等的分析和对比

❤️作者简介:Java领域优质创作者🏆,CSDN博客专家认证🏆
❤️技术活,该赏
❤️点赞 👍 收藏 ⭐再看,养成习惯

四:ZonedDateTime

支持版本及以上

jdk8

介绍

ZonedDateTime类说明

表示一个带时区的日期和时间,ZonedDateTime可以理解为LocalDateTime+ZoneId

从源码可以看出来,ZonedDateTime类中定义了LocalDateTime和ZoneId两个变量。

且ZonedDateTime类也是不可变类且是线程安全的。

public final class ZonedDateTime
        implements Temporal, ChronoZonedDateTime<LocalDate>, Serializable {

    /**
     * Serialization version.
     */
    private static final long serialVersionUID = -6260982410461394882L;

    /**
     * The local date-time.
     */
    private final LocalDateTime dateTime;
    /**
     * The time-zone.
     */
    private final ZoneId zone;
    
    ...
}

ZonedDateTime常用的用法

获取当前时间+带时区+时区转换

		// 默认时区获取当前时间		ZonedDateTime zonedDateTime = ZonedDateTime.now();		// 用指定时区获取当前时间,Asia/Shanghai为上海时区		ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));		//withZoneSameInstant为转换时区,参数为ZoneId		ZonedDateTime zonedDateTime2 = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));		System.out.println(zonedDateTime);		System.out.println(zonedDateTime1);		System.out.println(zonedDateTime2);

image-202107205246938

LocalDateTime+ZoneId变ZonedDateTime

		LocalDateTime localDateTime = LocalDateTime.now();		ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.systemDefault());		ZonedDateTime zonedDateTime2 = localDateTime.atZone(ZoneId.of("America/New_York"));		System.out.println(zonedDateTime1);		System.out.println(zonedDateTime2);

image-2021072094003

上面的例子说明了,LocalDateTime是可以转成ZonedDateTime的。

五:DateTimeFormatter

支持版本及以上

jdk8

介绍

DateTimeFormatter类说明

DateTimeFormatter的作用是进行格式化显示,且DateTimeFormatter是不可变类且是线程安全的。

public final class DateTimeFormatter {
...
}

说到时间的格式化显示,就要说老朋友SimpleDateFormat了,之前格式化Date就要用上。但是我们知道SimpleDateFormat是线程不安全的,还不清楚的,请看这里–>

DateTimeFormatter常用的用法

		ZonedDateTime zonedDateTime = ZonedDateTime.now();
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
		System.out.println(formatter.format(zonedDateTime));

		DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
		System.out.println(usFormatter.format(zonedDateTime));

		DateTimeFormatter chinaFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
		System.out.println(chinaFormatter.format(zonedDateTime));

image-202107209416958

DateTimeFormatter的坑

1、在正常配置按照标准格式的字符串日期,是能够正常转换的。如果月,日,时,分,秒在不足两位的情况需要补0,否则的话会转换失败,抛出异常。

		DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
		LocalDateTime dt1 = LocalDateTime.parse("2021-7-20 23:46:43.946", DATE_TIME_FORMATTER);
		System.out.println(dt1);

会报错:

image-202107208183

java.time.format.DateTimeParseException: Text '2021-7-20 23:46:43.946' could not be parsed at index 5

分析原因:是格式字符串与实际的时间不匹配

“yyyy-MM-dd HH:mm:ss.SSS”

“2021-7-20 23:46:43.946”

中间的月份格式是MM,实际时间是7

解决方案:保持格式字符串与实际的时间匹配

	DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
		LocalDateTime dt1 = LocalDateTime.parse("2021-07-20 23:46:43.946", DATE_TIME_FORMATTER);
		System.out.println(dt1);

image-20210720504067

2、YYYY和DD谨慎使用

		LocalDate date = LocalDate.of(2020,12,31);
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYYMM");
		// 结果是 202112
		System.out.println( formatter.format(date));

image-202107208183

Java’s DateTimeFormatter pattern “YYYY” gives you the week-based-year, (by default, ISO-8601 standard) the year of the Thursday of that week.

YYYY是取的当前周所在的年份,week-based year 是 ISO 8601 规定的。2020年12月31号,周算年份,就是2021年

image-2021072059555

 private static void tryit(int Y, int M, int D, String pat) {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pat);
        LocalDate         dat = LocalDate.of(Y,M,D);
        String            str = fmt.format(dat);
        System.out.printf("Y=%04d M=%02d D=%02d " +
            "formatted with " +
            "\"%s\" -> %s\n",Y,M,D,pat,str);
    }
    public static void main(String[] args){
        tryit(2020,01,20,"MM/DD/YYYY");
        tryit(2020,01,21,"DD/MM/YYYY");
        tryit(2020,01,22,"YYYY-MM-DD");
        tryit(2020,03,17,"MM/DD/YYYY");
        tryit(2020,03,18,"DD/MM/YYYY");
        tryit(2020,03,19,"YYYY-MM-DD");
    }
Y=2020 M=01 D=20 formatted with "MM/DD/YYYY" -> 01/20/2020
Y=2020 M=01 D=21 formatted with "DD/MM/YYYY" -> 21/01/2020
Y=2020 M=01 D=22 formatted with "YYYY-MM-DD" -> 2020-01-22
Y=2020 M=03 D=17 formatted with "MM/DD/YYYY" -> 03/77/2020
Y=2020 M=03 D=18 formatted with "DD/MM/YYYY" -> 78/03/2020
Y=2020 M=03 D=19 formatted with "YYYY-MM-DD" -> 2020-03-79

最后三个日期是有问题的,因为大写的DD代表的是处于这一年中那一天,不是处于这个月的那一天,但是dd就没有问题。

例子参考于:https://www.cnblogs.com/tonyY/p/12153335.html

所以建议使用yyyy和dd。

六:Instant

支持版本及以上

jdk8

介绍

Instant类说明

public final class Instant
        implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
        ...
        }

Instant也是不可变类且是线程安全的。其实Java.time 这个包是线程安全的。

Instant是java 8新增的特性,里面有两个核心的字段

	...	
	private final long seconds;
    
    private final int nanos;
	...

一个是单位为秒的时间戳,另一个是单位为纳秒的时间戳。

是不是跟**System.currentTimeMillis()**返回的long时间戳很像,System.currentTimeMillis()返回的是毫秒级,Instant多了更精确的纳秒级时间戳。

Instant常用的用法

 		Instant now = Instant.now();
		System.out.println("now:"+now);
		System.out.println(now.getEpochSecond()); // 秒
		System.out.println(now.toEpochMilli()); // 毫秒

image-20210720905353

Instant是没有时区的,但是Instant加上时区后,可以转化为ZonedDateTime

		Instant ins = Instant.now();
		ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
		System.out.println(zdt);

image-202107205211996

long型时间戳转Instant

要注意long型时间戳的时间单位选择Instant对应的方法转化

//1626796436 为秒级时间戳
Instant ins = Instant.ofEpochSecond(1626796436);
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println("秒级时间戳转化:"+zdt);
//1626796436111l 为秒级时间戳
Instant ins1 = Instant.ofEpochMilli(1626796436111l);
ZonedDateTime zdt1 = ins1.atZone(ZoneId.systemDefault());
System.out.println("毫秒级时间戳转化:"+zdt1);

Instant的坑

Instant.now()获取的时间与北京时间相差8个时区,这是一个细节,要避坑。

看源码,用的是UTC时间。

public static Instant now() {
        return Clock.systemUTC().instant();
    }

解决方案:

Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8));
System.out.println("now:"+now);

image-202107234326190

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。