JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

java 日期时间API(java的日期函数)

wys521 2025-04-30 20:28:29 精选教程 9 ℃ 0 评论

以下是Java日期时间API的全面解析,涵盖新旧API对比、核心类使用、时区处理及最佳实践:


一、Java日期时间演进史




二、新旧API核心对比

特性

java.util.Date

java.time API

可变性

可变(setTime方法)

不可变(线程安全)

月份表示

0-11(反直觉)

1-12(符合现实)

时区处理

依赖Calendar

内置ZoneId系统

格式化

SimpleDateFormat

DateTimeFormatter

扩展性

无法扩展

支持自定义调节器

纳秒精度

不支持

支持到纳秒级


三、核心类体系结构




四、基础操作全解析

1. 日期创建与获取

// 获取当前日期时间
LocalDate today = LocalDate.now();
LocalTime currentTime = LocalTime.now();
LocalDateTime currentDateTime = LocalDateTime.now();

// 指定日期
LocalDate nationalDay = LocalDate.of(2023, Month.OCTOBER, 1);
LocalTime preciseTime = LocalTime.of(14, 30, 45, 123_456_789);

// 解析字符串
LocalDate parsedDate = LocalDate.parse("2023-08-15");
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-08-15T20:30:45");

2. 日期时间运算

// 加减操作
LocalDateTime nextWeek = currentDateTime.plusWeeks(1);
LocalDate prevMonth = today.minus(Period.ofMonths(1));

// 使用TemporalAdjusters
LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate nextWorkingDay = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

// 自定义调节器
LocalDate customAdjust = today.with(t -> {
    LocalDate date = (LocalDate) t;
    return date.getDayOfWeek() == DayOfWeek.FRIDAY ? date.plusDays(3) : date.plusDays(1);
});

3. 时段与周期计算

// 计算两个日期之间
Period period = Period.between(LocalDate.of(2023, 1, 1), today);
System.out.printf("间隔:%d年%d月%d日%n", 
    period.getYears(), period.getMonths(), period.getDays());

// 精确时间差
Duration duration = Duration.between(
    LocalTime.of(8, 0), 
    LocalTime.of(17, 30)
);
System.out.println("工作时间:" + duration.toHours() + "小时");

五、时区处理深度剖析

1. 时区转换示例

// 上海时间转纽约时间
ZonedDateTime shanghaiTime = ZonedDateTime.of(
    LocalDateTime.parse("2023-08-15T09:00"), 
    ZoneId.of("Asia/Shanghai")
);

ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(
    ZoneId.of("America/New_York")
);
// 结果:2023-08-14T21:00-04:00[America/New_York]

2. 夏令时处理

// 测试夏令时转换(纽约时区)
ZonedDateTime beforeDst = ZonedDateTime.of(
    LocalDateTime.of(2023, 3, 12, 1, 30),
    ZoneId.of("America/New_York")
);

ZonedDateTime afterDst = beforeDst.plusHours(1);
// 结果:2023-03-12T03:30-04:00(跳过2点到3点)

六、格式化与解析

1. 预定义格式

// ISO标准格式
String isoDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE);

// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter
    .ofPattern("yyyy-MM-dd HH:mm:ss")
    .withZone(ZoneId.systemDefault());

String formatted = LocalDateTime.now().format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2023-08-15 14:30:00", formatter);

2. 本地化格式

DateTimeFormatter frenchFormatter = DateTimeFormatter
    .ofLocalizedDateTime(FormatStyle.FULL)
    .withLocale(Locale.FRANCE);

ZonedDateTime.now().format(frenchFormatter);
// 输出:"mercredi 16 ao^ut 2023 à 15:30:00 heure normale de la Chine"

七、与传统API互操作

// Date转LocalDateTime
Date legacyDate = new Date();
LocalDateTime newDateTime = legacyDate.toInstant()
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime();

// Calendar转ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime fromCalendar = ZonedDateTime.ofInstant(
    calendar.toInstant(), 
    calendar.getTimeZone().toZoneId()
);

八、最佳实践指南

1. 存储与传输建议

  • 数据库存储:使用TIMESTAMP WITH TIME ZONE类型
  • API传输:统一使用ISO-8601格式(如2023-08-15T14:30:45+08:00
  • 序列化:Jackson配置示例:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> {
        builder.serializers(new LocalDateSerializer(DateTimeFormatter.ISO_DATE));
        builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ISO_DATE_TIME));
    };
}

2. 性能优化

// 重用不可变对象
private static final DateTimeFormatter CACHED_FORMATTER 
    = DateTimeFormatter.ofPattern("yyyy/MM/dd");

// 避免频繁创建ZoneId
private static final ZoneId UTC_ZONE = ZoneId.of("UTC");

3. 常见陷阱规避

// 错误:忽略时区信息
LocalDateTime local = LocalDateTime.now();
ZonedDateTime zoned = local.atZone(ZoneId.systemDefault()); // 正确关联时区

// 错误:直接比较瞬时时间
Instant inst1 = Instant.now();
Instant inst2 = Instant.now().plusSeconds(1);
assert inst1.isBefore(inst2); // 正确比较方式

九、高级应用场景

1. 工作日计算

public static LocalDate addWorkingDays(LocalDate start, int days) {
    return Stream.iterate(start, date -> date.plusDays(1))
        .filter(date -> date.getDayOfWeek().getValue() < 6)
        .skip(days)
        .findFirst()
        .orElseThrow();
}

2. 生日提醒系统

public static List<LocalDate> getUpcomingBirthdays(LocalDate today, List<LocalDate> birthdays) {
    return birthdays.stream()
        .map(bd -> bd.withYear(today.getYear()))
        .filter(bd -> {
            Period period = Period.between(today, bd);
            return period.getMonths() == 0 && period.getDays() >= 0 && period.getDays() <= 7;
        })
        .sorted()
        .collect(Collectors.toList());
}

十、高频面试问题

Q1:为什么需要新的日期API?

  • 旧API存在设计缺陷(可变性、月份从0开始等)
  • 缺乏直观的日期计算API
  • 时区处理复杂且易错
  • 线程安全问题(如SimpleDateFormat)

Q2:LocalDateTime与ZonedDateTime区别?

  • LocalDateTime:不带时区的日期时间(如会议时间)
  • ZonedDateTime:带完整时区信息(如国际航班时刻)

Q3:如何计算两个日期间的工作日?

long workDays = startDate.datesUntil(endDate)
    .filter(d -> d.getDayOfWeek().getValue() < 6)
    .count();

掌握Java日期时间API的关键在于:

  1. 理解不可变设计哲学
  2. 严格区分时间概念(本地时间、时区时间、瞬时时间)
  3. 熟练使用TemporalAdjusters进行复杂计算
  4. 正确处理时区和夏令时问题

建议在实际项目中:

  • 完全弃用java.util.DateCalendar
  • 统一使用java.time进行所有日期时间操作
  • 建立严格的日期格式规范

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表