日期与时间
Date和Calendar(java.util)
-
计算机内存储的时间是一个时间戳,各种不同时区的时间只是对时间戳的不同展示罢了,
Epoch Time又称为时间戳,是long类型 -
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
API
java.util(旧)
- Date
- Calendar
- TimeZone
java8(新)
- LocalDateTime
- ZoneDateTime
- ZoneId
Date
-
基本用法
Date date =new Date() //好多API例如 一下都不在使用 System.out.println(date.getYear() + 1900); // 必须加上1900 System.out.println(date.getMonth() + 1); // 0~11,必须加上1 System.out.println(date.getDate()); // 1~31,不能加1 -
控制时间显示的格式用SimpleDateFormat
public class practice { public static void main(String[] args) { Date d=new Date(); SimpleDateFormat t=new SimpleDateFormat("yyyy-MM-dd"); System.out.println(t.format(d)); System.out.println(d.getTime());//时间戳 } } M:输出9 MM:输出09 MMM:输出Sep MMMM:输出September
Calendar
-
Calendar可以用于获取并设置年、月、日、时、分、秒,它和Date比,主要多了一个可以做简单的日期和时间运算的功能 -
创建:
Calendar c=Calendar.getInstance()public class Main { public static void main(String[] args) { // 获取当前时间: 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); } } 2020-10-25 1 5:53:53.713 // 如果我们想给它设置成特定的一个日期和时间,就必须先清除所有字段 public class Main { public static void main(String[] args) { // 当前时间: 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进行格式化
TimeZone
-
GMT+09:00、Asia/Shanghai都是有效的时区ID。要列出系统支持的所有ID,请使用TimeZone.getAvailableIDs()。System.out.println(TimeZone.getAvailableIDs().length); for (String f:TimeZone.getAvailableIDs()) { System.out.println(f); } -
有了时区,我们就可以对指定时间进行转换
例如将北京时间转化为纽约时间:
public class Main { public static void main(String[] args) { // 当前时间: 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进行时区转换的步骤是:-
清除所有字段;
-
设定指定时区;
-
设定日期和时间;
-
创建
SimpleDateFormat并设定目标时区; -
格式化获取的
Date对象(注意Date对象无时区信息,时区信息存储在SimpleDateFormat中)。SimpleDateFormat t1=new SimpleDateFormat("yyyy-M-dd HH-mm"); t1.setTimeZone(TimeZone.getTimeZone("America/New_York")); System.out.println(t1.format(new Date().getTime()));
-
java.time
LocalDateTime
java8的主要时间API
- 本地日期和时间:LocalDateTime
,LocalDate,LocalTime - 带时区的日期和时间:
ZonedDateTime - 时刻:
Instant; - 时区:
ZoneId,ZoneOffset; - 时间间隔:
Duration。 - 取代
SimpleDateFormat的格式化类型DateTimeFormatter - 仅有自己时区的信息
-
基本用法 .now()
public class Main { public static void main(String[] args) { 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格式打印 } } 2020-10-25 06:32:42.656969042 2020-10-25T06:32:42.657019909 本地日期和时间通过now()获取到的总是以当前默认时区返回的问题:由于获取时间有小小差异,导致两个时间不相同,解决:
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间 LocalDate d = dt.toLocalDate(); // 转换到当前日期 LocalTime t = dt.toLocalTime(); // 转换到当前时间 -
自己设定LocalDateTime(静态方法of 或者parse)
// 指定日期和时间: 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); //因为严格按照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"); //注意ISO 8601规定的日期和时间分隔符是T -
DateTimeFormatter
**基本用法:**DateTimeFormatter.ofPattern(" ")
public class Main { public static void main(String[] args) { // 自定义格式化: DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); System.out.println(dtf.format(LocalDateTime.now())); // 用自定义格式解析: LocalDateTime dt2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf); System.out.println(dt2); } } 2020/10/25 06:42:07 2019-11-30T15:16:17 -
用withXxx()和修改日期和时间
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-26T20:30:59 2019-10-31T20:30:59 2019-09-30T20:30:59 public class Main { public static void main(String[] args) { // 本月第一天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); } } -
Duration两个时间之间的间隔between,Period两个日期之间的额天数until
public class Main { public static void main(String[] args) { 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 } } PT1235H10M30S //1235小时10分钟30秒。 P1M21D //表示1个月21天。 // Duration和Period的表示方法也符合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分离出duration的数字
LocalDateTime l1=LocalDateTime.of(2020,10,24,14,55,0); LocalDateTime l2=LocalDateTime.now(); Duration d= Duration.between(l1,l2); String time=d.toString(); System.out.println(time); String [] timeCH=time.split("[A-Z]+"); time=String.join(" ",timeCH); time=time.trim(); System.out.println(time); timeCH=time.split("\\s"); System.out.println(timeCH.length); for(String i:timeCH){ System.out.println(i); } LocalDateTime l1=LocalDateTime.of(2020,10,24,14,55,0); LocalDateTime l2=LocalDateTime.now(); Duration d= Duration.between(l1,l2); String time=d.toString(); time=time.replace("PT",""); time=time.replace("H","小时"); time=time.replace("M","分钟"); time=time.replace("S","秒"); System.out.println(time);
ZoneDateTime:
LocalDateTime总是表示本地日期和时间,要表示一个其他时区的日期和时间,我们就需要ZonedDateTime。
可以简单地把ZonedDateTime理解成LocalDateTime加ZoneId
==ZonedDateTime.ofInstant(ins, ZoneId.of(zoneId))==ins是时间戳,LocalDateTime也包含时间戳信息
-
基本用法
//一种是通过now()方法返回当前时间 public class Main { public static void main(String[] args) { ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区 ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间 System.out.println(zbj); System.out.println(zny); } } //另一种方式是通过给一个LocalDateTime附加一个ZoneId,就可以变成ZonedDateTime: //不推荐 public class Main { public static void main(String[] args) { 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:17Z[Etc/UTC] 2019-09-15T15:16:17-04:00[America/New_York] //以这种方式创建的ZonedDateTime,它的日期和时间与LocalDateTime相同,但附加的时区不同,因此是两个不同的时刻 -
时区转化
//withZoneSameInstant() public class Main { public static void main(String[] args) { // 以中国时区获取当前时间: 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); } }
Instant
时间戳在java.time中以Instant类型表示,我们用Instant.now()获取当前时间戳,
-
获取时间戳
public class Main { public static void main(String[] args) { Instant now = Instant.now(); System.out.println(now.getEpochSecond()); // 秒 System.out.println(now.toEpochMilli()); // 毫秒 } } 1603612795 1603612795053 -
既然
Instant就是时间戳,那么,给它附加上一个时区(ZoneId),就可以创建出ZonedDateTime,得到了ZonedDateTime,继而可以获得了对应时区的LocalDateTime。Instant i=Instant.ofEpochSecond(1568568760); ZonedDateTime zdt=i.atZone(ZoneId.systemDefault()); LocalDateTime ldt=LocalDateTime.ofInstant(i,ZoneId.systemDefault()); DateTimeFormatter atf=DateTimeFormatter.ofPattern("yyyy-MM"); System.out.println(atf.format(ldt)); System.out.println(zdt); System.out.println(ldt);
数据库中的时间
在使用Java程序操作数据库时,我们需要把数据库类型与Java类型映射起来。下表是数据库类型与Java新旧API的映射关系:
| 数据库 | 对应Java类(旧) | 对应Java类(新) |
|---|---|---|
| DATETIME | java.util.Date | LocalDateTime |
| DATE | java.sql.Date | LocalDate |
| TIME | java.sql.Time | LocalTime |
| TIMESTAMP | java.sql.Timestamp | LocalDateTime |
- 实际上,在数据库中,我们需要存储的最常用的是时刻(
Instant),也就是时间戳有了时间戳,就可以转化成任何一个时区的时间。
所以,最好的方法是直接用长整数long表示,在数据库中存储为BIGINT类型。
通过存储一个long型时间戳,我们可以编写一个timestampToString()的方法,非常简单地为不同用户以不同的偏好来显示不同的本地时间:
public class Main {
public static void main(String[] args) {
long ts = 1574208900000L;
System.out.println(timestampToString(ts, Locale.CHINA, "Asia/Shanghai"));
System.out.println(timestampToString(ts, Locale.US, "America/New_York"));
}
static String timestampToString(long epochMilli, Locale lo, String zoneId) {
Instant ins = Instant.ofEpochMilli(epochMilli);
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT);
return f.withLocale(lo).format(ZonedDateTime.ofInstant(ins, ZoneId.of(zoneId)));
}
}
2019年11月20日 上午8:15
Nov 19, 2019, 7:15 PM
所以,LocalDateTime,ZoneId,Instant,ZonedDateTime和long都可以互相转换:
┌─────────────┐
│LocalDateTime│────┐
└─────────────┘ │ ┌─────────────┐
├───>│ZonedDateTime│
┌─────────────┐ │ └─────────────┘
│ ZoneId │────┘ ▲
└─────────────┘ ┌─────────┴─────────┐
│ │
加ZoneId
▼
▼
┌─────────────┐ ┌─────────────┐
│ Instant │<───>│ long │
└─────────────┘ └─────────────┘
本文深入讲解Java中的日期与时间处理,包括旧API如Date、Calendar的使用,以及Java 8引入的新API LocalDateTime、ZonedDateTime等,并介绍如何进行时区转换及时间戳的处理。
524

被折叠的 条评论
为什么被折叠?



