0%

java | 时间相关

在看这篇博文之前,请先看我之前的博文。


TimeZone

import java.util.TimeZone;

TimeZone表示一个时区(相对UTC)的偏移量,并且可以推算出夏令时,始于JDK1.1。 简而言之,用来表示各个时区。

常用时区

  • Asia/Shanghai
  • Asia/Urumqi
  • Hongkong
  • Europe/London
  • America/Los_Angeles
  • Japan
1
2
3
TimeZone aDefault = TimeZone.getDefault();                  //获取当前程序运行环境的默认TimeZone
String[] availableIDs = TimeZone.getAvailableIDs(); //获取所有Java可识别的TimeZone
TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); //根据TimeZone的ID创建一个TimeZone对象。此方法为常用!!

ps: TimeZone.getTimeZone("Asia/Shanghai")中参数也可直接填写”UTC+XX00”或”UTC-XX00”。


Locale


import java.util.Locale;

Locale类用来标识一个特定的地理位置,或政治、文化地区。

当进行一些”地区敏感”的操作时,需要用到该类。例如展示日期,不同地区展示日期的格式是不同的。

常见Locale

Locale有以下三种表示方式:

  • 语言:例如 zh,表示所有官方语言为中文的国家或地区。
  • 语言+国家:例如 zh_CN,约定只用于表示中国。
  • 语言+国家+变体:例如 zh_CN_Kwangtung,表示中国广东。变体也可理解为版本,中国的不同版本即中国的不同地区。

第三种表示方式不常用,一般都使用第二种方式。

常用语言地区

  • zh = 中文地区
  • en = 英语地区
  • ja = 日语地区
  • ko = 韩语地区

常用国家/地区

  • zh_CN = 中国
  • zh_HK = 中国香港
  • zh_TW = 中国台湾
  • en_US = 美国
  • en_GB = 英国
  • ja_JP = 日本
  • ko_KR = 韩国

使用

Locale类里已经声明两类常用的Locale常量:语言和国家,直接使用即可。

语言

1
2
3
Locale chinese = Locale.CHINESE;    //中文
Locale english = Locale.ENGLISH; //英语
//其他自查...

国家

1
2
3
Locale china = Locale.CHINA;       //中国
Locale us = Locale.US; //美国
//其他自查...

自定义,可使用以下三个构造方法new一个Locale

1
2
3
public Locale(String language);                                    //语言
public Locale(String language, String country); //语言 + 国家
public Locale(String language, String country, String variant); //语言 + 国家 + 变体

其他使用

1
2
3
4
Locale aDefault = Locale.getDefault();                      //获取当前程序运行环境的默认Locale
String[] isoLanguages = Locale.getISOLanguages(); //获取ISO指定的语言
String[] isoCountries = Locale.getISOCountries(); //获取ISO指定的国家或地区
Locale[] availableLocales = Locale.getAvailableLocales(); //获取所有Java可识别的Locale

Date


import java.util.Date;

拥有毫秒级精确度的、用来表示一个特定时刻的Java类,始于JDK1.0。

Date类目前只有两个推荐使用的构造方法,其余已被弃用。

  • 使用无参构造函数new一个Date类,表示当前时刻。内部实现:new Date(System.currentTimeMillis())(非源码,仅用于表意)
1
Date date = new Date();
  • 使用Unix时间戳new一个Date类,10位13位时间戳皆可;
1
Date date = new Date(1583547742);

Question:如何初始化一个指定时刻的Date类?| 如何更改Date对象的年、月、日等?

Answer:初始化指定时刻的构造方法和Date类setMonth()setDate()setHour()等方法已被弃用,改用Calendar类

转换

使用SimpleDateFormat类可将Date转化为指定ISO格式字符串、或解析符合ISO格式的字符串为Date类

DateString

1
2
3
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    //new SimpleDateFormat()时指定ISO格式
Date date = new Date();
String dateToString = format.format(date);

Date转指定时区String

1
2
3
4
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    //new SimpleDateFormat()时指定ISO格式
format.setTimeZone(TimeZone.getTimeZone("Japan"));
Date date = new Date(); //date值为 2020-03-07 10:20:22
String dateToString = format.format(date); //dateToString值为 2020-03-07 11:20:22

StringDate,注意此时字符串中日期格式要与SimpleDateFormat指定的格式相符;

1
2
3
4
5
6
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    //new SimpleDateFormat()时指定ISO格式
String stringDate = "2020-03-07 10:22";
Date date = format.parse(stringDate);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); //ISO标准格式
String stringDate = "2020-03-07T10:20:22.310Z";
Date date = format.parse(stringDate);

Date时间戳,注意此时的timeStamp13位

1
2
3
4
5
6
7
Date date = new Date();
long timeStamp = date.getTime();

// 转化为北京时间的时间戳
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z");
Date date = format.parse(timeString.replace("Z", " UTC"));
return date.getTime();

Calendar


import java.util.Calendar;
  • Calendar类提供了一些方法用于转换特定的时刻和Calendar字段(如YEARMONTHDAY_OF_MONTH等)。
  • Calendar类还提供了一些操作Calendar字段的方法,例如”getFirstDayOfWeek()”。
  • 简而言之,Calendar是专门用来操作年月日时分秒的类。
  • Calendar始于JDK1.1。

实例化

当前时间

Calendar中的两个构造方法均为protected,因此只能使用静态方法getInstance()来创建Calendar对象

1
Calendar calendar = Calendar.getInstance();        //初始化一个表示当前时刻的Calendar对象

getInstance()共有四个重载方法,用来初始化不同时区、不同地区的Calendar对象。

1
2
3
4
public static Calendar getInstance();                                //默认时区、默认地区
public static Calendar getInstance(TimeZone zone); //指定时区、默认地区
public static Calendar getInstance(Locale aLocale); //默认时区、指定地区
public static Calendar getInstance(TimeZone zone, Locale aLocale); //指定时区、指定地区

注意:getInstance()方法最终会将时间设为System.currentTimeMillis()

指定时间

在调用getInstance()创建Calendar对象之后,再调用下述set方法可将Calendar置为指定时间。

1
2
3
4
5
public final void setTime(Date date);          //根据Date类来设定
public void setTimeInMillis(long millis); //根据时间戳来设定
public void set(int field, int value); //设置指定Calendar字段的值,field为Calendar中定义的各个常量,例:Calendar.MONTH
//注意:calendar.set(Calendar.MONTH, 11); 其中11并不是11月,而是12月,可使用Calendar.DECEMBER代替
public final void set(int year, int month, int date, int hourOfDay, int minute, int second); //设定年月日时分秒

转换

CalendarDate

1
2
Calendar calendar = Calendar.getInstance();
Date date = calendar.getTime();

DateCalendar

1
2
3
Calendar calendar = Calendar.getInstance();
Date date = new Date();
calendar.setTime(date);

CalendarString

先将Calendar转为Date类,再使用SimpleDateFormat类DateString

使用

Calendar常用的几个方法。

get()

获取指定字段的值,返回值为int

1
2
3
4
5
6
public int get(int field);
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH); //month = 2,并不是指2月,而是Calendar类中3月对应的int是2
int day = calendar.get(Calendar.DATE);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); //dayOfWeek = 3,并不是指周三,而是周二

上述结果为:year = 2020,month = 2,day = 10,dayOfWeek = 3

getDisplayName()

获取指定字段的值,返回值为String

1
2
3
4
5
6
public String getDisplayName(int field, int style, Locale locale);      //根据style和locale展示某个field的值
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.MONTH, 11); //11 也可用 Calendar.DECEMBER 来代替,更加直观
String shortMon = calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.US);
String narrowMon = calendar.getDisplayName(Calendar.MONTH, Calendar.NARROW_FORMAT, Locale.US);
String longMon = calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US);

上述结果为:shortMon = Dec,narrowMon = D,longMon = December

注意:

  • 一般只使用Calendar.SHORTCalendar.LONG两种styleCalendar.NARROW_FORMAT只显示首字母,存在重复无法识别的情况。
  • get()获取到的是数字值,getDisplayName()获取到的是String值。
  • YearDay这类数据只有int一种表示方法,因此使用getDisplayName()会返回null

getDisplayNames()

获取某个字段的所有展示值,返回值为Map<String, Integer>

1
2
3
4
public String getDisplayNames(int field, int style, Locale locale);     //根据style和locale展示某个字段的所有展示
Calendar calendar = Calendar.getInstance();
Map<String, Integer> chinaNames = calendar.getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.CHINA);
Map<String, Integer> usNames = calendar.getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.US);

上述结果为:

chinaNames = {星期二=3, 星期六=7, 星期三=4, 星期四=5, 星期五=6, 星期日=1, 星期一=2}
usNames = {Monday=2, Thursday=5, Friday=6, Sunday=1, Wednesday=4, Tuesday=3, Saturday=7}

add()

1
2
3
4
abstract public void add(int field, int amount);      //增、减某个字段的值,amount正为增,负为减
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -7);
calendar.add(Calendar.DATE, 10);

注意:若当前为3月,amount为-5,add()之后月份为9月;日期类似,也可倒退到1之前。

比较

1
2
3
public boolean before(Object when);                  //是否早于
public boolean after(Object when); //是否迟于
public int compareTo(Calendar anotherCalendar); //比较。0 = 相等;小于0 = 早于;大于0 = 迟于

获取时间戳

1
2
3
4
5
6
7
8
9
10
11
// 精确到毫秒
// 获取当前时间戳
System.out.println(System.currentTimeMillis());
System.out.println(Calendar.getInstance().getTimeInMillis());
System.out.println(new Date().getTime());

// 精确到秒
// 获取当前时间戳
System.out.println(System.currentTimeMillis() / 1000);
System.out.println(Calendar.getInstance().getTimeInMillis() / 1000);
System.out.println(new Date().getTime() / 1000);

LocalDateTime 优先使用该库


为什么使用

  • LocalDateTimeJava8推出的Date类的升级版,若条件允许,推荐使用LocalDateTime
  • Date体系混乱且复杂,存在两个java.util.Datejava.sql.Date类,还需配套的CalendarTimeZoneLocale类等。
  • Date若不格式化,打印出的日期可读性差,而SimpleDateFormat格式化Date的操作是线程不安全的。
  • LocalDateTime简单,学习简单、使用简单。

引用

import java.time.LocalDateTime;

实例化

构造方法访问权限为private,不可用。使用静态方法now()来实例化。

1
LocalDateTime now = LocalDateTime.now();        //实例化一个表示当前时间的LocalDateTime类

或者使用静态方法of()直接指定年月日时分秒来实例化。

1
2
3
LocalDateTime time1 = LocalDateTime.of(2020, 3, 10, 18, 22, 34, 633);    //指定 年月日、时分秒、纳秒
LocalDateTime time2 = LocalDateTime.of(2020, 3, 10, 18, 22, 34); //指定 年月日、时分秒
LocalDateTime time3 = LocalDateTime.of(2020, 3, 10, 18, 22); //指定 年月日、时分

注意:

  • 月的范围是1到12,也可用枚举类java.time.Month来代替。
  • 日的范围是1到31,时的范围是0到23,分、秒的范围是0到59。
  • 若使用of()时超出上述范围,编译会报错。

转换

LocalDateTimeString

利用toString()方法进行转换,其默认格式为:yyyy-MM-ddTHH:mm:ss.SSS

1
2
LocalDateTime now = LocalDateTime.now();
System.out.println(now); //输出 2020-03-10T15:34:48.926

或者使用LocalDateTimeformat()方法转换为自定义格式的String。通过DateTimeFormatter来指定需要的String格式。

1
2
3
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String time = now.format(formatter);

注:ISO 8601标准格式转换DateTimeFormatter.ofPattern(“yyyy-MM-dd’T’HH:mm:ss’Z’”)

DateTimeFormatter有15种内置格式(以下为部分):

DateTimeFormatter.BASIC_ISO_DATE               20200310
DateTimeFormatter.ISO_LOCAL_DATE_TIME          2020-03-10T18:30:34
DateTimeFormatter.ISO_LOCAL_DATE               2020-03-10
DateTimeFormatter.ISO_DATE_TIME                2020-03-10T18:30:34
DateTimeFormatter.ISO_ORDINAL_DATE             2020-070

LocalDateTime转指定时区的String

1
2
LocalDateTime now = LocalDateTime.now(ZoneId.of("UTC"));        //实例化时指定时区
String time = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

或者

1
2
3
LocalDateTime now = LocalDateTime.now();
LocalDateTime utc = now.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
String time = utc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

StringLocalDateTime

使用LocalDateTime.parse()进行解析,String中的时间格式必须与DateTimeFormatterpattern匹配。

1
2
3
String time = "2020-03-10 11:11:11";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(time, formatter);

DateLocalDateTime

时间戳是所有系统通用的表示时间的方式,因此通过时间戳来转换DateLocalDateTime

具体使用LocalDateTime.ofInstant()进行转换(Java中将时间戳抽象为Instant类)。

1
2
Date date = new Date();
LocalDateTime time = LocalDateTime.ofInstant(date.toInstant(), ZoneId.of("Asia/Shanghai"));

注:因为时间戳不带有任何地区信息,所以ofInstant()方法需要借助ZoneId类。

LocalDateTimeDate

借助Instant类使用Date.from()方法来转换。

1
2
LocalDateTime now = LocalDateTime.now();
Date date = Date.from(now.toInstant(ZoneOffset.of("+8")));

注:ZoneOffset.of(“+8”)+8 代表中国时区,其他时区可+/- N

LocalDateTimeUnix

1
2
long utc = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);          //转表示UTC的unix
long cst = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")); //转表示CST的unix

使用

LocalDateTime常用的几类方法。

获取某个字段值

1
2
3
4
5
6
7
8
LocalDateTime now = LocalDateTime.now();
int year = now.getYear(); //年
Month month = now.getMonth(); //月
int monthValue = now.getMonthValue(); //int类型的月
int day = now.getDayOfMonth(); //日
DayOfWeek dayOfWeek = now.getDayOfWeek(); //星期几,dayOfWeek.getValue()获取int值,周一int值为1,周天为7
int hour = now.getHour(); //时
int minute = now.getMinute(); //分

加减

  • 加减操作有三类方法,分别以plusminuswith开头;
  • 三类方法入参都可正可负,意味着每类方法都可以单独实现加、减两个功能,但是根据约定plus用来加,minus用来减;
  • minus实际调用了plus方法,而plus方法最终又调用了with方法;
1
2
3
4
LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDays = now.plusDays(3);
LocalDateTime minusDays = now.minusDays(3);
LocalDateTime withDays = now.withDayOfMonth(3);

注意:三类方法并不改变自己身的值,而是返回一个修改过值得副本。

比较

1
2
3
4
5
6
LocalDateTime now = LocalDateTime.now();
LocalDateTime future = now.plusDays(3);
boolean after = now.isAfter(future); //是否迟于
boolean before = now.isBefore(future); //是否早于
boolean equal = now.isEqual(future); //是否相等
int i = now.compareTo(future); //负为早于,正为迟于,0为相等

计算

利用java.time.Duration类来计算两个LocalDateTime之间的时间差。

1
2
3
4
5
6
LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDays = now.plusDays(3);
Duration between = Duration.between(now, plusDays);
long days = between.toDays(); //正为plusDays迟于now几天,负为早于
long hours = between.toHours();
long minutes = between.toMinutes();
请我喝杯咖啡吧~