和其他各种编程语言一样,Java也有非常多的形式来表示日期和时间,除了最基础的字符串之外,还有功能更强大的类和包可以实现日期时间的灵活变换。以Java SE 17
为例,比较实用的相关类有:
java.time.Clock (abstract)
java.time.LocalDateTime (final)
java.time.Instant (final)
java.util.Calendar (abstract)
java.util.Date
java.sql.Date
java.sql.Time
作为一个抽象类,Clock的方法功能比较少,主要用于获取当前时间,可以与Duration
类等相结合使用,常用的方法为
static Clock systemDefaultZone();
static Clock systemUTC();
static Clock tick(Clock baseClock, Duration tickDuration);
获取Clock对象一般需要给出时区,时区只能是ZoneId类的对象,可以通过ZoneId的静态方法或构造方法获得。在输出时通常使用instant方法得到ISO-8601标准的时间。若需要确定时区则应使用instant().atZone(clock.getZone())
会在末尾输出时区
systemDefaultZone
即以默认时区返回当前时间,输出后会显示默认时区如
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println(Clock.systemDefaultZone);
System.out.println(clock.instant().atZone(clock.getZone()));
}
}
输出
SystemClock[Asia/Shanghai]
2022-07-02T19:04:13.769546900+08:00[Asia/Shanghai]
该静态方法设置的是默认ZoneId,默认的ZoneId也可以通过
ZoneId.of("Asia/Shanghai")
或ZoneId.systemDefault()
得到,也可以通过getZone()
方法输出Clock对象的时区。
systemUTC
方法与systemDefaultZone
方法基本相同,只是将对应的时区设置为UTC时区,在使用instant().atZone(clock.getZone())
方法输出时时区仍是Z
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println(Clock.systemUTC);
System.out.println(clock.instant().atZone(clock.getZone()));
}
}
输出
SystemClock[Z]
2022-07-02T11:04:13.769546900Z
此方法需要与Duration类结合,用于表示持续时间,tick方法会返回最接近相隔指定时间间隔的Clock对象,可以获得一系列等差时间。
在计时时可以充当取整的功能,如使用
Duration.ofSeconds()
或Clock.tickSeconds()
方法就能获得整秒的Clock对象
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Clock clock = Clock.systemDefaultZone();
Thread.sleep(990);
System.out.println(Clock.tick(clock, Duration.ofSeconds(1)).instant().atZone(clock.getZone()));
Thread.sleep(990);
System.out.println(Clock.tick(clock, Duration.ofSeconds(1)).instant().atZone(clock.getZone()));
Thread.sleep(990);
System.out.println(Clock.tick(clock, Duration.ofSeconds(1)).instant().atZone(clock.getZone()));
}
}
输出
2022-07-02T19:27:55+08:00[Asia/Shanghai]
2022-07-02T19:27:56+08:00[Asia/Shanghai]
2022-07-02T19:27:57+08:00[Asia/Shanghai]
LocalDateTime可以说是最实用的日期时间表示形式,它相当于将LocalDate和LocalTime结合到一起,同时保存日期和时间,不仅能获取并计算年月日时分秒,还能与LocalDate或LocalTime互相转换,在输出时也可以通过format()
和DateTimeFormatter.ofPattern()
等方法自定义格式,达到与其他类相同甚至更灵活的功能。主要的方法包括:
int compareTo(ChronoLocalDateTime<?> other);
String format(DateTimeFormatter formatter);
LocalDateTime (minus/plus)(Days/Hours/Minutes/Months...)(long days/hours/minutes/months...);
static LocalDateTime now();
static LocalDateTime of(...);
static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter);
将两个LocalDateTime对象直接比较,返回值与一般的数值比较相适应,最常见的应用应该是在ArrayList中按照对象的时间先后排序,可以实现Comparator接口或使用lambda函数来实现排序的比较,与该类的其他方法
isBefore
、isAfter
、isEqual
等相比更实用
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
ArrayList<LocalDateTime> localDateTimes = new ArrayList<>();
for(int i=0;i<10;i++) {
localDateTimes.add(LocalDateTime.now());
Thread.sleep(1000);
}
localDateTimes.sort(LocalDateTime::compareTo);
for(LocalDateTime localDateTime:localDateTimes)
System.out.println(localDateTime);
}
}
输出
2022-07-03T09:49:46.140729100
2022-07-03T09:49:47.143319400
2022-07-03T09:49:48.155282200
2022-07-03T09:49:49.166319900
2022-07-03T09:49:50.181316500
2022-07-03T09:49:51.182851900
2022-07-03T09:49:52.185095100
2022-07-03T09:49:53.196120600
2022-07-03T09:49:54.197270600
2022-07-03T09:49:55.210269400
该方法主要是对对象的格式化输出,通常与
DateTimeFormatter.ofPattern()
方法一同使用,年月日时分秒分别用yy
、MM
、dd
、HH
、mm
、ss
表示,可以输出不同格式的排列组合
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
LocalDateTime now = LocalDateTime.now();
System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy.MM.dd --- HH:mm:ss")));
System.out.println(now.format(DateTimeFormatter.ofPattern("yy-MM-dd HH--mm--ss")));
System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy MM dd")));
System.out.println(now.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println(now.format(DateTimeFormatter.ofPattern("(yyyy) HH ss----MMmm")));
}
}
输出
2022.07.03 --- 09:52:17
22-07-03 09--52--17
2022 07 03
09:52:17
(2022) 09 17----0752
该方法可以对LocalDateTime对象进行任意加减,可以用任意单位表示,在设定期限或时间限制时非常实用
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
System.out.println(localDateTime.plusHours(10));
System.out.println(localDateTime.minusDays(34));
System.out.println(localDateTime.plusWeeks(4));
System.out.println(localDateTime.minusYears(5));
}
}
输出
2022-07-03T00:24:34.840940900
2022-07-03T10:24:34.840940900
2022-05-30T00:24:34.840940900
2022-07-31T00:24:34.840940900
2017-07-03T00:24:34.840940900
直接获取当前时间,比一般的Date对象、LocalDate对象等存储了更多信息,时区为系统默认时区
该方法返回对应设定好的年月日时分秒值的LocalDateTime对象,包括多个重载,可以设定从纳秒到年的所有单位,也可以直接使用LocalDate和LocalTime对象拼接,除了
LocalDateTime.now()
方法外,该方法为比较简便的获取实例对象的方法。
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println(LocalDateTime.of(2022, 7, 3, 6, 6));
System.out.println(LocalDateTime.of(2022, 7, 3, 6, 6, 6));
System.out.println(LocalDateTime.of(2022, 7, 3, 6, 6, 6, 666666666));
}
}
输出
2022-07-03T06:06
2022-07-03T06:06:06
2022-07-03T06:06:06.666666666
该方法与format方法相似,都可以用于LocalDateTime与String的转换,主要参数格式变量也是使用
DateTimeFormatter.ofPattern()
方法自定义格式
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
String YYYYMMDDHHMMSSSSS = "yyyyMMddHHmmssSSS";
String YY_MM_DD_HH_MM_SS = "yy-MM-dd HH:mm:ss";
System.out.println(LocalDateTime.parse("20220606060606", DateTimeFormatter.ofPattern(YYYYMMDDHHMMSS)));
System.out.println(LocalDateTime.parse("20220606060606666", DateTimeFormatter.ofPattern(YYYYMMDDHHMMSSSSS)));
System.out.println(LocalDateTime.parse("22-06-06 06:06:06", DateTimeFormatter.ofPattern(YY_MM_DD_HH_MM_SS)));
}
}
输出
2022-06-06T06:06:06
2022-06-06T06:06:06.666
2022-06-06T06:06:06
Instant类代表某一个时间,精确到纳秒单位,经常与其他类结合使用,LocalDateTime、LocalDate、LocalTime、Calendar类都可以使用toInstant方法转换为Instant类。实际上Instant类的方法与LocalDateTime非常相似,但它不包含时区参数。Instant类代表Java的时间尺度,可以在其他类中通用,主要方法包括:
int compareTo(Instant otherInstant);
static Instant now();
static Instant parse(CharSequence text);
Instant (minus/plus)(Seconds/Millis/Nanos)(long amount);
与其他类的compareTo方法完全相同,可用于排序时比较时间先后
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Instant instant = Instant.now();
Instant now = instant;
Instant before = instant.minusSeconds(30);
Instant after = instant.plusSeconds(25);
System.out.println(instant.compareTo(before));
System.out.println(instant.compareTo(after));
System.out.println(instant.compareTo(now));
}
}
输出
1
-1
0
此方法可获取当前时间,也可以传入Clock类的参数,使用该参数获取当前时间
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Instant instant = Instant.now(Clock.systemDefaultZone());
Instant now = Instant.now();
System.out.println(instant);
System.out.println(now);
}
}
输出
2022-07-03T01:58:34.374565500Z
2022-07-03T01:58:34.374565500Z
Insant类的parse方法只能获得UTC时间格式的参数时间,由于Instant类不包含时区,因此输出时经常使用
OffsetDateTime atOffset(ZoneOffset offset)
方法和ZonedDateTime atZone(ZoneId zone)
方法
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Instant instant = Instant.parse("2022-07-03T01:58:34.374565500Z");
System.out.println(instant);
System.out.println(instant.atZone(ZoneId.systemDefault()));
System.out.println(instant.atOffset(ZoneOffset.ofHours(8)));
}
}
输出
2022-07-03T01:58:34.374565500Z
2022-07-03T09:58:34.374565500+08:00[Asia/Shanghai]
2022-07-03T09:58:34.374565500+08:00
Instant类也可以通过加减计算得到新的Instant实例,但Instant类只能对秒、毫秒、纳秒进行计算,若需要以年月日为单位进行计算需要写出表达式
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Instant instant = Instant.now();
System.out.println(instant);
System.out.println(instant.plusSeconds(30));
System.out.println(instant.minusMillis(20000));
System.out.println(instant.minusNanos(439095934904654L));
System.out.println(instant.plusSeconds(60*60*24*7));
System.out.println(instant.minusSeconds(60*60*24*30*6));
System.out.println(instant.plusSeconds(60*60*24*365*20));
}
}
输出
2022-07-03T02:10:16.235732300Z
2022-07-03T02:10:46.235732300Z
2022-07-03T02:09:56.235732300Z
2022-06-28T00:12:00.300827646Z
2022-07-10T02:10:16.235732300Z
2022-01-04T02:10:16.235732300Z
2042-06-28T02:10:16.235732300Z
Calendar类与LocalDateTime类大致相同,具有的功能也大同小异。它定义的时间常量非常详细,包括指示日期时间格式和星期几的常量,对每个月份、一周的每一天都有所代表的int值,常用的方法基本上都与LocalDateTime类一一对应。但若需要格式化输出,就需要使用String.format()
方法,并使用比较复杂的格式符,与LocalDateTime类相比不够简洁。
int compareTo(Calendar anotherCalendar);
static Calendar getInstance();
void set(int year, int month, int date...);
与LocalDateTime的compareTo方法相同,可用于排序
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Calendar calendar = Calendar.getInstance();
Calendar now = calendar;
Calendar after = Calendar.getInstance();
Calendar before = Calendar.getInstance();
after.roll(Calendar.DATE, true);
before.roll(Calendar.DATE, false);
System.out.println(calendar.compareTo(now));
System.out.println(calendar.compareTo(after));
System.out.println(calendar.compareTo(before));
}
}
输出
0
-1
1
与LocalDateTime的now方法相同,可获取当前时间,之后可配合toInstant方法输出UTC时间,若需要格式化输出需要
String.format()
或System.out.printf
方法
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Calendar calendar = Calendar.getInstance();
System.out.printf("%1$ty-%1$tm-%1$td%n", calendar);
System.out.printf("%1$tY-%1$tB-%1$td%n", calendar);
System.out.printf("%1$tD%n", calendar);
System.out.printf("%1$tF%n", calendar);
System.out.printf("%1$tH:%1$tM:%1$tS%n", calendar);
System.out.printf("%1$tH:%1$tM:%1$tS %1$tL%n", calendar);
System.out.printf("%1$tH:%1$tM:%1$tS %1$tL %1$tp%n", calendar);
System.out.printf("%1$tR%n", calendar);
System.out.printf("%1$tT%n", calendar);
System.out.printf("%1$tr%n", calendar);
System.out.printf("%1$tF %1$tA%n", calendar);
System.out.printf("%1$tF %1$ta%n", calendar);
System.out.printf("%1$tc%n", calendar);
}
}
输出
22-07-03
2022-七月-03
07/03/22
2022-07-03
10:35:39
10:35:39 405
10:35:39 405 上午
10:35
10:35:39
10:35:39 上午
2022-07-03 星期日
2022-07-03 周日
周日 7月 03 10:35:39 CST 2022
与LocalDateTime的withYear/withMonth/withDayOfMonth…等方法大致相同,可设置对象的具体时间单位,可修改从秒到年的各个单位。
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.toInstant());
calendar.set(2020, Calendar.JULY, 6);
System.out.println(calendar.toInstant());
calendar.set(2021, Calendar.AUGUST, 6, 6, 6);
System.out.println(calendar.toInstant());
calendar.set(2022, Calendar.SEPTEMBER, 6, 6, 6, 6);
System.out.println(calendar.toInstant());
}
}
输出
2022-07-02T17:17:10.696Z
2020-07-05T17:17:10.696Z
2021-08-05T22:06:10.696Z
2022-09-05T22:06:06.696Z
Date类为java中比较简单的时间类型,可以与SimpleDateFormat类配合输出特定格式,Date类可以直接使用构造方法简单地获得Date对象,可以毫秒数调用构造方法或直接获得当前时间,大多数方法与Calendar或LocalDateTime类重合,其中部分方法也被Calendar类替代,目前只能使用其毫秒数代表时间,输出时格式化。
Date()方法可以实例化Date对象,获得当前时间,Date(long date)方法参数为距1970年1月1日的毫秒数,可精确获得时间
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(6666666666666L)));
}
}
输出
2022-07-03 01:31:27
2181-04-04 19:51:06
SimpleDateFormat类可提供format方法将Date对象格式化为String,与DateTimeFormatter.ofPattern()
方法大致相同,使用yy
、MM
、dd
、HH
、mm
、ss
表示时间
System.out.printf方法中可以使用格式符输出Date对象,实现年月日时分秒的自定义输出,格式符包括
%ty/%tY | %tm | %td | %tB/%tb | %tH/%tI | %tM | %tS | %tL | %tp | %tA/%ta | %tc |
---|---|---|---|---|---|---|---|---|---|---|
年份 | 月份 | 日期 | 月份名 | 小时 | 分钟 | 秒 | 毫秒 | 上午/下午 | 星期几 | 所有信息 |
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
Date date = new Date();
System.out.printf("%1$ty-%1$tm-%1$td%n", date);
System.out.printf("%1$tY-%1$tB-%1$td%n", date);
System.out.printf("%1$tD%n", date);
System.out.printf("%1$tF%n", date);
System.out.printf("%1$tH:%1$tM:%1$tS%n", date);
System.out.printf("%1$tH:%1$tM:%1$tS %1$tL%n", date);
System.out.printf("%1$tH:%1$tM:%1$tS %1$tL %1$tp%n", date);
System.out.printf("%1$tR%n", date);
System.out.printf("%1$tT%n", date);
System.out.printf("%1$tr%n", date);
System.out.printf("%1$tF %1$tA%n", date);
System.out.printf("%1$tF %1$ta%n", date);
System.out.printf("%1$tc%n", date);
}
}
输出
22-07-03
2022-七月-03
07/03/22
2022-07-03
02:04:16
02:04:16 798
02:04:16 798 上午
02:04
02:04:16
02:04:16 上午
2022-07-03 星期日
2022-07-03 周日
周日 7月 03 02:04:16 CST 2022
该类为java.util.Date
类的子类,主要用于JDBC在数据库操作中识别SQL的Date格式对象,例如对于包含SQL语句的PreparedStatement对象或执行查询SQL语句得到的ResultSet对象,可分别使用setDate()
方法设置可变参数和getDate()
方法获得数据库查询结果中的Date对象,以MySQL 8.0,mysql-connector-java-8.0.29为例:
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
String queryStr = "SELECT * FROM dates WHERE date = ?";
PreparedStatement ps = null;
ResultSet result = null;
try {
ps.setDate(1, new Date(6666666));
result = ps.executeQuery();
while(result.next())
System.out.println(result.getDate(1).toInstant);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(ps != null)
ps.close();
} catch(SQLException e) {
e.printStackTrace();
}
try {
if(result != null)
result.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
}
}
该类为java.util.Date
类的子类,主要用于JDBC在数据库操作中识别SQL的Time格式对象,可使用setTime()
方法设置可变参数和getTime()
方法获得数据库查询结果中的Time对象,以MySQL 8.0,mysql-connector-java-8.0.29为例:
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class Test {
public static void main(String[] args) throws InterruptedException {
String queryStr = "SELECT * FROM times WHERE time = ?";
PreparedStatement ps = null;
ResultSet result = null;
try {
ps.setDate(1, new Time(6666666));
result = ps.executeQuery();
while(result.next())
System.out.println(result.getTime(1).toInstant);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(ps != null)
ps.close();
} catch(SQLException e) {
e.printStackTrace();
}
try {
if(result != null)
result.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
}
}
个人来看,LocalDateTime是所有时间形式中最常用、功能最多的类之一,自从Java 8加入LocalDateTime以来,它几乎就能完全代替Date和Calendar类,对于一般的涉及时间的Java程序,使用LocalDateTime已经完全足够,它的功能可以涵盖几乎所有的时间操作,因此个人推荐在需要时间表示的场合使用该类及其功能。
https://docs.oracle.com/en/java/javase/17/docs/api/index.html
https://docs.oracle.com/en/java/javase/11/docs/api/index.html
https://docs.oracle.com/javase/8/docs/api/index.html
https://www.runoob.com/java/java-date-time.html