使用方法
- 在事件列表里每行写一条事件,格式是「日期 空格 标题」,需要备注再接「|备注」。默认示例是 6 条产品里程碑,从
2024-01-15 项目立项到2026-10-03 第二版规划。 - 选排序方式:升序(默认)、降序或保持输入顺序。同一日期的多条事件在任何排序下都保持你输入的先后次序。
- 按 Ctrl/Cmd + Enter 或点「生成时间线」。结果区输出编号、ISO 日期、中文长日期、星期与标题;有备注的行会另起一行缩进展示。
- 看统计条里的跨度:它是首末两个日期的自然日差(不含首日),示例 6 条的跨度是 992 天。哪条是首日哪条是末日由日期决定,与排序方式无关。
- 「复制 Markdown」输出
- **2024-01-15** 项目立项形式的列表,可直接贴进 README;「复制对照表」「导出 CSV」作用于下方的日期写法对照表。
这个时间线生成工具能解决什么问题
一是把流水账变成有序的时间线。项目复盘、产品迭代记录、投标业绩罗列、个人履历里的项目经历,原始材料几乎都是想到哪写到哪。这个时间线生成工具按日期重排后给出编号、长日期与星期,直接可贴进周报或文档。
二是解决「日期少一天」的诡异现象。用 new Date('2026-10-03') 解析日期再取 getDate(),在西半球的机器上会得到 2 号——因为 ECMAScript 规定 date-only 字符串按 UTC 零点解析,本地时区一减就退回前一天。本页从解析到显示全链路固定 UTC,同一份输入在任何时区生成同一份时间线,协作文档里不会出现两个人看到不同日期。
三是把不统一的日期写法收拢成一种。收集来的材料里 2026/10/3、2026.10.03、2026年10月3日 混在一起,工具统一解析后输出统一的 YYYY-MM-DD 与「2026年10月3日(周六)」两种口径,排序也才有依据。
四是提供能直接二次加工的 Markdown。时间线的最终去处往往是 Markdown 文档、Notion 或_issue 列表,「复制 Markdown」按钮输出标准列表项,标题加粗、备注跟在后面,不用再手动排版。
这个时间线生成工具不做日期差与工作日计算,两个日期之间隔多少个工作日请用「日期时间」分类的专门工具;也不做时间戳与本地时间的互转,那是另一套带时区的口径;更不做图表绘制——饼图、条形图、行图谱各自有专门的生成页。本页只负责把带日期的事件序列变成文本时间线。
原理与规则说明:UTC 口径、稳定排序与闰年校验
解析是手工正则,不走 Date.parse。每行先用 split('|') 拆出备注,再对标题段用 /^\s*(\S+)\s+(.*)$/ 拆出日期 token 与标题,日期 token 交给 /^(\d{4})[-\/.](\d{1,2})[-\/.](\d{1,2})$/ 或 /^(\d{4})年(\d{1,2})月(\d{1,2})日$/ 两把正则。之所以不用 new Date('2026-10-03'),是因为 ECMAScript 的 Date Time String Format 只保证 YYYY-MM-DD(月日两位)被按 UTC 解析,而 2026-10-3、2026/10/3 这类写法属于实现相关的回退分支,不同浏览器可能给出不同结果甚至 NaN。
组装用 Date.UTC(y, m-1, d),显示用 Intl.DateTimeFormat('zh-CN', { timeZone: 'UTC' })。入口是手工拆出的整数,出口固定 timeZone: 'UTC',整条链路里没有任何本地时区参与。长日期走 year:'numeric', month:'long', day:'numeric' 得到「2026年10月3日」,星期走 weekday:'short' 得到「周六」——月份名、星期名全部复用 CLDR 数据,不手写 ['日','一','二'…] 查找表,也天然支持将来切换语言。
排序用 Array.prototype.sort 加时间戳比较。比较函数是 x.t - y.t,其中 t 是 Date.UTC 得到的毫秒数,整数相减没有精度问题(日期范围 1900~2100 年,时间戳约 ±4.1e12,远小于 253)。ECMAScript 2019 起明确要求 sort 必须稳定,所以同一天的多条事件在升序、降序、保持原序三种模式下都不会被打乱输入时的相对顺序——这是拿时间线讲年表时的实际需求。
闰年与每月天数用「当月最后一天」反推。不手写 y%4==0&&(y%100!=0||y%400==0),而是看 new Date(Date.UTC(y, m, 0)).getUTCDate()——第 m 月的第 0 天就是上个月最后一天,让引擎自己处理大小月与闰年。实测:2024 年 2 月 29 天、2026 年 2 月 28 天、1900 与 2100 年 2 月都是 28 天(整百年需被 400 整除才是闰年),所以 2024-02-29 合法而 2026-02-29、2100-02-29 都会被拒绝。
跨度是天数差,不含首日。末日 − 首日 的毫秒差除以 86400000 后取整。示例首日 2024-01-15、末日 2026-10-03,跨度 992 天;含首尾则是 993 天。本页统一用不含首日的口径,因为「项目从立项到规划走了多久」的自然理解是差值。
输出的体积上限是刻意设计的。200 条事件 × 每条约 60 字符 ≈ 1.2 万字符,一次 textContent 赋值在一帧内完成;超过 200 条直接拒绝并提示拆分,而不是悄悄渲染一份要滚动很久的结果。
完整示例(数值仅作演示)
示例条件与页面打开时的默认值完全一致:6 条事件、按日期升序。复算结果如下:
- 解析成功 6 行,其中
2025-11-05 正式发布|全渠道同步带一条备注; - 首日 2024-01-15(2024年1月15日,周一),末日 2026-10-03(2026年10月3日,周六),跨度 992 天;
- 星期为真值:2024-06-30 是周日、2025-03-12 与 2025-11-05 都是周三、2026-04-20 是周一;
- 输出文本共 252 个字符(含备注行与跨度行),一次赋值即可渲染;
- 切到「降序」时 #1 变成 2026-10-03,但跨度仍是 992 天——跨度只取决于首末日期,与展示顺序无关。
结果区里的文本就是上面这份输出的原样,Markdown 口径则是:
- **2024-01-15** 项目立项 - **2024-06-30** 原型验收 - **2025-03-12** 内测上线 - **2025-11-05** 正式发布(备注:全渠道同步) - **2026-04-20** 用户破十万 - **2026-10-03** 第二版规划
下面是 16 种常见日期写法在同一套解析器下的判定,前 12 行默认显示,点「加载更多」展开剩余 4 行:
| 输入写法 | 判定 | 解析结果 | 星期 | 说明 |
|---|---|---|---|---|
| 2026-10-03 | 可解析 | 2026年10月3日 | 周六 | 标准写法,月日两位 |
| 2026-10-3 | 可解析 | 2026年10月3日 | 周六 | 月日允许 1 位 |
| 2026/10/03 | 可解析 | 2026年10月3日 | 周六 | 斜杠分隔可用 |
| 2026.10.03 | 可解析 | 2026年10月3日 | 周六 | 点分隔可用 |
| 2026年10月3日 | 可解析 | 2026年10月3日 | 周六 | 中文年月日可用 |
| 2026-2-3 | 可解析 | 2026年2月3日 | 周二 | 1 位月日,输出统一补零 |
| 2024-02-29 | 可解析 | 2024年2月29日 | 周四 | 闰年成立 |
| 2100-12-31 | 可解析 | 2100年12月31日 | 周五 | 本页年份上限 |
| 2026-13-01 | 拒绝 | — | — | 月份 13 超出 1~12 |
| 2026-02-30 | 拒绝 | — | — | 2026 年 2 月只有 28 天 |
| 2026-04-31 | 拒绝 | — | — | 4 月只有 30 天 |
| 1899-12-31 | 拒绝 | — | — | 年份早于 1900 |
| 2100-02-29 | 拒绝 | — | — | 2100 非闰年:整百年须被 400 整除 |
| 明天 | 拒绝 | — | — | 不支持相对日期 |
| 2026-10-3 项目启动|立项会 | 可解析 | 2026年10月3日 | 周六 | 整行写法:日期 + 标题 + 备注 |
| 项目启动 | 拒绝 | — | — | 行首不是日期 |
输入范围与对照表
输入项的范围与超限动作如下,右列是填错时结果区会显示的具体提示:
| 字段/情况 | 允许范围 | 超限或填错时的动作 |
|---|---|---|
| 事件列表 | 1~200 行非空行 | 空文本拒绝;超过 200 条拒绝并报出实际条数 |
| 日期写法 | YYYY-M-D、YYYY/M/D、YYYY.M.D、YYYY年M月D日 四种 | 不可识别时报出第几行与原文,并列出四种支持写法 |
| 年份 | 1900~2100 | 越界时报出第几行与实际年份 |
| 月份 | 1~12 | 越界时报出第几行与实际月份 |
| 日 | 1~当月天数 | 不存在时报出该月实际天数,如「2026 年 2 月只有 28 天」 |
| 标题 | 1~80 字符 | 缺失或超长时报出第几行与实际长度 |
| 备注 | 0~60 字符,可省略 | 超长时报出第几行与实际长度 |
| 排序方式 | 升序 / 降序 / 保持输入顺序 | 下拉框只接受预设值;异常值回落到升序并提示 |
本页与日期、时间类兄弟工具的口径区隔:
| 需求 | 本页的做法 | 不该用本页的情况 |
|---|---|---|
| 把带日期的事件排成时间线 | 正是本页做的事 | — |
| 算两个日期差几天、几个工作日 | 只给首末跨度(自然日差),不做工作日剔除 | 需要工作日口径时用日期计算类工具 |
| 时间戳与日期互转 | 不处理秒/毫秒时间戳 | 涉及 Unix 时间戳时用时间戳类工具 |
| 展示某个时刻的本地时间 | 刻意固定 UTC,避免本地时区参与 | 要按时区显示时刻的场景不适用本页 |
| 同一天多条事件的先后 | 保持输入顺序(稳定排序) | — |
边界与精度说明
- 年份下限 1900、上限 2100。下限是为了避开老引擎里「两位年份被补成 19xx」的历史行为;上限是因为
Intl.DateTimeFormat对超远年份的历法换算依赖 CLDR 数据,2100 年内公历规则稳定。超出即拒绝,不做静默截断。 - 整百年的闰年判定交给引擎。1900 与 2100 都不是闰年(能被 100 整除但不能被 400 整除),2000 与 2400 是闰年。
Date.UTC(y, m, 0)反推当月天数的写法在这些边界上自动正确,不需要也不应该再手写一层%4判断。 - 时间戳精度上限远离 253。2100-12-31 的 UTC 毫秒数约为 4.1×1012,与安全整数上限 9007199254740991 相差 6 个数量级,加减乘除都不会丢精度;跨度计算是两个时间戳相减再除以 86400000,结果必然是整数或因输入非法而被提前拒绝。
- 日期不含时间部分。「2024-01-15」指的是那一天,而不是那天零点;本页不做任何时刻运算,因此也不存在夏令时跳变问题——某些时区在夏令时切换当天只有 23 或 25 小时,如果用本地午夜做差值就会差一小时,UTC 口径从根上避开了它。
- 排序稳定性是规范要求。ECMAScript 2019 起
sort必须稳定,同日多条事件的相对顺序在三种排序模式下都不变;若在更老的环境(如 IE)里运行,稳定性无保证,这就是把「保持输入顺序」做成显式选项的原因之一。 - 备注只取第一个
|之后的一段。一行里出现多个|时,第二个及之后的内容会被忽略并提示,避免把多列数据悄悄拼进备注。 - 输出是纯文本与 Markdown,不是 HTML 标签。结果区不生成
<ul>之类标签,贴进富文本编辑器也不会出现尖括号原文;需要网页里真正的时间轴组件时应使用前端组件库而不是本页。 - 拒绝全部非法行,不做「跳过错误继续」。只要有一行解析失败,整次生成就不输出,错误信息逐行列出行号与原文。宁可让你改完再生成,也不给一份悄悄少了几条的时间线。
常见问题
为什么别的工具算出来的日期会少一天,这里不会?
少一天的工具几乎都在某一步用了 new Date('2026-10-03') 然后按本地时间取值。ECMAScript 规定这种 date-only 字符串按 UTC 零点解析,机器时区是 UTC-5 时,本地时间还是 10 月 2 日晚上 7 点,getDate() 就成了 2。本页把日期拆成整数、用 Date.UTC 组装、用 timeZone:'UTC' 的 Intl.DateTimeFormat 显示,全程没有本地时区插手,结果与机器设置无关。
同一天的两条事件会不会被排序打乱?
不会。ECMAScript 2019 规定 Array.prototype.sort 是稳定排序,比较函数相等时保持原有顺序。示例里若再加一条 2025-11-05 发布会复盘,无论升序降序它都会排在「正式发布」后面。若担心运行环境太老,可以直接选「保持输入顺序」。
跨度 992 天是怎么算的?含首尾吗?
跨度 = 末日 − 首日的自然日差,即两个 Date.UTC 时间戳之差除以 86400000,不含首日。2024-01-15 到 2026-10-03 是 992 天;如果你要含首尾(例如「项目进行了 993 天」的口径),把结果加 1 即可。本页统一用差值口径,避免两种说法在页面里混用。
2026-02-29 明明格式对,为什么被拒绝?
格式对不等于日期存在。2026 年不是闰年,2 月只有 28 天,所以 2026-02-29 会被拒绝并提示该月实际天数;2024-02-29 则正常解析。判断方式不是手写 %4 公式,而是看 Date.UTC(2026, 2, 0) 的日值——第 3 月的第 0 天即 2 月最后一天,整百年如 2100(非闰年)也自动正确。
一行里能写多个备注吗?
不能。备注只取第一个 | 之后的整段,后面再出现 | 会被忽略并提示。这样设计是因为时间线是「一事件一备注」的结构,多列数据应该拆成多行,而不是把标签、负责人、状态都塞进一个备注字段里。
能导入 Excel 里的日期吗?
可以,但要注意 Excel 常把日期存成 2026/10/3 或带时刻的 2026/10/3 0:00。前者本页直接支持;后者行首 token 是 2026/10/3、标题会被解析成 0:00,需要先删掉时刻列再粘贴。带千分位引号或合并单元格的表格,先另存为 CSV 再复制日期与标题两列。
为什么某一行写错了,整份时间线都不出了?
故意的。若只跳过错误行,你拿到的时间线会悄悄少几条,事后很难发现少了什么;本页选择整次拒绝,并把每一行的行号、原文、错因列出来,改完再生成一次即可。错误信息会具体到「第 3 行开头的『xx』不是可识别的日期写法」这种粒度。
输出能直接贴进 Markdown 文档吗?
「复制 Markdown」按钮输出的就是标准列表项:- **2024-01-15** 项目立项,带备注时追加「(备注:xxx)」,直接粘进 README、Issue 或 Notion 都能正常渲染加粗。结果区里的带编号版本更适合贴进聊天窗口或邮件正文,两种口径随取随用。