1. java 8 新日期时间 api 概述
1.1 为什么需要新的日期时间 api
在 java 8 之前,java 的日期时间处理主要依赖于 java.util.date 和 java.util.calendar 类,但这些类存在诸多问题:
非线程安全:
simpledateformat和calendar不是线程安全的设计混乱:date 类既包含日期又包含时间,且年份从1900年开始计算
时区处理复杂:时区转换逻辑繁琐
api 难以使用:常见操作需要大量代码
不可变性缺失:日期时间对象可以被修改
1.2 java 8 新日期时间 api 的优势
java 8 引入了全新的 java.time 包,基于 joda-time 的设计理念,具有以下特点:
不可变且线程安全:所有核心类都是不可变的
清晰的领域模型:将日期、时间、时区等概念分离
流畅的 api:方法链式调用,代码更简洁
强大的时区支持:完善的时区处理机制
丰富的操作:提供大量日期时间计算和调整方法
2. 核心类介绍
2.1 主要类及其用途
| 类名 | 描述 | 示例 |
|---|---|---|
localdate | 只包含日期,不包含时间和时区 | 2023-12-25 |
localtime | 只包含时间,不包含日期和时区 | 10:15:30 |
localdatetime | 包含日期和时间,但不包含时区 | 2023-12-25t10:15:30 |
zoneddatetime | 包含日期、时间和时区 | 2023-12-25t10:15:30+08:00[asia/shanghai] |
instant | 时间戳,表示自1970-01-01t00:00:00z的秒数 | 1640400000 |
duration | 时间段,基于时间的(秒、纳秒) | pt1h30m (1小时30分钟) |
period | 时间段,基于日期的(年、月、日) | p1y2m3d (1年2个月3天) |
zoneid | 时区标识符 | asia/shanghai |
zoneoffset | 时区偏移量 | +08:00 |
datetimeformatter | 日期时间格式化器 |
2.2 包结构
// 主要包结构 java.time // 核心api java.time.chrono // 历法系统 java.time.format // 格式化 java.time.temporal // 时间字段和调整器 java.time.zone // 时区支持
3. localdate、localtime、localdatetime
3.1 localdate - 日期处理
3.1.1 创建 localdate 实例
import java.time.localdate;
import java.time.month;
import java.time.temporal.chronofield;
import java.time.temporal.temporaladjusters;
public class localdateexample {
public static void main(string[] args) {
// 1. 获取当前日期
localdate today = localdate.now();
system.out.println("今天: " + today);
// 2. 指定日期创建
localdate specificdate1 = localdate.of(2023, 12, 25);
system.out.println("指定日期1: " + specificdate1);
localdate specificdate2 = localdate.of(2023, month.december, 25);
system.out.println("指定日期2: " + specificdate2);
// 3. 解析字符串创建
localdate parseddate = localdate.parse("2023-12-25");
system.out.println("解析日期: " + parseddate);
// 4. 使用纪元日创建(1970-01-01为第0天)
localdate epochday = localdate.ofepochday(365 * 50); // 1970-01-01后50年
system.out.println("纪元日: " + epochday);
// 5. 使用年份和年份中的天数创建
localdate yearday = localdate.ofyearday(2023, 300);
system.out.println("年份第300天: " + yearday);
}
}3.1.2 获取日期信息
public class localdateinfoexample {
public static void main(string[] args) {
localdate date = localdate.of(2023, 12, 25);
// 获取基本信息
int year = date.getyear();
month month = date.getmonth();
int monthvalue = date.getmonthvalue();
int dayofmonth = date.getdayofmonth();
int dayofyear = date.getdayofyear();
dayofweek dayofweek = date.getdayofweek();
system.out.println("年份: " + year);
system.out.println("月份(枚举): " + month);
system.out.println("月份(数值): " + monthvalue);
system.out.println("月中的天数: " + dayofmonth);
system.out.println("年中的天数: " + dayofyear);
system.out.println("星期几: " + dayofweek);
system.out.println("星期几(数值): " + dayofweek.getvalue());
// 使用temporalfield获取
int year2 = date.get(chronofield.year);
int month2 = date.get(chronofield.month_of_year);
int day2 = date.get(chronofield.day_of_month);
// 检查日期属性
boolean isleapyear = date.isleapyear();
system.out.println("是否为闰年: " + isleapyear);
// 日期比较
localdate yesterday = localdate.now().minusdays(1);
localdate tomorrow = localdate.now().plusdays(1);
system.out.println("昨天是否在今天之前: " + yesterday.isbefore(today));
system.out.println("明天是否在今天之后: " + tomorrow.isafter(today));
system.out.println("是否是今天: " + today.equals(localdate.now()));
}
}3.1.3 日期计算与修改
public class localdatecalculationexample {
public static void main(string[] args) {
localdate date = localdate.of(2023, 12, 25);
// 增加/减少天数
localdate plusdays = date.plusdays(7);
localdate minusdays = date.minusdays(7);
// 增加/减少周数
localdate plusweeks = date.plusweeks(2);
// 增加/减少月数
localdate plusmonths = date.plusmonths(3);
localdate minusmonths = date.minusmonths(3);
// 增加/减少年数
localdate plusyears = date.plusyears(1);
localdate minusyears = date.minusyears(1);
system.out.println("原日期: " + date);
system.out.println("加7天: " + plusdays);
system.out.println("减7天: " + minusdays);
system.out.println("加2周: " + plusweeks);
system.out.println("加3个月: " + plusmonths);
system.out.println("减3个月: " + minusmonths);
system.out.println("加1年: " + plusyears);
system.out.println("减1年: " + minusyears);
// 使用with方法修改特定字段
localdate withyear = date.withyear(2024);
localdate withmonth = date.withmonth(6);
localdate withdayofmonth = date.withdayofmonth(15);
localdate withdayofyear = date.withdayofyear(100);
system.out.println("修改年份: " + withyear);
system.out.println("修改月份: " + withmonth);
system.out.println("修改日: " + withdayofmonth);
system.out.println("修改年为第100天: " + withdayofyear);
// 使用temporaladjusters进行复杂调整
localdate firstdayofmonth = date.with(temporaladjusters.firstdayofmonth());
localdate lastdayofmonth = date.with(temporaladjusters.lastdayofmonth());
localdate firstdayofnextmonth = date.with(temporaladjusters.firstdayofnextmonth());
localdate firstdayofyear = date.with(temporaladjusters.firstdayofyear());
localdate lastdayofyear = date.with(temporaladjusters.lastdayofyear());
localdate nextmonday = date.with(temporaladjusters.next(dayofweek.monday));
localdate previousmonday = date.with(temporaladjusters.previous(dayofweek.monday));
system.out.println("当月第一天: " + firstdayofmonth);
system.out.println("当月最后一天: " + lastdayofmonth);
system.out.println("下月第一天: " + firstdayofnextmonth);
system.out.println("当年第一天: " + firstdayofyear);
system.out.println("当年最后一天: " + lastdayofyear);
system.out.println("下一个周一: " + nextmonday);
system.out.println("上一个周一: " + previousmonday);
}
}3.2 localtime - 时间处理
3.2.1 创建和操作 localtime
import java.time.localtime;
import java.time.temporal.chronounit;
public class localtimeexample {
public static void main(string[] args) {
// 1. 获取当前时间
localtime now = localtime.now();
system.out.println("当前时间: " + now);
// 2. 指定时间创建
localtime specifictime1 = localtime.of(14, 30, 45);
localtime specifictime2 = localtime.of(14, 30, 45, 123456789);
// 3. 解析字符串创建
localtime parsedtime1 = localtime.parse("14:30:45");
localtime parsedtime2 = localtime.parse("14:30:45.123");
// 4. 获取时间信息
int hour = now.gethour();
int minute = now.getminute();
int second = now.getsecond();
int nano = now.getnano();
system.out.printf("时: %d, 分: %d, 秒: %d, 纳秒: %d%n", hour, minute, second, nano);
// 5. 时间计算
localtime plushours = now.plushours(2);
localtime minusminutes = now.minusminutes(30);
localtime plusseconds = now.plusseconds(45);
localtime plusnanos = now.plusnanos(1000000);
// 使用chronounit
localtime plushalfdays = now.plus(12, chronounit.hours);
// 6. 修改时间
localtime withhour = now.withhour(10);
localtime withminute = now.withminute(0);
localtime withsecond = now.withsecond(0);
localtime withnano = now.withnano(0);
// 7. 时间比较
localtime time1 = localtime.of(9, 0);
localtime time2 = localtime.of(17, 0);
system.out.println("time1是否在time2之前: " + time1.isbefore(time2));
system.out.println("time2是否在time1之后: " + time2.isafter(time1));
// 8. 时间范围检查
boolean isafternoon = now.isafter(localtime.noon);
boolean isbeforenoon = now.isbefore(localtime.noon);
system.out.println("是否是中午之后: " + isafternoon);
system.out.println("是否是中午之前: " + isbeforenoon);
}
}3.2.2 localtime 常量和方法
public class localtimeconstantsexample {
public static void main(string[] args) {
// 预定义的时间常量
system.out.println("午夜: " + localtime.midnight);
system.out.println("中午: " + localtime.noon);
system.out.println("最小时间: " + localtime.min);
system.out.println("最大时间: " + localtime.max);
// 时间单位转换
localtime time = localtime.of(14, 30, 45);
// 转换为秒数(从当天午夜开始)
int secondsofday = time.tosecondofday();
system.out.println("当天秒数: " + secondsofday);
// 转换为纳秒数
long nanosofday = time.tonanoofday();
system.out.println("当天纳秒数: " + nanosofday);
// 从秒数创建
localtime fromseconds = localtime.ofsecondofday(3600); // 01:00:00
system.out.println("从秒数创建: " + fromseconds);
// 从纳秒数创建
localtime fromnanos = localtime.ofnanoofday(3600000000000l); // 01:00:00
system.out.println("从纳秒数创建: " + fromnanos);
// 截断时间
localtime truncatedtominutes = time.truncatedto(chronounit.minutes);
localtime truncatedtohours = time.truncatedto(chronounit.hours);
system.out.println("截断到分钟: " + truncatedtominutes);
system.out.println("截断到小时: " + truncatedtohours);
}
}3.3 localdatetime - 日期时间处理
3.3.1 创建和操作 localdatetime
import java.time.localdate;
import java.time.localdatetime;
import java.time.localtime;
import java.time.month;
import java.time.temporal.chronofield;
public class localdatetimeexample {
public static void main(string[] args) {
// 1. 获取当前日期时间
localdatetime now = localdatetime.now();
system.out.println("当前日期时间: " + now);
// 2. 指定日期时间创建
localdatetime datetime1 = localdatetime.of(2023, 12, 25, 14, 30, 45);
localdatetime datetime2 = localdatetime.of(2023, month.december, 25, 14, 30, 45);
// 3. 从localdate和localtime组合
localdate date = localdate.of(2023, 12, 25);
localtime time = localtime.of(14, 30, 45);
localdatetime datetime3 = localdatetime.of(date, time);
// 4. 解析字符串
localdatetime parseddatetime = localdatetime.parse("2023-12-25t14:30:45");
// 5. 获取组件
localdate datepart = now.tolocaldate();
localtime timepart = now.tolocaltime();
int year = now.getyear();
month month = now.getmonth();
int dayofmonth = now.getdayofmonth();
int hour = now.gethour();
int minute = now.getminute();
int second = now.getsecond();
int nano = now.getnano();
// 6. 日期时间计算
localdatetime plusdays = now.plusdays(7);
localdatetime minushours = now.minushours(3);
localdatetime plusweeks = now.plusweeks(2);
localdatetime plusmonths = now.plusmonths(1);
localdatetime plusyears = now.plusyears(1);
// 7. 修改组件
localdatetime withyear = now.withyear(2024);
localdatetime withmonth = now.withmonth(6);
localdatetime withdayofmonth = now.withdayofmonth(15);
localdatetime withhour = now.withhour(9);
localdatetime withminute = now.withminute(0);
// 8. 比较操作
localdatetime tomorrow = now.plusdays(1);
localdatetime yesterday = now.minusdays(1);
system.out.println("明天是否在今天之后: " + tomorrow.isafter(now));
system.out.println("昨天是否在今天之前: " + yesterday.isbefore(now));
system.out.println("是否是同一时刻: " + now.equals(localdatetime.now()));
// 9. 使用temporalfield
int yearfield = now.get(chronofield.year);
int monthfield = now.get(chronofield.month_of_year);
int hourfield = now.get(chronofield.hour_of_day);
// 10. 转换为其他类型
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30);
localdate onlydate = datetime.tolocaldate();
localtime onlytime = datetime.tolocaltime();
system.out.println("仅日期: " + onlydate);
system.out.println("仅时间: " + onlytime);
}
}3.3.2 localdatetime 转换和格式化
import java.time.localdatetime;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
public class localdatetimeconversionexample {
public static void main(string[] args) {
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
// 1. 与字符串转换
string isostring = datetime.tostring(); // iso-8601格式
system.out.println("iso格式: " + isostring);
// 2. 自定义格式化
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy年mm月dd日 hh:mm:ss");
string formatted = datetime.format(formatter);
system.out.println("自定义格式: " + formatted);
// 3. 解析自定义格式
localdatetime parsed = localdatetime.parse("2023年12月25日 14:30:45", formatter);
system.out.println("解析结果: " + parsed);
// 4. 时间单位计算
long daysbetween = chronounit.days.between(
localdatetime.of(2023, 1, 1, 0, 0),
localdatetime.of(2023, 12, 31, 0, 0)
);
system.out.println("2023年天数: " + (daysbetween + 1));
// 5. 时间差计算(使用duration)
localdatetime start = localdatetime.of(2023, 12, 25, 9, 0);
localdatetime end = localdatetime.of(2023, 12, 25, 17, 30);
long hours = chronounit.hours.between(start, end);
long minutes = chronounit.minutes.between(start, end);
system.out.println("工作小时数: " + hours);
system.out.println("工作分钟数: " + minutes);
// 6. 时间调整
localdatetime adjusted = datetime
.withhour(9)
.withminute(0)
.withsecond(0)
.withnano(0);
system.out.println("调整到9点整: " + adjusted);
// 7. 检查特定时间点
boolean isweekend = datetime.getdayofweek().getvalue() >= 6; // 6=周六, 7=周日
boolean isbusinesshours = datetime.gethour() >= 9 && datetime.gethour() < 18;
boolean islunchtime = datetime.gethour() == 12 && datetime.getminute() >= 0 && datetime.getminute() <= 60;
system.out.println("是否是周末: " + isweekend);
system.out.println("是否是工作时间: " + isbusinesshours);
system.out.println("是否是午餐时间: " + islunchtime);
}
}4. instant、duration 和 period
4.1 instant - 时间戳
4.1.1 instant 的基本使用
import java.time.instant;
import java.time.localdatetime;
import java.time.zoneid;
import java.time.temporal.chronounit;
public class instantexample {
public static void main(string[] args) {
// 1. 获取当前时间戳
instant now = instant.now();
system.out.println("当前时间戳: " + now);
system.out.println("时间戳(毫秒): " + now.toepochmilli());
system.out.println("时间戳(秒): " + now.getepochsecond());
system.out.println("纳秒部分: " + now.getnano());
// 2. 创建特定时间戳
instant epoch = instant.epoch; // 1970-01-01t00:00:00z
instant specific1 = instant.ofepochsecond(1609459200l); // 2021-01-01t00:00:00z
instant specific2 = instant.ofepochmilli(1609459200000l);
instant specific3 = instant.ofepochsecond(1609459200l, 500_000_000l); // 加上500毫秒
system.out.println("纪元开始: " + epoch);
system.out.println("2021年元旦: " + specific1);
// 3. 时间戳计算
instant plusseconds = now.plusseconds(3600); // 加1小时
instant minusseconds = now.minusseconds(3600); // 减1小时
instant plusmillis = now.plusmillis(1000); // 加1秒
instant minusmillis = now.minusmillis(1000); // 减1秒
instant plusnanos = now.plusnanos(1_000_000_000l); // 加1秒
// 使用chronounit
instant tomorrow = now.plus(1, chronounit.days);
instant yesterday = now.minus(1, chronounit.days);
// 4. 比较时间戳
instant later = now.plusseconds(60);
instant earlier = now.minusseconds(60);
system.out.println("later是否在now之后: " + later.isafter(now));
system.out.println("earlier是否在now之前: " + earlier.isbefore(now));
// 5. 与localdatetime转换
localdatetime localdatetime = localdatetime.ofinstant(now, zoneid.systemdefault());
system.out.println("转换为本地时间: " + localdatetime);
instant fromlocaldatetime = localdatetime.atzone(zoneid.systemdefault()).toinstant();
system.out.println("转换回instant: " + fromlocaldatetime);
// 6. 时间戳范围
instant min = instant.min; // -1000000000-01-01t00:00:00z
instant max = instant.max; // +1000000000-12-31t23:59:59.999999999z
system.out.println("最小时间戳: " + min);
system.out.println("最大时间戳: " + max);
}
}4.1.2 instant 的实际应用场景
import java.time.instant;
import java.time.duration;
import java.time.localdatetime;
import java.time.zoneid;
import java.time.format.datetimeformatter;
public class instantapplications {
public static void main(string[] args) throws interruptedexception {
// 场景1:性能测试和代码执行时间测量
instant start = instant.now();
// 模拟耗时操作
thread.sleep(1000);
instant end = instant.now();
duration elapsed = duration.between(start, end);
system.out.println("执行时间: " + elapsed.tomillis() + " 毫秒");
system.out.println("执行时间: " + elapsed.tonanos() + " 纳秒");
// 场景2:日志时间戳
instant logtime = instant.now();
system.out.println("[" + logtime + "] 日志消息: 应用启动");
// 场景3:缓存过期时间
instant cachetime = instant.now();
instant expirytime = cachetime.plusseconds(300); // 5分钟后过期
system.out.println("缓存创建时间: " + cachetime);
system.out.println("缓存过期时间: " + expirytime);
// 检查缓存是否过期
boolean isexpired = instant.now().isafter(expirytime);
system.out.println("缓存是否过期: " + isexpired);
// 场景4:api请求时间戳
instant requesttime = instant.now();
system.out.println("api请求时间: " + requesttime);
// 转换为不同格式
datetimeformatter formatter = datetimeformatter
.ofpattern("yyyy-mm-dd hh:mm:ss")
.withzone(zoneid.systemdefault());
string formattedtime = formatter.format(requesttime);
system.out.println("格式化时间: " + formattedtime);
// 场景5:计算时间间隔
instant event1 = instant.parse("2023-12-25t09:00:00z");
instant event2 = instant.parse("2023-12-25t17:30:00z");
duration workduration = duration.between(event1, event2);
system.out.println("工作时间: " + workduration.tohours() + " 小时");
system.out.println("工作时间: " + workduration.tominutes() + " 分钟");
// 场景6:生成唯一id的时间戳部分
string uniqueid = "id-" + instant.now().toepochmilli() + "-" +
math.abs(system.nanotime() % 10000);
system.out.println("唯一id: " + uniqueid);
}
}4.2 duration - 时间段(基于时间)
4.2.1 duration 的基本操作
import java.time.duration;
import java.time.instant;
import java.time.localtime;
import java.time.temporal.chronounit;
public class durationexample {
public static void main(string[] args) {
// 1. 创建duration
duration onehour = duration.ofhours(1);
duration thirtyminutes = duration.ofminutes(30);
duration twoseconds = duration.ofseconds(2);
duration fivemillis = duration.ofmillis(5);
duration tennanos = duration.ofnanos(10);
// 使用chronounit
duration oneday = duration.of(1, chronounit.days);
duration halfday = duration.of(12, chronounit.hours);
system.out.println("1小时: " + onehour);
system.out.println("30分钟: " + thirtyminutes);
system.out.println("1天: " + oneday);
// 2. 解析字符串
duration parsed = duration.parse("pt1h30m15s"); // iso-8601格式
system.out.println("解析1小时30分15秒: " + parsed);
// 3. 从时间对象计算
localtime starttime = localtime.of(9, 0);
localtime endtime = localtime.of(17, 30);
duration workday = duration.between(starttime, endtime);
instant startinstant = instant.now();
instant endinstant = startinstant.plusseconds(3600);
duration onehourduration = duration.between(startinstant, endinstant);
system.out.println("工作日时长: " + workday);
system.out.println("1小时时长: " + onehourduration);
// 4. 获取duration的组成部分
long hours = workday.tohours();
long minutes = workday.tominutes();
long seconds = workday.getseconds();
int nano = workday.getnano();
// 分解为各部分
long dayspart = workday.todayspart();
long hourspart = workday.tohourspart();
long minutespart = workday.tominutespart();
long secondspart = workday.tosecondspart();
int millispart = workday.tomillispart();
int nanospart = workday.tonanospart();
system.out.printf("分解: %d天 %d小时 %d分钟 %d秒%n",
dayspart, hourspart, minutespart, secondspart);
// 5. duration计算
duration duration1 = duration.ofhours(2);
duration duration2 = duration.ofminutes(30);
duration sum = duration1.plus(duration2);
duration difference = duration1.minus(duration2);
duration multiplied = duration1.multipliedby(3);
duration divided = duration1.dividedby(2);
duration negated = duration1.negated();
duration abs = duration.ofhours(-2).abs();
system.out.println("2小时 + 30分钟 = " + sum);
system.out.println("2小时 - 30分钟 = " + difference);
system.out.println("2小时 × 3 = " + multiplied);
system.out.println("2小时 ÷ 2 = " + divided);
system.out.println("2小时的负数 = " + negated);
system.out.println("-2小时的绝对值 = " + abs);
// 6. 比较duration
duration shortduration = duration.ofminutes(10);
duration longduration = duration.ofhours(2);
system.out.println("short是否小于long: " + shortduration.minus(longduration).isnegative());
system.out.println("short是否为零: " + shortduration.iszero());
system.out.println("short是否为负: " + shortduration.isnegative());
}
}4.2.2 duration 的实际应用
import java.time.duration;
import java.time.instant;
import java.time.localdatetime;
import java.time.temporal.chronounit;
public class durationapplications {
public static void main(string[] args) throws interruptedexception {
// 应用1:计算代码执行时间
system.out.println("=== 性能测试 ===");
instant start = instant.now();
// 模拟耗时计算
long sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
instant end = instant.now();
duration elapsed = duration.between(start, end);
system.out.println("计算结果: " + sum);
system.out.println("执行时间: " + elapsed.tomillis() + "ms");
// 应用2:超时控制
system.out.println("\n=== 超时控制 ===");
duration timeout = duration.ofseconds(5);
instant operationstart = instant.now();
// 模拟长时间操作
thread.sleep(3000);
duration operationtime = duration.between(operationstart, instant.now());
if (operationtime.compareto(timeout) > 0) {
system.out.println("操作超时!");
} else {
system.out.println("操作在 " + operationtime.getseconds() + " 秒内完成");
}
// 应用3:计划任务间隔
system.out.println("\n=== 任务调度 ===");
duration taskinterval = duration.ofminutes(15);
localdatetime lastrun = localdatetime.now().minusminutes(10);
localdatetime nextrun = lastrun.plus(taskinterval);
system.out.println("上次运行: " + lastrun);
system.out.println("下次运行: " + nextrun);
duration timeuntilnext = duration.between(localdatetime.now(), nextrun);
system.out.println("距离下次运行还有: " + timeuntilnext.tominutes() + " 分钟");
// 应用4:视频时长处理
system.out.println("\n=== 媒体时长 ===");
duration videoduration = duration.ofminutes(45).plusseconds(30);
duration watchedduration = duration.ofminutes(30).plusseconds(15);
double progress = (double) watchedduration.toseconds() / videoduration.toseconds() * 100;
system.out.printf("视频总时长: %02d:%02d:%02d%n",
videoduration.tohours(),
videoduration.tominutespart(),
videoduration.tosecondspart());
system.out.printf("已观看: %02d:%02d:%02d%n",
watchedduration.tohours(),
watchedduration.tominutespart(),
watchedduration.tosecondspart());
system.out.printf("观看进度: %.1f%%%n", progress);
// 应用5:缓存策略
system.out.println("\n=== 缓存策略 ===");
duration cachettl = duration.ofhours(1);
duration agethreshold = duration.ofminutes(30);
instant cachecreated = instant.now().minusminutes(45);
duration cacheage = duration.between(cachecreated, instant.now());
system.out.println("缓存年龄: " + cacheage.tominutes() + " 分钟");
system.out.println("缓存ttl: " + cachettl.tominutes() + " 分钟");
if (cacheage.compareto(cachettl) > 0) {
system.out.println("缓存已过期,需要刷新");
} else if (cacheage.compareto(agethreshold) > 0) {
system.out.println("缓存较旧,建议异步刷新");
} else {
system.out.println("缓存新鲜,直接使用");
}
}
}4.3 period - 时间段(基于日期)
4.3.1 period 的基本操作
import java.time.localdate;
import java.time.period;
import java.time.temporal.chronounit;
public class periodexample {
public static void main(string[] args) {
// 1. 创建period
period oneyear = period.ofyears(1);
period sixmonths = period.ofmonths(6);
period threeweeks = period.ofweeks(3);
period tendays = period.ofdays(10);
// 组合创建
period yearmonthday = period.of(1, 6, 15); // 1年6个月15天
system.out.println("1年: " + oneyear);
system.out.println("6个月: " + sixmonths);
system.out.println("3周: " + threeweeks);
system.out.println("1年6个月15天: " + yearmonthday);
// 2. 解析字符串
period parsed = period.parse("p1y6m15d"); // iso-8601格式
system.out.println("解析1年6个月15天: " + parsed);
// 3. 计算两个日期之间的period
localdate birthdate = localdate.of(1990, 5, 20);
localdate currentdate = localdate.now();
period age = period.between(birthdate, currentdate);
system.out.println("出生日期: " + birthdate);
system.out.println("当前日期: " + currentdate);
system.out.println("年龄: " + age.getyears() + "岁 " +
age.getmonths() + "个月 " +
age.getdays() + "天");
// 4. 获取period的组成部分
int years = age.getyears();
int months = age.getmonths();
int days = age.getdays();
// 总天数(近似)
long totaldays = chronounit.days.between(birthdate, currentdate);
system.out.println("总天数: " + totaldays);
// 5. period计算
period period1 = period.of(1, 2, 3); // 1年2个月3天
period period2 = period.of(0, 6, 10); // 6个月10天
period sum = period1.plus(period2);
period difference = period1.minus(period2);
period multiplied = period1.multipliedby(2);
period negated = period1.negated();
period normalized = period.of(0, 25, 40).normalized(); // 标准化
system.out.println("period1 + period2 = " + sum);
system.out.println("period1 - period2 = " + difference);
system.out.println("period1 × 2 = " + multiplied);
system.out.println("-period1 = " + negated);
system.out.println("25个月40天标准化: " + normalized);
// 6. 添加到日期
localdate date = localdate.of(2023, 1, 1);
localdate plusperiod = date.plus(period1);
localdate minusperiod = date.minus(period2);
system.out.println("2023-01-01 加1年2个月3天: " + plusperiod);
system.out.println("2023-01-01 减6个月10天: " + minusperiod);
// 7. 比较period
period shortperiod = period.ofdays(10);
period longperiod = period.ofmonths(3);
system.out.println("10天是否小于3个月: " +
(shortperiod.tototalmonths() < longperiod.tototalmonths()));
system.out.println("period1是否为零: " + period1.iszero());
system.out.println("period1是否为负: " + period1.isnegative());
}
}4.3.2 period 的实际应用
import java.time.localdate;
import java.time.period;
import java.time.temporal.chronounit;
public class periodapplications {
public static void main(string[] args) {
// 应用1:计算年龄
system.out.println("=== 年龄计算 ===");
localdate birthdate = localdate.of(1990, 5, 20);
localdate today = localdate.now();
period age = period.between(birthdate, today);
system.out.printf("年龄: %d 岁 %d 个月 %d 天%n",
age.getyears(), age.getmonths(), age.getdays());
// 应用2:会员有效期
system.out.println("\n=== 会员系统 ===");
localdate joindate = localdate.of(2023, 1, 1);
period membershipduration = period.ofyears(1);
localdate expirydate = joindate.plus(membershipduration);
system.out.println("加入日期: " + joindate);
system.out.println("有效期: 1年");
system.out.println("到期日期: " + expirydate);
period remaining = period.between(today, expirydate);
if (remaining.isnegative()) {
system.out.println("会员已过期 " + remaining.negated().getmonths() + " 个月");
} else {
system.out.printf("剩余有效期: %d个月%d天%n",
remaining.getmonths(), remaining.getdays());
}
// 应用3:项目时间线
system.out.println("\n=== 项目管理 ===");
localdate projectstart = localdate.of(2023, 1, 1);
localdate projectend = localdate.of(2023, 12, 31);
localdate currentmilestone = localdate.of(2023, 6, 30);
period totalduration = period.between(projectstart, projectend);
period elapsed = period.between(projectstart, currentmilestone);
period remainingtime = period.between(currentmilestone, projectend);
system.out.println("项目总时长: " + totalduration.getmonths() + "个月");
system.out.println("已过去: " + elapsed.getmonths() + "个月" + elapsed.getdays() + "天");
system.out.println("剩余: " + remainingtime.getmonths() + "个月" + remainingtime.getdays() + "天");
// 计算进度百分比
long totaldays = chronounit.days.between(projectstart, projectend);
long passeddays = chronounit.days.between(projectstart, currentmilestone);
double progress = (double) passeddays / totaldays * 100;
system.out.printf("项目进度: %.1f%%%n", progress);
// 应用4:租赁计算
system.out.println("\n=== 租赁系统 ===");
localdate rentalstart = localdate.of(2023, 1, 15);
period rentalperiod = period.ofmonths(6);
localdate rentalend = rentalstart.plus(rentalperiod);
system.out.println("租赁开始: " + rentalstart);
system.out.println("租赁期限: 6个月");
system.out.println("租赁结束: " + rentalend);
// 计算已使用时间
period usedperiod = period.between(rentalstart, today);
if (usedperiod.isnegative()) {
system.out.println("租赁尚未开始");
} else if (period.between(today, rentalend).isnegative()) {
system.out.println("租赁已过期");
} else {
system.out.printf("已使用: %d个月%d天%n",
usedperiod.getmonths(), usedperiod.getdays());
}
// 应用5:保修期计算
system.out.println("\n=== 产品保修 ===");
localdate purchasedate = localdate.of(2023, 3, 15);
period warrantyperiod = period.ofyears(2);
localdate warrantyend = purchasedate.plus(warrantyperiod);
period warrantyremaining = period.between(today, warrantyend);
system.out.println("购买日期: " + purchasedate);
system.out.println("保修期限: 2年");
system.out.println("保修到期: " + warrantyend);
if (warrantyremaining.isnegative()) {
system.out.println("保修已过期");
} else {
system.out.printf("保修剩余: %d个月%d天%n",
warrantyremaining.getmonths(), warrantyremaining.getdays());
}
}
}5. 时区处理
5.1 zoneid 和 zoneoffset
5.1.1 zoneid - 时区标识
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.zone.zonerules;
import java.util.set;
public class zoneidexample {
public static void main(string[] args) {
// 1. 获取可用时区
set<string> zoneids = zoneid.getavailablezoneids();
system.out.println("总时区数量: " + zoneids.size());
// 显示前10个时区
system.out.println("\n前10个时区:");
zoneids.stream()
.sorted()
.limit(10)
.foreach(system.out::println);
// 2. 创建zoneid
zoneid systemzone = zoneid.systemdefault();
zoneid utczone = zoneid.of("utc");
zoneid shanghaizone = zoneid.of("asia/shanghai");
zoneid newyorkzone = zoneid.of("america/new_york");
zoneid tokyozone = zoneid.of("asia/tokyo");
system.out.println("\n系统默认时区: " + systemzone);
system.out.println("上海时区: " + shanghaizone);
system.out.println("纽约时区: " + newyorkzone);
// 3. 获取时区规则
zonerules shanghairules = shanghaizone.getrules();
zonerules newyorkrules = newyorkzone.getrules();
system.out.println("\n上海时区规则:");
system.out.println("是否固定偏移: " + shanghairules.isfixedoffset());
system.out.println("当前偏移: " + shanghairules.getoffset(instant.now()));
// 4. 时区转换
zoneddatetime nowinshanghai = zoneddatetime.now(shanghaizone);
zoneddatetime nowinnewyork = nowinshanghai.withzonesameinstant(newyorkzone);
zoneddatetime nowintokyo = nowinshanghai.withzonesameinstant(tokyozone);
system.out.println("\n同一时刻在不同时区:");
system.out.println("上海: " + nowinshanghai);
system.out.println("纽约: " + nowinnewyork);
system.out.println("东京: " + nowintokyo);
// 5. 处理夏令时
system.out.println("\n=== 夏令时处理 ===");
// 纽约的夏令时开始和结束
localdatetime beforedst = localdatetime.of(2023, 3, 11, 1, 59);
localdatetime afterdst = localdatetime.of(2023, 3, 12, 2, 1);
zoneddatetime zdtbefore = zoneddatetime.of(beforedst, newyorkzone);
zoneddatetime zdtafter = zoneddatetime.of(afterdst, newyorkzone);
system.out.println("夏令时前(3月11日 1:59): " + zdtbefore);
system.out.println("夏令时后(3月12日 2:01): " + zdtafter);
// 检查是否处于夏令时
boolean isdst = newyorkzone.getrules().isdaylightsavings(instant.now());
system.out.println("纽约当前是否夏令时: " + isdst);
// 6. 时区偏移量
zoneoffset shanghaioffset = shanghairules.getoffset(instant.now());
zoneoffset newyorkoffset = newyorkrules.getoffset(instant.now());
system.out.println("\n当前时区偏移:");
system.out.println("上海: " + shanghaioffset);
system.out.println("纽约: " + newyorkoffset);
// 7. 时区id规范化
zoneid normalized1 = zoneid.of("gmt+08:00");
zoneid normalized2 = zoneid.of("utc+08:00");
zoneid normalized3 = zoneid.of("+08:00");
system.out.println("\n规范化时区id:");
system.out.println("gmt+08:00 -> " + normalized1);
system.out.println("utc+08:00 -> " + normalized2);
system.out.println("+08:00 -> " + normalized3);
}
}5.1.2 zoneoffset - 时区偏移量
import java.time.localdatetime;
import java.time.offsetdatetime;
import java.time.zoneoffset;
import java.time.format.datetimeformatter;
public class zoneoffsetexample {
public static void main(string[] args) {
// 1. 创建zoneoffset
zoneoffset utc = zoneoffset.utc;
zoneoffset plus8 = zoneoffset.of("+08:00");
zoneoffset minus5 = zoneoffset.of("-05:00");
zoneoffset plus530 = zoneoffset.ofhoursminutes(5, 30); // +05:30
zoneoffset plus9 = zoneoffset.ofhours(9); // +09:00
system.out.println("utc偏移: " + utc);
system.out.println("东八区: " + plus8);
system.out.println("西五区: " + minus5);
system.out.println("东五区30分: " + plus530);
system.out.println("东九区: " + plus9);
// 2. 获取偏移量信息
int totalseconds = plus8.gettotalseconds();
string id = plus8.getid();
system.out.println("\n东八区信息:");
system.out.println("总秒数: " + totalseconds);
system.out.println("id: " + id);
// 3. 最大最小偏移
zoneoffset maxoffset = zoneoffset.max;
zoneoffset minoffset = zoneoffset.min;
system.out.println("\n最大偏移: " + maxoffset);
system.out.println("最小偏移: " + minoffset);
// 4. 创建带偏移的日期时间
localdatetime localdatetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
offsetdatetime offsetdatetimeutc = offsetdatetime.of(localdatetime, utc);
offsetdatetime offsetdatetimeplus8 = offsetdatetime.of(localdatetime, plus8);
offsetdatetime offsetdatetimeminus5 = offsetdatetime.of(localdatetime, minus5);
system.out.println("\n同一本地时间在不同偏移:");
system.out.println("utc: " + offsetdatetimeutc);
system.out.println("东八区: " + offsetdatetimeplus8);
system.out.println("西五区: " + offsetdatetimeminus5);
// 5. 偏移量计算
zoneoffset offset1 = zoneoffset.ofhours(8);
zoneoffset offset2 = zoneoffset.ofhours(-5);
system.out.println("\n偏移量比较:");
system.out.println("offset1.equals(offset2): " + offset1.equals(offset2));
system.out.println("offset1.compareto(offset2): " + offset1.compareto(offset2));
// 6. 格式化带偏移的时间
datetimeformatter formatter = datetimeformatter.iso_offset_date_time;
string formatted = offsetdatetimeplus8.format(formatter);
system.out.println("\n格式化带偏移时间: " + formatted);
// 7. 解析带偏移的时间字符串
string offsettimestr = "2023-12-25t14:30:45+08:00";
offsetdatetime parsed = offsetdatetime.parse(offsettimestr);
system.out.println("解析结果: " + parsed);
// 8. 实际应用:航班时间计算
system.out.println("\n=== 航班时间计算 ===");
// 从上海到纽约
localdatetime departureshanghai = localdatetime.of(2023, 12, 25, 14, 30);
zoneoffset shanghaioffset = zoneoffset.ofhours(8);
zoneoffset newyorkoffset = zoneoffset.ofhours(-5);
offsetdatetime departure = offsetdatetime.of(departureshanghai, shanghaioffset);
system.out.println("上海起飞时间: " + departure);
// 飞行14小时
offsetdatetime arrival = departure.plushours(14);
system.out.println("到达时间(utc): " + arrival);
// 转换为纽约时间
offsetdatetime arrivalnewyork = arrival.withoffsetsameinstant(newyorkoffset);
system.out.println("到达时间(纽约): " + arrivalnewyork);
// 9. 验证偏移量有效性
try {
zoneoffset invalid1 = zoneoffset.ofhoursminutes(18, 30); // 无效,最大±18
} catch (datetimeexception e) {
system.out.println("\n无效偏移量: " + e.getmessage());
}
try {
zoneoffset invalid2 = zoneoffset.of("25:00"); // 无效
} catch (datetimeexception e) {
system.out.println("无效偏移量字符串: " + e.getmessage());
}
}
}5.2 zoneddatetime
5.2.1 zoneddatetime 的基本使用
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
public class zoneddatetimeexample {
public static void main(string[] args) {
// 1. 创建zoneddatetime
// 当前时区的当前时间
zoneddatetime now = zoneddatetime.now();
system.out.println("当前时间: " + now);
// 指定时区的当前时间
zoneddatetime nowinshanghai = zoneddatetime.now(zoneid.of("asia/shanghai"));
zoneddatetime nowinnewyork = zoneddatetime.now(zoneid.of("america/new_york"));
zoneddatetime nowinutc = zoneddatetime.now(zoneoffset.utc);
system.out.println("上海时间: " + nowinshanghai);
system.out.println("纽约时间: " + nowinnewyork);
system.out.println("utc时间: " + nowinutc);
// 2. 从组件创建
zoneddatetime specific1 = zoneddatetime.of(2023, 12, 25, 14, 30, 45, 0,
zoneid.of("asia/shanghai"));
localdatetime localdatetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
zoneddatetime specific2 = zoneddatetime.of(localdatetime, zoneid.of("asia/shanghai"));
localdate date = localdate.of(2023, 12, 25);
localtime time = localtime.of(14, 30, 45);
zoneddatetime specific3 = zoneddatetime.of(date, time, zoneid.of("asia/shanghai"));
// 3. 解析字符串
zoneddatetime parsed1 = zoneddatetime.parse("2023-12-25t14:30:45+08:00[asia/shanghai]");
zoneddatetime parsed2 = zoneddatetime.parse("2023-12-25t14:30:45z"); // utc
// 4. 获取信息
localdate localdate = now.tolocaldate();
localtime localtime = now.tolocaltime();
localdatetime localdatetimefromzoned = now.tolocaldatetime();
instant instant = now.toinstant();
zoneid zone = now.getzone();
zoneoffset offset = now.getoffset();
system.out.println("\n分解zoneddatetime:");
system.out.println("日期部分: " + localdate);
system.out.println("时间部分: " + localtime);
system.out.println("时区: " + zone);
system.out.println("偏移: " + offset);
system.out.println("时间戳: " + instant);
// 5. 时区转换
zoneddatetime shanghaitime = zoneddatetime.now(zoneid.of("asia/shanghai"));
zoneddatetime newyorktime = shanghaitime.withzonesameinstant(zoneid.of("america/new_york"));
zoneddatetime tokyotime = shanghaitime.withzonesameinstant(zoneid.of("asia/tokyo"));
system.out.println("\n时区转换:");
system.out.println("上海: " + shanghaitime);
system.out.println("纽约: " + newyorktime);
system.out.println("东京: " + tokyotime);
// 保持本地时间,只改变时区
zoneddatetime samelocaltime = shanghaitime.withzonesamelocal(zoneid.of("america/new_york"));
system.out.println("相同本地时间,不同时区: " + samelocaltime);
// 6. 日期时间计算
zoneddatetime tomorrow = now.plusdays(1);
zoneddatetime nextweek = now.plusweeks(1);
zoneddatetime nextmonth = now.plusmonths(1);
zoneddatetime nextyear = now.plusyears(1);
zoneddatetime minushours = now.minushours(3);
zoneddatetime minusminutes = now.minusminutes(30);
// 7. 修改组件
zoneddatetime withyear = now.withyear(2024);
zoneddatetime withmonth = now.withmonth(6);
zoneddatetime withday = now.withdayofmonth(15);
zoneddatetime withhour = now.withhour(9);
zoneddatetime withzone = now.withzonesameinstant(zoneid.of("europe/london"));
// 8. 比较操作
zoneddatetime earlier = now.minushours(2);
zoneddatetime later = now.plushours(2);
system.out.println("\n时间比较:");
system.out.println("earlier是否在now之前: " + earlier.isbefore(now));
system.out.println("later是否在now之后: " + later.isafter(now));
system.out.println("是否是同一时刻: " + now.isequal(zoneddatetime.now()));
// 9. 格式化
datetimeformatter formatter1 = datetimeformatter.iso_zoned_date_time;
datetimeformatter formatter2 = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z");
datetimeformatter formatter3 = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z");
string formatted1 = now.format(formatter1);
string formatted2 = now.format(formatter2);
string formatted3 = now.format(formatter3);
system.out.println("\n格式化输出:");
system.out.println("iso格式: " + formatted1);
system.out.println("带偏移: " + formatted2);
system.out.println("带时区名: " + formatted3);
}
}5.2.2 zoneddatetime 的实际应用
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
import java.util.arraylist;
import java.util.list;
public class zoneddatetimeapplications {
public static void main(string[] args) {
// 应用1:国际会议时间安排
system.out.println("=== 国际会议安排 ===");
// 会议在伦敦时间 2023-12-25 14:00 开始
zoneddatetime meetinglondon = zoneddatetime.of(
2023, 12, 25, 14, 0, 0, 0,
zoneid.of("europe/london")
);
// 转换为其他时区
list<string> locations = list.of(
"america/new_york",
"america/los_angeles",
"europe/paris",
"asia/shanghai",
"asia/tokyo",
"australia/sydney"
);
system.out.println("国际会议时间:");
system.out.println("伦敦: " + formatzoneddatetime(meetinglondon));
for (string location : locations) {
zoneddatetime localtime = meetinglondon.withzonesameinstant(zoneid.of(location));
system.out.println(getlocationname(location) + ": " + formatzoneddatetime(localtime));
}
// 应用2:航班行程计算
system.out.println("\n=== 航班行程 ===");
// 上海 -> 旧金山
zoneddatetime departureshanghai = zoneddatetime.of(
2023, 12, 25, 14, 30, 0, 0,
zoneid.of("asia/shanghai")
);
// 飞行时间 11小时30分钟
duration flightduration = duration.ofhours(11).plusminutes(30);
zoneddatetime arrivalutc = departureshanghai.plus(flightduration);
zoneddatetime arrivalsanfrancisco = arrivalutc.withzonesameinstant(
zoneid.of("america/los_angeles")
);
system.out.println("航班信息:");
system.out.println("出发地: 上海");
system.out.println("出发时间: " + formatzoneddatetime(departureshanghai));
system.out.println("飞行时间: " + flightduration.tohours() + "小时" +
flightduration.tominutespart() + "分钟");
system.out.println("目的地: 旧金山");
system.out.println("到达时间: " + formatzoneddatetime(arrivalsanfrancisco));
// 应用3:跨时区工作时间计算
system.out.println("\n=== 跨时区协作 ===");
// 团队分布在三个时区
zoneid london = zoneid.of("europe/london");
zoneid newyork = zoneid.of("america/new_york");
zoneid shanghai = zoneid.of("asia/shanghai");
// 找出共同工作时间(9:00-17:00本地时间)
localtime workstart = localtime.of(9, 0);
localtime workend = localtime.of(17, 0);
system.out.println("共同工作时间分析:");
findcommonworkinghours(london, newyork, shanghai, workstart, workend);
// 应用4:定时任务调度
system.out.println("\n=== 定时任务调度 ===");
// 每天8:00在本地执行任务
zoneddatetime nextexecution = getnextexecutiontime(
localtime.of(8, 0),
zoneid.systemdefault()
);
system.out.println("下一次执行时间: " + formatzoneddatetime(nextexecution));
// 计算距离下一次执行还有多久
duration timeuntilnext = duration.between(zoneddatetime.now(), nextexecution);
system.out.println("距离下一次执行还有: " +
timeuntilnext.tohours() + "小时" +
timeuntilnext.tominutespart() + "分钟");
// 应用5:处理夏令时边界
system.out.println("\n=== 夏令时边界处理 ===");
handledaylightsavingtime();
}
private static string formatzoneddatetime(zoneddatetime zdt) {
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm z");
return zdt.format(formatter);
}
private static string getlocationname(string zoneid) {
return zoneid.substring(zoneid.lastindexof('/') + 1)
.replace('_', ' ');
}
private static void findcommonworkinghours(zoneid zone1, zoneid zone2, zoneid zone3,
localtime workstart, localtime workend) {
// 将工作时间转换为utc进行比较
zoneddatetime now = zoneddatetime.now();
// 获取今天的工作时间范围
zoneddatetime todaystartzone1 = zoneddatetime.of(now.tolocaldate(), workstart, zone1);
zoneddatetime todayendzone1 = zoneddatetime.of(now.tolocaldate(), workend, zone1);
// 转换为utc
instant startutc1 = todaystartzone1.toinstant();
instant endutc1 = todayendzone1.toinstant();
// 类似处理其他时区...
system.out.println("需要进一步计算重叠时间段...");
}
private static zoneddatetime getnextexecutiontime(localtime executiontime, zoneid zone) {
zoneddatetime now = zoneddatetime.now(zone);
zoneddatetime todayexecution = zoneddatetime.of(now.tolocaldate(), executiontime, zone);
if (now.isbefore(todayexecution)) {
return todayexecution;
} else {
// 今天已经过了执行时间,安排到明天
return todayexecution.plusdays(1);
}
}
private static void handledaylightsavingtime() {
zoneid london = zoneid.of("europe/london");
// 2023年伦敦夏令时开始:3月26日
localdatetime beforedst = localdatetime.of(2023, 3, 26, 0, 59);
localdatetime afterdst = localdatetime.of(2023, 3, 26, 2, 1);
zoneddatetime zdtbefore = zoneddatetime.of(beforedst, london);
zoneddatetime zdtafter = zoneddatetime.of(afterdst, london);
system.out.println("夏令时切换时刻:");
system.out.println("切换前(1:59): " + zdtbefore);
system.out.println("切换后(2:01): " + zdtafter);
// 计算实际的时间差
duration actualduration = duration.between(zdtbefore, zdtafter);
system.out.println("时钟显示差1小时2分钟,实际经过: " +
actualduration.tominutes() + "分钟");
}
}5.3 offsetdatetime
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
public class offsetdatetimeexample {
public static void main(string[] args) {
// 1. 创建offsetdatetime
offsetdatetime now = offsetdatetime.now();
system.out.println("当前偏移日期时间: " + now);
// 指定偏移
offsetdatetime utcnow = offsetdatetime.now(zoneoffset.utc);
offsetdatetime plus8now = offsetdatetime.now(zoneoffset.ofhours(8));
// 从组件创建
localdatetime localdatetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
offsetdatetime specific1 = offsetdatetime.of(localdatetime, zoneoffset.ofhours(8));
// 从年、月、日等创建
offsetdatetime specific2 = offsetdatetime.of(2023, 12, 25, 14, 30, 45, 0,
zoneoffset.ofhours(8));
// 2. 解析字符串
offsetdatetime parsed1 = offsetdatetime.parse("2023-12-25t14:30:45+08:00");
offsetdatetime parsed2 = offsetdatetime.parse("2023-12-25t14:30:45z");
// 3. 与zoneddatetime比较
system.out.println("\noffsetdatetime vs zoneddatetime:");
system.out.println("offsetdatetime: " + now);
system.out.println("zoneddatetime: " + zoneddatetime.now());
// 主要区别:offsetdatetime只有偏移,没有时区规则(如夏令时)
// 4. 获取信息
localdate localdate = now.tolocaldate();
localtime localtime = now.tolocaltime();
localdatetime localdatetimefromoffset = now.tolocaldatetime();
instant instant = now.toinstant();
zoneoffset offset = now.getoffset();
system.out.println("\n分解offsetdatetime:");
system.out.println("日期: " + localdate);
system.out.println("时间: " + localtime);
system.out.println("偏移: " + offset);
// 5. 偏移量操作
offsetdatetime withoffset = now.withoffsetsameinstant(zoneoffset.ofhours(-5));
offsetdatetime withlocaloffset = now.withoffsetsamelocal(zoneoffset.ofhours(-5));
system.out.println("\n偏移量操作:");
system.out.println("原时间: " + now);
system.out.println("相同瞬间,不同偏移: " + withoffset);
system.out.println("相同本地时间,不同偏移: " + withlocaloffset);
// 6. 日期时间计算
offsetdatetime tomorrow = now.plusdays(1);
offsetdatetime nexthour = now.plushours(1);
offsetdatetime lastweek = now.minusweeks(1);
// 7. 修改组件
offsetdatetime withyear = now.withyear(2024);
offsetdatetime withmonth = now.withmonth(6);
offsetdatetime withhour = now.withhour(9);
offsetdatetime withoffsetchange = now.withoffsetsameinstant(zoneoffset.utc);
// 8. 比较和判断
offsetdatetime earlier = now.minushours(2);
offsetdatetime later = now.plushours(2);
system.out.println("\n比较操作:");
system.out.println("earlier是否在now之前: " + earlier.isbefore(now));
system.out.println("later是否在now之后: " + later.isafter(now));
system.out.println("now是否在earlier和later之间: " +
(now.isafter(earlier) && now.isbefore(later)));
// 9. 实际应用场景
system.out.println("\n=== 实际应用 ===");
// 场景1:api时间戳(通常使用utc)
offsetdatetime apitimestamp = offsetdatetime.now(zoneoffset.utc);
system.out.println("api时间戳(utc): " + apitimestamp);
// 场景2:日志时间(带偏移)
offsetdatetime logtime = offsetdatetime.now();
system.out.println("日志时间: " + logtime.format(
datetimeformatter.iso_offset_date_time));
// 场景3:数据库存储(建议使用utc)
offsetdatetime dbtime = offsetdatetime.now(zoneoffset.utc);
// 存储到数据库...
// 从数据库读取后转换为本地时间
offsetdatetime localtimefromdb = dbtime.withoffsetsameinstant(
zoneoffset.ofhours(8));
system.out.println("数据库时间 -> 本地时间: " + localtimefromdb);
// 场景4:计算时间间隔(忽略时区变化)
offsetdatetime start = offsetdatetime.of(
2023, 12, 25, 9, 0, 0, 0, zoneoffset.ofhours(8));
offsetdatetime end = offsetdatetime.of(
2023, 12, 25, 17, 30, 0, 0, zoneoffset.ofhours(8));
duration workduration = duration.between(start, end);
system.out.println("工作时间: " + workduration.tohours() + "小时" +
workduration.tominutespart() + "分钟");
// 场景5:跨偏移时间计算
offsetdatetime meetingtime = offsetdatetime.of(
2023, 12, 25, 14, 0, 0, 0, zoneoffset.ofhours(8));
// 纽约参与者
offsetdatetime newyorktime = meetingtime.withoffsetsameinstant(
zoneoffset.ofhours(-5));
system.out.println("会议时间在各个地区:");
system.out.println("上海: " + meetingtime);
system.out.println("纽约: " + newyorktime);
}
}6. 日期时间格式化与解析
6.1 datetimeformatter
6.1.1 预定义的格式化器
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.format.formatstyle;
import java.util.locale;
public class datetimeformatterexample {
public static void main(string[] args) {
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
system.out.println("=== 预定义的格式化器 ===");
// 1. iso格式化器
system.out.println("\niso标准格式:");
system.out.println("basic_iso_date: " + datetime.format(datetimeformatter.basic_iso_date));
system.out.println("iso_local_date: " + datetime.format(datetimeformatter.iso_local_date));
system.out.println("iso_local_time: " + datetime.format(datetimeformatter.iso_local_time));
system.out.println("iso_local_date_time: " + datetime.format(datetimeformatter.iso_local_date_time));
// 带偏移和时区
offsetdatetime offsetdatetime = offsetdatetime.of(datetime, zoneoffset.ofhours(8));
zoneddatetime zoneddatetime = zoneddatetime.of(datetime, zoneid.of("asia/shanghai"));
system.out.println("iso_offset_date_time: " + offsetdatetime.format(datetimeformatter.iso_offset_date_time));
system.out.println("iso_zoned_date_time: " + zoneddatetime.format(datetimeformatter.iso_zoned_date_time));
// 2. 本地化格式化器
system.out.println("\n本地化格式:");
// 短格式
datetimeformatter shortdate = datetimeformatter.oflocalizeddate(formatstyle.short);
datetimeformatter shorttime = datetimeformatter.oflocalizedtime(formatstyle.short);
datetimeformatter shortdatetime = datetimeformatter.oflocalizeddatetime(formatstyle.short);
// 中格式
datetimeformatter mediumdate = datetimeformatter.oflocalizeddate(formatstyle.medium);
datetimeformatter mediumtime = datetimeformatter.oflocalizedtime(formatstyle.medium);
datetimeformatter mediumdatetime = datetimeformatter.oflocalizeddatetime(formatstyle.medium);
// 长格式
datetimeformatter longdate = datetimeformatter.oflocalizeddate(formatstyle.long);
datetimeformatter longtime = datetimeformatter.oflocalizedtime(formatstyle.long);
datetimeformatter longdatetime = datetimeformatter.oflocalizeddatetime(formatstyle.long);
// 完整格式
datetimeformatter fulldate = datetimeformatter.oflocalizeddate(formatstyle.full);
datetimeformatter fulltime = datetimeformatter.oflocalizedtime(formatstyle.full);
datetimeformatter fulldatetime = datetimeformatter.oflocalizeddatetime(formatstyle.full);
system.out.println("短格式日期: " + datetime.format(shortdate));
system.out.println("中格式日期: " + datetime.format(mediumdate));
system.out.println("长格式日期: " + datetime.format(longdate));
system.out.println("完整格式日期: " + datetime.format(fulldate));
system.out.println("短格式时间: " + datetime.format(shorttime));
system.out.println("中格式时间: " + datetime.format(mediumtime));
system.out.println("短格式日期时间: " + datetime.format(shortdatetime));
system.out.println("中格式日期时间: " + datetime.format(mediumdatetime));
// 3. 不同locale的格式化
system.out.println("\n不同locale的格式化:");
locale[] locales = {
locale.us,
locale.uk,
locale.france,
locale.germany,
locale.japan,
locale.china,
locale.taiwan
};
datetimeformatter formatter = datetimeformatter.oflocalizeddatetime(formatstyle.full);
for (locale locale : locales) {
datetimeformatter localizedformatter = formatter.withlocale(locale);
system.out.println(locale.getdisplayname() + ": " +
zoneddatetime.format(localizedformatter));
}
// 4. rfc格式
system.out.println("\nrfc格式:");
datetimeformatter rfc1123 = datetimeformatter.rfc_1123_date_time;
system.out.println("rfc_1123_date_time: " + zoneddatetime.format(rfc1123));
}
}6.1.2 自定义格式化模式
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.format.datetimeformatterbuilder;
import java.time.temporal.chronofield;
import java.util.locale;
public class customdatetimeformatter {
public static void main(string[] args) {
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45, 123456789);
zoneddatetime zoneddatetime = zoneddatetime.of(datetime, zoneid.of("asia/shanghai"));
system.out.println("=== 自定义格式化模式 ===");
// 1. 常用模式字符
system.out.println("\n基本模式字符:");
datetimeformatter[] formatters = {
datetimeformatter.ofpattern("yyyy-mm-dd"),
datetimeformatter.ofpattern("yyyy/mm/dd"),
datetimeformatter.ofpattern("dd/mm/yyyy"),
datetimeformatter.ofpattern("yyyy年mm月dd日"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss.sss"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss.ssssss"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss.sssssssss"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss a"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss z"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss vv"),
datetimeformatter.ofpattern("eeee, mmmm d, yyyy"),
datetimeformatter.ofpattern("eee, mmm d, ''yy"),
datetimeformatter.ofpattern("'date:' yyyy-mm-dd 'time:' hh:mm:ss"),
datetimeformatter.ofpattern("yyyy年第w周"),
datetimeformatter.ofpattern("yyyy-mm-dd't'hh:mm:ss"),
datetimeformatter.ofpattern("yyyymmddhhmmss"),
datetimeformatter.ofpattern("yymmdd")
};
for (datetimeformatter formatter : formatters) {
try {
system.out.println(formatter.tostring() + ": " + datetime.format(formatter));
} catch (exception e) {
system.out.println(formatter.tostring() + ": [需要zoneddatetime] " +
zoneddatetime.format(formatter));
}
}
// 2. 模式字符说明
system.out.println("\n模式字符说明:");
system.out.println("y - 年 (yy: 23, yyyy: 2023)");
system.out.println("m - 月 (m: 12, mm: 12, mmm: dec, mmmm: december)");
system.out.println("d - 月中的天 (d: 25, dd: 25)");
system.out.println("h - 小时 (0-23) (h: 14, hh: 14)");
system.out.println("h - 小时 (1-12, 需要am/pm) (h: 2, hh: 02)");
system.out.println("m - 分钟 (m: 30, mm: 30)");
system.out.println("s - 秒 (s: 45, ss: 45)");
system.out.println("s - 毫秒/微秒/纳秒 (sss: 123, ssssss: 123456)");
system.out.println("a - am/pm 标记");
system.out.println("e - 星期几 (e: mon, eeee: monday)");
system.out.println("d - 年中的天 (1-366)");
system.out.println("w - 年中的周 (1-53)");
system.out.println("w - 月中的周 (1-5)");
system.out.println("z - 时区偏移 (+0800)");
system.out.println("z - 时区名称 (cst)");
system.out.println("vv - 时区id (asia/shanghai)");
// 3. 带locale的格式化
system.out.println("\n带locale的格式化:");
locale french = locale.french;
locale german = locale.german;
locale japanese = locale.japanese;
datetimeformatter frenchformatter = datetimeformatter.ofpattern("eeee, d mmmm yyyy", french);
datetimeformatter germanformatter = datetimeformatter.ofpattern("eeee, d. mmmm yyyy", german);
datetimeformatter japaneseformatter = datetimeformatter.ofpattern("gy年m月d日 eeee", japanese);
system.out.println("法语: " + datetime.format(frenchformatter));
system.out.println("德语: " + datetime.format(germanformatter));
system.out.println("日语: " + datetime.format(japaneseformatter));
// 4. 解析字符串
system.out.println("\n解析字符串:");
string[] datestrings = {
"2023-12-25",
"2023/12/25",
"25/12/2023",
"2023年12月25日",
"2023-12-25 14:30:45",
"2023-12-25 02:30:45 pm",
"2023-12-25t14:30:45+08:00"
};
datetimeformatter[] parsers = {
datetimeformatter.ofpattern("yyyy-mm-dd"),
datetimeformatter.ofpattern("yyyy/mm/dd"),
datetimeformatter.ofpattern("dd/mm/yyyy"),
datetimeformatter.ofpattern("yyyy年mm月dd日"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss"),
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss a"),
datetimeformatter.iso_offset_date_time
};
for (int i = 0; i < datestrings.length; i++) {
try {
if (i < 4) {
localdate parseddate = localdate.parse(datestrings[i], parsers[i]);
system.out.println(datestrings[i] + " -> " + parseddate);
} else if (i < 6) {
localdatetime parseddatetime = localdatetime.parse(datestrings[i], parsers[i]);
system.out.println(datestrings[i] + " -> " + parseddatetime);
} else {
offsetdatetime parsedoffset = offsetdatetime.parse(datestrings[i], parsers[i]);
system.out.println(datestrings[i] + " -> " + parsedoffset);
}
} catch (exception e) {
system.out.println(datestrings[i] + " -> 解析失败: " + e.getmessage());
}
}
}
}6.1.3 datetimeformatterbuilder 高级用法
import java.time.localdatetime;
import java.time.format.datetimeformatter;
import java.time.format.datetimeformatterbuilder;
import java.time.format.textstyle;
import java.time.temporal.chronofield;
import java.util.locale;
public class datetimeformatterbuilderexample {
public static void main(string[] args) {
system.out.println("=== datetimeformatterbuilder 高级用法 ===");
// 1. 构建复杂格式化器
datetimeformatter complexformatter = new datetimeformatterbuilder()
.appendtext(chronofield.year, textstyle.full) // 完整年份
.appendliteral("年")
.appendtext(chronofield.month_of_year, textstyle.full) // 完整月份
.appendliteral(" ")
.appendtext(chronofield.day_of_month) // 月份中的天
.appendliteral("日, ")
.appendtext(chronofield.day_of_week, textstyle.full) // 完整星期
.appendliteral(" ")
.appendvalue(chronofield.hour_of_day, 2) // 2位小时
.appendliteral(":")
.appendvalue(chronofield.minute_of_hour, 2) // 2位分钟
.appendliteral(":")
.appendvalue(chronofield.second_of_minute, 2) // 2位秒
.appendliteral(".")
.appendfraction(chronofield.milli_of_second, 3, 3, true) // 3位毫秒
.toformatter(locale.china);
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45, 123456789);
system.out.println("复杂格式化: " + datetime.format(complexformatter));
// 2. 可选部分
datetimeformatter optionalformatter = new datetimeformatterbuilder()
.appendpattern("yyyy-mm-dd")
.optionalstart() // 开始可选部分
.appendliteral(" ")
.appendpattern("hh:mm:ss")
.optionalstart() // 嵌套可选部分
.appendliteral(".")
.appendfraction(chronofield.nano_of_second, 0, 9, true)
.optionalend()
.optionalend()
.toformatter();
system.out.println("\n可选部分格式化:");
system.out.println("只有日期: " + localdate.of(2023, 12, 25).format(optionalformatter));
system.out.println("日期时间: " + localdatetime.of(2023, 12, 25, 14, 30).format(optionalformatter));
system.out.println("完整日期时间: " + datetime.format(optionalformatter));
// 3. 默认值
datetimeformatter defaultformatter = new datetimeformatterbuilder()
.appendpattern("yyyy-mm-dd[ hh:mm:ss]")
.parsedefaulting(chronofield.hour_of_day, 0)
.parsedefaulting(chronofield.minute_of_hour, 0)
.parsedefaulting(chronofield.second_of_minute, 0)
.toformatter();
system.out.println("\n带默认值的解析:");
system.out.println("解析日期: " + localdatetime.parse("2023-12-25", defaultformatter));
system.out.println("解析日期时间: " + localdatetime.parse("2023-12-25 14:30:45", defaultformatter));
// 4. 严格和宽松模式
datetimeformatter strictformatter = new datetimeformatterbuilder()
.appendpattern("yyyy-mm-dd")
.parsestrict()
.toformatter();
datetimeformatter lenientformatter = new datetimeformatterbuilder()
.appendpattern("yyyy-mm-dd")
.parselenient()
.toformatter();
system.out.println("\n严格 vs 宽松解析:");
string invaliddate = "2023-13-32"; // 无效日期
try {
system.out.println("严格解析: " + localdate.parse(invaliddate, strictformatter));
} catch (exception e) {
system.out.println("严格解析失败: " + e.getmessage());
}
try {
system.out.println("宽松解析: " + localdate.parse(invaliddate, lenientformatter));
} catch (exception e) {
system.out.println("宽松解析失败: " + e.getmessage());
}
// 5. 自定义文本
datetimeformatter customtextformatter = new datetimeformatterbuilder()
.appendtext(chronofield.month_of_year,
java.util.map.of(
1l, "壹月", 2l, "贰月", 3l, "叁月", 4l, "肆月",
5l, "伍月", 6l, "陆月", 7l, "柒月", 8l, "捌月",
9l, "玖月", 10l, "拾月", 11l, "冬月", 12l, "腊月"
))
.appendliteral(" ")
.appendvalue(chronofield.day_of_month)
.appendliteral("日")
.toformatter();
system.out.println("\n自定义文本: " + datetime.format(customtextformatter));
// 6. 组合多个格式化器
datetimeformatter combinedformatter = new datetimeformatterbuilder()
.append(datetimeformatter.iso_local_date)
.appendliteral("t")
.append(datetimeformatter.iso_local_time)
.toformatter();
system.out.println("组合格式化器: " + datetime.format(combinedformatter));
// 7. 解析不完整的日期时间
datetimeformatter partialformatter = new datetimeformatterbuilder()
.appendpattern("yyyy[-mm[-dd]]")
.parsedefaulting(chronofield.month_of_year, 1)
.parsedefaulting(chronofield.day_of_month, 1)
.toformatter();
system.out.println("\n解析不完整日期:");
system.out.println("只有年: " + localdate.parse("2023", partialformatter));
system.out.println("年月: " + localdate.parse("2023-12", partialformatter));
system.out.println("完整日期: " + localdate.parse("2023-12-25", partialformatter));
}
}6.2 解析和格式化实战
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.format.datetimeparseexception;
import java.time.temporal.temporalaccessor;
import java.util.locale;
public class parsingformattingpractice {
public static void main(string[] args) {
system.out.println("=== 日期时间解析和格式化实战 ===");
// 场景1:处理多种日期格式
system.out.println("\n1. 处理多种日期格式:");
string[] dateformats = {
"yyyy-mm-dd",
"yyyy/mm/dd",
"dd/mm/yyyy",
"mm/dd/yyyy",
"yyyymmdd",
"yyyy年mm月dd日",
"dd mmm yyyy",
"dd-mmm-yyyy"
};
string[] datestrings = {
"2023-12-25",
"2023/12/25",
"25/12/2023",
"12/25/2023",
"20231225",
"2023年12月25日",
"25 dec 2023",
"25-dec-2023"
};
for (int i = 0; i < datestrings.length; i++) {
try {
datetimeformatter formatter = datetimeformatter.ofpattern(dateformats[i]);
localdate date = localdate.parse(datestrings[i], formatter);
system.out.printf("%-20s -> %s%n", datestrings[i], date);
} catch (datetimeparseexception e) {
system.out.printf("%-20s -> 解析失败: %s%n",
datestrings[i], e.getmessage());
}
}
// 场景2:灵活解析(尝试多种格式)
system.out.println("\n2. 灵活解析:");
string input = "25/12/2023"; // 也可能是 "2023-12-25"
datetimeformatter[] possibleformatters = {
datetimeformatter.ofpattern("yyyy-mm-dd"),
datetimeformatter.ofpattern("yyyy/mm/dd"),
datetimeformatter.ofpattern("dd/mm/yyyy"),
datetimeformatter.ofpattern("mm/dd/yyyy"),
datetimeformatter.iso_local_date
};
localdate parseddate = null;
for (datetimeformatter formatter : possibleformatters) {
try {
parseddate = localdate.parse(input, formatter);
system.out.println("成功解析: " + parseddate + " (使用格式: " +
formatter.tostring() + ")");
break;
} catch (datetimeparseexception e) {
// 继续尝试下一个格式
}
}
if (parseddate == null) {
system.out.println("无法解析日期: " + input);
}
// 场景3:智能日期解析
system.out.println("\n3. 智能日期解析:");
datetimeformatter smartformatter = new datetimeformatterbuilder()
.appendoptional(datetimeformatter.ofpattern("yyyy-mm-dd"))
.appendoptional(datetimeformatter.ofpattern("yyyy/mm/dd"))
.appendoptional(datetimeformatter.ofpattern("dd/mm/yyyy"))
.appendoptional(datetimeformatter.ofpattern("mm/dd/yyyy"))
.toformatter();
string[] testdates = {"2023-12-25", "25/12/2023", "12/25/2023"};
for (string testdate : testdates) {
try {
temporalaccessor temporal = smartformatter.parsebest(testdate,
localdate::from,
yearmonth::from,
year::from);
if (temporal instanceof localdate) {
system.out.println(testdate + " -> 完整日期: " + temporal);
} else if (temporal instanceof yearmonth) {
system.out.println(testdate + " -> 年月: " + temporal);
} else if (temporal instanceof year) {
system.out.println(testdate + " -> 年份: " + temporal);
}
} catch (exception e) {
system.out.println(testdate + " -> 解析失败");
}
}
// 场景4:本地化格式化
system.out.println("\n4. 本地化格式化:");
zoneddatetime now = zoneddatetime.now(zoneid.of("asia/shanghai"));
locale[] locales = {locale.us, locale.uk, locale.france, locale.germany,
locale.japan, locale.china, locale.taiwan};
for (locale locale : locales) {
datetimeformatter localizedformatter = datetimeformatter
.oflocalizeddatetime(formatstyle.full, formatstyle.short)
.withlocale(locale)
.withzone(zoneid.of("asia/shanghai"));
system.out.printf("%-15s: %s%n",
locale.getdisplayname(),
now.format(localizedformatter));
}
// 场景5:性能优化的格式化器
system.out.println("\n5. 性能优化:");
// 线程安全的格式化器(可以静态共享)
final datetimeformatter cached_formatter =
datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
// 多次使用同一个格式化器
for (int i = 0; i < 3; i++) {
string formatted = localdatetime.now().format(cached_formatter);
system.out.println("格式化 " + (i + 1) + ": " + formatted);
try {
thread.sleep(100);
} catch (interruptedexception e) {
e.printstacktrace();
}
}
// 场景6:错误处理和验证
system.out.println("\n6. 错误处理:");
string[] problematicdates = {
"2023-13-25", // 无效月份
"2023-12-32", // 无效日期
"2023-02-30", // 2月没有30日
"not-a-date", // 完全无效
"2023-12-25 25:61:61", // 无效时间
"" // 空字符串
};
for (string problemdate : problematicdates) {
try {
localdate date = localdate.parse(problemdate);
system.out.println(problemdate + " -> " + date);
} catch (datetimeparseexception e) {
system.out.println(problemdate + " -> 解析错误: " + e.getmessage());
} catch (exception e) {
system.out.println(problemdate + " -> 错误: " + e.getclass().getsimplename());
}
}
// 场景7:自定义错误消息
system.out.println("\n7. 自定义解析:");
string userinput = "december 25, 2023";
try {
datetimeformatter formatter = datetimeformatter.ofpattern("mmmm d, yyyy", locale.us);
localdate date = localdate.parse(userinput, formatter);
system.out.println("用户输入: \"" + userinput + "\" -> " + date);
} catch (datetimeparseexception e) {
system.out.println("无法解析日期: \"" + userinput + "\"");
system.out.println("请使用格式: month day, year (例如: december 25, 2023)");
}
}
}7. 日期时间计算与调整
7.1 temporaladjusters 工具类
import java.time.*;
import java.time.temporal.temporaladjusters;
import java.time.temporal.chronofield;
import java.time.temporal.temporaladjuster;
public class temporaladjustersexample {
public static void main(string[] args) {
system.out.println("=== temporaladjusters 工具类 ===");
localdate date = localdate.of(2023, 12, 25);
system.out.println("基准日期: " + date);
// 1. 月份相关调整
system.out.println("\n1. 月份相关调整:");
system.out.println("当月第一天: " + date.with(temporaladjusters.firstdayofmonth()));
system.out.println("当月最后一天: " + date.with(temporaladjusters.lastdayofmonth()));
system.out.println("下月第一天: " + date.with(temporaladjusters.firstdayofnextmonth()));
system.out.println("下月最后一天: " + date.with(temporaladjusters.lastdayofnextmonth()));
// 2. 年份相关调整
system.out.println("\n2. 年份相关调整:");
system.out.println("当年第一天: " + date.with(temporaladjusters.firstdayofyear()));
system.out.println("当年最后一天: " + date.with(temporaladjusters.lastdayofyear()));
system.out.println("明年第一天: " + date.with(temporaladjusters.firstdayofnextyear()));
// 3. 星期相关调整
system.out.println("\n3. 星期相关调整:");
system.out.println("下一个周一: " + date.with(temporaladjusters.next(dayofweek.monday)));
system.out.println("下一个或当天周一: " + date.with(temporaladjusters.nextorsame(dayofweek.monday)));
system.out.println("上一个周一: " + date.with(temporaladjusters.previous(dayofweek.monday)));
system.out.println("上一个或当天周一: " + date.with(temporaladjusters.previousorsame(dayofweek.monday)));
// 测试当天就是周一的情况
localdate mondaydate = localdate.of(2023, 12, 18); // 2023-12-18是周一
system.out.println("\n测试日期(周一): " + mondaydate);
system.out.println("下一个周一: " + mondaydate.with(temporaladjusters.next(dayofweek.monday)));
system.out.println("下一个或当天周一: " + mondaydate.with(temporaladjusters.nextorsame(dayofweek.monday)));
// 4. 月中第几个星期几
system.out.println("\n4. 月中第几个星期几:");
system.out.println("当月第一个周一: " + date.with(temporaladjusters.firstinmonth(dayofweek.monday)));
system.out.println("当月第二个周一: " + date.with(temporaladjusters.dayofweekinmonth(2, dayofweek.monday)));
system.out.println("当月第三个周一: " + date.with(temporaladjusters.dayofweekinmonth(3, dayofweek.monday)));
system.out.println("当月第四个周一: " + date.with(temporaladjusters.dayofweekinmonth(4, dayofweek.monday)));
system.out.println("当月最后一个周一: " + date.with(temporaladjusters.lastinmonth(dayofweek.monday)));
// 5. 复杂业务场景
system.out.println("\n5. 复杂业务场景:");
// 发薪日(每月15日,如果是周末则提前到周五)
localdate payday = date.withdayofmonth(15);
if (payday.getdayofweek() == dayofweek.saturday) {
payday = payday.minusdays(1);
} else if (payday.getdayofweek() == dayofweek.sunday) {
payday = payday.minusdays(2);
}
system.out.println("发薪日: " + payday);
// 季度末
int month = date.getmonthvalue();
int quarterendmonth = ((month - 1) / 3) * 3 + 3;
localdate quarterend = date.withmonth(quarterendmonth)
.with(temporaladjusters.lastdayofmonth());
system.out.println("季度末: " + quarterend);
// 6. 组合使用
system.out.println("\n6. 组合使用:");
// 感恩节(11月第四个星期四)
localdate thanksgiving = localdate.of(date.getyear(), 11, 1)
.with(temporaladjusters.dayofweekinmonth(4, dayofweek.thursday));
system.out.println("感恩节: " + thanksgiving);
// 圣诞节(固定12月25日)
localdate christmas = localdate.of(date.getyear(), 12, 25);
system.out.println("圣诞节: " + christmas);
// 黑色星期五(感恩节后第一天)
localdate blackfriday = thanksgiving.plusdays(1);
system.out.println("黑色星期五: " + blackfriday);
// 7. 自定义temporaladjuster
system.out.println("\n7. 自定义temporaladjuster:");
// 下一个工作日(跳过周末)
temporaladjuster nextworkingday = temporal -> {
localdate d = localdate.from(temporal);
dayofweek dow = d.getdayofweek();
if (dow == dayofweek.friday) {
return d.plusdays(3); // 周五 -> 下周一
} else if (dow == dayofweek.saturday) {
return d.plusdays(2); // 周六 -> 下周一
} else {
return d.plusdays(1); // 其他 -> 明天
}
};
localdate[] testdates = {
localdate.of(2023, 12, 21), // 周四
localdate.of(2023, 12, 22), // 周五
localdate.of(2023, 12, 23), // 周六
localdate.of(2023, 12, 24) // 周日
};
for (localdate testdate : testdates) {
system.out.println(testdate + " (" + testdate.getdayofweek() +
") 的下一个工作日: " + testdate.with(nextworkingday));
}
// 8. 使用lambda表达式创建简单调整器
system.out.println("\n8. lambda表达式调整器:");
// 调整为当月15日
temporaladjuster to15th = temporaladjusters.ofdateadjuster(d -> d.withdayofmonth(15));
system.out.println("调整为当月15日: " + date.with(to15th));
// 调整为下个季度第一天
temporaladjuster nextquarterstart = temporaladjusters.ofdateadjuster(d -> {
int currentmonth = d.getmonthvalue();
int nextquartermonth = ((currentmonth - 1) / 3) * 3 + 4;
if (nextquartermonth > 12) {
return d.withmonth(1).withyear(d.getyear() + 1).withdayofmonth(1);
} else {
return d.withmonth(nextquartermonth).withdayofmonth(1);
}
});
system.out.println("下个季度第一天: " + date.with(nextquarterstart));
}
}7.2 日期时间计算
import java.time.*;
import java.time.temporal.chronounit;
import java.time.temporal.chronofield;
import java.time.temporal.temporal;
import java.time.temporal.temporalunit;
import java.util.list;
public class datetimecalculation {
public static void main(string[] args) {
system.out.println("=== 日期时间计算 ===");
// 1. 基本计算
system.out.println("\n1. 基本计算:");
localdate today = localdate.now();
system.out.println("今天: " + today);
system.out.println("昨天: " + today.minusdays(1));
system.out.println("明天: " + today.plusdays(1));
system.out.println("一周后: " + today.plusweeks(1));
system.out.println("一月后: " + today.plusmonths(1));
system.out.println("一年后: " + today.plusyears(1));
localtime now = localtime.now();
system.out.println("\n当前时间: " + now);
system.out.println("一小时前: " + now.minushours(1));
system.out.println("30分钟后: " + now.plusminutes(30));
system.out.println("15秒后: " + now.plusseconds(15));
// 2. 使用chronounit
system.out.println("\n2. 使用chronounit:");
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
system.out.println("基准时间: " + datetime);
system.out.println("加10天: " + datetime.plus(10, chronounit.days));
system.out.println("减2周: " + datetime.minus(2, chronounit.weeks));
system.out.println("加3个月: " + datetime.plus(3, chronounit.months));
system.out.println("加半日: " + datetime.plus(12, chronounit.half_days));
system.out.println("加3个十年: " + datetime.plus(3, chronounit.decades));
system.out.println("加5个世纪: " + datetime.plus(5, chronounit.centuries));
// 3. 计算时间差
system.out.println("\n3. 计算时间差:");
localdatetime start = localdatetime.of(2023, 1, 1, 0, 0);
localdatetime end = localdatetime.of(2023, 12, 31, 23, 59);
long daysbetween = chronounit.days.between(start, end);
long monthsbetween = chronounit.months.between(start, end);
long hoursbetween = chronounit.hours.between(start, end);
long minutesbetween = chronounit.minutes.between(start, end);
system.out.println("2023年天数: " + (daysbetween + 1)); // +1 包含最后一天
system.out.println("月数差: " + monthsbetween);
system.out.println("小时数差: " + hoursbetween);
system.out.println("分钟数差: " + minutesbetween);
// 4. 工作日计算
system.out.println("\n4. 工作日计算:");
localdate startdate = localdate.of(2023, 12, 18); // 周一
localdate enddate = localdate.of(2023, 12, 25); // 下周一
long workingdays = calculateworkingdays(startdate, enddate);
system.out.println(startdate + " 到 " + enddate + " 的工作日数: " + workingdays);
// 5. 年龄计算
system.out.println("\n5. 年龄计算:");
localdate birthdate = localdate.of(1990, 5, 20);
period age = period.between(birthdate, today);
system.out.println("出生日期: " + birthdate);
system.out.println("当前日期: " + today);
system.out.printf("年龄: %d 岁 %d 个月 %d 天%n",
age.getyears(), age.getmonths(), age.getdays());
// 6. 持续时间计算
system.out.println("\n6. 持续时间计算:");
localtime workstart = localtime.of(9, 0);
localtime workend = localtime.of(18, 0);
localtime lunchstart = localtime.of(12, 0);
localtime lunchend = localtime.of(13, 0);
duration workduration = duration.between(workstart, workend);
duration lunchduration = duration.between(lunchstart, lunchend);
duration actualworkduration = workduration.minus(lunchduration);
system.out.println("工作时间: " + formatduration(workduration));
system.out.println("午餐时间: " + formatduration(lunchduration));
system.out.println("实际工作时间: " + formatduration(actualworkduration));
// 7. 复杂周期计算
system.out.println("\n7. 复杂周期计算:");
// 计算信用卡账单周期
localdate billingcyclestart = localdate.of(2023, 12, 1);
localdate billingcycleend = billingcyclestart.plusmonths(1).minusdays(1);
localdate paymentduedate = billingcycleend.plusdays(20);
system.out.println("账单周期开始: " + billingcyclestart);
system.out.println("账单周期结束: " + billingcycleend);
system.out.println("还款截止日: " + paymentduedate);
// 计算距离还款日还有多少天
long daysuntildue = chronounit.days.between(today, paymentduedate);
system.out.println("距离还款日还有 " + daysuntildue + " 天");
// 8. 时区时间计算
system.out.println("\n8. 时区时间计算:");
zoneddatetime shanghaitime = zoneddatetime.now(zoneid.of("asia/shanghai"));
zoneddatetime newyorktime = shanghaitime.withzonesameinstant(zoneid.of("america/new_york"));
system.out.println("上海时间: " + shanghaitime);
system.out.println("纽约时间: " + newyorktime);
// 计算会议持续时间(跨时区)
zoneddatetime meetingstart = zoneddatetime.of(
2023, 12, 25, 14, 0, 0, 0, zoneid.of("asia/shanghai"));
zoneddatetime meetingend = meetingstart.plushours(2);
duration meetingduration = duration.between(
meetingstart.toinstant(),
meetingend.toinstant());
system.out.println("会议持续时间: " + meetingduration.tohours() + "小时");
// 9. 闰年和闰秒
system.out.println("\n9. 闰年和闰秒处理:");
localdate leapyeardate = localdate.of(2024, 2, 28);
system.out.println(leapyeardate + " 加1天: " + leapyeardate.plusdays(1));
system.out.println(leapyeardate + " 是闰年吗? " + leapyeardate.isleapyear());
// 计算两个日期之间的天数(考虑闰年)
localdate date1 = localdate.of(2020, 1, 1);
localdate date2 = localdate.of(2024, 1, 1);
long totaldays = chronounit.days.between(date1, date2);
system.out.println("2020-01-01 到 2024-01-01 的总天数: " + totaldays);
}
private static long calculateworkingdays(localdate start, localdate end) {
long workingdays = 0;
localdate date = start;
while (!date.isafter(end)) {
dayofweek dayofweek = date.getdayofweek();
if (dayofweek != dayofweek.saturday && dayofweek != dayofweek.sunday) {
workingdays++;
}
date = date.plusdays(1);
}
return workingdays;
}
private static string formatduration(duration duration) {
long hours = duration.tohours();
int minutes = duration.tominutespart();
int seconds = duration.tosecondspart();
return string.format("%d小时%d分钟%d秒", hours, minutes, seconds);
}
}7.3 时间查询和提取
import java.time.*;
import java.time.temporal.*;
import java.time.format.datetimeformatter;
import java.util.list;
public class temporalqueries {
public static void main(string[] args) {
system.out.println("=== 时间查询和提取 ===");
// 1. 预定义的temporalquery
system.out.println("\n1. 预定义的temporalquery:");
localdatetime datetime = localdatetime.of(2023, 12, 25, 14, 30, 45);
zoneddatetime zoneddatetime = zoneddatetime.of(datetime, zoneid.of("asia/shanghai"));
offsetdatetime offsetdatetime = offsetdatetime.of(datetime, zoneoffset.ofhours(8));
// 查询本地日期
localdate localdate = datetime.query(temporalqueries.localdate());
system.out.println("localdate查询: " + localdate);
// 查询本地时间
localtime localtime = datetime.query(temporalqueries.localtime());
system.out.println("localtime查询: " + localtime);
// 查询时区
zoneid zone = zoneddatetime.query(temporalqueries.zone());
system.out.println("zoneid查询: " + zone);
// 查询偏移
zoneoffset offset = offsetdatetime.query(temporalqueries.offset());
system.out.println("zoneoffset查询: " + offset);
// 查询精度
temporalunit precision = datetime.query(temporalqueries.precision());
system.out.println("精度查询: " + precision);
// 2. 自定义temporalquery
system.out.println("\n2. 自定义temporalquery:");
// 查询是否是工作时间
temporalquery<boolean> isworkinghours = temporal -> {
localtime time;
if (temporal instanceof localdatetime) {
time = ((localdatetime) temporal).tolocaltime();
} else if (temporal instanceof localtime) {
time = (localtime) temporal;
} else if (temporal instanceof zoneddatetime) {
time = ((zoneddatetime) temporal).tolocaltime();
} else if (temporal instanceof offsetdatetime) {
time = ((offsetdatetime) temporal).tolocaltime();
} else {
return false;
}
return !time.isbefore(localtime.of(9, 0)) &&
!time.isafter(localtime.of(18, 0));
};
localtime[] testtimes = {
localtime.of(8, 30), // 工作时间前
localtime.of(9, 0), // 工作时间开始
localtime.of(12, 0), // 中午
localtime.of(18, 0), // 工作时间结束
localtime.of(18, 30) // 工作时间后
};
for (localtime time : testtimes) {
system.out.println(time + " 是否是工作时间: " + time.query(isworkinghours));
}
// 3. 使用temporalaccessor获取字段值
system.out.println("\n3. 使用temporalaccessor:");
temporalaccessor accessor = datetime;
// 检查字段是否支持
boolean supportsyear = accessor.issupported(chronofield.year);
boolean supportsnano = accessor.issupported(chronofield.nano_of_second);
system.out.println("是否支持year字段: " + supportsyear);
system.out.println("是否支持nano_of_second字段: " + supportsnano);
// 获取字段值
int year = accessor.get(chronofield.year);
int month = accessor.get(chronofield.month_of_year);
int day = accessor.get(chronofield.day_of_month);
int hour = accessor.get(chronofield.hour_of_day);
int minute = accessor.get(chronofield.minute_of_hour);
int second = accessor.get(chronofield.second_of_minute);
system.out.printf("日期时间: %d-%02d-%02d %02d:%02d:%02d%n",
year, month, day, hour, minute, second);
// 4. 范围查询
system.out.println("\n4. 范围查询:");
valuerange yearrange = accessor.range(chronofield.year);
valuerange monthrange = accessor.range(chronofield.month_of_year);
valuerange dayrange = accessor.range(chronofield.day_of_month);
valuerange hourrange = accessor.range(chronofield.hour_of_day);
system.out.println("year范围: " + yearrange.getminimum() + " - " + yearrange.getmaximum());
system.out.println("month_of_year范围: " + monthrange.getminimum() + " - " + monthrange.getmaximum());
system.out.println("day_of_month范围: " + dayrange.getminimum() + " - " + dayrange.getmaximum());
system.out.println("hour_of_day范围: " + hourrange.getminimum() + " - " + hourrange.getmaximum());
// 5. 提取特定信息
system.out.println("\n5. 提取特定信息:");
// 提取季度信息
temporalquery<integer> quarterquery = temporal -> {
int monthvalue = temporal.get(chronofield.month_of_year);
return (monthvalue - 1) / 3 + 1;
};
int quarter = datetime.query(quarterquery);
system.out.println("季度: q" + quarter);
// 提取半年度信息
temporalquery<integer> halfyearquery = temporal -> {
int monthvalue = temporal.get(chronofield.month_of_year);
return monthvalue <= 6 ? 1 : 2;
};
int halfyear = datetime.query(halfyearquery);
system.out.println("半年度: h" + halfyear);
// 6. 组合查询
system.out.println("\n6. 组合查询:");
// 查询下一个工作日
temporalquery<localdate> nextworkingdayquery = temporal -> {
localdate date;
if (temporal instanceof localdate) {
date = (localdate) temporal;
} else if (temporal instanceof localdatetime) {
date = ((localdatetime) temporal).tolocaldate();
} else if (temporal instanceof zoneddatetime) {
date = ((zoneddatetime) temporal).tolocaldate();
} else {
throw new datetimeexception("不支持的temporal类型");
}
localdate nextday = date.plusdays(1);
while (nextday.getdayofweek() == dayofweek.saturday ||
nextday.getdayofweek() == dayofweek.sunday) {
nextday = nextday.plusdays(1);
}
return nextday;
};
localdate friday = localdate.of(2023, 12, 22); // 周五
localdate nextworkingday = friday.query(nextworkingdayquery);
system.out.println(friday + " 的下一个工作日: " + nextworkingday);
// 7. 实际应用:日期验证
system.out.println("\n7. 日期验证:");
temporalquery<boolean> isvaliddatequery = temporal -> {
try {
// 尝试创建localdate,如果成功则是有效日期
localdate.from(temporal);
return true;
} catch (datetimeexception e) {
return false;
}
};
// 测试有效和无效日期
int[][] testdatevalues = {
{2023, 12, 25}, // 有效
{2023, 13, 25}, // 无效月份
{2023, 2, 29}, // 2023年不是闰年,无效
{2024, 2, 29} // 2024年是闰年,有效
};
for (int[] values : testdatevalues) {
try {
localdatetime testdatetime = localdatetime.of(values[0], values[1], values[2], 0, 0);
boolean isvalid = testdatetime.query(isvaliddatequery);
system.out.printf("%d-%02d-%02d 是有效日期吗? %s%n",
values[0], values[1], values[2], isvalid);
} catch (datetimeexception e) {
system.out.printf("%d-%02d-%02d 是有效日期吗? false%n",
values[0], values[1], values[2]);
}
}
}
}8. 与旧版 api 的互操作
8.1 与 java.util.date 的转换
import java.time.*;
import java.time.format.datetimeformatter;
import java.util.date;
import java.util.calendar;
import java.util.timezone;
import java.sql.timestamp;
public class legacydateconversion {
public static void main(string[] args) {
system.out.println("=== 与旧版 date api 的互操作 ===");
// 1. instant 与 date 的转换
system.out.println("\n1. instant 与 date 的转换:");
// date -> instant
date olddate = new date();
instant instantfromdate = olddate.toinstant();
system.out.println("date: " + olddate);
system.out.println("转换为instant: " + instantfromdate);
// instant -> date
instant nowinstant = instant.now();
date datefrominstant = date.from(nowinstant);
system.out.println("instant: " + nowinstant);
system.out.println("转换为date: " + datefrominstant);
// 2. localdatetime 与 date 的转换(通过instant)
system.out.println("\n2. localdatetime 与 date 的转换:");
// localdatetime -> date
localdatetime localdatetime = localdatetime.now();
date datefromlocaldatetime = date.from(localdatetime.atzone(zoneid.systemdefault()).toinstant());
system.out.println("localdatetime: " + localdatetime);
system.out.println("转换为date: " + datefromlocaldatetime);
// date -> localdatetime
date anotherdate = new date();
localdatetime localdatetimefromdate = localdatetime.ofinstant(
anotherdate.toinstant(), zoneid.systemdefault());
system.out.println("date: " + anotherdate);
system.out.println("转换为localdatetime: " + localdatetimefromdate);
// 3. localdate 与 date 的转换
system.out.println("\n3. localdate 与 date 的转换:");
// localdate -> date
localdate localdate = localdate.now();
date datefromlocaldate = date.from(localdate.atstartofday(zoneid.systemdefault()).toinstant());
system.out.println("localdate: " + localdate);
system.out.println("转换为date: " + datefromlocaldate);
// date -> localdate
localdate localdatefromdate = localdatetime.ofinstant(
anotherdate.toinstant(), zoneid.systemdefault()).tolocaldate();
system.out.println("date: " + anotherdate);
system.out.println("转换为localdate: " + localdatefromdate);
// 4. zoneddatetime 与 date 的转换
system.out.println("\n4. zoneddatetime 与 date 的转换:");
// zoneddatetime -> date
zoneddatetime zoneddatetime = zoneddatetime.now();
date datefromzoneddatetime = date.from(zoneddatetime.toinstant());
system.out.println("zoneddatetime: " + zoneddatetime);
system.out.println("转换为date: " + datefromzoneddatetime);
// date -> zoneddatetime
zoneddatetime zoneddatetimefromdate = zoneddatetime.ofinstant(
anotherdate.toinstant(), zoneid.systemdefault());
system.out.println("date: " + anotherdate);
system.out.println("转换为zoneddatetime: " + zoneddatetimefromdate);
// 5. 与 calendar 的转换
system.out.println("\n5. 与 calendar 的转换:");
// calendar -> zoneddatetime
calendar calendar = calendar.getinstance();
zoneddatetime zoneddatetimefromcalendar = zoneddatetime.ofinstant(
calendar.toinstant(), calendar.gettimezone().tozoneid());
system.out.println("calendar: " + calendar.gettime());
system.out.println("转换为zoneddatetime: " + zoneddatetimefromcalendar);
// zoneddatetime -> calendar
calendar calendarfromzoned = calendar.getinstance();
calendarfromzoned.settime(date.from(zoneddatetime.toinstant()));
system.out.println("zoneddatetime: " + zoneddatetime);
system.out.println("转换为calendar: " + calendarfromzoned.gettime());
// 6. 与 timezone 的转换
system.out.println("\n6. 与 timezone 的转换:");
// timezone -> zoneid
timezone timezone = timezone.gettimezone("asia/shanghai");
zoneid zoneidfromtimezone = timezone.tozoneid();
system.out.println("timezone: " + timezone.getid());
system.out.println("转换为zoneid: " + zoneidfromtimezone);
// zoneid -> timezone
zoneid zoneid = zoneid.of("america/new_york");
timezone timezonefromzoneid = timezone.gettimezone(zoneid);
system.out.println("zoneid: " + zoneid);
system.out.println("转换为timezone: " + timezonefromzoneid.getid());
// 7. 与 java.sql 日期类的转换
system.out.println("\n7. 与 java.sql 日期类的转换:");
// java.sql.date -> localdate
java.sql.date sqldate = new java.sql.date(system.currenttimemillis());
localdate localdatefromsqldate = sqldate.tolocaldate();
system.out.println("java.sql.date: " + sqldate);
system.out.println("转换为localdate: " + localdatefromsqldate);
// localdate -> java.sql.date
java.sql.date sqldatefromlocaldate = java.sql.date.valueof(localdate);
system.out.println("localdate: " + localdate);
system.out.println("转换为java.sql.date: " + sqldatefromlocaldate);
// java.sql.time -> localtime
java.sql.time sqltime = new java.sql.time(system.currenttimemillis());
localtime localtimefromsqltime = sqltime.tolocaltime();
system.out.println("java.sql.time: " + sqltime);
system.out.println("转换为localtime: " + localtimefromsqltime);
// localtime -> java.sql.time
java.sql.time sqltimefromlocaltime = java.sql.time.valueof(localtime.now());
system.out.println("localtime: " + localtime.now());
system.out.println("转换为java.sql.time: " + sqltimefromlocaltime);
// java.sql.timestamp -> instant/localdatetime
timestamp timestamp = new timestamp(system.currenttimemillis());
instant instantfromtimestamp = timestamp.toinstant();
localdatetime localdatetimefromtimestamp = timestamp.tolocaldatetime();
system.out.println("java.sql.timestamp: " + timestamp);
system.out.println("转换为instant: " + instantfromtimestamp);
system.out.println("转换为localdatetime: " + localdatetimefromtimestamp);
// localdatetime -> java.sql.timestamp
timestamp timestampfromlocaldatetime = timestamp.valueof(localdatetime);
system.out.println("localdatetime: " + localdatetime);
system.out.println("转换为java.sql.timestamp: " + timestampfromlocaldatetime);
// 8. 实战:旧系统迁移
system.out.println("\n8. 实战:旧系统迁移示例:");
// 旧系统代码
date legacydate = new date();
calendar legacycalendar = calendar.getinstance();
legacycalendar.settime(legacydate);
legacycalendar.add(calendar.day_of_month, 7);
date oneweeklater = legacycalendar.gettime();
system.out.println("旧系统方式(一周后): " + oneweeklater);
// 新系统代码
localdatetime newdatetime = localdatetime.now();
localdatetime oneweeklaternew = newdatetime.plusweeks(1);
system.out.println("新系统方式(一周后): " + oneweeklaternew);
// 兼容处理
date converteddate = date.from(oneweeklaternew.atzone(zoneid.systemdefault()).toinstant());
system.out.println("转换回date: " + converteddate);
// 9. 工具方法
system.out.println("\n9. 转换工具方法示例:");
// 工具方法:date转换为指定格式的字符串
string formatted = formatdate(legacydate, "yyyy-mm-dd hh:mm:ss");
system.out.println("格式化date: " + formatted);
// 工具方法:字符串转换为date
date parseddate = parsedate("2023-12-25 14:30:45", "yyyy-mm-dd hh:mm:ss");
system.out.println("解析字符串为date: " + parseddate);
}
// 工具方法:将date格式化为字符串
public static string formatdate(date date, string pattern) {
instant instant = date.toinstant();
localdatetime datetime = localdatetime.ofinstant(instant, zoneid.systemdefault());
datetimeformatter formatter = datetimeformatter.ofpattern(pattern);
return datetime.format(formatter);
}
// 工具方法:将字符串解析为date
public static date parsedate(string datestring, string pattern) {
datetimeformatter formatter = datetimeformatter.ofpattern(pattern);
localdatetime datetime = localdatetime.parse(datestring, formatter);
instant instant = datetime.atzone(zoneid.systemdefault()).toinstant();
return date.from(instant);
}
}8.2 兼容性处理和工具类
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.temporal;
import java.util.*;
import java.util.concurrent.concurrenthashmap;
public class compatibilityutils {
// 缓存格式化器以提高性能
private static final map<string, datetimeformatter> formatter_cache = new concurrenthashmap<>();
public static void main(string[] args) {
system.out.println("=== 兼容性工具类 ===");
// 1. 获取当前时间的不同表示
system.out.println("\n1. 当前时间的不同表示:");
system.out.println("date: " + new date());
system.out.println("instant: " + instant.now());
system.out.println("localdatetime: " + localdatetime.now());
system.out.println("zoneddatetime: " + zoneddatetime.now());
system.out.println("calendar: " + calendar.getinstance().gettime());
// 2. 日期格式兼容性
system.out.println("\n2. 日期格式兼容性:");
// 旧格式
string oldformat = "yyyy/mm/dd hh:mm:ss";
// 新格式
string newformat = "yyyy-mm-dd't'hh:mm:ss";
date date = new date();
string oldformatted = format(date, oldformat);
string newformatted = format(date, newformat);
system.out.println("旧格式: " + oldformatted);
system.out.println("新格式: " + newformatted);
// 3. 时区兼容性
system.out.println("\n3. 时区兼容性:");
timezone oldtimezone = timezone.gettimezone("gmt+08:00");
zoneid newzoneid = zoneid.of("asia/shanghai");
system.out.println("旧时区: " + oldtimezone.getid());
system.out.println("新时区: " + newzoneid.getid());
// 转换
calendar calendar = calendar.getinstance(oldtimezone);
zoneddatetime zoneddatetime = zoneddatetime.ofinstant(
calendar.toinstant(), oldtimezone.tozoneid());
system.out.println("calendar -> zoneddatetime: " + zoneddatetime);
// 4. 日期计算兼容性
system.out.println("\n4. 日期计算兼容性:");
// 旧方式:使用calendar
calendar cal = calendar.getinstance();
cal.set(2023, calendar.december, 25); // 注意:calendar月份从0开始
cal.add(calendar.month, 1);
system.out.println("旧方式加一个月: " + cal.gettime());
// 新方式:使用localdate
localdate localdate = localdate.of(2023, 12, 25);
localdate plusonemonth = localdate.plusmonths(1);
system.out.println("新方式加一个月: " + plusonemonth);
// 5. 工具类方法演示
system.out.println("\n5. 工具类方法演示:");
// 安全的日期转换
string datestr = "2023-12-25";
date safedate = parsedatesafe(datestr, "yyyy-mm-dd");
system.out.println("安全解析: " + datestr + " -> " + safedate);
// 无效日期
string invaliddatestr = "2023-13-25";
date invaliddate = parsedatesafe(invaliddatestr, "yyyy-mm-dd");
system.out.println("无效日期解析: " + invaliddatestr + " -> " +
(invaliddate != null ? invaliddate : "null"));
// 6. 批量转换
system.out.println("\n6. 批量转换:");
list<date> olddates = arrays.aslist(
new date(),
new date(system.currenttimemillis() + 86400000), // 明天
new date(system.currenttimemillis() - 86400000) // 昨天
);
list<localdate> newdates = convertdatestolocaldates(olddates);
system.out.println("批量date -> localdate:");
for (int i = 0; i < olddates.size(); i++) {
system.out.println(olddates.get(i) + " -> " + newdates.get(i));
}
// 7. 处理空值和默认值
system.out.println("\n7. 空值和默认值处理:");
date nulldate = null;
localdate defaultlocaldate = tolocaldate(nulldate, localdate.now());
system.out.println("空date的默认值: " + defaultlocaldate);
string nullstring = null;
date defaultdate = parsedatesafe(nullstring, "yyyy-mm-dd", new date());
system.out.println("空字符串的默认值: " + defaultdate);
// 8. 性能对比
system.out.println("\n8. 性能对比:");
int iterations = 10000;
// 旧方式性能
long startold = system.currenttimemillis();
for (int i = 0; i < iterations; i++) {
calendar c = calendar.getinstance();
c.set(2023, 11, 25); // 12月
c.add(calendar.day_of_month, i % 30);
}
long endold = system.currenttimemillis();
// 新方式性能
long startnew = system.currenttimemillis();
for (int i = 0; i < iterations; i++) {
localdate d = localdate.of(2023, 12, 25);
d.plusdays(i % 30);
}
long endnew = system.currenttimemillis();
system.out.println("calendar操作 " + iterations + " 次耗时: " + (endold - startold) + "ms");
system.out.println("localdate操作 " + iterations + " 次耗时: " + (endnew - startnew) + "ms");
}
// 安全的日期格式化(带缓存)
public static string format(date date, string pattern) {
if (date == null) {
return null;
}
datetimeformatter formatter = formatter_cache.computeifabsent(
pattern, datetimeformatter::ofpattern);
instant instant = date.toinstant();
localdatetime datetime = localdatetime.ofinstant(instant, zoneid.systemdefault());
return datetime.format(formatter);
}
// 安全的日期解析
public static date parsedatesafe(string datestring, string pattern) {
return parsedatesafe(datestring, pattern, null);
}
public static date parsedatesafe(string datestring, string pattern, date defaultvalue) {
if (datestring == null || datestring.trim().isempty()) {
return defaultvalue;
}
try {
datetimeformatter formatter = formatter_cache.computeifabsent(
pattern, datetimeformatter::ofpattern);
temporalaccessor temporal = formatter.parsebest(datestring,
localdatetime::from,
localdate::from,
yearmonth::from,
year::from);
instant instant;
if (temporal instanceof localdatetime) {
instant = ((localdatetime) temporal).atzone(zoneid.systemdefault()).toinstant();
} else if (temporal instanceof localdate) {
instant = ((localdate) temporal).atstartofday(zoneid.systemdefault()).toinstant();
} else if (temporal instanceof yearmonth) {
instant = ((yearmonth) temporal).atday(1).atstartofday(zoneid.systemdefault()).toinstant();
} else if (temporal instanceof year) {
instant = ((year) temporal).atmonth(1).atday(1).atstartofday(zoneid.systemdefault()).toinstant();
} else {
return defaultvalue;
}
return date.from(instant);
} catch (exception e) {
return defaultvalue;
}
}
// 批量转换date到localdate
public static list<localdate> convertdatestolocaldates(list<date> dates) {
list<localdate> result = new arraylist<>();
if (dates == null) {
return result;
}
for (date date : dates) {
if (date != null) {
result.add(localdatetime.ofinstant(date.toinstant(),
zoneid.systemdefault()).tolocaldate());
} else {
result.add(null);
}
}
return result;
}
// 转换date到localdate,支持默认值
public static localdate tolocaldate(date date, localdate defaultvalue) {
if (date == null) {
return defaultvalue;
}
return localdatetime.ofinstant(date.toinstant(),
zoneid.systemdefault()).tolocaldate();
}
// 创建兼容的日期范围
public static daterange createdaterange(date start, date end) {
return new daterange(start, end);
}
// 日期范围类
public static class daterange {
private final date start;
private final date end;
public daterange(date start, date end) {
this.start = start;
this.end = end;
}
public boolean contains(date date) {
return !date.before(start) && !date.after(end);
}
public duration toduration() {
return duration.between(
start.toinstant(),
end.toinstant()
);
}
public period toperiod() {
localdate startdate = localdatetime.ofinstant(start.toinstant(),
zoneid.systemdefault()).tolocaldate();
localdate enddate = localdatetime.ofinstant(end.toinstant(),
zoneid.systemdefault()).tolocaldate();
return period.between(startdate, enddate);
}
// getters
public date getstart() { return start; }
public date getend() { return end; }
}
}9. 实战示例
9.1 完整的日期时间工具类
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.format.datetimeparseexception;
import java.time.temporal.*;
import java.util.*;
import java.util.concurrent.concurrenthashmap;
/**
* 日期时间工具类
* 提供常用的日期时间操作方法
*/
public final class datetimeutils {
private datetimeutils() {
// 工具类,防止实例化
}
// 常用格式化模式
public static final string pattern_date = "yyyy-mm-dd";
public static final string pattern_time = "hh:mm:ss";
public static final string pattern_datetime = "yyyy-mm-dd hh:mm:ss";
public static final string pattern_datetime_iso = "yyyy-mm-dd't'hh:mm:ss";
public static final string pattern_timestamp = "yyyymmddhhmmss";
public static final string pattern_chinese_date = "yyyy年mm月dd日";
public static final string pattern_chinese_datetime = "yyyy年mm月dd日 hh时mm分ss秒";
// 缓存格式化器
private static final map<string, datetimeformatter> formatter_cache =
new concurrenthashmap<>();
// 获取格式化器(带缓存)
private static datetimeformatter getformatter(string pattern) {
return formatter_cache.computeifabsent(pattern, datetimeformatter::ofpattern);
}
/**
* 格式化日期时间
*/
public static string format(localdatetime datetime, string pattern) {
if (datetime == null) {
return null;
}
return datetime.format(getformatter(pattern));
}
public static string format(localdate date, string pattern) {
if (date == null) {
return null;
}
return date.format(getformatter(pattern));
}
public static string format(localtime time, string pattern) {
if (time == null) {
return null;
}
return time.format(getformatter(pattern));
}
public static string format(zoneddatetime zoneddatetime, string pattern) {
if (zoneddatetime == null) {
return null;
}
return zoneddatetime.format(getformatter(pattern));
}
/**
* 解析日期时间
*/
public static localdatetime parsedatetime(string str, string pattern) {
if (str == null || str.trim().isempty()) {
return null;
}
try {
return localdatetime.parse(str, getformatter(pattern));
} catch (datetimeparseexception e) {
throw new illegalargumentexception("无法解析日期时间: " + str + ", 格式: " + pattern, e);
}
}
public static localdate parsedate(string str, string pattern) {
if (str == null || str.trim().isempty()) {
return null;
}
try {
return localdate.parse(str, getformatter(pattern));
} catch (datetimeparseexception e) {
throw new illegalargumentexception("无法解析日期: " + str + ", 格式: " + pattern, e);
}
}
public static localtime parsetime(string str, string pattern) {
if (str == null || str.trim().isempty()) {
return null;
}
try {
return localtime.parse(str, getformatter(pattern));
} catch (datetimeparseexception e) {
throw new illegalargumentexception("无法解析时间: " + str + ", 格式: " + pattern, e);
}
}
/**
* 安全解析(返回默认值)
*/
public static localdatetime parsedatetimesafe(string str, string pattern, localdatetime defaultvalue) {
try {
return parsedatetime(str, pattern);
} catch (exception e) {
return defaultvalue;
}
}
public static localdate parsedatesafe(string str, string pattern, localdate defaultvalue) {
try {
return parsedate(str, pattern);
} catch (exception e) {
return defaultvalue;
}
}
/**
* 获取当前时间
*/
public static localdatetime now() {
return localdatetime.now();
}
public static localdate today() {
return localdate.now();
}
public static string now(string pattern) {
return format(now(), pattern);
}
/**
* 计算年龄
*/
public static int calculateage(localdate birthdate) {
return calculateage(birthdate, localdate.now());
}
public static int calculateage(localdate birthdate, localdate currentdate) {
if (birthdate == null || currentdate == null) {
throw new illegalargumentexception("日期不能为空");
}
if (birthdate.isafter(currentdate)) {
throw new illegalargumentexception("出生日期不能晚于当前日期");
}
return period.between(birthdate, currentdate).getyears();
}
/**
* 计算两个日期之间的工作日数
*/
public static long calculateworkingdays(localdate start, localdate end) {
if (start == null || end == null) {
throw new illegalargumentexception("日期不能为空");
}
if (start.isafter(end)) {
throw new illegalargumentexception("开始日期不能晚于结束日期");
}
long workingdays = 0;
localdate date = start;
while (!date.isafter(end)) {
dayofweek dayofweek = date.getdayofweek();
if (dayofweek != dayofweek.saturday && dayofweek != dayofweek.sunday) {
workingdays++;
}
date = date.plusdays(1);
}
return workingdays;
}
/**
* 获取下一个工作日
*/
public static localdate nextworkingday(localdate date) {
if (date == null) {
date = localdate.now();
}
localdate nextday = date.plusdays(1);
while (nextday.getdayofweek() == dayofweek.saturday ||
nextday.getdayofweek() == dayofweek.sunday) {
nextday = nextday.plusdays(1);
}
return nextday;
}
/**
* 获取季度信息
*/
public static int getquarter(localdate date) {
if (date == null) {
date = localdate.now();
}
int month = date.getmonthvalue();
return (month - 1) / 3 + 1;
}
public static localdate getquarterstart(localdate date) {
if (date == null) {
date = localdate.now();
}
int quarter = getquarter(date);
int month = (quarter - 1) * 3 + 1;
return localdate.of(date.getyear(), month, 1);
}
public static localdate getquarterend(localdate date) {
if (date == null) {
date = localdate.now();
}
int quarter = getquarter(date);
int month = quarter * 3;
return localdate.of(date.getyear(), month, 1)
.with(temporaladjusters.lastdayofmonth());
}
/**
* 时间范围检查
*/
public static boolean iswithinworkinghours(localtime time) {
if (time == null) {
return false;
}
localtime workstart = localtime.of(9, 0);
localtime workend = localtime.of(18, 0);
return !time.isbefore(workstart) && !time.isafter(workend);
}
public static boolean isweekend(localdate date) {
if (date == null) {
date = localdate.now();
}
dayofweek dayofweek = date.getdayofweek();
return dayofweek == dayofweek.saturday || dayofweek == dayofweek.sunday;
}
/**
* 计算持续时间
*/
public static duration calculateduration(localdatetime start, localdatetime end) {
if (start == null || end == null) {
throw new illegalargumentexception("开始时间和结束时间不能为空");
}
if (start.isafter(end)) {
throw new illegalargumentexception("开始时间不能晚于结束时间");
}
return duration.between(start, end);
}
public static string formatduration(duration duration) {
if (duration == null) {
return "0秒";
}
long days = duration.todays();
long hours = duration.tohourspart();
long minutes = duration.tominutespart();
long seconds = duration.tosecondspart();
stringbuilder sb = new stringbuilder();
if (days > 0) {
sb.append(days).append("天");
}
if (hours > 0) {
sb.append(hours).append("小时");
}
if (minutes > 0) {
sb.append(minutes).append("分钟");
}
if (seconds > 0 || sb.length() == 0) {
sb.append(seconds).append("秒");
}
return sb.tostring();
}
/**
* 时区转换
*/
public static zoneddatetime converttimezone(localdatetime datetime, zoneid fromzone, zoneid tozone) {
if (datetime == null || fromzone == null || tozone == null) {
throw new illegalargumentexception("参数不能为空");
}
return datetime.atzone(fromzone).withzonesameinstant(tozone);
}
public static zoneddatetime converttoutc(localdatetime datetime, zoneid zone) {
return converttimezone(datetime, zone, zoneid.of("utc"));
}
public static zoneddatetime convertfromutc(localdatetime datetime, zoneid zone) {
return converttimezone(datetime, zoneid.of("utc"), zone);
}
/**
* 生成时间范围
*/
public static list<localdate> generatedaterange(localdate start, localdate end) {
if (start == null || end == null) {
throw new illegalargumentexception("开始日期和结束日期不能为空");
}
if (start.isafter(end)) {
throw new illegalargumentexception("开始日期不能晚于结束日期");
}
list<localdate> dates = new arraylist<>();
localdate date = start;
while (!date.isafter(end)) {
dates.add(date);
date = date.plusdays(1);
}
return dates;
}
/**
* 获取某月的所有工作日
*/
public static list<localdate> getworkingdaysofmonth(int year, int month) {
localdate firstday = localdate.of(year, month, 1);
localdate lastday = firstday.with(temporaladjusters.lastdayofmonth());
list<localdate> workingdays = new arraylist<>();
localdate date = firstday;
while (!date.isafter(lastday)) {
if (!isweekend(date)) {
workingdays.add(date);
}
date = date.plusdays(1);
}
return workingdays;
}
/**
* 验证日期有效性
*/
public static boolean isvaliddate(int year, int month, int day) {
try {
localdate.of(year, month, day);
return true;
} catch (datetimeexception e) {
return false;
}
}
public static boolean isvalidtime(int hour, int minute, int second) {
try {
localtime.of(hour, minute, second);
return true;
} catch (datetimeexception e) {
return false;
}
}
/**
* 与旧date api的转换
*/
public static date todate(localdatetime datetime) {
if (datetime == null) {
return null;
}
return date.from(datetime.atzone(zoneid.systemdefault()).toinstant());
}
public static localdatetime fromdate(date date) {
if (date == null) {
return null;
}
return localdatetime.ofinstant(date.toinstant(), zoneid.systemdefault());
}
/**
* 获取时间戳
*/
public static long gettimestamp(localdatetime datetime) {
if (datetime == null) {
return 0;
}
return datetime.atzone(zoneid.systemdefault()).toinstant().toepochmilli();
}
public static localdatetime fromtimestamp(long timestamp) {
return localdatetime.ofinstant(instant.ofepochmilli(timestamp), zoneid.systemdefault());
}
/**
* 主方法:测试工具类
*/
public static void main(string[] args) {
system.out.println("=== datetimeutils 测试 ===");
// 1. 格式化测试
system.out.println("\n1. 格式化测试:");
system.out.println("当前时间: " + format(now(), pattern_datetime));
system.out.println("当前日期: " + format(today(), pattern_date));
system.out.println("中文格式: " + format(now(), pattern_chinese_datetime));
// 2. 解析测试
system.out.println("\n2. 解析测试:");
localdatetime parsed = parsedatetime("2023-12-25 14:30:45", pattern_datetime);
system.out.println("解析结果: " + parsed);
// 3. 年龄计算
system.out.println("\n3. 年龄计算:");
localdate birthdate = localdate.of(1990, 5, 20);
int age = calculateage(birthdate);
system.out.println("出生日期: " + birthdate);
system.out.println("年龄: " + age + "岁");
// 4. 工作日计算
system.out.println("\n4. 工作日计算:");
localdate startdate = localdate.of(2023, 12, 18);
localdate enddate = localdate.of(2023, 12, 25);
long workingdays = calculateworkingdays(startdate, enddate);
system.out.println(startdate + " 到 " + enddate + " 的工作日数: " + workingdays);
// 5. 季度计算
system.out.println("\n5. 季度计算:");
localdate testdate = localdate.of(2023, 8, 15);
int quarter = getquarter(testdate);
localdate quarterstart = getquarterstart(testdate);
localdate quarterend = getquarterend(testdate);
system.out.println(testdate + " 属于第 " + quarter + " 季度");
system.out.println("季度开始: " + quarterstart);
system.out.println("季度结束: " + quarterend);
// 6. 时间范围检查
system.out.println("\n6. 时间范围检查:");
localtime testtime = localtime.of(14, 30);
system.out.println(testtime + " 是否是工作时间: " + iswithinworkinghours(testtime));
system.out.println(testdate + " 是否是周末: " + isweekend(testdate));
// 7. 持续时间计算
system.out.println("\n7. 持续时间计算:");
localdatetime starttime = localdatetime.of(2023, 12, 25, 9, 0);
localdatetime endtime = localdatetime.of(2023, 12, 25, 17, 30);
duration duration = calculateduration(starttime, endtime);
system.out.println("持续时间: " + formatduration(duration));
// 8. 时区转换
system.out.println("\n8. 时区转换:");
localdatetime localtime = localdatetime.of(2023, 12, 25, 14, 30);
zoneddatetime utctime = converttoutc(localtime, zoneid.of("asia/shanghai"));
system.out.println("上海时间: " + localtime);
system.out.println("utc时间: " + utctime);
// 9. 生成日期范围
system.out.println("\n9. 生成日期范围:");
list<localdate> daterange = generatedaterange(
localdate.of(2023, 12, 20),
localdate.of(2023, 12, 25)
);
system.out.println("日期范围: " + daterange);
// 10. 获取月工作日
system.out.println("\n10. 获取月工作日:");
list<localdate> workingdayslist = getworkingdaysofmonth(2023, 12);
system.out.println("2023年12月工作日数: " + workingdayslist.size());
system.out.println("前5个工作日: " + workingdayslist.sublist(0, math.min(5, workingdayslist.size())));
}
}9.2 完整的业务应用示例
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.temporaladjusters;
import java.util.*;
import java.util.concurrent.concurrenthashmap;
import java.util.stream.collectors;
/**
* 会议调度系统
* 演示java 8日期时间api在实际业务中的应用
*/
public class meetingscheduler {
// 会议类
public static class meeting {
private final string id;
private final string title;
private final zoneddatetime starttime;
private final duration duration;
private final set<string> participants;
private final string organizer;
public meeting(string id, string title, zoneddatetime starttime,
duration duration, set<string> participants, string organizer) {
this.id = id;
this.title = title;
this.starttime = starttime;
this.duration = duration;
this.participants = collections.unmodifiableset(new hashset<>(participants));
this.organizer = organizer;
}
public zoneddatetime getendtime() {
return starttime.plus(duration);
}
public boolean overlapswith(meeting other) {
return !(this.getendtime().isbefore(other.starttime) ||
other.getendtime().isbefore(this.starttime));
}
public boolean isparticipant(string email) {
return participants.contains(email) || organizer.equals(email);
}
// getters
public string getid() { return id; }
public string gettitle() { return title; }
public zoneddatetime getstarttime() { return starttime; }
public duration getduration() { return duration; }
public set<string> getparticipants() { return participants; }
public string getorganizer() { return organizer; }
@override
public string tostring() {
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm z");
return string.format("会议[%s]: %s, 时间: %s, 时长: %d分钟, 组织者: %s, 参与者: %d人",
id, title, starttime.format(formatter),
duration.tominutes(), organizer, participants.size());
}
}
// 用户可用时间
public static class useravailability {
private final string email;
private final localtime workstart;
private final localtime workend;
private final zoneid timezone;
private final set<dayofweek> workingdays;
public useravailability(string email, localtime workstart, localtime workend,
zoneid timezone, set<dayofweek> workingdays) {
this.email = email;
this.workstart = workstart;
this.workend = workend;
this.timezone = timezone;
this.workingdays = collections.unmodifiableset(new hashset<>(workingdays));
}
public boolean isavailableat(zoneddatetime time) {
// 转换为用户时区
zoneddatetime usertime = time.withzonesameinstant(timezone);
// 检查是否是工作日
if (!workingdays.contains(usertime.getdayofweek())) {
return false;
}
// 检查是否在工作时间内
localtime userlocaltime = usertime.tolocaltime();
return !userlocaltime.isbefore(workstart) &&
!userlocaltime.isafter(workend.minusminutes(1));
}
// getters
public string getemail() { return email; }
public localtime getworkstart() { return workstart; }
public localtime getworkend() { return workend; }
public zoneid gettimezone() { return timezone; }
public set<dayofweek> getworkingdays() { return workingdays; }
}
// 调度器
public static class scheduler {
private final map<string, useravailability> useravailabilities;
private final list<meeting> scheduledmeetings;
private final map<string, set<meeting>> usermeetings; // 用户参与的会议
public scheduler() {
this.useravailabilities = new concurrenthashmap<>();
this.scheduledmeetings = new arraylist<>();
this.usermeetings = new concurrenthashmap<>();
}
// 添加用户可用时间
public void adduseravailability(useravailability availability) {
useravailabilities.put(availability.getemail(), availability);
}
// 安排会议
public meeting schedulemeeting(string title, duration duration,
set<string> participantemails,
string organizeremail,
zoneddatetime preferredtime) throws schedulingexception {
// 验证参与者
for (string email : participantemails) {
if (!useravailabilities.containskey(email)) {
throw new schedulingexception("用户 " + email + " 的可用时间未设置");
}
}
if (!useravailabilities.containskey(organizeremail)) {
throw new schedulingexception("组织者 " + organizeremail + " 的可用时间未设置");
}
// 查找所有参与者的共同可用时间
zoneddatetime besttime = findbesttime(participantemails, organizeremail,
preferredtime, duration);
if (besttime == null) {
throw new schedulingexception("无法找到所有参与者的共同可用时间");
}
// 创建会议
string meetingid = generatemeetingid();
meeting meeting = new meeting(meetingid, title, besttime, duration,
participantemails, organizeremail);
// 检查时间冲突
if (hastimeconflict(meeting)) {
throw new schedulingexception("发现时间冲突");
}
// 保存会议
scheduledmeetings.add(meeting);
// 更新用户会议映射
for (string email : participantemails) {
usermeetings.computeifabsent(email, k -> new hashset<>()).add(meeting);
}
usermeetings.computeifabsent(organizeremail, k -> new hashset<>()).add(meeting);
return meeting;
}
// 查找最佳时间
private zoneddatetime findbesttime(set<string> participantemails,
string organizeremail,
zoneddatetime preferredtime,
duration duration) {
// 从首选时间开始,向前后各搜索7天
localdate startdate = preferredtime.tolocaldate().minusdays(7);
localdate enddate = preferredtime.tolocaldate().plusdays(7);
// 收集所有参与者的邮箱(包括组织者)
set<string> allparticipants = new hashset<>(participantemails);
allparticipants.add(organizeremail);
// 搜索可用时间
for (localdate date = startdate; !date.isafter(enddate); date = date.plusdays(1)) {
list<zoneddatetime> availableslots = findavailableslotsondate(
date, allparticipants, duration);
if (!availableslots.isempty()) {
// 返回最接近首选时间的时间
return findclosesttime(availableslots, preferredtime);
}
}
return null;
}
// 查找某天的可用时间段
private list<zoneddatetime> findavailableslotsondate(localdate date,
set<string> participants,
duration meetingduration) {
list<zoneddatetime> availableslots = new arraylist<>();
// 获取第一个参与者的时区作为参考时区
string firstparticipant = participants.iterator().next();
zoneid referencezone = useravailabilities.get(firstparticipant).gettimezone();
// 检查一天中的每个30分钟时段
localdatetime daystart = date.atstartofday();
localdatetime dayend = date.attime(localtime.max);
localdatetime currentslot = daystart;
while (currentslot.isbefore(dayend)) {
zoneddatetime slotinreferencezone = currentslot.atzone(referencezone);
// 检查所有参与者在此时段是否都可用
boolean allavailable = participants.stream().allmatch(email -> {
useravailability availability = useravailabilities.get(email);
zoneddatetime slotinuserzone = slotinreferencezone
.withzonesameinstant(availability.gettimezone());
// 检查用户在该时段是否可用
if (!availability.isavailableat(slotinuserzone)) {
return false;
}
// 检查用户在该时段是否有其他会议
set<meeting> usermeetings = this.usermeetings.get(email);
if (usermeetings != null) {
zoneddatetime slotend = slotinuserzone.plus(meetingduration);
for (meeting meeting : usermeetings) {
if (meeting.getstarttime().isbefore(slotend) &&
meeting.getendtime().isafter(slotinuserzone)) {
return false; // 时间冲突
}
}
}
return true;
});
if (allavailable) {
availableslots.add(slotinreferencezone);
}
currentslot = currentslot.plusminutes(30);
}
return availableslots;
}
// 查找最接近的时间
private zoneddatetime findclosesttime(list<zoneddatetime> availableslots,
zoneddatetime preferredtime) {
return availableslots.stream()
.min(comparator.comparinglong(
slot -> math.abs(duration.between(slot, preferredtime).tominutes())))
.orelse(null);
}
// 检查时间冲突
private boolean hastimeconflict(meeting newmeeting) {
for (meeting existingmeeting : scheduledmeetings) {
// 检查参与者是否有重叠
set<string> commonparticipants = new hashset<>(newmeeting.getparticipants());
commonparticipants.retainall(existingmeeting.getparticipants());
commonparticipants.add(newmeeting.getorganizer());
commonparticipants.add(existingmeeting.getorganizer());
if (!commonparticipants.isempty() &&
newmeeting.overlapswith(existingmeeting)) {
return true;
}
}
return false;
}
// 生成会议id
private string generatemeetingid() {
return "mtg-" + uuid.randomuuid().tostring().substring(0, 8).touppercase();
}
// 获取用户的会议
public list<meeting> getusermeetings(string email, localdate date) {
set<meeting> meetings = usermeetings.get(email);
if (meetings == null) {
return collections.emptylist();
}
return meetings.stream()
.filter(meeting -> meeting.getstarttime().tolocaldate().equals(date))
.sorted(comparator.comparing(meeting::getstarttime))
.collect(collectors.tolist());
}
// 获取用户下个月的会议
public list<meeting> getusermeetingsnextmonth(string email) {
localdate today = localdate.now();
localdate firstdayofnextmonth = today.with(temporaladjusters.firstdayofnextmonth());
localdate lastdayofnextmonth = firstdayofnextmonth
.with(temporaladjusters.lastdayofmonth());
set<meeting> meetings = usermeetings.get(email);
if (meetings == null) {
return collections.emptylist();
}
return meetings.stream()
.filter(meeting -> {
localdate meetingdate = meeting.getstarttime().tolocaldate();
return !meetingdate.isbefore(firstdayofnextmonth) &&
!meetingdate.isafter(lastdayofnextmonth);
})
.sorted(comparator.comparing(meeting::getstarttime))
.collect(collectors.tolist());
}
// getters
public list<meeting> getscheduledmeetings() {
return collections.unmodifiablelist(scheduledmeetings);
}
}
// 调度异常
public static class schedulingexception extends exception {
public schedulingexception(string message) {
super(message);
}
}
// 测试主方法
public static void main(string[] args) {
system.out.println("=== 会议调度系统演示 ===");
// 创建调度器
scheduler scheduler = new scheduler();
// 设置用户可用时间
system.out.println("\n1. 设置用户可用时间:");
// 用户1:上海,工作日9:00-18:00
useravailability user1 = new useravailability(
"alice@example.com",
localtime.of(9, 0),
localtime.of(18, 0),
zoneid.of("asia/shanghai"),
enumset.of(dayofweek.monday, dayofweek.tuesday, dayofweek.wednesday,
dayofweek.thursday, dayofweek.friday)
);
// 用户2:纽约,工作日9:00-17:00
useravailability user2 = new useravailability(
"bob@example.com",
localtime.of(9, 0),
localtime.of(17, 0),
zoneid.of("america/new_york"),
enumset.of(dayofweek.monday, dayofweek.tuesday, dayofweek.wednesday,
dayofweek.thursday, dayofweek.friday)
);
// 用户3:伦敦,工作日8:00-16:00
useravailability user3 = new useravailability(
"charlie@example.com",
localtime.of(8, 0),
localtime.of(16, 0),
zoneid.of("europe/london"),
enumset.of(dayofweek.monday, dayofweek.tuesday, dayofweek.wednesday,
dayofweek.thursday, dayofweek.friday)
);
scheduler.adduseravailability(user1);
scheduler.adduseravailability(user2);
scheduler.adduseravailability(user3);
system.out.println("已添加3个用户的可用时间");
// 安排会议
system.out.println("\n2. 安排会议:");
try {
// 安排一个1小时的会议
set<string> participants = new hashset<>();
participants.add("bob@example.com");
participants.add("charlie@example.com");
// 首选时间:明天上午10点(上海时间)
zoneddatetime preferredtime = zoneddatetime.now()
.withzonesameinstant(zoneid.of("asia/shanghai"))
.plusdays(1)
.withhour(10)
.withminute(0)
.withsecond(0)
.withnano(0);
meeting meeting = scheduler.schedulemeeting(
"项目启动会",
duration.ofhours(1),
participants,
"alice@example.com",
preferredtime
);
system.out.println("会议安排成功:");
system.out.println(meeting);
// 显示各时区的时间
system.out.println("\n各时区时间:");
system.out.println("上海: " + meeting.getstarttime()
.withzonesameinstant(zoneid.of("asia/shanghai"))
.format(datetimeformatter.ofpattern("yyyy-mm-dd hh:mm z")));
system.out.println("纽约: " + meeting.getstarttime()
.withzonesameinstant(zoneid.of("america/new_york"))
.format(datetimeformatter.ofpattern("yyyy-mm-dd hh:mm z")));
system.out.println("伦敦: " + meeting.getstarttime()
.withzonesameinstant(zoneid.of("europe/london"))
.format(datetimeformatter.ofpattern("yyyy-mm-dd hh:mm z")));
} catch (schedulingexception e) {
system.out.println("安排会议失败: " + e.getmessage());
}
// 获取用户会议
system.out.println("\n3. 查询用户会议:");
list<meeting> alicemeetings = scheduler.getusermeetings(
"alice@example.com",
localdate.now().plusdays(1)
);
system.out.println("alice 明天的会议:");
if (alicemeetings.isempty()) {
system.out.println(" 无会议");
} else {
alicemeetings.foreach(m -> system.out.println(" " + m));
}
// 尝试安排冲突的会议
system.out.println("\n4. 尝试安排冲突的会议:");
try {
set<string> participants2 = new hashset<>();
participants2.add("alice@example.com");
zoneddatetime conflictingtime = zoneddatetime.now()
.withzonesameinstant(zoneid.of("asia/shanghai"))
.plusdays(1)
.withhour(10)
.withminute(30)
.withsecond(0)
.withnano(0);
meeting meeting2 = scheduler.schedulemeeting(
"技术讨论",
duration.ofminutes(30),
participants2,
"bob@example.com",
conflictingtime
);
system.out.println("冲突会议安排成功: " + meeting2);
} catch (schedulingexception e) {
system.out.println("安排冲突会议失败(预期中): " + e.getmessage());
}
// 显示所有已安排的会议
system.out.println("\n5. 所有已安排的会议:");
list<meeting> allmeetings = scheduler.getscheduledmeetings();
if (allmeetings.isempty()) {
system.out.println(" 无会议");
} else {
allmeetings.foreach(m -> system.out.println(" " + m));
}
}
}10. 最佳实践与常见陷阱
10.1 最佳实践
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
import java.util.*;
import java.util.concurrent.concurrenthashmap;
public class bestpractices {
public static void main(string[] args) {
system.out.println("=== java 8 日期时间 api 最佳实践 ===");
// 1. 使用不可变对象
system.out.println("\n1. 使用不可变对象:");
localdate date = localdate.of(2023, 12, 25);
localdate modifieddate = date.plusdays(1); // 返回新对象,原对象不变
system.out.println("原日期: " + date);
system.out.println("修改后日期: " + modifieddate);
system.out.println("原日期未改变: " + date);
// 2. 线程安全
system.out.println("\n2. 线程安全:");
// 所有java.time类都是线程安全的,可以在多线程环境中共享
// 3. 明确的类型选择
system.out.println("\n3. 明确的类型选择:");
system.out.println("只有日期 -> 使用 localdate");
system.out.println("只有时间 -> 使用 localtime");
system.out.println("日期时间(无时区)-> 使用 localdatetime");
system.out.println("需要时区 -> 使用 zoneddatetime");
system.out.println("时间戳 -> 使用 instant");
system.out.println("持续时间 -> 使用 duration(基于时间)或 period(基于日期)");
// 4. 时区处理最佳实践
system.out.println("\n4. 时区处理最佳实践:");
// 存储时使用utc
instant utcinstant = instant.now();
system.out.println("存储时间(utc): " + utcinstant);
// 显示时转换为本地时区
zoneddatetime localtime = utcinstant.atzone(zoneid.systemdefault());
system.out.println("本地时间: " + localtime);
// 5. 格式化器缓存
system.out.println("\n5. 格式化器缓存:");
// 错误的做法:每次创建新格式化器
// datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd");
// 正确的做法:缓存格式化器
final datetimeformatter cached_formatter =
datetimeformatter.ofpattern("yyyy-mm-dd");
string formatted = localdate.now().format(cached_formatter);
system.out.println("格式化日期: " + formatted);
// 6. 避免空指针
system.out.println("\n6. 避免空指针:");
localdate nullabledate = getdatefromdatabase(); // 可能返回null
localdate safedate = optional.ofnullable(nullabledate)
.orelse(localdate.now());
system.out.println("安全日期: " + safedate);
// 7. 使用合适的api进行比较
system.out.println("\n7. 日期比较:");
localdate date1 = localdate.of(2023, 12, 25);
localdate date2 = localdate.of(2023, 12, 26);
// 正确的比较方式
system.out.println("date1是否在date2之前: " + date1.isbefore(date2));
system.out.println("date2是否在date1之后: " + date2.isafter(date1));
system.out.println("是否是同一天: " + date1.isequal(date2));
// 8. 处理闰年和月份天数
system.out.println("\n8. 处理闰年和月份天数:");
localdate feb28 = localdate.of(2023, 2, 28);
localdate feb28nextyear = localdate.of(2024, 2, 28);
system.out.println("2023-02-28 加一天: " + feb28.plusdays(1));
system.out.println("2024-02-28 加一天(闰年): " + feb28nextyear.plusdays(1));
// 安全的月份天数获取
localdate anydate = localdate.of(2023, 6, 1);
int daysinmonth = anydate.lengthofmonth();
system.out.println(anydate.getmonth() + " 月有 " + daysinmonth + " 天");
// 9. 持续时间和周期选择
system.out.println("\n9. duration vs period:");
// 基于时间的间隔使用duration
localtime starttime = localtime.of(9, 0);
localtime endtime = localtime.of(17, 30);
duration workduration = duration.between(starttime, endtime);
system.out.println("工作时间: " + workduration.tohours() + "小时");
// 基于日期的间隔使用period
localdate startdate = localdate.of(2023, 1, 1);
localdate enddate = localdate.of(2023, 12, 31);
period yearperiod = period.between(startdate, enddate);
system.out.println("期间: " + yearperiod.getmonths() + "个月" +
yearperiod.getdays() + "天");
// 10. 性能优化
system.out.println("\n10. 性能优化:");
// 批量操作时重用对象
localdate basedate = localdate.of(2023, 1, 1);
list<localdate> dates = new arraylist<>();
for (int i = 0; i < 100; i++) {
// 重用basedate而不是每次创建新对象
dates.add(basedate.plusdays(i));
}
system.out.println("生成了 " + dates.size() + " 个日期");
// 11. 异常处理
system.out.println("\n11. 异常处理:");
try {
// 无效日期
localdate invaliddate = localdate.of(2023, 13, 32);
system.out.println("无效日期: " + invaliddate);
} catch (datetimeexception e) {
system.out.println("捕获异常: " + e.getmessage());
}
// 安全的解析
string userinput = "2023-12-32";
localdate parseddate = parsedatesafe(userinput);
system.out.println("安全解析结果: " +
(parseddate != null ? parseddate : "解析失败"));
// 12. 与数据库交互
system.out.println("\n12. 与数据库交互:");
// 保存到数据库
instant dbtimestamp = instant.now();
system.out.println("保存到数据库的时间戳: " + dbtimestamp);
// 从数据库读取
localdatetime dbdatetime = localdatetime.ofinstant(dbtimestamp, zoneid.systemdefault());
system.out.println("从数据库读取的本地时间: " + dbdatetime);
}
private static localdate getdatefromdatabase() {
// 模拟数据库查询,可能返回null
return math.random() > 0.5 ? localdate.now() : null;
}
private static localdate parsedatesafe(string datestring) {
try {
return localdate.parse(datestring, datetimeformatter.iso_local_date);
} catch (datetimeexception e) {
return null;
}
}
// 常见陷阱
public static class commonpitfalls {
// 陷阱1:混淆localdatetime和zoneddatetime
public void pitfall1() {
// 错误:使用localdatetime表示带时区的时间
localdatetime localdatetime = localdatetime.now();
// 这实际上不包含时区信息!
// 正确:如果需要时区,使用zoneddatetime
zoneddatetime zoneddatetime = zoneddatetime.now(zoneid.of("asia/shanghai"));
}
// 陷阱2:错误的时区转换
public void pitfall2() {
zoneddatetime shanghaitime = zoneddatetime.now(zoneid.of("asia/shanghai"));
// 错误:直接修改时区,不转换时间
// zoneddatetime newyorktime = shanghaitime.withzonesamelocal(zoneid.of("america/new_york"));
// 正确:保持相同瞬间,转换时区
zoneddatetime newyorktime = shanghaitime.withzonesameinstant(zoneid.of("america/new_york"));
}
// 陷阱3:忽略夏令时
public void pitfall3() {
// 纽约的夏令时切换
localdatetime beforedst = localdatetime.of(2023, 3, 12, 1, 59);
localdatetime afterdst = localdatetime.of(2023, 3, 12, 3, 1);
zoneddatetime zdtbefore = zoneddatetime.of(beforedst, zoneid.of("america/new_york"));
zoneddatetime zdtafter = zoneddatetime.of(afterdst, zoneid.of("america/new_york"));
duration duration = duration.between(zdtbefore, zdtafter);
system.out.println("实际经过时间: " + duration.tominutes() + "分钟");
}
// 陷阱4:使用错误的单位
public void pitfall4() {
// 错误:使用duration表示长的时间间隔
// duration oneyear = duration.ofdays(365); // 忽略了闰年
// 正确:使用period
period oneyear = period.ofyears(1);
// duration适合基于时间的间隔(小时、分钟、秒)
// period适合基于日期的间隔(年、月、日)
}
// 陷阱5:线程安全的误区
public void pitfall5() {
// 虽然java.time类本身是线程安全的,但下面的代码不是:
// 错误:共享的可变状态
// class counter {
// private localdatetime lastupdate;
// public void update() {
// lastupdate = localdatetime.now(); // 需要同步
// }
// }
// 正确:使用不可变对象或适当同步
}
// 陷阱6:性能问题
public void pitfall6() {
// 错误:在循环中重复创建格式化器
// for (int i = 0; i < 1000; i++) {
// datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd");
// // ...
// }
// 正确:在循环外创建并重用
// datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd");
// for (int i = 0; i < 1000; i++) {
// // 使用formatter
// }
}
// 陷阱7:忽略纳秒精度
public void pitfall7() {
// 错误:假设所有时间都有纳秒精度
localdatetime dt1 = localdatetime.parse("2023-12-25t14:30:45");
localdatetime dt2 = localdatetime.parse("2023-12-25t14:30:45.123456789");
system.out.println("dt1纳秒: " + dt1.getnano()); // 0
system.out.println("dt2纳秒: " + dt2.getnano()); // 123456789
// 正确:根据需求处理精度
}
}
}10.2 常见问题解答
import java.time.*;
import java.time.format.datetimeformatter;
import java.time.temporal.chronounit;
import java.time.temporal.temporaladjusters;
import java.util.*;
public class faq {
public static void main(string[] args) {
system.out.println("=== java 8 日期时间 api 常见问题解答 ===");
// q1: 如何获取当前时间戳?
system.out.println("\nq1: 如何获取当前时间戳?");
instant timestamp = instant.now();
system.out.println("当前时间戳: " + timestamp);
system.out.println("毫秒时间戳: " + timestamp.toepochmilli());
// q2: 如何格式化日期时间?
system.out.println("\nq2: 如何格式化日期时间?");
localdatetime now = localdatetime.now();
datetimeformatter formatter = datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss");
string formatted = now.format(formatter);
system.out.println("格式化结果: " + formatted);
// q3: 如何解析字符串为日期时间?
system.out.println("\nq3: 如何解析字符串为日期时间?");
string datestr = "2023-12-25 14:30:45";
localdatetime parsed = localdatetime.parse(datestr, formatter);
system.out.println("解析结果: " + parsed);
// q4: 如何计算两个日期之间的天数?
system.out.println("\nq4: 如何计算两个日期之间的天数?");
localdate date1 = localdate.of(2023, 1, 1);
localdate date2 = localdate.of(2023, 12, 31);
long daysbetween = chronounit.days.between(date1, date2);
system.out.println(date1 + " 和 " + date2 + " 之间相差 " + daysbetween + " 天");
// q5: 如何添加或减去时间?
system.out.println("\nq5: 如何添加或减去时间?");
localdate today = localdate.now();
localdate tomorrow = today.plusdays(1);
localdate nextweek = today.plusweeks(1);
localdate lastmonth = today.minusmonths(1);
system.out.println("今天: " + today);
system.out.println("明天: " + tomorrow);
system.out.println("下周: " + nextweek);
system.out.println("上月: " + lastmonth);
// q6: 如何获取某月的最后一天?
system.out.println("\nq6: 如何获取某月的最后一天?");
localdate lastdayofmonth = today.with(temporaladjusters.lastdayofmonth());
system.out.println("本月最后一天: " + lastdayofmonth);
// q7: 如何处理时区?
system.out.println("\nq7: 如何处理时区?");
zoneddatetime shanghaitime = zoneddatetime.now(zoneid.of("asia/shanghai"));
zoneddatetime newyorktime = shanghaitime.withzonesameinstant(zoneid.of("america/new_york"));
system.out.println("上海时间: " + shanghaitime);
system.out.println("纽约时间: " + newyorktime);
// q8: 如何计算年龄?
system.out.println("\nq8: 如何计算年龄?");
localdate birthdate = localdate.of(1990, 5, 20);
int age = period.between(birthdate, today).getyears();
system.out.println("出生日期: " + birthdate);
system.out.println("年龄: " + age + "岁");
// q9: 如何检查闰年?
system.out.println("\nq9: 如何检查闰年?");
boolean isleapyear = today.isleapyear();
system.out.println(today.getyear() + "年" + (isleapyear ? "是" : "不是") + "闰年");
// q10: 如何获取星期几?
system.out.println("\nq10: 如何获取星期几?");
dayofweek dayofweek = today.getdayofweek();
system.out.println("今天是: " + dayofweek + " (" + dayofweek.getvalue() + ")");
// q11: 如何比较日期时间?
system.out.println("\nq11: 如何比较日期时间?");
localdatetime dt1 = localdatetime.of(2023, 12, 25, 14, 30);
localdatetime dt2 = localdatetime.of(2023, 12, 26, 10, 0);
system.out.println("dt1: " + dt1);
system.out.println("dt2: " + dt2);
system.out.println("dt1是否在dt2之前: " + dt1.isbefore(dt2));
system.out.println("dt2是否在dt1之后: " + dt2.isafter(dt1));
system.out.println("是否相等: " + dt1.equals(dt2));
// q12: 如何计算工作时间(排除周末)?
system.out.println("\nq12: 如何计算工作日?");
localdate start = localdate.of(2023, 12, 18); // 周一
localdate end = localdate.of(2023, 12, 25); // 下周一
long workingdays = calculateworkingdays(start, end);
system.out.println(start + " 到 " + end + " 的工作日数: " + workingdays);
// q13: 如何将date转换为localdatetime?
system.out.println("\nq13: 如何将date转换为localdatetime?");
date olddate = new date();
localdatetime newdatetime = localdatetime.ofinstant(olddate.toinstant(), zoneid.systemdefault());
system.out.println("date: " + olddate);
system.out.println("localdatetime: " + newdatetime);
// q14: 如何处理不同的日期格式?
system.out.println("\nq14: 如何处理不同的日期格式?");
string[] dateformats = {"yyyy-mm-dd", "dd/mm/yyyy", "mm/dd/yyyy", "yyyymmdd"};
string datestring = "25/12/2023";
for (string format : dateformats) {
try {
datetimeformatter fmt = datetimeformatter.ofpattern(format);
localdate date = localdate.parse(datestring, fmt);
system.out.println("成功解析 (" + format + "): " + date);
break;
} catch (exception e) {
// 继续尝试下一个格式
}
}
// q15: 如何获取季度信息?
system.out.println("\nq15: 如何获取季度信息?");
int quarter = (today.getmonthvalue() - 1) / 3 + 1;
system.out.println("当前季度: q" + quarter);
// q16: 如何生成一段时间内的所有日期?
system.out.println("\nq16: 如何生成一段时间内的所有日期?");
list<localdate> dates = new arraylist<>();
localdate current = start;
while (!current.isafter(end)) {
dates.add(current);
current = current.plusdays(1);
}
system.out.println("生成 " + dates.size() + " 个日期");
// q17: 如何计算时间差(时、分、秒)?
system.out.println("\nq17: 如何计算时间差?");
localtime time1 = localtime.of(9, 0);
localtime time2 = localtime.of(17, 30);
duration duration = duration.between(time1, time2);
system.out.println("时间差: " + duration.tohours() + "小时" +
duration.tominutespart() + "分钟");
// q18: 如何处理夏令时?
system.out.println("\nq18: 如何检查夏令时?");
zoneid zone = zoneid.of("america/new_york");
boolean isdst = zone.getrules().isdaylightsavings(instant.now());
system.out.println(zone + " 当前是否夏令时: " + isdst);
// q19: 如何获取当前月份的天数?
system.out.println("\nq19: 如何获取当前月份的天数?");
int daysinmonth = today.lengthofmonth();
system.out.println(today.getmonth() + " 月有 " + daysinmonth + " 天");
// q20: 如何安全地处理用户输入的日期?
system.out.println("\nq20: 如何安全地处理用户输入的日期?");
string userinput = "2023-13-32"; // 无效日期
localdate safedate = parsedatesafe(userinput, localdate.now());
system.out.println("用户输入: " + userinput);
system.out.println("安全处理结果: " + safedate);
}
private static long calculateworkingdays(localdate start, localdate end) {
long days = 0;
localdate date = start;
while (!date.isafter(end)) {
dayofweek dayofweek = date.getdayofweek();
if (dayofweek != dayofweek.saturday && dayofweek != dayofweek.sunday) {
days++;
}
date = date.plusdays(1);
}
return days;
}
private static localdate parsedatesafe(string datestr, localdate defaultvalue) {
try {
return localdate.parse(datestr, datetimeformatter.iso_local_date);
} catch (exception e) {
return defaultvalue;
}
}
// 总结:关键要点
public static class keypoints {
/*
1. 选择正确的类:
- localdate: 只有日期
- localtime: 只有时间
- localdatetime: 日期+时间(无时区)
- zoneddatetime: 日期+时间+时区
- instant: 时间戳
2. 所有类都是不可变且线程安全的
3. 使用工厂方法(of(), parse(), now())而不是构造函数
4. 使用with()方法修改,plus()/minus()方法计算
5. 使用isbefore(), isafter(), isequal()进行比较
6. 格式化使用datetimeformatter,解析使用parse()方法
7. 时区转换使用withzonesameinstant()
8. 时间间隔:duration用于时间,period用于日期
9. 复杂日期调整使用temporaladjusters
10. 与旧api互操作:通过instant转换
*/
}
}总结
java 8 的日期时间 api 提供了一个强大、直观且线程安全的框架来处理日期和时间。通过本详解,我们涵盖了:
核心概念:localdate、localtime、localdatetime、zoneddatetime、instant
时间计算:duration、period、chronounit
时区处理:zoneid、zoneoffset、时区转换
格式化解析:datetimeformatter、自定义模式
日期调整:temporaladjusters、自定义调整器
互操作性:与旧版 date、calendar 的转换
实战应用:完整的工具类和业务示例
最佳实践:性能优化、错误处理、常见陷阱
核心优势:
不可变且线程安全:无需担心并发问题
清晰的api设计:方法名直观,链式调用
完善的时区支持:内置夏令时处理
丰富的操作:提供大量日期时间计算方法
良好的性能:优化过的实现
迁移建议:
新项目直接使用 java.time api
旧项目逐步迁移,使用兼容方法转换
数据库交互使用 instant 或 localdatetime
日志和序列化使用 iso 格式
到此这篇关于java8中处理日期和时间的文章就介绍到这了,更多相关java8处理日期和时间内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论