应使用 localdatetime 而非 date 存储日程时间,因其不可变、线程安全、语义清晰;避免混用 zoneddatetime 或 instant,用户输入按本地时区解析为 localdatetime,用 datetimeformatter 安全解析,重复日程存 rrule 规则而非多条记录,提醒依赖数据库持久化轮询。

用 LocalDateTime 而不是 Date 存储日程时间
Java 8 引入的 LocalDateTime 是不可变、线程安全、语义清晰的选择。老式 Date 类既可变又带时区隐含逻辑,容易在序列化、比较、加减操作中出错。
实际设计中,日程的「开始时间」和「结束时间」应统一用 LocalDateTime,避免混用 ZonedDateTime 或 Instant——除非你明确需要跨时区调度(比如全球会议提醒)。
- 用户输入的时间默认按本地时区解释,存为
LocalDateTime即可 - 做时间比较(如判断是否已过期)直接用
isBefore()/isAfter() - 需要计算持续分钟数:用
Duration.between(start, end).toMinutes() - 别用
new Date().getTime() - otherDate.getTime()手动算毫秒差——易溢出且不直观
如何用 DateTimeFormatter 安全解析用户输入的时间字符串
用户可能输入 “2024-05-20 14:30”、“今天 15:00”、“明天上午9点” 等格式,但 DateTimeFormatter 只处理标准模式,不支持自然语言。
真实项目中建议分层处理:
立即学习“Java免费学习笔记(深入)”;
- 前端或 CLI 层先做简单归一化:把“今天”→替换为当前日期,“明天”→+1天,再交给后端解析
- 后端只接收 ISO 格式(如
"yyyy-MM-dd HH:mm")或严格定义的自定义格式 - 用
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")解析,捕获DateTimeParseException并返回具体错误位置 - 禁止用
SimpleDateFormat——它非线程安全,多线程下会静默解析错乱
重复日程(如每周一 10:00)该不该存成多条记录?
不该。存成一条带规则的记录(RecurrenceRule),运行时按需生成实例,否则数据爆炸且难以修改规则。
推荐轻量方案:用字符串存 ICS 标准的 RRULE 简化版,例如 "FREQ=WEEKLY;BYDAY=MO;UNTIL=20241231",解析时用开源库 ical4j 或手动提取关键字段。
- 数据库字段类型选
VARCHAR(255),别用 JSON 字段——增加查询负担 - 查询「未来7天内所有日程实例」时,不要 SELECT * 再内存过滤;而是用 Java 计算出这7天对应的全部
LocalDateTime,再用 IN 查询 - 用户修改某次实例(如“跳过本周会议”),要单独建一张
exception_instances表标记例外,而不是删原规则
定时提醒怎么避免漏触发或重复触发?
用内存定时器(如 ScheduledThreadPoolExecutor)只适合单机轻量场景。一旦服务重启或扩容,未触发的提醒就丢了。
生产环境必须依赖持久化 + 轮询机制:
- 把待提醒日程的
trigger_time(LocalDateTime)存进数据库,并建索引 - 写一个后台任务,每30秒查一次
WHERE trigger_time - 查到后用
SELECT ... FOR UPDATE锁住这批记录,更新为'processing',再发通知 - 通知成功后才设为
'done';失败则重试计数+告警,别直接丢弃
这个逻辑看似啰嗦,但比任何“精准到毫秒”的调度器都可靠——因为真正难的不是计算时间,而是确保状态一致。










