当前位置: 首页 > 图灵资讯 > 技术篇> 玩转时间操作(附面试题)

玩转时间操作(附面试题)

来源:图灵教育
时间:2023-05-28 09:34:19

在 JDK 8 之前,Java 语言为我们提供了两种操作时间,即:java.util.Date 和 java.util.Calendar,但在 JDK 8 为了解决旧时间操作的一些缺陷,为操作时间和日期提供了几个新的类别,它们分别是:LocalTime、LocalDateTime、Instant,都位于 java.time 包下。

时间操作经常出现在我们的日常开发中,例如,业务数据记录创建时间和修改时间,并显示这些时间格式化到前端页面,如我们需要计算业务数据时间间隔,不能与时间操作分开,那么如何正确优雅地使用时间呢?这是我们接下来要讨论的话题。

基础知识科普格林威治时间

格林威治(又译格林尼治)是英国伦敦南郊原格林威治天文台的所在地,是世界计算时间和地球经度的起点,也是国际经度会议 1884 2000年,它在美国华盛顿举行。会议通过协议,以格林威治天文台的经线为零度经线(即初子午线),作为地球经度的起点,以格林威治为“世界时区”的起点。

格林威治时间与北京时间的关系

格林威治时间被定义为世界时间,即 0 北京是东八区。也就是说,格林威治时间 1 日 0 点,对应北京的时间是 1 日 8 点。

时间戳

时间戳是指格林威治时间戳 1970-01-01 00:00:00(北京时间 1970-01-01 08:00:00)到现在的总秒数。

JDK 8 之前的时间操作1 获取时间

Date date = new Date();System.out.println(date);Calendar calendar = Calendar.getInstance();Date time = calendar.getTime();System.out.println(time);

2 获取时间戳

long ts = new Date().getTime();System.out.println(ts);long ts2 = System.currentTimeMillis();System.out.println(ts2);long ts3 = Calendar.getInstance().getTimeInMillis();System.out.println(ts3);

3 格式化时间

SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(sf.format(new Date()));  // output:2019-08-16 21:46:22

SimpleDateFormat 构造参数的含义请参考以下表格信息:

字符

含义

示例

y

yyyy-1996

M

MM-07

d

月中的天数

dd-02

D

年中的天数

121

E

星期几

星期四

H

小时数(0-23)

HH-23

h

小时数(1-12)

hh-11

m

分钟数

mm-02

s

秒数

ss-03

Z

时区

+0800

使用示例:

  • 获得星期几:new SimpleDateFormat("E").format(new Date())
  • 获取当前时区:new SimpleDateFormat("Z").format(new Date*())

注:多线程下注: SimpleDateFormat 它是非线程安全的,所以在使用中 SimpleDateFormat 注意这个问题。在多线程下,如果使用不当,可能会导致错误的结果或内存泄漏。

4 时间转换

SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// String 转 DateString str = "2019-10-10 10:10:10";System.out.println(sf.parse(str));//时间戳字符串 转 DateString tsString = "1556788591462";// import java.sqlTimestamp ts = new Timestamp(Long.parseLong(tsString)); // 字符串转时间戳 DateSystem.out.println(sf.format(ts));

注意事项:使用时 SimpleDateFormat.parse() 当时间转换方法时,SimpleDateFormat 构造函数必须与待转换字符串格式一致。

5 获得昨天此刻的时间

Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, -1);System.out.println(calendar.getTime());

JDK 8 时间操作

JDK 8 时间操作新增了三类:LocalDateTime、LocalDate、LocalTime。

  • LocalDate 只包括日期,不包括时间,不可变,线程安全。
  • LocalTime 只包括时间,不包括日期,不可变,线程安全。
  • LocalDateTime 既包含时间,又包含日期,不可变,线程安全。

线程安全性

值得一提的是 JDK 8 这三类与线程安全有关,大大降低了多线程下代码开发的风险。

1 获取时间

// Localdate获取日期 localDate = LocalDate.now();System.out.println(localDate);    // output:2019-08-16// Localtime获取时间 localTime = LocalTime.now();System.out.println(localTime);    // output:21:09:13.708// LocalDatetime的获取日期和时间 localDateTime = LocalDateTime.now();System.out.println(localDateTime);    // output:2019-08-16T21:09:13.708

2 获取时间戳

long milli = Instant.now().toEpochMilli(); // 获取当前时间戳(精确到毫秒)long second = Instant.now().getEpochSecond(); // 获取当前时间戳(精确到秒)System.out.println(milli);  // output:1569324357.out.println(second); // output:1565932435

3 时间格式化

// 时间格式化①DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String timeFormat = dateTimeFormatter.format(LocalDateTime.now());System.out.println(timeFormat);  // output:2019-08-16 21:15:43// 时间格式化②String timeFormat2 = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println(timeFormat2);    // output:2019-08-16 21:17:48

4 时间转换

String timeStr = "2019-10-10 06:06:06";LocalDateTime dateTime = LocalDateTime.parse(timeStr,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));System.out.println(dateTime);

5 获得昨天此刻的时间

LocalDateTime today = LocalDateTime.now();LocalDateTime yesterday = today.plusDays(-1);System.out.println(yesterday);

相关面试题1. 获取当前时间的方法有多少?

答:获取当前时间的常见方法有三种:

  • new Date()
  • Calendar.getInstance().getTime()
  • LocalDateTime.now()
2. 昨天此刻如何获得时间?

答:以下是获得昨天此刻时间的两种方式:

// 获得昨天此刻的时间(JDK 8 以前)Calendar c = Calendar.getInstance();c.add(Calendar.DATE,-1);System.out.println(c.getTime());// 获取昨天此刻的时间(JDK 8)LocalDateTime todayTime = LocalDateTime.now();System.out.println(todayTime.plusDays(-1));

3. 这个月的最后一天是怎样获得的?

答:本月最后一天有两种获取方式:

// 这个月的最后一天(JDK 8 以前)Calendar ca = Calendar.getInstance();ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));System.out.println(ca.getTime());// 这个月的最后一天(JDK 8)LocalDate today = LocalDate.now();System.out.println(today.with(TemporalAdjusters.lastDayOfMonth()));

4. 有几种方法可以获得当前时间的时间戳?

答:以下是获取当前时间戳的几种方式:

  • System.currentTimeMillis()
  • new Date().getTime()
  • Calendar.getInstance().getTime().getTime()
  • Instant.now().toEpochMilli()
  • LocalDateTime.now().toInstant(ZoneOffset.of(+8).toEpochMilli()

其中,第四和第五种方法是 JDK 8 才新加的。

5. 如何优雅地计算两个时间的间隔?

答:JDK 8 中可以使用 Duration 优雅地计算两个时间的相隔时间,代码如下:

LocalDateTime dt1 = LocalDateTime.now();LocalDateTime dt2 = dt1.plusSeconds(60);Duration duration = Duration.between(dt1, dt2);System.out.println(duration.getSeconds());  // output:60

6. 如何优雅地计算两个日期的相隔日期?

答:JDK 8 中可以使用 Period 优雅地计算两个日期之间的相隔日期,代码如下:

LocalDate d1 = LocalDate.now();LocalDate d2 = d1.plusDays(2);Period period = Period.between(d1, d2);System.out.println(period.getDays());   //output:2

7. SimpleDateFormat 是线程安全吗?为什么?

答:SimpleDateFormat 非线程安全。因为查看 SimpleDateFormat 可以知道,所有的格式化和分析都需要通过中间对象进行转换,这就是中间对象 Calendar,这将导致非线程安全。想象一下,我们有多个线程来操作同一个线程 Calendar 后来的线程会覆盖先来线程的数据,最后实际上会返回后来线程的数据,所以 SimpleDateFormat 它变成了非线程。

8. 怎么保证 SimpleDateFormat 线程安全?

答:保证 SimpleDateFormat 线程安全的方式如下:

  • 使用 Synchronized,使用需要时间格式化的操作 Synchronized 包装关键字,确保线程堵塞格式化;
  • 手动加锁,将需要格式化时间的代码写入加锁部分,相对 Synchronized 编码效率低,性能稍好,代码风险大(风险在于操作结束时不要忘记手动释放锁);
  • 使用 JDK 8 的 DateTimeFormatter 替代 SimpleDateFormat。
9. JDK 8 中新时间类有哪些优势?

答:JDK 8 具体优点如下:

  • 线程安全性
  • 使用方便(如获取当前时间戳的方便性、增减日期的方便性等。)
  • 比如当前时间的格式化,编写代码更简单优雅:LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
10. 如何比较两个时间(Date)的大小?

答:时间比较有三种方式:

  • 获得两个时间戳,获得两个时间戳 long 类型变量,两个变量相减,大小由结果的正负值判断;
  • 通过 Date 自带的 before()、after()、equals() 比较代码示例等方法 date1.before(date2);
  • 通过 compareTo() 比较方法,代码示例:date1.compareTo(date2),返回值 -1 说明前一次比后一次小,0 表示两个时间相等,1 说明前一次大于后一次。
总结

JDK 8 之前使用 java.util.Date 和 java.util.Calendar 在操作时间上,它们有两个明显的缺点,一是非线程安全;二是API 调用不方便。JDK 8 增加了几个时间操作类别 java.time 包下的 LocalDateTime、LocalDate、LocalTime、Duration(计算相隔时间)、Period(计算相隔日期)和 DateTimeFormatter,在多线程下提供线程安全性和易用性,使我们能够更好地运行时间。