java-日期与时间

标准库API

旧API:定义在java.util包里,包括DateCalendarTimeZone

新API:Java8引入,定义在java.time里,包括LocalDateTimeZonedDateTimeZoneId

旧API

Date

例子:

// 获取当前时间:
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()); // Tue Jan 11 18:57:51 CST 2022
// 转换为GMT时区:
System.out.println(date.toGMTString()); //格林尼治时间(晚8小时) 11 Jan 2022 10:57:51 GMT
// 转换为本地时区:
System.out.println(date.toLocaleString()); //本地时区(zh_CN) 2022-1-11 18:57:51

// 自定义格式输出
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date)); // 2022-01-11 18:57:51

缺点:

(1)不能转换时区,除了toGMTString()可以按GMT+0:00输出外,Date总是以当前计算机系统的默认时区为基础进行输出。

(2)很难对日期和时间进行加减,计算两个日期相差多少天,计算某个月第一个星期一的日期等。

Calendar

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

例子:

// 获取当前时间:
Calendar c = Calendar.getInstance();

int y = c.get(Calendar.YEAR);//2022
int m = 1 + c.get(Calendar.MONTH);//1
int d = c.get(Calendar.DAY_OF_MONTH);//11
int w = c.get(Calendar.DAY_OF_WEEK);//3(一周从周日开始)
int hh = c.get(Calendar.HOUR_OF_DAY);//19
int mm = c.get(Calendar.MINUTE);//24
int ss = c.get(Calendar.SECOND);//17
int ms = c.get(Calendar.MILLISECOND);//471

//2022-1-11 3 19:24:17.471
System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms);

Calendar只有一种方式获取,即Calendar.getInstance(),而且一获取到就是当前时间。如果我们想给它设置成特定的一个日期和时间,就必须先清除所有字段:

// 获取当前时间:
Calendar c = Calendar.getInstance();
// 清除所有字段
c.clear();
// 设置year
c.set(Calendar.YEAR, 2021);
// year增加1年
c.add(Calendar.YEAR, 1);
// 打印时间:2022-01-01 00:00:00
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));

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

将北京时间2020-01-01 20:15:00按纽约时间打印:

// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置为北京时区:
c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
// 设置年月日时分秒:
c.set(2020,0,1,20,30,0);
// 显示时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 2020-01-01 20:30:00
System.out.println(sdf.format(c.getTime()));
// 修改 sdf 的时区信息
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
// 2020-01-01 07:30:00
System.out.println(sdf.format(c.getTime()));

新API

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

  • 本地日期和时间:LocalDateTimeLocalDateLocalTime
  • 带时区的日期和时间:ZonedDateTime
  • 时刻:Instant
  • 时区:ZoneIdZoneOffset
  • 时间间隔:Duration

(2)以及一套新的用于取代SimpleDateFormat的格式化类型DateTimeFormatter

(3)和旧的API相比,新API严格区分了时刻、本地日期、本地时间和带时区的日期时间,并且,对日期和时间进行运算更加方便。

(4)此外,新API修正了旧API不合理的常量设计:

  • Month的范围用1~12表示1月到12月;
  • Week的范围用1~7表示周一到周日。

(5)最后,新API的类型几乎全部是不变类型(和String类似),可以放心使用不必担心被修改。

LocalDateTime、LocalDate、LocalTime

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

LocalDate d = LocalDate.now();// 当前日期
LocalTime t = LocalTime.now();// 当前时间
LocalDateTime dt = LocalDateTime.now();// 当前日期和时间
//严格按照ISO 8601格式打印
System.out.println(d);//2022-01-12
System.out.println(t);//08:33:00.768
System.out.println(dt);//2022-01-12T08:33:00.768

(2)LocalDateTime 转 LocalDate, LocalTime:

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

(3)指定日期创建 LocalDateTime:

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);

(4)按照ISO 8601的格式,将字符串转换为LocalDateTime

LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-11-19");
LocalTime t = LocalTime.parse("15:16:17");

(5)对日期和时间进行加减:

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

LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);// 2019-10-26T20:30:59
// 加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

(6)设置日期和时间:

对日期和时间进行调整则使用withXxx()方法,例如:withHour(15)会把10:11:12变为15:11:12

  • 调整年:withYear()
  • 调整月:withMonth()
  • 调整日:withDayOfMonth()
  • 调整时:withHour()
  • 调整分:withMinute()
  • 调整秒:withSecond()

(7)通用的with()方法做更复杂的运算:

// 本月第一天0:00时刻:
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
// 本月最后1天:
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
// 下月第1天:
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
// 本月第1个周一:
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));

(8)要判断两个LocalDateTime的先后:

LocalDate d_2020_7_1 = LocalDate.of(2020, 7, 1);
LocalDate d_2020_8_1 = LocalDate.of(2020, 8, 1);
boolean b1 = d_2020_7_1.isBefore(d_2020_8_1);//true
boolean b2 = d_2020_7_1.isAfter(d_2020_8_1);//false
boolean b3 = d_2020_7_1.isEqual(d_2020_8_1);//false

DateTimeFormatter

(1)用于自定义输出格式,或者把一个非ISO 8601格式的字符串解析成LocalDateTime

// 自定义输出格式:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String s1 = dtf.format(LocalDateTime.now());//2022/01/12 08:48:10

// 用自定义格式解析:
LocalDateTime dt = LocalDateTime.parse("2020/01/02 15:16:17", dtf);
System.out.println(dt);//2020-01-02T15:16:17

(2)创建DateTimeFormatter时,指定地区习惯Locale:

ZonedDateTime zdt = ZonedDateTime.now();

//默认地区
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm");
System.out.println(dtf1.format(zdt));//2022 一月 12 星期三 15:23

//中国地区
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
System.out.println(dtf2.format(zdt));//2022 一月 12 星期三 15:23

//美国地区
DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.US);
System.out.println(dtf3.format(zdt));//2022 Jan 12 Wed 15:23

ZonedDateTime

(1)

LocalDateTime无法与时间戳进行转换,因为LocalDateTime没有时区,无法确定某一时刻。

ZonedDateTime相当于LocalDateTime加时区的组合,它具有时区,可以与long表示的时间戳进行转换。

可以简单地把ZonedDateTime理解成LocalDateTimeZoneIdZoneIdjava.time引入的新的时区类,注意和旧的java.util.TimeZone区别。

(2)指定时间:

//创建时间
LocalDateTime dt = LocalDateTime.of(2020, 1, 2, 15, 16, 17);

//创建时区
ZoneId zone_shanghai = ZoneId.of("Asia/Shanghai");
ZoneId zone_0800 = ZoneId.of("+08:00");
ZoneId zone_utc = ZoneOffset.UTC;
ZoneId zone_0000 = ZoneId.of("+00:00");

System.out.println(zone_shanghai);//Asia/Shanghai
System.out.println(zone_0800);//+08:00
System.out.println(zone_utc);//Z
System.out.println(zone_0800.equals(zone_shanghai));//false
System.out.println(zone_0000.equals(zone_utc));//true

//给时间指定时区(shanghai)
ZonedDateTime zdt_shanghai = ZonedDateTime.of(dt, zone_shanghai);
//上海时区 的时间
System.out.println(zdt_shanghai);//2020-01-02T15:16:17+08:00[Asia/Shanghai]
//上海时区 对应的时刻(即0时区现在的时间)
System.out.println(zdt_shanghai.toInstant());//2020-01-02T07:16:17Z

//给时间指定时区(+08:00)
ZonedDateTime zdt_0800 = ZonedDateTime.of(dt, zone_0800);
//东8区 的时间
System.out.println(zdt_0800);//2020-01-02T15:16:17+08:00
//东8区 对应的时刻(即0时区现在的时间)
System.out.println(zdt_0800.toInstant());//2020-01-02T07:16:17Z

//东8区 与 上海时区,虽然时刻相等,但本身不相等
System.out.println(zdt_0800.equals(zdt_shanghai));//false

//给时间指定时区(UTC)
ZonedDateTime zdt_utc = ZonedDateTime.of(dt, zone_utc);
//UTC(0时区)的时间
System.out.println(zdt_utc);//2020-01-02T15:16:17Z
//UTC 对应的时刻(即0时区现在的时间)
System.out.println(zdt_utc.toInstant());//2020-01-02T15:16:17Z

(3)当前时间:

ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间

(4)时区转换(将北京时间转换为纽约时间):

// 以中国时区获取当前时间:
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));

输出:

2022-01-12T14:45:43.906+08:00[Asia/Shanghai]
2022-01-12T01:45:43.906-05:00[America/New_York]

Duration和Period

(1)Duration:时刻的间隔。Period:日期的间隔。

(2)Duration 和 Period 的表示方法符合ISO 8601的格式,它以P...T...的形式表示,P...T之间表示日期间隔,T后面表示时间间隔。如果是PT...的格式表示仅有时间间隔。

Duration格式:PT...Period格式:P...

(3)

Duration:

/*--- 创建Duration ---*/
Duration d1 = Duration.ofDays(1);
System.out.println(d1);//PT24H

Duration d2 = Duration.of(1, ChronoUnit.HOURS);
System.out.println(d2);//PT1H

Duration d3 = Duration.ofHours(2);
System.out.println(d3);//PT2H

Duration d4 = Duration.parse("PT1H2M3.123456789S");
System.out.println(d4);//PT1H2M3.456789123S

Duration d5 = Duration.parse("-PT1H");
System.out.println(d5);//PT-1H

Duration d6 = Duration.between(LocalDateTime.of(2020, 10, 1, 15, 15, 15), LocalDateTime.of(2020, 10, 1, 16, 16, 16));
System.out.println(d6);//PT1H1M1S

Period:

/*--- 创建Period ---*/
Period p1 = Period.ofDays(1);
System.out.println(p1);//P1D

Period p2 = Period.of(1, 2, 3);
System.out.println(p2);//P1Y2M3D

Period p3 = Period.parse("P1D");
System.out.println(p3);//P1D

Period p4 = Period.between(LocalDate.of(2020, 10, 1), LocalDate.of(2020, 10, 2));//只能传入LocalDate
System.out.println(p4);//P1D

Instant

Instant 代表时刻。

我们用Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似:

Instant now = Instant.now();
System.out.println(now); // 2022-01-12T07:41:22.862Z
System.out.println(now.getEpochSecond()); // 转换为秒
System.out.println(now.toEpochMilli()); // 转换为毫秒

最佳实践

旧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()));
calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值