特此说明:此文章来源于初学Java8时间API时的笔记
格式化编码
字母 | 描述 | 示例 |
---|---|---|
G | 纪元标记 | AD |
y | 四位年份 | 2001 |
M | 月份 | July or 07 |
d | 一个月的日期 | 10 |
h | A.M./P.M. (1~12)格式小时 | 12 |
H | 一天中的小时 (0~23) | 22 |
m | 分钟数 | 30 |
s | 秒数 | 55 |
S | 毫秒数 | 234 |
E | 星期几 | Tuesday |
D | 一年中的日子 | 360 |
F | 一个月中第几周的周几 | 2 (second Wed. in July) |
w | 一年中第几周 | 40 |
W | 一个月中第几周 | 1 |
a | A.M./P.M. 标记 | PM |
k | 一天中的小时(1~24) | 24 |
K | A.M./P.M. (0~11)格式小时 | 10 |
z | 时区 | Eastern Standard Time |
" | 单引号 | ` |
使用printf格式化日期
printf 方法可以很轻松地格式化时间和日期。使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾
转换符 | 说明 | 示例 |
---|---|---|
c | 包括全部日期和时间信息 | 星期六 十月 27 14:21:20 CST 2007 |
F | "年-月-日"格式 | 2007-10-27 |
D | "月/日/年"格式 | 10/27/07 |
r | "HH:MM:SS PM"格式(12时制) | 02:25:51 下午 |
T | "HH:MM:SS"格式(24时制) | 14:28:16 |
R | "HH:MM"格式(24时制) | 14:28 |
public void test21() {
System.out.printf("%tF %<tT\n", new Date());// 2018-05-05 16:59:35
// 引用值必须紧跟在%后面,而且必须以$结束
System.out.printf("%tF %1$tT\n", new Date());
System.out.printf("%1$tF %1$tT\n", LocalDateTime.now());
System.out.printf("%1$s %2$tF %2$tT\n", "现在时间为:", LocalDateTime.now());
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
String time = String.format(new SimpleDateFormat("[a]好,现在是[y年M月d日 E H点m分]").format(new Date()));
String time = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("'['a']'好,现在是'['y年M月d日 E H点m分']'"));
System.out.println(time);
// [下午]好,现在是[2018年5月5日 周六 16点59分]
}
@Test
public void test22() {
// localDate()方法有其默认的格式化属性
System.out.printf("%tF\n", LocalDate.now());// 2018-05-05
System.out.println(LocalDate.now());// 2018-05-05
System.out.printf("%tF %<tT\n", LocalDateTime.now());
// 2018-05-05 17:00:56
String str = DateTimeFormatter.ofPattern("[a]好,现在是[y年M月d日 E H点m分]", Locale.CHINA).format(LocalDateTime.now());
System.out.println(str);// 下午好,现在是2018年5月5日 周六 17点2分
}
@Test
public void test3() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("[a]好,现在是[y年M月d日 E H点m分]");
String str = dtf.format(LocalDateTime.now());
System.out.println(str);// 下午好,现在是2018年5月5日 周六 17点3分
LocalDateTime ldt = LocalDateTime.now().parse(str, dtf);
System.out.println(ldt);// 2018-05-05T17:03
}
LocalDateTime now = LocalDateTime.now();
// b的使用,月份简称
String str = String.format(Locale.US, "英文月份简称:%tb", now);
System.out.println(str); // Sep
System.out.printf("本地月份简称:%tb%n", now); // 九月
// B的使用,月份全称
str = String.format(Locale.US, "英文月份全称:%tB", now);
System.out.println(str);// September
System.out.printf("本地月份全称:%tB%n", now); // 九月
// a的使用,星期简称
str = String.format(Locale.US, "英文星期的简称:%ta", now);
System.out.println(str); // Thu
// A的使用,星期全称
System.out.printf("本地星期的简称:%tA%n", now); // 星期四
// C的使用,年前两位
System.out.printf("年的前两位数字(不足两位前面补0):%tC%n", now); // 20
// y的使用,年后两位
System.out.printf("年的后两位数字(不足两位前面补0):%ty%n", now); // 20
// j的使用,一年的天数
System.out.printf("一年中的天数(即年的第几天):%tj%n", now); // 261
// m的使用,月份
System.out.printf("两位数字的月份(不足两位前面补0):%tm%n", now); // 09
// d的使用,日(二位,不够补零)
System.out.printf("两位数字的日(不足两位前面补0):%td%n", now); // 17
// e的使用,日(一位不补零)
System.out.printf("月份的日(前面不补0):%te", now); // 17
Java8时间
打印输出日历
public static void main(String[] args) {
String string = "2019-04-09";
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(string, timeFormatter);
int currDay = date.getDayOfMonth();// 9
LocalDate first = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDate first2 = date.with(ChronoField.DAY_OF_MONTH, 1);
LocalDate first3 = date.withDayOfMonth(1);
LocalDate last = date.with(TemporalAdjusters.firstDayOfNextMonth());
int MaxDay = date.lengthOfMonth();// 30
LocalDate date2 = date.withDayOfMonth(1);
int firstDay = date2.getDayOfWeek().getValue();
System.out.println("日\t一\t二\t三\t四\t五\t六");
// 在Java8的时间中,星期日不再是0,而是对应数字7了
for (int j = 0; firstDay != 7 && j < firstDay; j++) {
System.out.print("\t");
}
for (int j = 1; j <= MaxDay; j++) {
System.out.print(j + "\t");
if (date2.getDayOfWeek().getValue() == 6) {
// if (date2.getDayOfWeek() == DayOfWeek.SATURDAY) {
System.out.println();
}
// 可以把plusDays放在上面,这时if里面的条件就要变为以7或者星期日为标准了
date2 = date2.plusDays(1);
}
}
时间戳
// Instant:时间戳(以Unix元年,1970年1月1日 零点 到某个时间的毫秒值)
Instant instant = Instant.now();
System.out.println(instant); // 2020-01-11T22:09:38.601Z
// 这个代表当前时间
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime); // 2020-01-12T06:09:38.601+08:00
System.out.println(Instant.now().toEpochMilli() == System.currentTimeMillis()); // true
// 这个是相较于Unix元年的偏移量
Instant instant1 = Instant.ofEpochSecond(1);
时间
LocalDate
相隔天数
LocalDate start = LocalDate.of(1993, 9, 24);
LocalDate end = LocalDate.of(1994, 12, 6);
long days = start.until(end, ChronoUnit.DAYS);
long between = ChronoUnit.DAYS.between(start, end);
System.out.println(LocalDate.now().toString()); // 2020-01-13
System.out.println(LocalDateTime.now().toString()); // 2020-01-13T07:30:00.351
System.out.println(LocalTime.now().toString()); // 07:30:00.351
@Test
public void test1() {
LocalDate now = LocalDate.now();
// of(int year, int month, int dayOfMonth)
LocalDate birth = LocalDate.of(1993, 9, 23);
// 通过某一时间去调用指定自己想要的时间
LocalDate birth2 = now.with(ChronoField.YEAR, 1993)
.with(ChronoField.MONTH_OF_YEAR, 9)
.with(ChronoField.DAY_OF_MONTH, 23);
System.out.println(birth2);// 1993-09-23
// 这个是用形参减去调用者(后面减前面)
Period period = birth.until(now);
System.out.println(period);// P25Y6M18D
System.out.println(period.getYears());// 25
System.out.println(period.getMonths());// 6
System.out.println(period.getDays());// 18
// 获取两个日期相差天数
System.out.println(date.until(date2, ChronoUnit.DAYS));
}
@Test
public void test2() {
LocalDate now = LocalDate.now();
LocalDate birth = LocalDate.of(1993, 9, 23);
Period between = Period.between(birth, now);
System.out.println(between);// P25Y6M18D
/**
* Period 是表示“日期”间隔的(LocalDate)
* Duration是表示“时间”间隔的(LocalTime)
*/
Period between2 = Period.between(LocalDate.of(1993, 9, 23), LocalDate.now());
System.out.println(between2);// P25Y6M18D
}
LocalTime lt = LocalTime.now(); // 06:18:56.132
LocalTime lt2 = LocalTime.of(8, 0);
Duration duration = Duration.between(lt, lt2);
System.out.println(duration.getSeconds());
// get 方法只能用来获取秒和纳秒
// System.out.println(duration.get(ChronoUnit.DAYS));
System.out.println(duration.toDays());
System.out.println(duration.toMillis());
System.out.println(duration.toHours());
TemporalAdjuster:时间校正器。有时可能需要获取例如:将日期调整到“下个周日”等操作
TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster实现
public class FirstDay {
public static void main(String[] args) {
// 当前时间的下个周五
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
LocalDate first = LocalDate.now()
.with(TemporalAdjusters.firstDayOfMonth());
// with(TemporalField field, long newValue)
LocalDate first2 = LocalDate.now().with(ChronoField.DAY_OF_MONTH, 1);
LocalDate first3 = LocalDate.now().withDayOfMonth(1);
System.out.println(first2.getDayOfWeek().getValue());
LocalDate last = LocalDate.now()
.with(TemporalAdjusters.firstDayOfNextMonth());
}
}
public void test() {
LocalDate now = LocalDate.of(2020, 1, 11);
// 获取下一个工作日
LocalDate localDate = now.with(temporal -> {
LocalDate ld = (LocalDate) temporal;
DayOfWeek dow = ld.getDayOfWeek();
if (dow.equals(DayOfWeek.FRIDAY)) {
return ld.plusDays(3);
} else if (dow.equals(DayOfWeek.SATURDAY)) {
return ld.plusDays(2);
} else {
return ld.plusDays(1);
}
});
System.out.println(localDate);
}
public void test1() {
LocalDate today = LocalDate.now();
System.out.println(today);// 2018-05-05
// 当前日期所在的天数
System.out.println(today.lengthOfMonth());// 当前月最大天数31
System.out.println(today.getDayOfWeek().getValue());// 6(代表星期六)
System.out.println(today.getMonth().maxLength());// 31
System.out.println(today.lengthOfYear());// 365
System.out.println(today.isLeapYear());// false
System.out.println(today.getMonth().name());// MAY
// 增加、减少指定的天数
System.out.println(today.plus(2, ChronoUnit.DAYS));// 2018-05-07
System.out.println(today.plusDays(2));// 2018-05-07
System.out.println(today.minusDays(2));// 2018-05-03
// 获取当前年月日等信息
System.out.println(today.getYear());// 2018
System.out.println(today.getMonthValue());// 5
System.out.println(today.getDayOfMonth());// 5
}
LocalDateTime修改
public void test2() {
LocalDateTime now = LocalDateTime.now();
/**
* 将LocalDateTime转化为对应的LocalDate和LocalTime的时间
*/
LocalDate date = now.toLocalDate();
LocalTime time = now.toLocalTime();
LocalDateTime now2 = LocalDateTime.of(date, time);// 2018-05-05T16:26:25.925296
LocalDateTime now3 = date.atTime(time);// 2018-05-05T16:26:25.925296
LocalDateTime now4 = date.atTime(13, 34);// 2018-05-05T13:34
LocalDateTime now5 = time.atDate(date);// 2018-05-05T16:26:25.925296
System.out.println(date);// 2018-05-05
System.out.println(time);// 16:24:27.971391200
}
public void test3() {
// 获取特定日期
LocalDate dateOfBirth = LocalDate.of(1993, 8, 9);
String s = "19931027";
LocalDate date = LocalDate.parse(s, DateTimeFormatter.BASIC_ISO_DATE);// 1993-10-27
LocalDate parse = LocalDate.parse("2017-12-12");// 2017-12-12
LocalTime parse2 = LocalTime.parse("12:22:25");// 12:22:25
// 使用自定义的格式器
String s1 = "10 27 1993";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM dd yyyy");
LocalDate date1 = LocalDate.parse(s1, formatter);
System.out.println(date1);// 1993-10-27
}
MonthDay修改
public static void main(String[] args) {
// MonthDay类仅包含日月信息,不包含年,用于检查特定日期,出生日,节日等
MonthDay birthDay = MonthDay.of(10, 12);
System.out.println(MonthDay.now().equals(birthDay));// false
// 从某一日期获取MonthDay
LocalDate dateOfBirth = LocalDate.of(2000, 2, 29);
MonthDay speilaDay = MonthDay.from(dateOfBirth);
// 检查某年是否含有该日期(后面对应的月日在该年中是否存在)
System.out.println(speilaDay.isValidYear(2020));// true
// YearMonth用于检查信用卡过期信息等
System.out.println(YearMonth.of(2020, Month.AUGUST));// 2020-08
}
public static void main(String[] args) {
// Duration是用来计算两个给定的日期之间包含多少秒,多少毫秒,它是不可变类且线程安全的
// Duration中的get方法只会换算到秒,秒为Duration最大单位
LocalTime localTime = LocalTime.now();
Duration duration = Duration.between(localTime, localTime.plusMinutes(3));
System.out.println(duration.getSeconds());// 180
System.out.println(duration.toMillis());// 180000
System.out.println(duration.get(ChronoUnit.SECONDS));// 180
//Period是用来计算两个给定的日期之间包含多少天,多少月或者多少年,它是不可变类且线程安全的
// Period中的get方法只会换算到年,年为Period最大单位
LocalDate localDate = LocalDate.now();// 300
Period period2 = Period.between(localDate, localDate.plus(3, ChronoUnit.CENTURIES));
Period period = Period.between(localDate.parse("1993-08-09"), localDate);
System.out.println(period.getYears());// 24
System.out.println(period.getMonths());// 8
System.out.println(period.getDays());// 26
Period period1 = Period.between(localDate, localDate.plus(3, ChronoUnit.DAYS));
System.out.println(period1.getYears());// 0
System.out.println(period1.getMonths());// 0
System.out.println(period1.getDays());// 3
}
时区操作
Java8中加入了对时区的支持,带时区的时间分别为:ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都有对应着ID,地区ID都为"{区域}/{城市}"的格式,例如:Asia/Shanghai等
ZoneId:该类中包含了所有的时区信息
- getAvailableZoneIds:获取所有的时区信息
- of(id):指定的时区信息获取ZoneId对象
LocalDateTime ctt = LocalDateTime.now(ZoneId.of("CTT", ZoneId.SHORT_IDS));
// 此两种方式都是获取上海时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
// 获取的就是本地时区
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zonedDateTime); // 2020-01-12T06:54:46.474+08:00[Asia/Shanghai]
// 下面这种方式没意义
zonedDateTime = now.atZone(ZoneId.of("Europe/Paris"));
System.out.println(zonedDateTime); // 2020-01-12T06:54:46.474+01:00[Europe/Paris]
线程安全
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
推荐使用 Spring ConversionService自定义格式化转化器 https://blog.csdn.net/weixin_43476471/article/details/114484451
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate(LocalDateTime date) {
return formatter.format(date);
}
public static LocalDateTime parse(String dateNow) {
return LocalDateTime.parse(dateNow, formatter);
}
附录
UTC和GMT的区别
讲解TimeZone
时提到了UTC和GMT,下面对这两个概念进行科普
- GMT:格林尼治标准时间。指位于伦敦郊区的皇家格林尼治天文台的标准时间(所以它是天文学时间),因为本初子午线被定义在通过那里的经线。随着科技的进步,人们发现该时间不够精确,因为地球自转的速度是不均衡的,而且自转速度也会越来越慢,所以不再使用
- UTC:世界协调时间(世界统一时间)。是基于原子物理学的特性,它的精确度更高,所以是现在全球都在使用的时间
UTC是根据原子钟来计算时间,非常的精确和平均;而GMT是根据地球的自转和公转来计算时间,准确度不够已被淘汰
粗略的理解:可认为它俩是一回事。只不过UTC的称呼更为正式一点。两者的区别在于前者是一个天文上的概念,而后者是基于一个原子钟。在UTC中,每一年或两年会有一个“闰秒”
计算公式
UTC + 时区差 = 本地时间。如UTC+8
表示北京时间(同GMT+8/GMT+08:00
)。
Java中的应用示例
在JDK1.1时使用TimeZone
类来表示时区,Java8新增了ZoneOffset
来表示时区偏移量
System.out.println(TimeZone.getDefault());
System.out.println(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.println(TimeZone.getTimeZone("GMT+8"));
System.out.println(TimeZone.getTimeZone("GMT+8:00"));
System.out.println(TimeZone.getTimeZone("GMT"));
System.out.println(TimeZone.getTimeZone("UTC"));
System.out.println(TimeZone.getTimeZone("UTC+8"));
// ZoneOffset
System.out.println(ZoneOffset.systemDefault());
// System.out.println(ZoneOffset.of("Asia/Shanghai")); //抛错
// System.out.println(ZoneOffset.of("+8:00")); // 抛错
System.out.println(ZoneOffset.of("+08:00"));
System.out.println(ZoneOffset.ofHours(8));
// System.out.println(ZoneOffset.of("GMT")); // 抛错
通过现象至少能整理出如下结论:
- 在国内,
TimeZone.getDefault()/getTimeZone("Asia/Shanghai") /getTimeZone("GMT+8")/getTimeZone("GMT+8:00")
完全等效 TimeZone.getTimeZone("GMT")/getTimeZone("UTC")
对程序来说可以认为是完全一样的(只不过一个id叫GMT,一个叫UTC)TimeZone.getTimeZone("UTC+8")
其实并不支持UTC+8
这种写法(这么写不报错,但效果同UTC
)ZoneOffset.systemDefault()
的底层源码其实是TimeZone.getDefault().toZoneId()
这么一句话ZoneOffset.of("+08:00")
效果完全同ZoneOffset.ofHours(8)
- 另请注意代码中我备注的抛错的几条语句,书写的时候需要注意规范性