Gemini API 时间戳精度修复:如何解决 web_search 的 400 错误

——

Gemini API 时间戳精度修复:如何解决 web_search 的 400 错误

OpenClaw 最新更新解决了 Gemini API 中 google_search.time_range_filter 因毫秒级时间戳导致的 400 错误,该问题自 2026.5.19 版本起影响所有使用 freshness 参数的实时搜索调用。本文详解故障根因、修复方案及开发者应对策略。

问题背景:为什么你的 AI 搜索突然报错?

OpenClaw 2026.5.19 版本发布以来,部分开发者反馈 Gemini APIweb_search 工具在启用 freshness(时效性)过滤时频繁返回 400 错误:

[FIELD_INVALID] Granularity of nano is not supported

矛盾的是,该错误仅出现在生产环境,测试环境却完全正常。经过深入排查,团队发现问题根源在于 JavaScript 时间戳的亚秒精度处理

核心矛盾点

| 场景 | 时间戳格式 | Gemini 响应 |
|:—|:—|:—|
| 测试环境(固定时间) | 2026-04-15T12:00:00.000Z | ✅ 正常 |
| 生产环境(实时时间) | 2026-04-15T12:00:00.123Z | ❌ 400 错误 |

根本原因:Gemini 的 google_search.time_range_filter 端点对 google.protobuf.Timestamp 类型的限制比官方规范更严格——拒绝任何非零的分数秒,即使底层类型理论上支持 3/6/9 位小数精度。

技术深潜:时间戳精度陷阱

JavaScript 的 toISOString() 行为

JavaScript 的 Date.prototype.toISOString() 始终输出毫秒级精度(3 位小数),这是语言规范决定的:

const now = new Date();
console.log(now.toISOString());
// 输出: "2026-06-15T08:30:45.789Z"  ← 总是带 .xxxZ

// 即使是整秒时刻,也是 .000Z 而非无小数 const exact = new Date("2026-01-01T00:00:00Z"); console.log(exact.toISOString()); // 输出: "2026-01-01T00:00:00.000Z"

Gemini API 的实际限制

通过实测验证,Gemini 的 time_range_filter 仅接受以下两种格式:

| 格式 | 示例 | 结果 |
|:—|:—|:—|
| 无分数秒 | 2026-06-15T08:30:45Z | ✅ 通过 |
| 全零毫秒 | 2026-06-15T08:30:45.000Z | ✅ 通过 |
| 非零毫秒 | 2026-06-15T08:30:45.123Z | ❌ 400 错误 |

修复方案:toGeminiTimeRangeTimestamp() 实现

OpenClaw 团队引入了专用工具函数,统一处理所有时间范围过滤器的时间戳序列化:

/**
 * 将 Date 转换为 Gemini 兼容的时间戳字符串
 * 关键:移除所有分数秒精度,仅保留秒级精度
 * 
 * @param {Date} date - 输入日期
 * @returns {string} - ISO 8601 格式,无分数秒(如 "2026-06-15T08:30:45Z")
 */
function toGeminiTimeRangeTimestamp(date) {
  // 创建副本避免修改原对象
  const d = new Date(date);
  
  // 关键步骤:将毫秒设为 0,然后使用 toISOString()
  d.setMilliseconds(0);
  
  // 替换 ".000Z" 为 "Z",确保完全无分数秒表示
  return d.toISOString().replace('.000Z', 'Z');
}

// 使用示例 const freshnessStart = toGeminiTimeRangeTimestamp(new Date()); // 输出: "2026-06-15T08:30:45Z" (无 .000Z)

const dateAfter = toGeminiTimeRangeTimestamp(new Date("2026-01-01")); // 输出: "2026-01-01T00:00:00Z"

修复覆盖的四个关键位置

该函数被应用于所有 timeRangeFilter 时间戳生成点:

1. freshness 参数的起始时间startTime
2. date_after 过滤的起始时间
3. date_before 过滤的结束时间(含 “now” 回退逻辑)
4. isoDateExclusiveEnd 生成的结束时间(虽本身为 .000Z,统一处理以增强鲁棒性)

测试策略:为什么 CI 没能提前捕获?

原始测试的盲点

// ❌ 问题测试代码(修复前)
vi.setSystemTime(new Date("2026-04-15T12:00:00Z"));
// toISOString() → "2026-04-15T12:00:00.000Z" 
// Gemini 接受 .000Z,测试通过,但生产环境失败

关键问题:固定时间字符串 "2026-04-15T12:00:00Z" 被解析后,毫秒恰好为 0,导致 toISOString() 输出 .000Z——这是 Gemini 唯一接受的分数秒形式

修复后的真实场景测试

// ✅ 修正后的测试代码
vi.setSystemTime(new Date("2026-04-15T12:00:00.123Z"));
// toISOString() → "2026-04-15T12:00:00.123Z"
// 触发真实的 400 错误场景,验证修复有效性

开发者实践指南

如果你直接调用 Gemini API

若你的项目直接构造 time_range_filter,务必确保时间戳格式:

// ❌ 错误:直接使用 toISOString()
const filter = {
  startTime: new Date().toISOString(),  // "2026-...T12:00:00.123Z" → 400 错误
  endTime: "2026-12-31T23:59:59Z"
};

// ✅ 正确:移除分数秒 function toGeminiTimestamp(date) { return date.toISOString().split('.')[0] + 'Z'; }

const filter = { startTime: toGeminiTimestamp(new Date()), // "2026-...T12:00:00Z" endTime: "2026-12-31T23:59:59Z" };

OpenClaw 用户无需操作

已升级至 OpenClaw 最新版本 的用户,所有 Gemini web_search 调用已自动应用修复。可通过以下命令验证版本:

检查 OpenClaw 版本

openclaw --version

建议升级到最新版

npm update -g @openclaw/cli

pip install --upgrade openclaw

FAQ:常见问题解答

Q1: 这个 bug 会影响哪些 OpenClaw 功能?

A: 主要影响使用 Gemini 模型 且启用 web_search 工具的 AI Agent,特别是配置了 freshness 时效性过滤或 date_after/date_before 时间范围过滤的场景。其他模型(如 GPT-4、Claude)不受影响。

Q2: 为什么 Gemini 的限制与 protobuf 规范不一致?

A: google.protobuf.Timestamp 官方规范允许 0/3/6/9 位小数精度,但 Gemini 的 grounding 服务端 实施了更严格的校验策略。这是服务端实现细节,非协议层面问题。建议开发者始终遵循”无分数秒”的最小公分母策略。

Q3: 如何验证我的时间戳格式是否正确?

A: 使用以下快速检测方法:

function isGeminiCompatible(isoString) {
  // 正确格式:以 Z 结尾,不含小数点
  return isoString.endsWith('Z') && !isoString.includes('.');
}

// 测试 console.log(isGeminiCompatible("2026-06-15T08:30:45Z")); // true console.log(isGeminiCompatible("2026-06-15T08:30:45.000Z")); // false(虽 Gemini 接受,但不建议) console.log(isGeminiCompatible("2026-06-15T08:30:45.123Z")); // false

Q4: 这个修复是否向后兼容?

A: 完全兼容。.000ZZ 两种格式均被 Gemini 接受,修复仅将前者统一转换为后者,不改变语义,仅增强兼容性。

Q5: 除 Gemini 外,其他 Google API 是否有类似限制?

A: 经测试,Vertex AI 的部分时间戳字段也存在类似限制。建议对所有 Google Cloud API 的时间戳参数采用相同的”无分数秒”处理策略,除非文档明确说明支持更高精度。

总结与下一步

本次 OpenClaw 更新解决了 Gemini API web_search 因时间戳精度导致的 400 错误,核心要点:

| 要点 | 说明 |
|:—|:—|
| 根因 | Gemini time_range_filter 拒绝非零分数秒 |
| 修复 | 引入 toGeminiTimeRangeTimestamp() 统一处理 |
| 影响 | 2026.5.19 后所有 freshness 调用 |
| 行动 | 升级 OpenClaw 即可,无需代码改动 |

推荐下一步

相关阅读

参考来源