From dce26c203d52f92d7daa4cb5eef7b7fafaee40b9 Mon Sep 17 00:00:00 2001 From: Ti Chi Robot Date: Wed, 22 May 2024 10:32:46 +0800 Subject: [PATCH] expression: wrong result of timestampadd(month,1,date '2024-01-31') (#53101) (#53423) close pingcap/tidb#41052 --- expression/builtin_time.go | 19 +++++++++++++------ expression/builtin_time_test.go | 12 ++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 9901b9323d060..1d7c2a25dc1b2 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -6969,6 +6969,13 @@ func (b *builtinTimestampAddSig) evalString(row chunk.Row) (string, bool, error) tb = tm1.AddDate(0, 0, 7*int(v)) case "MONTH": tb = tm1.AddDate(0, int(v), 0) + + // For corner case: timestampadd(month,1,date '2024-01-31') = "2024-02-29", timestampadd(month,1,date '2024-01-30') = "2024-02-29" + // `tb.Month()` refers to the actual result, `t.Month()+v` refers to the expect result. + // Actual result may be greater than expect result, we need to judge and modify it. + for int(tb.Month())%12 != (int(tm1.Month())+int(v))%12 { + tb = tb.AddDate(0, 0, -1) + } case "QUARTER": tb = tm1.AddDate(0, 3*int(v), 0) case "YEAR": @@ -7381,12 +7388,12 @@ func CalAppropriateTime(minTime, maxTime, minSafeTime time.Time) time.Time { } // For a SafeTS t and a time range [t1, t2]: -// 1. If t < t1, we will use t1 as the result, -// and with it, a read request may fail because it's an unreached SafeTS. -// 2. If t1 <= t <= t2, we will use t as the result, and with it, -// a read request won't fail. -// 2. If t2 < t, we will use t2 as the result, -// and with it, a read request won't fail because it's bigger than the latest SafeTS. +// 1. If t < t1, we will use t1 as the result, +// and with it, a read request may fail because it's an unreached SafeTS. +// 2. If t1 <= t <= t2, we will use t as the result, and with it, +// a read request won't fail. +// 2. If t2 < t, we will use t2 as the result, +// and with it, a read request won't fail because it's bigger than the latest SafeTS. func calAppropriateTime(minTime, maxTime, minSafeTime time.Time) time.Time { if minSafeTime.Before(minTime) || minSafeTime.After(maxTime) { logutil.BgLogger().Warn("calAppropriateTime", diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 2fcf98d3db3d8..a113644a43f42 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2492,6 +2492,18 @@ func TestTimestampAdd(t *testing.T) { {"WEEK", 1, "2003-01-02 23:59:59", "2003-01-09 23:59:59"}, {"MICROSECOND", 1, 950501, "1995-05-01 00:00:00.000001"}, {"DAY", 28768, 0, ""}, + + // issue41052 + {"MONTH", 1, "2024-01-31", "2024-02-29 00:00:00"}, + {"MONTH", 1, "2024-01-30", "2024-02-29 00:00:00"}, + {"MONTH", 1, "2024-01-29", "2024-02-29 00:00:00"}, + {"MONTH", 1, "2024-01-28", "2024-02-28 00:00:00"}, + {"MONTH", 1, "2024-10-31", "2024-11-30 00:00:00"}, + {"MONTH", 3, "2024-01-31", "2024-04-30 00:00:00"}, + {"MONTH", 15, "2024-01-31", "2025-04-30 00:00:00"}, + {"MONTH", 10, "2024-10-31", "2025-08-31 00:00:00"}, + {"MONTH", 1, "2024-11-30", "2024-12-30 00:00:00"}, + {"MONTH", 13, "2024-11-30", "2025-12-30 00:00:00"}, } fc := funcs[ast.TimestampAdd]