最佳实践
由于Java提供了新旧两套日期和时间的API,除非涉及到遗留代码,否则我们应该坚持使用新的API。
如果需要与遗留代码打交道,如何在新旧API之间互相转换呢?
旧API转新API
如果要把旧式的Date
或Calendar
转换为新API对象,可以通过toInstant()
方法转换为Instant
对象,再继续转换为ZonedDateTime
:
// Date -> Instant:
Instant ins1 = new Date().toInstant();
// Calendar -> Instant -> ZonedDateTime:
Calendar calendar = Calendar.getInstance();
Instant ins2 = calendar.toInstant();
ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());
从上面的代码还可以看到,旧的TimeZone
提供了一个toZoneId()
,可以把自己变成新的ZoneId
。
新API转旧API
如果要把新的ZonedDateTime
转换为旧的API对象,只能借助long
型时间戳做一个“中转”:
// ZonedDateTime -> long:
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);
从上面的代码还可以看到,新的ZoneId
转换为旧的TimeZone
,需要借助ZoneId.getId()
返回的String
完成。
在数据库中存储日期和时间
除了旧式的java.util.Date
,我们还可以找到另一个java.sql.Date
,它继承自java.util.Date
,但会自动忽略所有时间相关信息。这个奇葩的设计原因要追溯到数据库的日期与时间类型。
在数据库中,也存在几种日期和时间类型:
DATETIME
:表示日期和时间;DATE
:仅表示日期;TIME
:仅表示时间;TIMESTAMP
:和DATETIME
类似,但是数据库会在创建或者更新记录的时候同时修改TIMESTAMP
。
在使用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()
的方法,非常简单地为不同用户以不同的偏好来显示不同的本地时间:
import java.time.*;
import java.time.format.*;
import java.util.Locale;
----
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
小结
处理日期和时间时,尽量使用新的java.time
包;
在数据库中存储时间戳时,尽量使用long
型时间戳,它具有省空间,效率高,不依赖数据库的优点。
相关文章
- Java编程中忽略这些细节,Bug肯定找上你
- 9个问题,带你掌握流程控制语句中的java原理
- 从IDC Marketscape报告看区块链政务数字化未来:权威解读新热点、新机遇
- chatGPT的火爆,并不偶然
- React 开发 | 常用 Hooks
- JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK12特性讲解】
- Eolink 让我“重新认识“了自动化测试...
- 老板:你也把咱们网站弄成灰色——网站变灰色如何实现
- iptables规则案例
- ‘极锐’-一种新的锐化算法
- PS/LR滤镜插件套装 Nik Collection v5.3.0 Win/Mac
- Chrome插件:uBlock Origin – Chrome浏览器高效低占用的广告拦截插件
- 前端与区块链
- 云原生之微服务
- 集群动态环境管理神器 Modules
- 记 os_object_release Crash 排查
- 记 libAccessibility 通知 Crash 排查
- Ant Design Pro 中 点击子菜单的时候,其他菜单不自动收起来
- ETC 可视化
- 1267-Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLIC for o...