关于Java等语言中的日期处理
2017年9月21日,Java 9正式发布?
但是今天我要谈论的是Java8中引入的日期和时间API。
历史
Java自从1.0版本就存在java.util.Date类。
Java 1.0发布于1996年。
Date类保存的是自纪元(epoch)以来的毫秒数,不包含时区信息。
由于Date类是可变的,所以经常会发生意外。
如果将Date作为函数参数传递,因为存在可能在函数内部被修改的风险,
有时需要进行防御性复制(defensive copy)后再传递。
日期
import java.text.SimpleDateFormat;
import java.util.Date;
public class Aaa {
public static void main(String[] args) throws Exception {
Date today = new Date();
System.out.println("Today is: " + new SimpleDateFormat("yyyy-MM-dd").format(today));
// 昨日の日付を表示したいな
昨日の日付を表示(today);
// 今日の日付を表示したいな
System.out.println("Today is: " + new SimpleDateFormat("yyyy-MM-dd").format(today));
}
public static void 昨日の日付を表示(Date date) {
// 昨日の日付を作る
date.setTime(date.getTime() - 24L * 60 * 60 * 1000);
System.out.println("Yesterday is: " + new SimpleDateFormat("yyyy-MM-dd").format(date));
}
}
结果 (jié guǒ) – the result
Today is: 2017-12-19
Yesterday is: 2017-12-18
Today is: 2017-12-18
日历时代
Java 1.1 在1997年引入了 java.util.Calendar。
Java 1.1 是在1997年发布的。
实质上,Calendar 等于 GregorianCalendar。
它是作为 JDK 1.1 国际化支持的一部分引入的。
Calendar 保存了从纪元开始的毫秒数(Date)和时区相关信息。
但它的API设计很糟糕。
同样像 java.util.Date 一样,它是可变的,所以容易出现意外。
我想要制作日本时间2001年12月25日14:00准确的日历,但是…
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Bbb {
public static void main(String[] args) throws Exception {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2001);
cal.set(Calendar.MONTH, 11); // 0 はじまり
cal.set(Calendar.DAY_OF_MONTH, 25);
cal.set(Calendar.HOUR_OF_DAY, 14); // Calendar.HOUR を使うとAM/PMがおかしくなる
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").format(cal.getTime()));
}
}
漫长的冬天时代
日本的化妆(雅加达)Commons-Lang的DateUtils每天的努力…
也有Joda-Time这个选择
在2003年左右出现的.Net Framework1.1的DateTime已经牢固地成为了Immutable。
顺便提一下,Java 6.0(2006年)新增了处理和历的JapaneseImperialCalendar。
全新的黎明
Java 8.0 (2014年) 加入了日期和时间API。
基于Joda-Time(最早可能是在2002年左右)制定了JSR-310。
以不变性为特点
提供在java.time包中。
日期和时间 API
主要/核心的班级
本地日期
一个保持年月日的类。基于ISO8601标准。使用先进的公历来处理日期。java.util.Date在处理上有朱利历和公历切换的问题。
本地时间
一个用于保存小时,分钟,秒(精确到纳秒)的类。不具备时区信息。
本地日期时间
LocalDate和LocalTime类。
区域编号
保持着亚洲/东京等地理时区信息,之前已经有java.util.TimeZone,但新创建了一个类。不仅可以处理偏移信息,还能保留夏令时信息。为了处理单纯的偏移,存在一个名为ZoneOffset的子类。
立即
含义:瞬间、瞬时、(特定的)时刻
ZonedDateTime: 时区日期时间
本地日期 + 本地时间 + 时区
先创立的公历是指…
「ISO8601 是对1582年10月开始实施的格里高利历制定的一种适用于1582年以前的历法。」
在1582年之前,处理系统的处理方式各异。
ISO8601不提供对公元前1年的处理规定。
Java的LocalDate将公元1年的前一年视为0年来处理。
LocalDate.of(1,1,1).minusDays(1) -> 0000-12-31
Postgres将1年的前一年视为-1年。
在数据库中处理日期
就算是DATE类型,根据不同的DBMS处理方式各不相同。
在标准的SQL中,日期时间相关的数据类型包括:
* DATE类型(日期)
* TIME类型(时间)
* TIMESTAMP类型(日期和时间的组合)
TIME和TIMESTAMP不保存时区信息。
在Oracle中…
日付時間相关的数据类型有四种:
* DATE:保存年月日和时分秒,不包含时区信息。
* TIMESTAMP:保存年月日和时分秒,可指定小数位数(最多9位),不包含时区信息。
* TIMESTAMP WITH TIME ZONE:在上述的基础上添加了时区信息。
* TIMESTAMP WITH LOCAL TIME ZONE:拥有时区信息,但是以数据库的时区为准。
参考链接:http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/server.111/E05765-03/datatype.htm#18523
日期在内部以儒略日和秒为单位。7字节。范围从公元前4712年01月01日到公元9999年12月31日。
使用儒略历和格里高利历。
公元1年的前一年是0年,0年的前一年被称为“公元前1年”。
在Postgres中……
-
- DATE型(日付)
-
- TIME型(時刻)
-
- TIMESTAMP型(日付と時刻を足したもの)
- TIMEとTIMESTAMPはタイムゾーンの有り無しを指定できる
与Oracle相同,使用儒略日历公元前4713年1月1日至294276年12月31日。
采用最初的格利高里历法(先行历法)。
公元1年的前一年是公元前1年,公元前1年的前一年是公元前2年。
0001-01-01的前一天是’0001-12-31 BC’.
MySQL被广泛用于数据库管理系统。
-
- DATE型(日付)0年月日
-
- TIME型(時刻) ’00:00:00′ マイクロ秒まで。タイムゾーンない
- DATETIME型(日付と時刻を足したもの) タイムゾーンない
在日期中,使用 3 个字节的存储空间。范围从 ‘1000-01-01’ 到 ‘9999-12-31’,采用先行的公历制。
其他
Git 不仅仅是一个版本控制系统。
保留从Epoch开始的秒数和时区信息
Apache Spark 是一个快速、可扩展且易于使用的开源大数据处理框架。
以下是对上述内容的中文表达:
日期时间相关有两种类型:
* TimestampType:保存年月日和时分秒信息,没有时区信息。
* DateType:保存年月日信息。
请参考链接:https://spark.apache.org/docs/latest/sql-programming-guide.html#data-types
镶木地板 mù dì
日付時間相关有三种类型:
– DATE以INT32存储自1970-01-01以来的天数。
– TIME_MILLIS以INT32存储从0:00开始的毫秒数。
– TIMESTAMP_MILLIS以INT64存储自时代开始以来的毫秒数。没有时区信息。
总结
在处理日期和时间时,应该了解每个处理系统的行为。
要考虑时区的存在与否。
即使存在夏令时,也能正常工作吗?
最好设计一个能够在更改处理机的时区时不会出问题的结构。
获取当前日本本地时间
LocalDate.now(ZoneId.of(“Asia/Tokyo”))
ZoneId.getAvailableZoneIds.asScala.foreach(println)
JapaneseDate.of(1900,1,1).getEra
在Oracle中,日期时间相关的数据类型有四种。
我们在内部保持着儒略日。从-4712/01/01到9999/12/31。我们同时使用儒略历和格里高利历。将日期2001-01-01转换为儒略日的值是2451911。
把日期 ‘0001-01-01’ 转换为儒略日,结果为1721424。
将日期’1582-10-04’转换为J格式的字符串输出为2299160,将日期’1582-10-15’转换为J格式的字符串输出为2299161。
在Postgres中,与Oracle一样,使用了儒略日历,从公元前4713年1月1日到公元294276年12月31日。在Postgres中,执行SELECT TO_CHAR(DATE ‘2001-01-01’, ‘J’)命令可以得到结果。
将 to_char 翻译成中文。
2451911(1行)可以转述为:一行中有2451911条数据。
postgres=# SELECT TO_CHAR (DATE ‘0001-01-01’, ‘J’);
将日期 ‘0001-01-01’ 转换为字符表示,使用J模式。
转换成字符
1721426
(1 行)
postgres=# 查询 (日期 ‘1582-10-04’, ‘J’) 的TO_CHAR值;
将to_char转换为字符串
2299150可以翻译成”二百二十九万九千一百五十”。