|
|
|
@ -261,16 +261,17 @@ public class SystemFunctionHandler: NSObject { |
|
|
|
"properties": [ |
|
|
|
"format": [ |
|
|
|
"type": "string", |
|
|
|
"description": "时间格式,可选" |
|
|
|
"enum": ["short", "date", "time", "full", "iso"], |
|
|
|
"description": "时间输出粒度,仅可使用枚举值:short(仅时分)、date(仅日期)、time(时分秒)、full(日期+星期+时间)、iso(机器可读)。禁止传入 SimpleDateFormat/strftime 等格式串。" |
|
|
|
] |
|
|
|
], |
|
|
|
"required": [] |
|
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
let handler = GetCurrentTimeHandler() |
|
|
|
_ = client.registerLocalFunction( |
|
|
|
name: "get_current_time", |
|
|
|
description: "获取当前时间", |
|
|
|
description: "获取当前时间,返回值已按当前语言本地化并对TTS友好(不含冒号等易被误读的符号)。播报或回复给用户时必须原样使用 result 字段的文本,不要自行改写为 HH:mm:ss 等格式。", |
|
|
|
parameters: parameters, |
|
|
|
handler: handler |
|
|
|
) |
|
|
|
@ -863,59 +864,72 @@ private class SetReminderHandler: FunctionHandler { |
|
|
|
private class GetCurrentTimeHandler: FunctionHandler { |
|
|
|
func handle(arguments: [String: Any]) async throws -> String { |
|
|
|
let format = arguments["format"] as? String |
|
|
|
let result = getCurrentTime(format: format) |
|
|
|
return "{\"result\": \"\(result.message)\", \"time\": \"\(result.message)\"}" |
|
|
|
do { |
|
|
|
let now = Date() |
|
|
|
let resultTime = try buildResultTime(format: format, now: now) |
|
|
|
let payload: [String: Any] = [ |
|
|
|
"result": resultTime, |
|
|
|
"time": resultTime, |
|
|
|
"timestamp": Int64(now.timeIntervalSince1970 * 1000) |
|
|
|
] |
|
|
|
return jsonString(payload) |
|
|
|
} catch { |
|
|
|
let payload: [String: Any] = [ |
|
|
|
"error": [ |
|
|
|
"code": "time_error", |
|
|
|
"message": "获取时间失败: \(error.localizedDescription)" |
|
|
|
] |
|
|
|
] |
|
|
|
return jsonString(payload) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private func getCurrentTime(format: String?) -> Result { |
|
|
|
let now = Date() |
|
|
|
private func buildResultTime(format: String?, now: Date) throws -> String { |
|
|
|
let lang = Locale.current.languageCode ?? "en" |
|
|
|
|
|
|
|
let timeStr: String |
|
|
|
switch format?.lowercased() { |
|
|
|
case "short": |
|
|
|
timeStr = formatShortTime(now, lang: lang) |
|
|
|
case "date": |
|
|
|
timeStr = formatDate(now, lang: lang) |
|
|
|
case "time": |
|
|
|
timeStr = formatTime(now, lang: lang) |
|
|
|
case "full": |
|
|
|
timeStr = formatFull(now, lang: lang) |
|
|
|
case "short": timeStr = formatShortTime(now, lang: lang) |
|
|
|
case "date": timeStr = formatDate(now, lang: lang) |
|
|
|
case "time": timeStr = formatTime(now, lang: lang) |
|
|
|
case "full": timeStr = formatFull(now, lang: lang) |
|
|
|
case "iso": |
|
|
|
let f = DateFormatter() |
|
|
|
f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" |
|
|
|
timeStr = f.string(from: now) |
|
|
|
default: |
|
|
|
timeStr = formatDefault(now, lang: lang) |
|
|
|
default: timeStr = formatDefault(now, lang: lang) |
|
|
|
} |
|
|
|
|
|
|
|
// 根据格式决定是否添加星期几 |
|
|
|
let resultTime: String |
|
|
|
// 根据格式决定是否追加星期(与 Android 一致:short/time/iso 不追加,full 已自带) |
|
|
|
switch format?.lowercased() { |
|
|
|
case "short", "time", "iso": |
|
|
|
resultTime = timeStr |
|
|
|
case "full": |
|
|
|
resultTime = timeStr // full格式已包含星期 |
|
|
|
case "short", "time", "iso", "full": |
|
|
|
return timeStr |
|
|
|
default: |
|
|
|
let weekFormatter = DateFormatter() |
|
|
|
weekFormatter.dateFormat = "EEEE" |
|
|
|
weekFormatter.locale = Locale.current |
|
|
|
resultTime = "\(timeStr) \(weekFormatter.string(from: now))" |
|
|
|
return "\(timeStr) \(weekFormatter.string(from: now))" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return Result(success: true, message: resultTime) |
|
|
|
private func jsonString(_ dict: [String: Any]) -> String { |
|
|
|
guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []), |
|
|
|
let s = String(data: data, encoding: .utf8) else { |
|
|
|
return "{}" |
|
|
|
} |
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
|
// TTS友好的时间格式,避免AI将":"分隔的时间误读为数字 |
|
|
|
// CJK 使用单位数 m/s(去掉前导零),避免 TTS 读出 "零五分零三秒" |
|
|
|
// 其他语言使用英文单词 hour/minute/second,避免非英语 TTS 对冒号的解析不稳定 |
|
|
|
private func formatShortTime(_ date: Date, lang: String) -> String { |
|
|
|
let f = DateFormatter() |
|
|
|
switch lang { |
|
|
|
case "zh": f.dateFormat = "H时mm分"; f.locale = Locale(identifier: "zh_CN") |
|
|
|
case "ja": f.dateFormat = "H時mm分"; f.locale = Locale(identifier: "ja_JP") |
|
|
|
case "ko": f.dateFormat = "H시 mm분"; f.locale = Locale(identifier: "ko_KR") |
|
|
|
default: f.dateFormat = "h:mm a"; f.locale = Locale.current |
|
|
|
case "zh": f.dateFormat = "H时m分"; f.locale = Locale(identifier: "zh_CN"); return f.string(from: date) |
|
|
|
case "ja": f.dateFormat = "H時m分"; f.locale = Locale(identifier: "ja_JP"); return f.string(from: date) |
|
|
|
case "ko": f.dateFormat = "H시 m분"; f.locale = Locale(identifier: "ko_KR"); return f.string(from: date) |
|
|
|
default: return formatTimeWestern(date, withSeconds: false) |
|
|
|
} |
|
|
|
return f.string(from: date) |
|
|
|
} |
|
|
|
|
|
|
|
private func formatDate(_ date: Date, lang: String) -> String { |
|
|
|
@ -932,34 +946,59 @@ private class GetCurrentTimeHandler: FunctionHandler { |
|
|
|
private func formatTime(_ date: Date, lang: String) -> String { |
|
|
|
let f = DateFormatter() |
|
|
|
switch lang { |
|
|
|
case "zh": f.dateFormat = "H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") |
|
|
|
case "ja": f.dateFormat = "H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") |
|
|
|
case "ko": f.dateFormat = "H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") |
|
|
|
default: f.dateFormat = "h:mm:ss a"; f.locale = Locale.current |
|
|
|
case "zh": f.dateFormat = "H时m分s秒"; f.locale = Locale(identifier: "zh_CN"); return f.string(from: date) |
|
|
|
case "ja": f.dateFormat = "H時m分s秒"; f.locale = Locale(identifier: "ja_JP"); return f.string(from: date) |
|
|
|
case "ko": f.dateFormat = "H시 m분 s초"; f.locale = Locale(identifier: "ko_KR"); return f.string(from: date) |
|
|
|
default: return formatTimeWestern(date, withSeconds: true) |
|
|
|
} |
|
|
|
return f.string(from: date) |
|
|
|
} |
|
|
|
|
|
|
|
private func formatFull(_ date: Date, lang: String) -> String { |
|
|
|
let f = DateFormatter() |
|
|
|
switch lang { |
|
|
|
case "zh": f.dateFormat = "yyyy年M月d日 EEEE H时mm分ss秒"; f.locale = Locale(identifier: "zh_CN") |
|
|
|
case "ja": f.dateFormat = "yyyy年M月d日 EEEE H時mm分ss秒"; f.locale = Locale(identifier: "ja_JP") |
|
|
|
case "ko": f.dateFormat = "yyyy년 M월 d일 EEEE H시 mm분 ss초"; f.locale = Locale(identifier: "ko_KR") |
|
|
|
default: f.dateFormat = "MMMM d, yyyy, EEEE, h:mm:ss a"; f.locale = Locale.current |
|
|
|
case "zh": f.dateFormat = "yyyy年M月d日 EEEE H时m分s秒"; f.locale = Locale(identifier: "zh_CN"); return f.string(from: date) |
|
|
|
case "ja": f.dateFormat = "yyyy年M月d日 EEEE H時m分s秒"; f.locale = Locale(identifier: "ja_JP"); return f.string(from: date) |
|
|
|
case "ko": f.dateFormat = "yyyy년 M월 d일 EEEE H시 m분 s초"; f.locale = Locale(identifier: "ko_KR"); return f.string(from: date) |
|
|
|
default: |
|
|
|
f.dateFormat = "MMMM d, yyyy, EEEE, "; f.locale = Locale.current |
|
|
|
return f.string(from: date) + formatTimeWestern(date, withSeconds: true) |
|
|
|
} |
|
|
|
return f.string(from: date) |
|
|
|
} |
|
|
|
|
|
|
|
private func formatDefault(_ date: Date, lang: String) -> String { |
|
|
|
let f = DateFormatter() |
|
|
|
switch lang { |
|
|
|
case "zh": f.dateFormat = "yyyy年M月d日 H时mm分"; f.locale = Locale(identifier: "zh_CN") |
|
|
|
case "ja": f.dateFormat = "yyyy年M月d日 H時mm分"; f.locale = Locale(identifier: "ja_JP") |
|
|
|
case "ko": f.dateFormat = "yyyy년 M월 d일 H시 mm분"; f.locale = Locale(identifier: "ko_KR") |
|
|
|
default: f.dateFormat = "MMMM d, yyyy, h:mm a"; f.locale = Locale.current |
|
|
|
case "zh": f.dateFormat = "yyyy年M月d日 H时m分"; f.locale = Locale(identifier: "zh_CN"); return f.string(from: date) |
|
|
|
case "ja": f.dateFormat = "yyyy年M月d日 H時m分"; f.locale = Locale(identifier: "ja_JP"); return f.string(from: date) |
|
|
|
case "ko": f.dateFormat = "yyyy년 M월 d일 H시 m분"; f.locale = Locale(identifier: "ko_KR"); return f.string(from: date) |
|
|
|
default: |
|
|
|
f.dateFormat = "MMMM d, yyyy, "; f.locale = Locale.current |
|
|
|
return f.string(from: date) + formatTimeWestern(date, withSeconds: false) |
|
|
|
} |
|
|
|
return f.string(from: date) |
|
|
|
} |
|
|
|
|
|
|
|
/// 非 CJK 语言的时间格式(英语及其他 Latin 语系) |
|
|
|
/// 避免冒号,使用英文单词 hour/minute/second + AM/PM,对绝大多数 TTS 引擎更稳定 |
|
|
|
/// 例:2 hours 30 minutes 15 seconds PM |
|
|
|
private func formatTimeWestern(_ date: Date, withSeconds: Bool) -> String { |
|
|
|
let cal = Calendar.current |
|
|
|
let hour24 = cal.component(.hour, from: date) |
|
|
|
let minute = cal.component(.minute, from: date) |
|
|
|
let second = cal.component(.second, from: date) |
|
|
|
let h12Raw = hour24 % 12 |
|
|
|
let h12 = h12Raw == 0 ? 12 : h12Raw |
|
|
|
let ampm = hour24 < 12 ? "AM" : "PM" |
|
|
|
|
|
|
|
var parts: [String] = [] |
|
|
|
parts.append("\(h12) \(h12 == 1 ? "hour" : "hours")") |
|
|
|
if minute > 0 || withSeconds { |
|
|
|
parts.append("\(minute) \(minute == 1 ? "minute" : "minutes")") |
|
|
|
} |
|
|
|
if withSeconds { |
|
|
|
parts.append("\(second) \(second == 1 ? "second" : "seconds")") |
|
|
|
} |
|
|
|
parts.append(ampm) |
|
|
|
return parts.joined(separator: " ") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|