code: func getBf4TimeAddSub(ctx sessionctx.Context, args []Expression) (tp1, tp2 *types.FieldType, bf baseBuiltinFunc, err error) {
  	tp1, tp2 = args[0].GetType(), args[1].GetType()
  	var argTp1, argTp2, retTp types.EvalType
  	switch tp1.Tp {
  	case mysql.TypeDatetime, mysql.TypeTimestamp:
  		argTp1, retTp = types.ETDatetime, types.ETDatetime
  	case mysql.TypeDuration:
  		argTp1, retTp = types.ETDuration, types.ETDuration
  	case mysql.TypeDate:
  		argTp1, retTp = types.ETDuration, types.ETString
  	default:
  		argTp1, retTp = types.ETString, types.ETString
  	}
  	switch tp2.Tp {
  	case mysql.TypeDatetime, mysql.TypeDuration:
  		argTp2 = types.ETDuration
  	default:
  		argTp2 = types.ETString
  	}
  	arg0Dec, err := getExpressionFsp(ctx, args[0])
  	if err != nil {
  		return
  	}
  	arg1Dec, err := getExpressionFsp(ctx, args[1])
  	if err != nil {
  		return
  	}
  
  	bf = newBaseBuiltinFuncWithTp(ctx, args, retTp, argTp1, argTp2)
  	bf.tp.Decimal = mathutil.Min(mathutil.Max(arg0Dec, arg1Dec), types.MaxFsp)
  	if retTp == types.ETString {
  		bf.tp.Tp, bf.tp.Flen, bf.tp.Decimal = mysql.TypeString, mysql.MaxDatetimeWidthWithFsp, types.UnspecifiedLength
  	}
  	return
  }
docs: // getBf4TimeAddSub parses input types, generates baseBuiltinFunc and set related attributes for
  // builtin function 'ADDTIME' and 'SUBTIME'