类型转换与日期处理 · 把最容易踩坑的细节吃透
第 3 章 · 第 4 节
写表达式时60% 的”为什么结果不对”都是类型问题。这一节专门讲:JS 的类型陷阱、n8n 的类型自动转换习惯、日期时区的坑。
JS 的类型陷阱(先复习)
Section titled “JS 的类型陷阱(先复习)” ⚠ js-quirks.txt
// 字符串和数字混合
"3" + 2 → "32" (字符串拼接!)
"3" - 2 → 1 (减号会强制转数字)
"3" * 2 → 6
Number("3") + 2 → 5
// 真值判断
Boolean(0) → false
Boolean("") → false
Boolean("0") → true (注意!非空字符串都是 true)
Boolean("false") → true (字符串 "false" 也是 true!)
Boolean(null) → false
Boolean(undefined)→ false
Boolean([]) → true (空数组也是 true)
Boolean({}) → true (空对象也是 true)
// 相等比较
"3" == 3 → true (== 会强转,别用)
"3" === 3 → false (=== 严格比较,推荐)
null == undefined → true (== 唯一例外)
null === undefined→ false
永远用 === 比较,别用 ==。
n8n 字段类型常见来源
Section titled “n8n 字段类型常见来源”每个上游节点输出的字段类型不同,经常需要显式转换:
🔤 common-types.txt
数据源 字段 类型
───── ──── ────
Google Sheets 单元格 string(即使是数字!)
Webhook query string ?id=123 string
Webhook JSON body { "id": 123 } number
Database (SQL) 数字字段 number
Database (SQL) 日期字段 ISO string
Manual Trigger 无 $json 是 {}
教训:从 Sheets / Webhook query 来的数字,必须先转!
安全的类型转换
Section titled “安全的类型转换” 🔁 type-cast.txt
// 字符串 → 数字
Number("123") → 123
Number("123abc") → NaN (不是 0!)
parseInt("123abc") → 123
parseFloat("12.5kg") → 12.5
// 处理 NaN
Number("abc") || 0 → 0 (兜底)
Number("0") || 0 → 0 (注意 "0" 转出来是 0,假值!)
isNaN(Number("abc")) → true (判断转失败)
// 数字 → 字符串
String(123) → "123"
(123).toString() → "123"
`${123}` → "123"
// 任意 → 布尔
Boolean("hello") → true
!!"hello" → true (简写)
!!"" → false
// 字符串 → 布尔(严格意义上的解析)
"true" === "true" → true
JSON.parse("true") → true
JSON.parse("false") → false
日期处理:用 Luxon
Section titled “日期处理:用 Luxon”n8n 注入了 Luxon —— 现代 JS 的日期库。不要用原生 new Date(),除非你乐意调时区 bug。
获取当前时间
Section titled “获取当前时间” 📅 luxon-now.txt
$now → Luxon DateTime(当前时刻,含 n8n 实例时区)
$today → 今天 00:00
$now.toISO() → "2026-05-13T10:23:45.123+08:00"
$now.toUTC() → 转 UTC
$now.toMillis() → Unix 毫秒时间戳
$now.toSeconds() → Unix 秒时间戳
📅 luxon-parse.txt
// ISO 字符串
DateTime.fromISO("2026-05-13T10:23:45Z")
// 自定义格式
DateTime.fromFormat("13/05/2026", "dd/MM/yyyy")
DateTime.fromFormat("2026 年 5 月 13 日", "yyyy 年 M 月 d 日", { locale: "zh-CN" })
// Unix 时间戳
DateTime.fromMillis(1715000000000)
DateTime.fromSeconds(1715000000)
// 检查解析是否成功
const dt = DateTime.fromISO("invalid")
dt.isValid → false
dt.invalidReason → "unparsable"
📅 luxon-math.txt
$now.plus({ days: 7 }) → 7 天后
$now.plus({ hours: 3, minutes: 30 })
$now.minus({ months: 1 }) → 1 个月前
$now.startOf("day") → 今天 00:00:00
$now.startOf("month") → 本月 1 号 00:00
$now.startOf("week") → 本周一 00:00
$now.endOf("day") → 今天 23:59:59.999
// 两个时间差
const d1 = $now
const d2 = $now.minus({ days: 3 })
d1.diff(d2, "days").days → 3
d1.diff(d2, ["days", "hours"]) → { days: 3, hours: 0 }
📅 luxon-format.txt
$now.toFormat("yyyy-MM-dd") → "2026-05-13"
$now.toFormat("HH:mm:ss") → "10:23:45"
$now.toFormat("yyyy-MM-dd HH:mm") → "2026-05-13 10:23"
$now.toFormat("yyyy 年 M 月 d 日") → "2026 年 5 月 13 日"
$now.toFormat("EEEE") → "Wednesday"
$now.toFormat("EEEE", { locale: "zh-CN" }) → "星期三"
// ISO 各种变体
$now.toISO() → 完整 ISO
$now.toISODate() → 只日期 "2026-05-13"
$now.toISOTime() → 只时间
🌍 luxon-tz.txt
// 切到指定时区
$now.setZone("Asia/Shanghai") → 上海时区
$now.setZone("America/New_York") → 纽约时区
$now.setZone("UTC") → UTC
// 查看当前时区
$now.zoneName → "Asia/Shanghai"
$now.offset → 480 (UTC+8 分钟数)
一些常翻车的小问题
Section titled “一些常翻车的小问题”问题 1 · “我从 Sheets 读出来的数字加 1 变成字符串拼接”
Section titled “问题 1 · “我从 Sheets 读出来的数字加 1 变成字符串拼接”” ❌ trap-sheets.txt
从 Sheets 来:$json.count = "5" (字符串!)
错误写法:
={{ $json.count + 1 }} → "51" ❌
正确写法:
={{ Number($json.count) + 1 }} → 6 ✅
={{ +$json.count + 1 }} → 6 ✅ (前缀 + 是简写)
问题 2 · “字段可能不存在导致整个表达式爆”
Section titled “问题 2 · “字段可能不存在导致整个表达式爆”” ❌ trap-missing.txt
错误:
={{ $json.user.email.toLowerCase() }}
→ 当 user 或 email 缺失时:TypeError: Cannot read properties of undefined
正确:
={{ $json.user?.email?.toLowerCase() ?? '(no email)' }}
→ 安全链 + 默认值
问题 3 · “对象用 == 比较结果不对”
Section titled “问题 3 · “对象用 == 比较结果不对”” ❌ trap-object-eq.txt
错误:
={{ $json.tags == ['a', 'b'] }} → false (对象比较引用,不比值)
正确:
={{ JSON.stringify($json.tags) === JSON.stringify(['a', 'b']) }} ✅
或用 lodash isEqual(Code 节点里可用)
本节要点回顾
Section titled “本节要点回顾”- 用
===比较,不用== - 字符串/数字常需显式
Number()转换(特别从 Sheets / Webhook query) - 安全取字段:
obj?.field ?? '默认' - 日期处理用 Luxon:
$now/DateTime.fromISO()/.plus({})/.toFormat() - 时区:检查
$now.zoneName,需要时.setZone() - 跨字段相等比较用
JSON.stringify()
下一节新手 5 大表达式坑——把最痛的几个 case 集中亮相,让你提前免疫。