大家好,我是你的好朋友思创斯。今天说一说java的时间日期类型_java编写一个日期类date,希望您对编程的造诣更进一步.
《java core》ed.11 读书笔记
java1.0有了date
类(native方式)来处理时间相关
java1.1有了calendar
类(不完美,实例是可变的(mutable),无法处理润秒(leap second)),date
类中的大部分方法在这个版本过时
java8的java.time
包解决了之前处理时间的类的缺点
时间线
秒的定义是源自地球自转一圈(606024=86400s),因为地球自传有轻微抖动(wobble),所以秒的精确定义根据铯-133原子的属性(1967)。此后,原子钟网络在保持官方时间。1972年开始,”润秒“偶尔出现(出现修改时间系统的讨论)。大多数计算机系统使用”更平滑“的方式来保持每天86400s。这是因为计算机的时间并不精确,且它是从外部时间服务来同步时间
java的date和time api规定java使用时间精度需要:
- 一天86400s
- 每天中午(noon)要匹配官方时间
- 在一个精度控制下,尽量在其它时间匹配官方时间
调用静态方法instant.now()
来获取当前时刻,通过equals
compareto
可以比较两个instant,可以把instant当作时间戳来用
instant start = instant.now();
runalgorithm();
instant end = instant.now();
// 获取两个instant的间隔
// duration是两个instant之间度过多少时间,可以通过调用tonanos, tomillis, toseconds(java8改为getseconds), tominutes, tohours, or todays来获取不同时间单位
duration timeelapsed = duration.between(start, end);
long millis = timeelapsed.tomillis();
希望我今天分享的这篇文章可以帮到您。
如果需要纳秒精度,那么要注意溢出问题。一个long值可以保存300年的纳秒,如果duration中的值小于这个数,可以直接转换
duration
instant
都是immutable的,它们的方法都是返回一个新的对象
// 得到一个duration对象的不同部分,100s -> 1分40秒
int to(nanos|millis|seconds|minutes|hours)part() 9
long to(days|hours|minutes|seconds|millis|nanos)part() 9
local date
在java api中人类的时间有两种
- local date/time
- zoned time
local date/time拥有一天的日期信息和时间信息,但是没有时区相关的信息。例如一个日期june 14, 1903,它没有一天中的时间点也没有时区信息,所以它没有相关的instant时间点。july 16, 1969, 09:32:00 edt是一个zoned date/time,在时间线中代表一个准确时间
有很多计算是不需要带时区的时间的,有时甚至带时区时间会有负面影响(如果每周一10:00要开周会,那么下一次的时间是(t 60*60*24*7),如果碰巧度过了夏令时(白天长的夏天会把时钟调快1小时),那么实际的开会时间可能会早1个小时
因此,除非必须要处理时区,否则使用local date/time即可
localdate today = localdate.now(); // today's date
localdate alonzosbirthday = localdate.of(1903, 6, 14);
// uses the month enumeration month.june
// 月份不再是从0开始,年份也不是从1900年开始
alonzosbirthday = localdate.of(1903, month.june, 14);
local date的间隔是period
,表示度过的年、月、日
birthday.plus(period.ofyears(1))
birthday.plusyears(1)
// 如果是闰年,得不到明年生日的准确日期
birthday.plus(duration.ofdays(365))
// 两个local date的间隔
independenceday.until(christmas)
independenceday.until(christmas, chronounit.days) // 174 days
// 返回1,即当周的第几天 dayofweek.monday
localdate.of(1900, 1, 1).getdayofweek().getvalue()
// dayofweek.tuesday
dayofweek.saturday.plus(3)
localdate start = localdate.of(2000, 1, 1);
localdate endexclusive = localdate.now();
// java9新方法,得到一个localdate对象的流
stream alldays = start.datesuntil(endexclusive);
// 每个月的第一天
stream firstdaysinmonth = start.datesuntil(endexclusive, period.ofmonths(1));
date adjusters
调节器是用来根据一个给定日期得到和它相关的某个日期
temporaladjusters
类提供了一些静态方法来做一般的调节
// 一个月的第一个星期二
// 返回的localdate是一个新对象
localdate firsttuesday = localdate.of(year, month, 1)
.with(temporaladjusters.nextorsame(dayofweek.tuesday));
通过实现temporaladjuster
接口,可以实现自己的调节器(adjuster)
// 计算下一个工作日
// w的类型是temporal,必须要转为localdate,也可以通过ofdateadjuster(参数是unaryoperator类型的lambda表达式)来替代类型转换
temporaladjuster next_workday = w ->
{
var result = (localdate) w;
do
{
result = result.plusdays(1);
}
// 周日开始工作日
while (result.getdayofweek().getvalue() >= 6);
return result;
};
localdate backtowork = today.with(next_workday);
// 通过ofdateadjuster
temporaladjuster next_workday = temporaladjusters.ofdateadjuster(w ->
{
localdate result = w; // no cast
do
{
result = result.plusdays(1);
}
while (result.getdayofweek().getvalue() >= 6);
return result;
});
local time
localtime
代表一天中的时间,例如15:30:00
// 获取时间
localtime rightnow = localtime.now();
localtime bedtime = localtime.of(22, 30); // or localtime.of(22, 30, 0)
// plus/minus 操作是在24小时内范围
localtime wakeup = bedtime.plushours(8); // wakeup is 6:30:00
localtime
本身不关心am pm
,使用格式化类来处理这类问题
localdatetime
类代表一个日期 一个时间,这个类适合存储在固定时区下的一个时间点。然而,如果要处理跨过夏令时或者处理不同时区的用户,那么应该使用zoneddatetime
zoned time
时区是人为划分的,所以比地球自转带来的复杂性更烦(!)。现实世界中,我们遵循格林威治时间(中国横跨4个时区)
internet assigned numbers authority (iana)拥有一个数据库(www.iana.org/time-zones),它包括全世界所有的时区,每年都会更新几次,这些更新是为了处理夏令时的规则。java使用了iana数据库。每个时区有一个id,例如america/new_york europe/berlin。为了找到所有的时区,调用zoneid.getavailablezoneids
(写书的时候有将近600个id)(写这篇的时候是601)。给定一个时区id,调用zoneid.of(id)
会产生一个zoneid
对象,可以使用这个对象将localdatetime
对象改为一个zoneddatetime
对象(调用local.atzone(zoneid)
),或者通过zoneddatetime.of(year, month, day, hour, minute, second, nano, zoneid)
来构建一个zoneddatetime
对象
// 1969-07-16t09:32-04:00[america/new_york]
zoneddatetime apollo11launch = zoneddatetime.of(1969, 7, 16, 9, 32, 0, 0, zoneid.of("america/new_york"));
// 获取instant
apollo11launch.toinstant()
// 如果有一个instant,那么可以将其转为某时区 zoneddatetime,也可以传不同的时区id
instant.atzone(zoneid.of("utc"))
utc时间是在greenwich royal observatory的时间,没有夏令时
zoneddatetime
的大部分方法和localdatetime
类似(参考api)
夏令时问题:
在2013年,中欧在3月31号2:00调整了夏令时,如果想创建一个不存在时间 3月31号2:30,实际上会得到一个3:30
// constructs march 31 3:30
zoneddatetime skipped = zoneddatetime.of(localdate.of(2013, 3, 31), localtime.of(2, 30), zoneid.of("europe/berlin"));
相反,夏令时结束的时候,时候会被调回1个小时,那么同一个local time会有两个instant,在这个时间跨度中创建一个时间,会拿到这两个时间点中早的那个
zoneddatetime ambiguous = zoneddatetime.of(localdate.of(2013, 10, 27), // end of daylight savings time
localtime.of(2, 30),
// 2013-10-27t02:30 02:00[europe/berlin]
zoneid.of("europe/berlin"));
// 2013-10-27t02:30 01:00[europe/berlin]
zoneddatetime anhourlater = ambiguous.plushours(1);
一个小时后,时间有了固定的小时和分钟数,但是时区的偏移值已经改变了。在调节度过夏令时的日期时,不要加7天,而是使用period
// 不要使用这种方式
zoneddatetime nextmeeting = meeting.plus(duration.ofdays(7));
// ok
zoneddatetime nextmeeting = meeting.plus(period.ofdays(7));
offsetdatetime
类表示utc时间的偏移量(不算时区规则),这个类一般用于特殊应用(不需要那些时区规则的,例如一些网络协议)。对人类时间来说,使用zoneddatetime
formatting and parsing
datetimeformatter
提供了三种formatter来打印date/time值
- 预定义的标准formatter
- 地区指定(locale-specific)formatter
- 自定义模式的formatter
要使用formatter,调用format
// 1969-07-16t09:32:00-04:00"
string formatted = datetimeformatter.iso_offset_date_time.format(launch)
预定义的formatter
formatter | 描述 | 例子 |
---|---|---|
basic_iso_date | 年月日-时区偏移(offset),没有分隔符 | 19690713-0500 |
iso_local_date,iso_local_time,iso_local_date_time | 分隔符 – : t | 1969-07-16,09:32:00,1969-07-16t09:32:00 |
iso_offset_date,iso_offset_time,iso_offset_date_time | 和iso_local_xx类似,但是带时区偏移 | xx-05:00 |
iso_zoned_date_time | 带有时区偏移和id | xx[america/new_york] |
iso_instant | utc,z的时区id | 1969-07-19t14:32:00z |
iso_date,iso_time,iso_date_time | 和iso_offset_date这些类似,但是时区信息是可选的 | xx-05:00[america/new_york] |
iso_ordinal_date | 年,和年的第多少天,对于localdate | 1969-197 |
iso_week_date | 年,周,该周第几天,对于localdate | 1969-w29-3 |
rfc_1123_date_time | email的标准时间戳,rfc822整理的,在rfc1123把年更新成4位 | wed, 16 jul 1969 09:32:00 -0500 |
这些标准的formatter主要是为了机器易读的时间戳。人类易读的日期和时间使用locale-specific formatter。4中style short medium long full
style | date | time |
---|---|---|
short | 7/16/69 | 9:32 am |
medium | jul 16, 1999 | 9:32:00 am |
long | july 16, 1969 | 8:32:00 am edt |
full | wednesday, july 16, 1969 | 9:22:00 am edt |
使用oflocalizeddate
oflocalizedtime
oflocalizeddatetime
来创建一个formatter
datetimeformatter formatter = datetimeformatter.oflocalizeddatetime(formatstyle.long);
// july 16, 1969 9:32:00 am edt
string formatted = formatter.format(apollo11launch);
// 改变默认地区
// 16 juillet 1969 09:32:00 edt
formatted = formatter.withlocale(locale.french).format(apollo11launch);
// prints mon tue wed thu fri sat sun
for (dayofweek w : dayofweek.values())
system.out.print(w.getdisplayname(textstyle.short, locale.english) " ");
// datetimeformatter是dateformat的代替,如果需要后者,可以使用toformat
formatter.toformat().
通过指定pattern自己创建formatter,模式参考下表
要从一个字符串转为一个日期/时间对象,调用parse
方法
localdate churchsbirthday = localdate.parse("19030614");
zoneddatetime apollo11launch = zoneddatetime.parse("19690716 03:32:000400", datetimeformatter.ofpattern("yyyymmdd hh:mm:ssxx"));
和历史代码的互操作
java曾经的时间相关处理类包括java.util.date
java.util.gregoriancalendar
java.sql.date/time/timestamp
instant
类和java.util.date
类是最为相似的,在java8中,java.util.date
类添加了两个方法(toinstant
将date转为instant from
从别的位置转过来)
zoneddatetime
和java.util.gregoriancalendar
最为相似,java8中,java.util.gregoriancalendar
使用tozoneddatetime
方法将gregoriancalendar转为zoneddatetime,from
方法做相反的转换
java.sql
包中的日期、时间不允许使用(!)
可以把datetimeformatter
类传入历史代码需要java.text.format
对象的地方
文章由思创斯整理,转载请注明出处:https://ispacesoft.com/381197.html