harden(migrate): 抄送回填取切换时刻改 CAST(TEXT) —— PG 的 timestamptz 会被扫成 time.Time
判据只在 SQLite 上跑过:把 MIN(read_at) 扫进 sql.NullString 在 PG 上依赖驱动返回类型, 不可靠。改成 CAST(... AS TEXT) 再解析,并把 PG 的文本形态(+00 时区后缀)加进可识别的 layout 列表。认不出来时退回现在= 把当下已有的已读邮件全算作迁移前 —— 首次升级时 正是对的,且有一次标记守着不会反复跑。
This commit is contained in:
@ -120,13 +120,21 @@ func switchoverAt(ctx context.Context) (time.Time, error) {
|
||||
return time.Time{}, err
|
||||
}
|
||||
var raw sql.NullString
|
||||
if err := DB.QueryRowContext(ctx, `SELECT MIN(read_at) FROM mail_reads`).Scan(&raw); err != nil {
|
||||
// CAST 成文本再解析:两种方言的驱动对 timestamptz 返回的类型不一样
|
||||
// (PG 给 time.Time,SQLite 给字符串),文本形态才是共通的。
|
||||
if err := DB.QueryRowContext(ctx, `SELECT CAST(MIN(read_at) AS TEXT) FROM mail_reads`).Scan(&raw); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
if !raw.Valid {
|
||||
return time.Now().UTC(), nil // 表还是空的:本机没有"老邮件"要照顾
|
||||
}
|
||||
for _, layout := range []string{"2006-01-02 15:04:05.000", "2006-01-02 15:04:05", time.RFC3339} {
|
||||
// 认不出来的形态退回"现在":那等于把当下已有的已读邮件全算作迁移前 ——
|
||||
// 首次升级时正是对的;而且这条路有一次性标记守着,不会反复跑。
|
||||
for _, layout := range []string{
|
||||
"2006-01-02 15:04:05.000", "2006-01-02 15:04:05",
|
||||
"2006-01-02 15:04:05.999999-07", "2006-01-02 15:04:05-07",
|
||||
time.RFC3339,
|
||||
} {
|
||||
if t, perr := time.Parse(layout, raw.String); perr == nil {
|
||||
return t.UTC(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user