Epoch Time又称为时间戳,通常是用long表示的毫秒数
long t = 1574208900123L;
转换成北京时间就是2019-11-20T8:15:00.123。要获取当前时间戳可以使用System.currentTimeMillis()。
Java新旧标准库
- 一套定义在java.util这个包里面,主要包括Date、Calendar和TimeZone这几个类;
- 一套新的API是在Java 8引入的,定义在java.time这个包里面,主要包括LocalDateTime、Zon
新旧之间仍存有一定的关系,因此不能全部抛弃。
Date(旧)
java.util.Date表示一个日期与时间的对象,在其源码中实际存储了一个Long类型的时间戳。
Date基本用法
public class Main {
public static void main(String[] args) {
// 获取当前时间:
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());
}
}
输出:
2020
7
9
Thu Jul 09 01:36:39 UTC 2020
9 Jul 2020 01:36:39 GMT
Jul 9, 2020, 1:36:39 AM
注意getYear()返回的年份必须加上1900,getMonth()返回的月份是011分别表示112月,所以要加1,而getDate()返回的日期范围是1~31,又不能加1。
想要将其更加格式化:
使用SimpleDateFormat对其进行转换
public class Main {
public static void main(String[] args) {
// 获取当前时间:
Date date = new Date();
var sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
}
}
输出:
2020-07-09 01:38:12
其中: yyyy:年, MM:月 ,dd: 日 ,HH: 小时 ,mm: 分钟,ss: 秒
一般来说,字母越长,输出越长,M为例:
- M:输出9
- MM:输出09
- MMM:输出Sep
- MMMM:输出September
Date是根据系统默认时间进行加载,因此有很多局限性,不能转换时区、不能对时间日期进行加减等。
Calendar(旧)
它比Date比多了一个可以计算日期和时间的功能。
使用Calendar需要用到该类的getInstance()静态方法,获取到该对象。另外,获取年月日信息时用到的是get()方法,参数为Calendar.MONTH等。年份不用换算,但月份加一,日期1-7是从周日到周六这样计算的。
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-7-9 5 1:49:13.622
另外,如果要单独设置时间的话,要将其全部清除,并重新set()。
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
}
}
2019-09-02 21:22:23
对时区的转换中,需要用到java.util包中另一个类,TimeZone。
该类只用来获取时区ID,不是用来获取时间。
要列出系统支持的所有ID,请使用TimeZone.getAvailableIDs()。
下面例子是对时区的转换:
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);
// 显示时间:
var 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
}
}
时区转换步骤:
- 清除所有字段;
- 设定指定时区;
- 设定日期和时间;
- 创建SimpleDateFormat并设定目标时区;
- 格式化获取的Date对象(注意Date对象无时区信息,时区信息存储在SimpleDateFormat中)。
Calendar也可以对时间进行加减:
public class Main {
public static void main(String[] args) {
// 当前时间:
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);
// 显示时间:
var 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
}
}
2019-11-25 06:15:00
新的日期和时间API
主要涉及的类型有:
- 本地日期和时间:LocalDateTime,LocalDate,LocalTime;
- 带时区的日期和时间:ZonedDateTime;
- 时刻:Instant;
- 时区:ZoneId,ZoneOffset;
- 时间间隔:Duration。
- 格式化类型:DateTimeFormatter
LocalDateTime(新)
表示当前日期:LocalDate,表示当前时间:LocalTime,表示当前日期和时间:LocalDateTime
在创建以上对象时,由于会消耗一定时间,因此三个对象创建的时间不一定一致,因此采用:
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间
创建指定的日期时,用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);
例:
public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
LocalDate localDate = localDateTime.toLocalDate();
System.out.println(localDate);
LocalTime localTime = localDateTime.toLocalTime();
System.out.println(localTime);
}
LocalDateTime对时间的加减:
public class Main {
public static void main(String[] args) {
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
}
}
对日期和时间进行调整则使用withXxx()方法,例如:withHour(15)会把10:11:12变为15:11:12:
- 调整年:withYear()
- 调整月:withMonth()
- 调整日:withDayOfMonth()
- 调整时:withHour()
- 调整分:withMinute()
- 调整秒:withSecond()
时间间隔:Duration和Period
Duration表示时刻的间隔,Period表示日期之间间隔的天数。
Duration d = Duration.between(start, end);//start和end都是LocalTime的实例
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
ZonedDateTime
ZonedDateTime适用于操作时区,可用于时区转换。
创建ZonedDateTime对象,用now()返回当前时间。
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
ZoneId是java.time新引入的时区类,因此LocalDateTime也可以调用。
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"));
withZoneSameInstant()方法将一个时区转换为另一个时区。
// 以中国时区获取当前时间:
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
DateTimeFormatter
这个对象与SimpleDateFormat相似,都是对日期时间进行格式化。
SimpleDateFormat不是线程安全的,但是DateTimeFormatter是线程安全的
对ZonedDateTime或LocalDateTime进行格式化,需要使DateTimeFormatter类
DateTimeFormatter可以通过格式化字符串和Locale对日期和时间进行定制输出。
Instant
Instant表示高精度时间戳,它可以和ZonedDateTime以及long互相转换。
Instant now = Instant.now();
System.out.println(now.getEpochSecond()); // 秒
System.out.println(now.toEpochMilli()); // 毫秒
1594272512
1594272512239
旧API转新的API
可以利用Instant,将旧的对象转换为Instant,再将Instant转换为新的对象
新API转旧API:
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.sql.Date
在数据库中,我们最常用的方式是存储一个Instant用BIGINT型。
在数据库中存储时间戳时,尽量使用long型时间戳,它具有省空间,效率高,不依赖数据库的优点。