详解java中的日期与时间;新旧API对比;各种日期格式转换

news2025/1/10 3:17:55

一、基本概念

1、本地时间

  • 不同的时区,在同一时刻,本地时间是不同的。
  • 全球一共分为24个时区,伦敦所在的时区称为标准时区,其他时区按东/西偏移的小时区分,北京所在的时区是东八区

在这里插入图片描述

2、时区

  • GMT
    • GMT+08:00:东八区
    • 格林威治时区/GMT+00:00
  • UTC
    • UTC+08:00:东八区
    • GMTUTC可以认为基本是等价的,只是UTC使用更精确的原子钟计时,每隔几年会有一个闰秒,我们在开发程序的时候可以忽略两者的误差,因为计算机的时钟在联网的时候会自动与时间服务器同步时间。
  • CST
    • China Standard Time:中国标准时间
    • Central Standard Time USA:美国中部时间
    • 缩写容易产生混淆,尽量不要使用缩写
  • 洲/城市
    • Asia/Shanghai 表示上海所在地的时区
    • 特别注意城市名称不是任意的城市,而是由国际标准组织规定的城市

3、夏令时

  • 所谓夏令时,就是夏天开始的时候,把时间往后拨1小时,夏天结束的时候,再把时间往前拨1小时
  • 我们国家实行过一段时间夏令时,1992年就废除了,但是美国人到现在还在使用,所以时间换算更加复杂
  • 计算夏令时请使用标准库提供的相关类,不要试图自己计算夏令时。

4、本地化

  • 在计算机中,通常使用Locale表示一个国家或地区的日期、时间、数字、货币等格式
  • Locale语言_国家的字母缩写构成,例如,zh_CN表示中文+中国,en_US表示英文+美国。语言使用小写,国家使用大写

如:

地区中国 (zh_CN)美国 (en_US)
购买价格12000.0012,000.00
购买日期2016-11-3011/30/2016

二、新旧API概述

API所在包主要类
java.utilDate、Calendar、TimeZone
新(Java1.8)java.timeLocalDateTime、ZonedDateTime、ZoneId

三、旧 java.util

1、Epoch_Time

  • Epoch Time是计算从1970年1月1日零点(格林威治时区/GMT+00:00)到现在所经历的秒数,例如:

1574208900表示从1970年1月1日零点GMT时区到该时刻一共经历了1574208900秒,换算成伦敦、北京和纽约时间分别是:

1574208900 
= 北京时间2019-11-20 8:15:00
= 伦敦时间2019-11-20 0:15:00
= 纽约时间2019-11-19 19:15:00
  • 在计算机中,只需要存储一个整数表示某一时刻,如1574208900。当需要显示为某一地区的当地时间时,就把它格式化为一个字符串:
String displayDateTime(int n, String timezone) { ... }
  • Epoch Time又称为时间戳,在不同的编程语言中,会有几种存储方式:
    • 以秒为单位的整数:1574208900,缺点是精度只能到秒;
    • 以毫秒为单位的整数:1574208900123,最后3位表示毫秒数;
    • 以秒为单位的浮点数:1574208900.123,小数点后面表示零点几秒。

它们之间转换非常简单。而在Java程序中,时间戳通常是用long表示的毫秒数,即:

long t = 1574208900123L;
2019-11-20T8:15:00.123//北京时间

//获取时间戳最常用的方法
System.currentTimeMillis(); 

2、Date

  • java.util.Date是用于表示一个日期和时间的对象
  • 注意与java.sql.Date区分,后者用在数据库中
  • 如果观察Date的源码,可以发现它实际上存储了一个long类型的以毫秒表示的时间戳
public class Date implements Serializable, Cloneable, Comparable<Date> {
    private transient long fastTime;
    ...
}
// 获取当前时间:
Date date = new Date();
System.out.println(date.getYear() + 1900); // 必须加上1900
System.out.println(date.getMonth() + 1); // 0~11,必须加上1
System.out.println(date.getDate()); // 1~31,不能加1
// 转换为String:
System.out.println(date.toString());
// 转换为GMT时区:
System.out.println(date.toGMTString());
// 转换为本地时区:
System.out.println(date.toLocaleString());
  • 注意getYear()返回的年份必须加上1900

  • getMonth()返回的月份是0-11,分别表示1~12月,所以要加1

  • getDate()返回的日期范围是1-31,又不能加1

格式化输出SimpleDateFormat

// 获取当前时间:
Date date = new Date();
//yyyy:年//MM:月//dd: 日//HH: 小时//mm: 分钟//ss: 秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
  • Data对象的严重问题
    • 它不能转换时区,除了toGMTString()可以按GMT+0:00输出外,Date总是以当前计算机系统的默认时区为基础进行输出
    • 很难对日期和时间进行加减,计算两个日期相差多少天,计算某个月第一个星期一的日期等

3、Calendar

可以用于获取并设置年、月、日、时、分、秒,它和Date比,主要多了一个可以做简单的日期和时间运算的功能

// 获取当前时间:
Calendar c = Calendar.getInstance();
int y = c.get(Calendar.YEAR);
int m = 1 + c.get(Calendar.MONTH);
int d = c.get(Calendar.DAY_OF_MONTH);
int w = c.get(Calendar.DAY_OF_WEEK);
int hh = c.get(Calendar.HOUR_OF_DAY);
int mm = c.get(Calendar.MINUTE);
int ss = c.get(Calendar.SECOND);
int ms = c.get(Calendar.MILLISECOND);
System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms);
  • Calendar获取年月日为get(int field)
  • 返回的年份不必转换
  • 返回的月份仍然要加1
  • 返回的星期要特别注意,1-7分别表示周日,周一,……,周六
  • Calendar只有一种方式获取,即Calendar.getInstance(),而且一获取到就是当前时间
  • 如果想给它设置成特定的一个日期和时间,就必须先清除所有字段:
// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置2019年:
c.set(Calendar.YEAR, 2019);
// 设置9月:注意8表示9月:
c.set(Calendar.MONTH, 8);
// 设置2日:
c.set(Calendar.DATE, 2);
// 设置时间:
c.set(Calendar.HOUR_OF_DAY, 21);
c.set(Calendar.MINUTE, 22);
c.set(Calendar.SECOND, 23);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));// 2019-09-02 21:22:23
  • 利用Calendar.getTime()可以将一个Calendar对象转换成Date对象,然后就可以用SimpleDateFormat进行格式化了

4、TimeZone

  • CalendarDate相比,它提供了时区转换的功能。时区用TimeZone对象表示
TimeZone tzDefault = TimeZone.getDefault(); // 当前时区
TimeZone tzGMT9 = TimeZone.getTimeZone("GMT+09:00"); // GMT+9:00时区
TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); // 纽约时区
System.out.println(tzDefault.getID()); // Asia/Shanghai
System.out.println(tzGMT9.getID()); // GMT+09:00
System.out.println(tzNY.getID()); // America/New_York
  • 时区的唯一标识是以 字符串 表示的ID,获取指定TimeZone对象也是以这个ID为参数获取,GMT+09:00Asia/Shanghai都是有效的时区ID。
  • 要列出系统支持的所有ID,请使用TimeZone.getAvailableIDs()

有了时区,我们就可以对指定时间进行转换。例如,下面的例子演示了如何将北京时间2019-11-20 8:15:00转换为纽约时间:

// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置为北京时区:
c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
// 设置年月日时分秒:
c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
// 显示时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println(sdf.format(c.getTime()));// 2019-11-19 19:15:00

可见,利用Calendar进行时区转换的步骤是:

  1. 清除所有字段;
  2. 设定指定时区;
  3. 设定日期和时间;
  4. 创建SimpleDateFormat并设定目标时区;
  5. 格式化获取的Date对象(注意Date对象无时区信息,时区信息存储在SimpleDateFormat中)

因此,本质上时区转换只能通过SimpleDateFormat在显示的时候完成

  • Calendar也可以对日期和时间进行简单的加减
// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置年月日时分秒:
c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
// 加5天并减去2小时:
c.add(Calendar.DAY_OF_MONTH, 5);
c.add(Calendar.HOUR_OF_DAY, -2);
// 显示时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = c.getTime();
System.out.println(sdf.format(d));// 2019-11-25 6:15:00

四、新 java.time

1、新API概述

从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:

  • 本地日期和时间:LocalDateTimeLocalDateLocalTime

  • 带时区的日期和时间:ZonedDateTime

  • 时刻:Instant

  • 时区:ZoneIdZoneOffset

  • 时间间隔:Duration

  • 天数间隔:Period

  • 格式化类型:DateTimeFormatter

    • 可以取代旧的 SimpleDateFormat
    区别DateTimeFormatterSimpleDateFormat
    可变性final非final
    多线程线程安全线程非安全

新特性

  • 新API严格区分了时刻、本地日期、本地时间和带时区的日期时间
  • 对日期和时间进行运算更加方便
  • 新API修正了旧API不合理的常量设计
    • Month的范围用1~12,表示1月到12月
    • Week的范围用1~7,表示周一到周日
  • 新API的类型几乎全部是不变类型(和String类似),可以放心使用不必担心被修改。线程安全。

2、本地日期时间

LocalDateTime、LocalDate、LocalTime

2.1、得到本地日期和时间

1)now:直接获取

LocalDate d = LocalDate.now(); // 当前日期
LocalTime t = LocalTime.now(); // 当前时间
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
System.out.println(d); // 严格按照ISO 8601格式打印
System.out.println(t); // 严格按照ISO 8601格式打印
System.out.println(dt); // 严格按照ISO 8601格式打印

本地日期和时间通过now()获取到的总是以当前默认时区返回的,和旧API不同,LocalDateTimeLocalDateLocalTime默认严格按照ISO 8601规定的日期和时间格式进行打印

  • 优化:上述代码其实有一个小问题,在获取3个类型的时候,由于执行一行代码总会消耗一点时间,因此,3个类型的日期和时间很可能对不上(时间的毫秒数基本上不同)。为了保证获取到同一时刻的日期和时间,可以改写如下:
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间

2)of:指定

// 指定日期和时间:
LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);

3)parse:解析

  • 参数为字符串。严格按标准格式。
  • 如果某项是一位数,须在前面补0。如:“2019-5-19"不符合,应为"2019-05-19”
LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-05-19");
LocalTime t = LocalTime.parse("15:06:17");

4)toXXX:转换

从 LocalDateTime 转换为 LocalDate、LocalTime

LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间

2.2、标准格式

  • 日期:yyyy-MM-dd
  • 时间:HH:mm:ss
  • 带毫秒的时间:HH:mm:ss.SSS
  • 日期和时间:yyyy-MM-ddTHH:mm:ss
  • 带毫秒的日期和时间:yyyy-MM-ddTHH:mm:ss.SSS

注意ISO 8601规定的日期和时间分隔符是T

2.3、格式化 DateTimeFormatter

// 自定义格式化:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now()));
// 用自定义格式解析:
// 字符串和dtf格式一致
// 如果不加dtf,字符串需按标准格式:"2019-11-30T15:16:17"
LocalDateTime dt2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
System.out.println(dt2);

2.4、日期加减

LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 加5天减3小时:
LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
System.out.println(dt2); // 2019-10-31T17:30:59
// 减1月:
LocalDateTime dt3 = dt2.minusMonths(1);
System.out.println(dt3); // 2019-09-30T17:30:59

月份加减会自动调整日期,例如从2019-10-31减去1个月得到的结果是2019-09-30,因为9月没有31日

2.5、日期调整 withXXX()

  • 调整年:withYear()
  • 调整月:withMonth()
  • 调整日:withDayOfMonth()
  • 调整时:withHour()
  • 调整分:withMinute()
  • 调整秒:withSecond()
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 日期变为31日:
LocalDateTime dt2 = dt.withDayOfMonth(31);
System.out.println(dt2); // 2019-10-31T20:30:59
// 月份变为9:
LocalDateTime dt3 = dt2.withMonth(9);
System.out.println(dt3); // 2019-09-30T20:30:59

调整月份时,会相应地调整日期,即把2019-10-31的月份调整为9时,日期也自动变为30

2.6、通用with()方法

  • 用LocalDate,没有具体的时间
  • TemporalAdjusters
    • dayOfWeekInMonth() :一周中的某一天,例如,三月中第二个星期二
    • firstDayOfMonth() :当前月的第一天
    • firstDayOfNextMonth() : 下一个月的第一天
    • firstDayOfNextYear() : 下一年的第一天
    • firstDayOfYear() : 当年的第一天
    • lastDayOfMonth() : 当月的最后一天
    • nextOrSame() : 下一次或当天发生的一周中的某天
// 本月第一天0:00时刻:
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay);

// 本月最后1天:
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay);

// 下月第1天:
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay);

// 本月第1个周一:
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekday);

2.7、比较先后

  • isBefore()
  • isAfter()
LocalDateTime now = LocalDateTime.now();
LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
System.out.println(now.isBefore(target));
System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));

2.8、获取时间戳

  • LocalDateTime无法与时间戳进行转换,因为LocalDateTime没有时区,无法确定某一时刻。
  • ZonedDateTime相当于LocalDateTime加时区的组合,具有时区,可以与long表示的时间戳进行转换
  • 获取时间戳
Long milliSecond = LocalDateTime.now()
    .toInstant(ZoneOffset.of("+8")).toEpochMilli();

2.9、Duration和Period

  • Duration 表示两个时刻之间的时间间隔。精确到分秒
  • Period 表示两个日期之间的天数
LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
Duration d = Duration.between(start, end);
System.out.println(d); // PT1235H10M30S

Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
System.out.println(p); // P1M21D
  • 两个LocalDateTime之间的差值使用Duration表示,类似PT1235H10M30S,表示1235小时10分钟30秒
  • 两个LocalDate之间的差值用Period表示,类似P1M21D,表示1个月21天
  • DurationPeriod的表示方法也符合ISO 8601的格式,它以P...T...的形式表示,P...T之间表示日期间隔,T后面表示时间间隔。如果是PT...的格式表示仅有时间间隔
  • 利用ofXxx()或者parse()方法也可以直接创建Duration
Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes

2.10、历史

因为开源的Joda Time设计很好,应用广泛,所以JDK团队邀请Joda Time的作者Stephen Colebourne共同设计了java.timeAPI

3、ZonedDateTime

3.1、简介

  • LocalDateTime表示本地日期和时间,要表示一个带时区的日期和时间,就需要ZonedDateTime
  • 可以简单地把ZonedDateTime理解成LocalDateTimeZoneId
  • ZoneIdjava.time引入的新的时区类,注意和旧的java.util.TimeZone区别

3.2、创建ZonedDateTime对象

  • now()
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区Zoned
DateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
System.out.println(zbj);
System.out.println(zny);
//输出
2019-09-15T20:58:18.786182+08:00[Asia/Shanghai]
2019-09-15T08:58:18.788860-04:00[America/New_York]

观察打印的两个ZonedDateTime,发现它们时区不同,但表示的时间都是同一时刻(毫秒数不同是执行语句时的时间差)

  • 给LocalDateTime附加ZoneId
LocalDateTime ldt = LocalDateTime.of(2019, 9, 15, 15, 16, 17);
ZonedDateTime zbj = ldt.atZone(ZoneId.systemDefault());
ZonedDateTime zny = ldt.atZone(ZoneId.of("America/New_York"));
System.out.println(zbj);System.out.println(zny);
//输出
2019-09-15T15:16:17+08:00[Asia/Shanghai]
2019-09-15T15:16:17-04:00[America/New_York]

以这种方式创建的ZonedDateTime,它的日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻

3.3、时区转换

  1. 需要有一个ZonedDateTime对象
  2. 通过withZoneSameInstant()将关联时区转换到另一个时区,转换后日期和时间都会相应调整
// 以中国时区获取当前时间:
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(zbj);
System.out.println(zny);
  • 要特别注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的

  • 涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时

3.4、转为LocalDateTime

转换为LocalDateTime时,直接丢弃时区信息

ZonedDateTime zdt = ...
LocalDateTime ldt = zdt.toLocalDateTime();

3.5、加减等操作

和LocalDateTime一样

4、DateTimeFormatter

  • 线程安全
  • 可以只创建一个实例,到处引用
//创建方式一
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
//创建方式二
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);

5、Instant 时刻 时间戳

  • java.time中以Instant类型表示时间戳,即时刻
  • Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似
Instant now = Instant.now();//当前时刻
System.out.println(now);
System.out.println(now.getEpochSecond());//秒数
System.out.println(now.toEpochMilli());//毫秒
//类似于
System.out.println(System.currentTimeMillis());

// 输出
2023-05-19T01:33:41.578Z
1684460021
1684460021578
1684460021633
//最后三位毫秒值不同是因为两条语句的运行时差
  • Instant内部只有两个核心字段:
public final class Instant implements ... {
    private final long seconds;
    private final int nanos;
}

6、相互转换

在这里插入图片描述

  • LocalDateTime 转 ZonedDateTime

    LocalDateTime ldt = LocalDateTime.now();
    //给LocalDateTime添加时区
    ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
    ZonedDateTime zdt1 = ldt.atZone(ZoneId.of("America/New_York"));//转换后不是同一时刻
    
  • LocalDateTime 转 Instant

    LocalDateTime ldt = LocalDateTime.now();
    //给LocalDateTime添加时区,转为ZonedDateTime,再调用toInstant()
    Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
    
  • LocalDateTime 转 long

    LocalDateTime ldt = LocalDateTime.now();
    //给LocalDateTime添加时区,转为ZonedDateTime,再调用toInstant()
    Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
    //由instant 转为 long
    long second = instant.getEpochSecond();
    long milli = instant.toEpochMilli();
    
  • ZonedDateTime 转 LocalDateTime

    ZonedDateTime zdt = ZonedDateTime.now();
    LocalDateTime ldt = zdt.toLocalDateTime();//转换为LocalDateTime后,直接丢弃时区信息
    
  • ZonedDateTime 转 Instant

    Instant instant = ZonedDateTime.now().toInstant();
    
  • ZonedDateTime 转 long

    long second = ZonedDateTime.now().toInstant().getEpochSecond();
    long milli = ZonedDateTime.now().toInstant().toEpochMilli();
    
  • Instant 转 LocalDateTime

    LocalDateTime ldt = Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime();
    
  • Instant 转 ZonedDateTime

    // atZone()
    ZonedDateTime zdt = Instant.now().atZone(ZoneId.systemDefault());
    // ofInstant()
    ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
    
  • Instant 转 long

    long second = Instant.now().getEpochSecond();
    long milli = Instant.now().toEpochMilli();
    
  • long 转 LocalDateTime

    long s = 1623729859;
    LocalDateTime ldt = Instant.ofEpochSecond(s).atZone(ZoneId.systemDefault()).toLocalDateTime();
    
  • long 转 ZonedDateTime

    long s = 1623729859;
    ZonedDateTime zdt = Instant.ofEpochSecond(s).atZone(ZoneId.systemDefault());
    
  • long 转 Instant

    long s = 1623729859;
    Instant instant = Instant.ofEpochSecond(s);
    

五、最佳实践

  • 由于Java提供了新旧两套日期和时间的API,除非涉及到遗留代码,否则我们应该坚持使用新的API

  • 旧API转新API

    • 如果要把旧式的DateCalendar转换为新API对象,可以通过toInstant()方法转换为Instant对象,再继续转换为ZonedDateTime
    // Date -> Instant:
    Instant ins1 = new Date().toInstant();
    // Calendar -> Instant -> ZonedDateTime:
    Calendar calendar = Calendar.getInstance();
    Instant ins2 = calendar.toInstant();
    ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());
    
  • 新API转旧API

    • 如果要把新的ZonedDateTime转换为旧的API对象,只能借助long型时间戳做一个“中转”:
    // ZonedDateTime -> long:
    ZonedDateTime zdt = ZonedDateTime.now();
    long ts = zdt.toEpochSecond() * 1000;
    // long -> Date:
    Date date = new Date(ts);
    // long -> Calendar:
    Calendar calendar = Calendar.getInstance();
    calendar.clear();
    calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId()));
    calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
    
  • 在数据库中存储日期和时间

数据库对应Java类(旧)对应Java类(新)
DATETIMEjava.util.DateLocalDateTime
DATEjava.sql.DateLocalDate
TIMEjava.sql.TimeLocalTime
TIMESTAMPjava.sql.TimestampLocalDateTime

实际上,在数据库中,我们需要存储的最常用的是时刻(Instant),因为有了时刻信息,就可以根据用户自己选择的时区,显示出正确的本地时间。所以,最好的方法是直接用长整数long表示,在数据库中存储为BIGINT类型

六、获取日期时间方法汇总

编号途经代码说明建议
1java.util.Datenew Date()Date对象过时
2SystemSystem.currentTimeMillis()时间戳常用
3java.util.CalendarCalendar.getInstance().getTime()Date升级版较常用
4java.time.LocalDateLocalDate.now()只有日期推荐 (JDK1.8新API)
5java.time.LocalTimeLocalTime.now()只有时间推荐 (JDK1.8新API)
6java.time.LocalDateTimeLocalDateTime.now()日期时间推荐 (JDK1.8新API)
7java.time.ZonedDateTimeZonedDateTime.now()有时区推荐 (JDK1.8新API)
8InstantInstant.now()时刻推荐 (JDK1.8新API)
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Calendar;
import java.util.Date;

/**
 * @author ajun
 * Date 2021/7/9
 * @version 1.0
 * 获取当前时间
 */
public class CurrentTime {
    public static void main(String[] args) {
        //getByDate();
        //getBySystem();
        //getByCalendar();
        //getByLocalDate();
        //getByLocalTime();
        //getByLocalDateTime();
        //getByZonedDateTime();
        getByInstant();
    }

    // 使用java.util.Date类
    public static void getByDate(){
        Date date = new Date();
        //SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //System.out.println(format.format(date));
        System.out.println(date);
    }

    // 使用System类
    public static void getBySystem(){
        long l = System.currentTimeMillis();
        Date date = new Date(l);
        System.out.println(date);
    }

    // 使用Calendar类
    public static void getByCalendar(){
        Date time = Calendar.getInstance().getTime();
        System.out.println(time);
    }

    // 使用LocalDate类:只有日期,无时间
    public static void getByLocalDate(){
        LocalDate now = LocalDate.now();
        System.out.println(now);
    }

    // 使用LocalTime类:只有时间,无日期
    public static void getByLocalTime(){
        LocalTime now = LocalTime.now();
        System.out.println(now);
    }

    // 使用LocalDateTime类
    public static void getByLocalDateTime(){
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
    }

    // 使用ZonedDateTime类
    public static void getByZonedDateTime(){
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println(now);
    }

    // 使用Instant类
    public static void getByInstant(){
        Instant now = Instant.now();
        System.out.println(now);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/543143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux基础指令(含英文注释)

前言&#xff1a; Linux下一切皆文件。 文件 文件内容 文件属性。 操作文件 对内容的操作 对属性的操作。 主要的文件是目录文件dir&#xff0c;和普通文件。&#xff08;还有其它的&#xff09; 一、ls/pwd 1、ls 对于目录&#xff0c;该命令列出该目录下的所有子目…

建站笔记——跟小枫社长学建站

该文章为看视频时的一些笔记&#xff0c;完整版可以看小枫社长的视频 小枫社长视频原址 一、租云服务器 学生或者新人可以免费试用几个月 腾讯云阿里云 阿里云对控制台进行了全面升级&#xff0c;在首页使用了新的设计方案&#xff0c;云服务器ECS位置如下&#xff0c;点击左…

浙大数据结构第六周之初识图

题目详情&#xff1a;06-图1 列出连通集 给定一个有N个顶点和E条边的无向图&#xff0c;请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时&#xff0c;假设我们总是从编号最小的顶点出发&#xff0c;按编号递增的顺序访问邻接点。 输入格式: 输入第1…

SAP从入门到放弃系列之MTS策略测试记录

​包括&#xff1a;10策略、11策略、30策略、40策略、52策略、63策略。 10策略 业务特点&#xff1a; 策略 10 在大规模生产场景中特别有用&#xff0c;它通常与重复制造 (REM) 相结合。如果企业PMC排产时&#xff0c;希望产品由生产计划&#xff08;需求管理&#xff09;决…

了解“感应雷”危害,针对性防护

直击雷的危害&#xff0c;比较容易理解&#xff0c;其防范措施也相对简单明了——避雷针、引下线、接地装置&#xff08;当然&#xff0c;这里面也有很多的细节和要求&#xff0c;也是需要专业人员来从事的&#xff0c;在此就不展开了&#xff09;&#xff0c;所以对直击雷的防…

springCloud使用maven

springCloud项目使用maven集成nexus 一&#xff1a;故事背景二&#xff1a;基础概念2.1 什么是Maven2.2 什么是nexus 三&#xff1a;实操3.1 setting文件配置3.2 项目内pom.xml配置3.3 jar上传3.3.1 maven插件上传3.3.2 mvn命令上传3.3.3 页面上传3.3.4 通过Rest的方式进行上传…

c++之到底什么时候需要加入析构函数定义?以及内存泄漏

析构函数(destructor)是在类对象销毁时自动调用的特殊成员函数。它的主要作用是释放对象的资源,如堆内存、文件、网络连接等。析构函数的特点是: 1. 析构函数名称与类名相同,前面加~。如~MyClass()。 2. 析构函数没有返回类型。 3. 析构函数可以重载,但不能有参数。 4. 一个…

2023年认证杯二阶段C题数据合并python以及matlab多途径实现代码

对于每种心率下给出的数据&#xff0c;我们需要进行合并才能方便后续处理&#xff0c;这里为大家展示利用python以及matlab分别实现合并的代码 import pandas as pd import os# 创建一个空的DataFrame对象 merged_data pd.DataFrame()# 设置数据文件所在的文件夹路径 folder_…

本地编译和交叉编译的理解

1、本地编译&#xff1a;常见的软件开发&#xff0c;都是属于本地编译&#xff1a;在当前的PC下&#xff0c;x86的CPU下&#xff0c;直接编译出来程序&#xff0c;可以运行的程序&#xff08;或者库文件&#xff09;&#xff0c;其可以直接在当前的环境&#xff0c;即x86的CPU下…

【linux】挖矿病毒nanominer伪装成python占用服务器GPU!本文带你分析并杀毒!

病毒表现 gpustat -cpu 可以看到root用户将GPU的核心跑满了每个占用都是100%&#xff0c;显存吃了6G多。 nvidia-smi 不能正常显示GPU被哪些进程占用 ![在这里插入图片描述](https://img-blog.csdnimg.cn/780f90080a084a44ac59227e384f985b.png 病毒文件分析 在/tmp/.x/…

浅比一下,各大免费CDN的速度

前言 CDN那么多&#xff0c;到底哪个体验最好呢&#xff1f;今天来简单对比一下免费的CDN之间的差距&#xff0c;看看那家最适合白嫖 百度CDN 官网&#xff1a;https://su.baidu.com/ 平均速度62.7ms&#xff0c;需要备案的域名才行&#xff0c;支持SSL&#xff0c;流量5G/天…

Spring 整合 Mybatis -- Spring入门保姆级教程(四)

文章目录 前言五、Spring 整合 Mybatis1.Mybatis一般开发流程2.spring整合mybatis思路分析3.Spring整合Mybatis环境准备&#xff08;注解开发&#xff09;4.Spring整合Mybatis5.小结 引用网站及博客总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类…

​AI + 非遗文化传播,人工智能师资培训重磅招募

大语言模型热度空前&#xff0c;诸如文心一言、 ChatGPT 等已经能够与人对话互动、回答问题、协助创作&#xff0c;逐渐应用于人们的工作和生活&#xff0c;也引发了社会热议。为推动大模型及人工智能相关专业人员的培养&#xff0c;同时将人工智能技术融入非遗文化传播&#x…

电子采购系统实现方式(SRM供应商管理)

企事业数字化转型专家&#xff0c;提供各类应用解决方案。您身边的赋能小助手&#xff01; 文章目录 前言一、当下采购的痛点二、解决方案-供应商管理1.供应商管理 三、解决方案-企业询价、供应商报价管理四、解决方案-采购订单五、送货、到货、订单管理总结 前言 随着各类产业…

软件工程(五) 结构化需求与面向对象需求分析

1、结构化需求分析(SA) 结构化需求分析要完成功能模型、数据模型和行为模型的构建。 1.1、功能模型 一般用数据流图进行建模,也就是DFD。 比如我们要开发一个子啊先教育平台系统,我们把这个系统看作一个整体,去分析哪些人员会用到这个系统。比如有学员,培训部,辅导老师…

阿里云、腾讯云、移动云飙“价”:智能普惠成新风向?

经过过去一年的“低迷”境况之后&#xff0c;2023年云服务商因为AI大模型的爆发&#xff0c;重新燃起了斗志。站在当下的时间节点&#xff0c;云服务商们也在重新思考如何在新形势下&#xff0c;让自己占据更大的优势&#xff0c;于是一场围绕“技术竞争与市场争夺”的新战争打…

spring boot 项目yml方式区分开发、测试生产,并在maven中配置

1、新建配置文件&#xff0c;分别为application.yml、application-dev.yml、application-test.yml和application-prod.yml 2、在application.yml文件中添加如下代码 spring:profiles:## ??pom???????profile??active: "profiles.active"3、在pom中的proj…

class生命周期

组件实例 化的过程称作组件的挂载(mount)。 组件挂载的过程: 构造函数(constructor)最先执行&#xff0c;componentWillMount() 会在 render() 方法之前 执行&#xff0c;而 componentDidMount() 在 render() 方法之后执行。组件更新&#xff0c;调用顺序如下: • componentWi…

为什么 PostQuitMessage 会被设计出来?

众所周知&#xff0c;我们可以使用 SendMessage 或者 PostMessage 来发送消息&#xff0c;那为什么在 Win32 API 中&#xff0c;会单独设计一个 PostQuitMessage 呢? 有一位读者 A. Skrobov 问我&#xff0c;”PostQuitMessage 和 PostThreadMessage(GetCurrentThreadId, WM_…

【PyTorch框架】——框架安装使用流程搭建PyTorch神经网络气温预测

目录 一、引言 二、使用流程——最简单例子试手 三、分类任务——气温预测 总结&#xff1a; 一、引言 Torch可以当作是能在GPU中计算的矩阵&#xff0c;就是ndarray的GPU版&#xff01;TensorFlow和PyTorch可以说是当今最流行的框架&#xff01;PyTorch用起来简单&#xff…