文章目录
时间和日期
Java程序中, 时间戳(Epoch Time)通常是用long(64位)表示的毫秒数, 获取方法是System.currentTimeMillis()
标准库API: Java有新旧两套处理时间和日期的API
- 旧API在
java.util
包里, 主要包括Date
,Calendar
,TimeZone
- 新API在
java.time
包里, 主要包括LocalDateTime
,ZonedDateTime
,ZoneId
由于遗留代码问题, 旧API也要了解
旧API
Date
Date d = new Date(); // 获取当前时间(Date内部使用long来存储)
d.getYear(); // 获取1900年开始年数, 实际年份要+1900
d.getMonth(); // 获取月份(0-11), 实际月份要+1
d.getDate(); // 获取日份(1-31), 正常使用, 注意不是getDay
d.toString(); // 转换成标准时间字符串
d.toGMTString(); // 转换成GMT时区
d.toLocaleString(); // 转换成本地时间, 注意不是local是locale
自定义显示格式:
- yyyy:年
- MM:月
- EE: 周
- dd: 日
- HH: 小时
- mm: 分钟
- ss: 秒
- zz: 时区
字母越多, 显示越多/越复杂
import java.text.SimpleDateFormat;
Date d = new Date();
var sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(d));
Date
不能任意转换时区, 也不能进行时间加减
Calendar
-
Calendar可以用于获取并设置年、月、日、时、分、秒,它和Date比,主要多了一个可以做简单的日期和时间运算的功能
get(int field)
Calendar c = Calendar.getInstance(); // 获取当前时间, 唯一Calendar的获取方式 int y = c.get(Calendar.YEAR); // 获取年份, 正常使用 int m = 1 + c.get(Calendar.MONTH); // 获取月份(0-11), 实际月份需要+1 int d = c.get(Calendar.DAY_OF_MONTH); // 获取日份, 正常使用 int w = c.get(Calendar.DAY_OF_WEEK); // 获取星期(1-7), 注意从周日开始算 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); // 获取毫秒
-
可以设置特定的时间, 设置前可以使用
c.clear()
清除所有字段, 设置时日期格式同获取时一样, 对某字段设置set(int field, int value)
,也可以直接设置完整时间setTime(Data d)
等 -
c.getTime()
将Calendar
转换成Date
类型
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参数获取 -
TimeZone.getAvailableIDs()
可以列出所有有效的时区ID -
时区转换:
// 当前时间: Calendar c = Calendar.getInstance(); // 清除所有: c.clear(); // 设置为北京时区: c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置年月日时分秒: c.set(2019, 10, 20, 8, 15, 0); // 注意11月 // 显示时间: var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("America/New_York")); // 指定时区 System.out.println(sdf.format(c.getTime()));
-
利用
Calendar
进行时区转换步骤:- 清除所有字段
- 设定指定时区
- 设定日期和时间
- 创建SimpleDateFormat并设定目标时区
- 格式化获取的Date对象(注意Date对象无时区信息, 时区信息存储在SimpleDateFormat中)
-
时间加减:
c.add(Calendar.DAY_OF_MONTH, 5); // 加5天 c.add(Calendar.HOUR_OF_DAY, -2); // 减2小时
java.time
java.time.*
修复了年月和周的数字对应
- 本地日期和时间:
LocalDateTime
,LocalDate
,LocalTime
- 带时区的日期和时间:
ZonedDateTime
- 时刻:
Instant
- 时区:
ZoneId
,ZoneOffset
- 时间间隔:
Duration
- 格式化:
DateTimeFormatter
- 时间类型属于不变类
LocalDateTime
-
本地时间, 默认格式按ISO 8601规定输出:
LocalDateTime.now()
-> 2020-03-10T10:03:12.500267LocalDate.now()
-> 2020-03-10LocalTime.now()
-> 10:03:12.500267
-
时间日期转换
LocalDateTime dt = LocalDateTime.now()
:LocalDate d = dt.toLocalDate()
LocalTime t = dt.toLocalTime()
LocalDate ld = LocalDate.of(2020, 3, 10)
LocalTime lt = LocalTime.of(10, 22[, 33])
LocalDateTime ldt = ld.atStartOfDay()
LocalDateTime ldt = LocalDateTime.of(2020, 3, 10, 10, 37, 59)
LocalDateTime ldt = LocalDateTime.of(ld, lt)
LocalDateTime sdt = LocalDateTime.parse("2020-3-10T15:16:17")
LocalDate sd = LocalDate.parse("2020-3-10")
LocalTime st = LocalTime.parse("15:16:17")
DateTimeFormatter.ofPattern(String)
时间格式化, 解析时间字符串时可以传入指定的格式进行解析
-
ISO 8601标准规定日期和时间的分隔符为
T
, 标准格式:- 日期:yyyy-MM-dd
- 时间:HH:mm:ss
- 带毫秒的时间:HH:mm:ss.SSS
- 日期和时间:yyyy-MM-dd’T’HH:mm:ss
- 带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS
-
时间运算(三个时间类都可以用):
plusDays(int)
: 天数加minusMonths(int)
: 月份减minusHours(int)
- …
withYear()
withMonth()
withDayOfMonth()
withHour()
withMinute()
withSecond()
- 运算结果会自动调整
- 通用的
with()
可以进行更复杂的运算:import java.time.temporal.*;
LocalDate.now().with(TemporalAdjusters.lastDayOfMonth())
LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth())
LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY))
- 判断时间的先后(同类型比较):
isBefore()
isAfter()
- 因为
LocalDateTime
没有时区, 所以不能与时间戳进行转换
Duration和Period
-
Duration
表示时间(LocalDateTime/LocalTime)的间隔, 常用格式为P...H...M...S
, 表示间隔时分秒(可正可负):-
Duration d = Duration.between(LocalTime.of(12, 13, 14), LocalTime.of(11, 12, 13));`
-
-
Period
表示日期(LocalDate)间隔, 常用格式为P...M...D
, 表示间隔月日:-
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
-
-
创建时间间隔:
Duration d1 = Duration.ofHours(10); // P10H Duration d2 = Duration.parse("P1DT2H3M"); // 可以添加D表示天数, 自动换算成H
ZonedDateTime
ZonedDateTime
可以可以看成是LocalDateTime
和ZoneId
的结合- 创建:
ZonedDateTime.now()
: 默认时区ZonedDateTime.now(ZoneId.of("America/New_York"))
LocalDateTime.now().atZone(ZoneId.systemDefault())
LocalDateTime.now().atZone(ZoneId.of("America/New_York"))
- 时区转换:
ZonedDateTime.now().withZoneSameInstant(ZoneId.of("America/New_York"))
- 时区转换涉及夏令时, 转换后的日期和时间会相应调整
LocalDateTime ldt = ZonedDateTime.now().toLocalDateTime()
直接丢弃时区信息, 变成本地时间
ZonedDateTime
也提供了时间的加减操作
DateTimeFormatter
DateTimeFormatter
可以对LocalDateTime
和ZonedDateTime
进行时间格式化DateTimeFormatter
是不可变类, 而且是线程安全的, 可以一次获取到处引用- 创建:
var f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
var f = DateTimeFormatter.ofPattern("E, yyyy-MM-dd HH:mm", Locale.US);
f.format(ZonedDateTime.now())
Instant
java.time
中用Instant
表示时间戳, 效果与System.currentTimeMillis()
类似, 但是多了一个纳秒级的精度Instant.now()
获取当前时间戳Instant.now().getEpochSecond()
: 秒Instant.now().toEpochMilli()
: 毫秒- 创建和转换:
Instant ins = Instant.ofEpochSecond(1567891234)
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault())
ZonedDateTime.ofInstant(Instant ins, ZoneId zid)