func (e *Evaluator) funcDateArith(v *ast.FuncDateArithExpr) bool { // health check for date and interval nodeDate := v.Date.GetValue() if types.IsNil(nodeDate) { v.SetValue(nil) return true } nodeInterval := v.Interval.GetValue() if types.IsNil(nodeInterval) { v.SetValue(nil) return true } // parse date fieldType := mysql.TypeDate var resultField *types.FieldType switch x := nodeDate.(type) { case mysql.Time: if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } case string: if !mysql.IsDateFormat(x) { fieldType = mysql.TypeDatetime } case int64: if t, err := mysql.ParseTimeFromInt64(x); err == nil { if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } } } if mysql.IsClockUnit(v.Unit) { fieldType = mysql.TypeDatetime } resultField = types.NewFieldType(fieldType) resultField.Decimal = mysql.MaxFsp value, err := types.Convert(nodeDate, resultField) if err != nil { e.err = ErrInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate) return false } if types.IsNil(value) { e.err = ErrInvalidOperation.Gen("DateArith invalid args, need date but get %v", value) return false } result, ok := value.(mysql.Time) if !ok { e.err = ErrInvalidOperation.Gen("DateArith need time type, but got %T", value) return false } // parse interval var interval string if strings.ToLower(v.Unit) == "day" { day, err2 := parseDayInterval(nodeInterval) if err2 != nil { e.err = ErrInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeInterval) return false } interval = fmt.Sprintf("%d", day) } else { interval = fmt.Sprintf("%v", nodeInterval) } year, month, day, duration, err := mysql.ExtractTimeValue(v.Unit, interval) if err != nil { e.err = errors.Trace(err) return false } if v.Op == ast.DateSub { year, month, day, duration = -year, -month, -day, -duration } result.Time = result.Time.Add(duration) result.Time = result.Time.AddDate(int(year), int(month), int(day)) if result.Time.Nanosecond() == 0 { result.Fsp = 0 } v.SetValue(result) return true }
func builtinDateArith(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // Op is used for distinguishing date_add and date_sub. // args[0] -> Op // args[1] -> Date // args[2] -> DateArithInterval // health check for date and interval if args[1].Kind() == types.KindNull { d.SetNull() return d, nil } nodeDate := args[1] nodeInterval := args[2].GetInterface().(ast.DateArithInterval) nodeIntervalIntervalDatum := nodeInterval.Interval.GetDatum() if nodeIntervalIntervalDatum.Kind() == types.KindNull { d.SetNull() return d, nil } // parse date fieldType := mysql.TypeDate var resultField *types.FieldType switch nodeDate.Kind() { case types.KindMysqlTime: x := nodeDate.GetMysqlTime() if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } case types.KindString: x := nodeDate.GetString() if !mysql.IsDateFormat(x) { fieldType = mysql.TypeDatetime } case types.KindInt64: x := nodeDate.GetInt64() if t, err1 := mysql.ParseTimeFromInt64(x); err1 == nil { if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } } } if mysql.IsClockUnit(nodeInterval.Unit) { fieldType = mysql.TypeDatetime } resultField = types.NewFieldType(fieldType) resultField.Decimal = mysql.MaxFsp value, err := nodeDate.ConvertTo(resultField) if err != nil { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate) } if value.Kind() == types.KindNull { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %v", value.GetValue()) } if value.Kind() != types.KindMysqlTime { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith need time type, but got %T", value.GetValue()) } result := value.GetMysqlTime() // parse interval var interval string if strings.ToLower(nodeInterval.Unit) == "day" { day, err1 := parseDayInterval(*nodeIntervalIntervalDatum) if err1 != nil { d.SetNull() return d, ErrInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeIntervalIntervalDatum.GetString()) } interval = fmt.Sprintf("%d", day) } else { if nodeIntervalIntervalDatum.Kind() == types.KindString { interval = fmt.Sprintf("%v", nodeIntervalIntervalDatum.GetString()) } else { ii, err1 := nodeIntervalIntervalDatum.ToInt64() if err1 != nil { d.SetNull() return d, errors.Trace(err1) } interval = fmt.Sprintf("%v", ii) } } year, month, day, duration, err := mysql.ExtractTimeValue(nodeInterval.Unit, interval) if err != nil { d.SetNull() return d, errors.Trace(err) } op := args[0].GetInterface().(ast.DateArithType) if op == ast.DateSub { year, month, day, duration = -year, -month, -day, -duration } result.Time = result.Time.Add(duration) result.Time = result.Time.AddDate(int(year), int(month), int(day)) if result.Time.Nanosecond() == 0 { result.Fsp = 0 } d.SetMysqlTime(result) return d, nil }