时间
- GMT:(格林尼治平均时间),旧时间标准(基于地球自转),已被UTC取代。
- UTC:现代国际标准时间(基于原子钟),计算机系统默认时间基准。
- **CST(北京时间(UTC+8)/美国中部时间(UTC-6)/澳大利亚中部时间(UTC+9:30)…):明确标注为UTC+8或Asia/Shanghai时区,避免使用CST缩写。
- Date存储:以UTC时间1970-01-01T00:00:00为基准的毫秒值,显示时受系统时区影响。
JDK7时间类(线程不安全)
Date
-
存储基准:内部通过
long
类型存储的是从1970年1月1日00:00:00 UTC(而非GMT+8)起经过的毫秒值,与时区无关。 -
中国时间显示问题:当系统时区设置为东八区时,
Date
的toString()
方法会将UTC时间自动偏移+8小时显示(如
1970-01-01 08:00:00 CST
),但底层存储的毫秒值仍为0。
构造方法
new Date()//当前时间
new Date(0L)//创建日期对象,把当前的毫秒值转成日期对象
常用方法
public long getTime() //把日期对象转换成对应的时间毫秒值。
public void setTime(long time) //把方法参数给定的毫秒值设置给日期对象
SimpleDateFormat
构造方法
public SimpleDateFormat(String pattern);
- 用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。
- 不写是默认格式
常用方法
public String format(Date date):将Date对象格式化为字符串。
public Date parse(String source):将字符串解析为Date对象。
Calendar
-
Calendar代表了系统当前时间的日历对象,可以单独修改、获取时间中的年,月,日
-
细节:
Calendar
是抽象类,不能直接实例化,必须通过静态方法getInstance()
获取子类对象。Calendar c = Calendar.getInstance(); //2.修改一下日历代表的时间 Date d = new Date(OL); c.setTime(d);
底层原理
- 时区敏感:
根据系统默认时区创建日历对象,默认表示当前时间。 - 时间字段存储:
将纪元(Era)、年、月、日、时、分、秒、星期等时间字段存入内部数组,通过常量索引访问(如Calendar.YEAR
)。
关键细节
月份范围
-
范围:
0~11
-
映射关系:
数值 实际月份 0 1月 1 2月 … … 11 12月 星期范围
-
范围:
1~7
-
映射关系:
数值 实际星期 1 星期日(Sunday) 2 星期一(Monday) … … 7 星期六(Saturday) - 时区敏感:
常用方法
public int get(int field) 取日期中的某个字段信息
public void set(int field,int value) 修改日历的某个字段信息
public void add(int field,int amount) 为某个字段增加/减少指定的值
JDK8新增时间类(线程安全)
JDK8时间类类名 | 作用 |
---|---|
ZoneId | 时区 |
Instant | 时间戳 |
ZoneDateTime | 带时区的时间 |
DateTimeFormatter | 用于时间的格式化和解析 |
LocalDate | 年、月、日 |
LocalTime | 时、分、秒 |
LocalDateTime | 年、月、日、时、分、秒 |
Duration | 时间间隔(秒,纳,秒) |
Period | 时间间隔(年,月,日) |
ChronoUnit | 时间间隔(所有单位) |
Date上位
ZoneId 时区
static Set<string> getAvailableZoneIds() 获取Java中支持的所有时区(600多)
static ZoneId systemDefault() 获取系统默认时区(可以在系统设置中改)
static Zoneld of(string zoneld) 获取一个指定时区(配合后续的时间类使用)
//1.获取所有的时区名称
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds.size());//600
//2.获取当前系统的默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);//Asia/Shanghai
//3.获取指定的时区
ZoneId zoneId1 = ZoneId.of("Asia/Pontianak");
System.out.println(zoneId1);//Asia/Pontianak
Instant 时间戳
时间戳不可变在减少时间系列的方法与增加时间系列的方法中会返回一个新的时间戳对象,保证线程安全
Instant
的默认输出遵循 ISO-8601 标准,格式为 YYYY-MM-DDTHH:mm:ss.SSSSSSSSSZ
,其中:
-
T
是日期和时间的分隔符; -
Z
表示UTC时区(零时区); -
.SSSSSSSSS
部分为纳秒精度,若纳秒值为零则省略2024-01-07T14:44:58.805Z
此处
805
表示 805 毫秒(即 805,000,000 纳秒),若存在非零纳秒,会显示完整 9 位(如123456789
纳秒会显示为.123456789
)
static Instant now() 获取当前时间的Instant对象(标准时间)
static Instant ofXxxx(long epochMilli) 根据(秒/毫秒/纳秒)获取Instant对象
ZonedDateTime atZone(ZoneIdzone) 指定时区
boolean isxxx(Instant otherInstant) 判断系列的方法(大于还是小于)
Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant plusXxx(long millisToSubtract) 增加时间系列的方法
//1.获取当前时间的Instant对象(标准时间)
Instant now = Instant.now();
System.out.println(now);
//2.根据(秒/毫秒/纳秒)获取Instant对象
Instant instant1 = Instant.ofEpochMilli(0L);
System.out.println(instant1);//1970-01-01T00:00:00z
Instant instant2 = Instant.ofEpochSecond(1L);
System.out.println(instant2);//1970-01-01T00:00:01Z
Instant instant3 = Instant.ofEpochSecond(1L, 1000000000L);
System.out.println(instant3);//1970-01-01T00:00:027
//3. 指定时区
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time); 会加上8小时
//4.isXxx 判断
Instant instant4=Instant.ofEpochMilli(0L);
Instant instant5 =Instant.ofEpochMilli(1000L);
//5.用于时间的判断
//isBefore:判断调用者代表的时间是否在参数表示时间的前面
boolean result1=instant4.isBefore(instant5);
System.out.println(result1);//true
//isAfter:判断调用者代表的时间是否在参数表示时间的后面
boolean result2 = instant4.isAfter(instant5);
System.out.println(result2);//false
//6.Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant instant6 =Instant.ofEpochMilli(3000L);
System.out.println(instant6);//1970-01-01T00:00:03Z
Instant instant7 =instant6.minusSeconds(1);
System.out.println(instant7);//1970-01-01T00:00:02Z
ZoneDateTime 带时区的时间
与时间戳一样都为不可变的,修改返回的都是新对象
static ZonedDateTime now() 获取当前时间的ZonedDateTime对象(带时区,默认电脑时区)
static ZonedDateTime ofXxxx(。。。) 获取指定时间的ZonedDateTime对象
ZonedDateTime withXxx(时间) 修改时间系列的方法
ZonedDateTime minusXxx(时间) 减少时间系列的方法
ZonedDateTime plusXxx(时间) 增加时间系列的方法
//1.获取当前时间对象(带时区)
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
//2.获取指定的时间对象(带时区)1/年月日时分秒纳秒方式指定
ZonedDateTime time1 = ZonedDateTime.of(2023,10,1,11,12,12,0,ZoneId.of("Asia/Shanghai"));
System.out.println(time1);
//通过Instant + 时区的方式指定获取时间对象
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2);
//3.withXxx 修改时间系列的方法
ZonedDateTime time3 = time2.withYear(2000);
System.out.println(time3);
//4. 减少时间
ZonedDateTime time4 = time3.minusYears(1);
System.out.println(time4);
//5.增加时间
ZonedDateTime time5 = time4.plusYears(1);
System.out.println(time5);
SimpleDateFormat上位
DateTimeFormatter 用于时间的格式化和解析
static DateTimeFormatter ofPattern(格式) 获取格式对象
String format(时间对象) 按照指定方式格式化
//获取时间对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
// 解析/格式化器
DateTimeFormatter dtf1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm;ss EE a");
// 格式化
System.out.println(dtf1.format(time));
Calendar上位
LocalDate 年、月、日
LocalTime 时、分、秒
LocalDateTime 年、月、日、时、分、秒
方法类型 | 方法名/模式 | 功能说明 | 适用类 |
---|---|---|---|
静态工厂方法 | static XXX now() | 获取当前时间对象(默认时区) | LocalDate /Time /DateTime |
static XXX of(...) | 指定参数创建时间对象(如 of(2025, 4, 27) ) | 同上 | |
字段获取 | getYear() / getMonth() | 获取年、月、日、时、分、秒等字段值 | 同上 |
getDayOfWeek() | 获取星期枚举(如 SUNDAY ) | LocalDate /DateTime | |
时间比较 | isBefore(其他时间对象) | 判断当前时间是否早于指定时间 | 同上 |
isAfter(其他时间对象) | 判断当前时间是否晚于指定时间 | 同上 | |
时间修改 | withYear(2026) | 直接修改特定字段(年、月、日、时等) | 同上 |
with(TemporalAdjuster) | 高级调整(如设置为当月第一天) | LocalDate /DateTime | |
时间增减 | plusDays(7) | 增加指定时间量(天、月、年、时、分等) | 同上 |
minusHours(3) | 减少指定时间量(链式操作,返回新对象) | 同上 |
工具类
-
**Duration **: 用于计算两个“时间”间隔(秒,纳秒)
System.out.println("相差的时间间隔对象:" + duration); // 输出:PT197637H27M42.639166S
-
格式规则:
-
PT
表示“时间段开始”(P
是 Period 前缀,T
分隔日期与时间)。 -
H
表示小时,M
表示分钟,S
表示秒(含纳秒精度)。 -
含义:时间差为
197,637 小时 27 分钟 42.639166 秒。
- 注意:
Duration
不会自动将小时转换为天(如 24 小时 = 1 天),而是以单一单位累加(25小时,26小时…)。
使用:
duration.toXXX()
,将其转化为天/小时/分钟等 -
-
**Period **: 用于计算两个“日期”间隔(年、月、日)
Period age = Period.between(birthDate, LocalDate.now()); System.out.println("相差的时间间隔对象:" + period); // 输出:P22Y6M17D System.out.println(period.getYears()); // 输出:22 System.out.println(period.getMonths()); // 输出:6 System.out.println(period.getDays()); // 输出:17
- 格式说明:
P
是 Period 的前缀,后跟年(Y)
、月(M)
、日(D)
的组合。 - 含义:表示时间间隔为 22 年 6 个月 17 天。
- 注意:若某个字段为 0 会被省略(例如
P0Y3M2D
会显示为P3M2D
)
- 格式说明:
-
ChronoUnit :用于计算两个“日期”间隔(最常用)
long totalMinutes = ChronoUnit.MINUTES.between(birthDate, today);
long between(Temporal start, Temporal end)
中间加上要返回的单位
包装类
包装类:基本数据类型对应的引用类型
基本类型 | 对应的包装类(位于java.lang包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
integer
// 测试 Integer 缓存范围(-128~127)
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6 == i7); // true(值在缓存范围内,对象复用)
Integer i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8 == i9); // false(超出缓存范围,创建新对象)
// 使用 new 关键字强制创建新对象(即使值在缓存范围内)
Integer i10 = new Integer(127);
Integer i11 = new Integer(127);
System.out.println(i10 == i11); // false(new 每次生成新对象)
创建方式 | 行为 | 适用场景 |
---|---|---|
Integer.valueOf(int) | 对 -128~127 的整数,直接从缓存池返回对象;否则创建新对象。 | 优先使用,节省内存(复用缓存对象) |
new Integer(int) | 强制创建新对象,无论数值是否在缓存范围内。 | 需要独立对象时(极少使用) |
2. 关键规则
-
缓存机制:
Integer
类默认缓存了 -128~127 之间的整数对象(存在cache数组中),通过valueOf()
或自动装箱(如Integer i = 127
)会复用这些对象。- 超出此范围的数值,每次调用
valueOf()
或自动装箱都会创建新对象。
-
对象比较:
==
:比较对象内存地址。仅对缓存范围内的Integer
对象返回true
。equals()
:比较实际数值,推荐使用。
Integer a = 128; Integer b = 128; System.out.println(a.equals(b)); // true(数值相等)
自动装箱(Autoboxing)
代码示例:
Integer i1 = 10;
// 等价于:
Integer i1 = Integer.valueOf(10);
- 底层方法:
Integer.valueOf(int)
:将基本类型int
转换为Integer
对象。- 缓存机制:对
-128~127
范围内的值,返回缓存对象;超出范围则新建对象。
自动拆箱(Unboxing)
代码示例:
Integer i2 = new Integer(20);
int i = i2;
// 等价于:
int i = i2.intValue();
- 底层方法:
Integer.intValue()
:将Integer
对象转换为基本类型int
。
这两个在集合中非常常用
常用方法
方法名 | 功能分类 | 参数说明 | 返回值 | 示例 | 注意事项 |
---|---|---|---|---|---|
Integer.toBinaryString(int i) | 进制转换 | i : 十进制整数 | String | toBinaryString(10) → "1010" | • 结果无前缀(如 "1010" 而非 0b1010 ) • 负数返回补码二进制字符串 |
Integer.toOctalString(int i) | 进制转换 | i : 十进制整数 | String | toOctalString(10) → "12" | • 结果无前缀(如 "12" 而非 0o12 ) |
Integer.toHexString(int i) | 进制转换 | i : 十进制整数 | String | toHexString(255) → "ff" | • 结果无前缀且字母小写(如 "ff" 而非 0xff 或 "FF" ) |
Integer.parseInt(String s) | 字符串转整数 | s : 十进制数字字符串 | int | parseInt("123") → 123 | • 字符串非法时抛出 NumberFormatException • 仅支持十进制输入 |
Integer.parseInt(String s, int radix) | 字符串转整数 | s : 数字字符串 radix : 进制(2~36) | int | parseInt("1010", 2) → 10 | • 支持 2~36 进制(如 16 进制允许 0-9 和 a-f ) • 字母不区分大小写 |
- 除了Character都有parseXXX的方法
计算闰年取巧
使用java的时间类:
- 设置为所需判断的年份的第一天
2000年1月1日
与最后一天2000年12月31日
然后相减,判断是不是366天. - 设置为判断的年份的
3月1日
然后减一天,判断是否为2月29日