private ScalarOperator reduceDatetimeToDateCast(BinaryPredicateOperator operator) {
        ScalarOperator castChild = operator.getChild(0).getChild(0);
        ConstantOperator child2 = (ConstantOperator) operator.getChild(1);
        if (child2.isNull()) {
            return operator;
        }

        LocalDateTime originalDate = child2.getDate().truncatedTo(ChronoUnit.DAYS);
        LocalDateTime targetDate;
        BinaryType binaryType = operator.getBinaryType();
        int offset;
        ScalarOperator resultBinaryPredicateOperator;
        ConstantOperator newDatetime;
        switch (binaryType) {
            case GE:
                // when the BinaryType is >= , cast date to equivalent datetime type
                // E.g. cast(id_datetime as date) >= 2021-12-28
                // optimized to id_datetime >= 2021-12-28 00:00:00.0
                offset = 0;
                targetDate = originalDate.plusDays(offset);
                newDatetime = ConstantOperator.createDatetime(targetDate);
                resultBinaryPredicateOperator = BinaryPredicateOperator.ge(castChild, newDatetime);
                break;
            case GT:
                // when the BinaryType is > , cast date to equivalent datetime type of next day
                // E.g. cast(id_datetime as date) > 2021-12-28
                // optimized to id_datetime >= 2021-12-29 00:00:00.0
                offset = 1;
                targetDate = originalDate.plusDays(offset);
                newDatetime = ConstantOperator.createDatetime(targetDate);
                resultBinaryPredicateOperator = BinaryPredicateOperator.ge(castChild, newDatetime);
                break;
            case LE:
                // when the BinaryType is <= , cast date to equivalent datetime type of next day
                // E.g. cast(id_datetime as date) <= 2021-12-28
                // optimized to id_datetime < 2021-12-29 00:00:00.0
                offset = 1;
                targetDate = originalDate.plusDays(offset);
                newDatetime = ConstantOperator.createDatetime(targetDate);
                resultBinaryPredicateOperator = BinaryPredicateOperator.lt(castChild, newDatetime);
                break;
            case LT:
                // when the BinaryType is < , cast date to equivalent datetime type
                // E.g. cast(id_datetime as date) < 2021-12-28
                // optimized to id_datetime < 2021-12-28 00:00:00.0
                offset = 0;
                targetDate = originalDate.plusDays(offset);
                newDatetime = ConstantOperator.createDatetime(targetDate);
                resultBinaryPredicateOperator = BinaryPredicateOperator.lt(castChild, newDatetime);
                break;
            case EQ:
                // when the BinaryType is = , replace it with compound operator
                // E.g. cast(id_datetime as date) = 2021-12-28
                // optimized to id_datetime >= 2021-12-28 and id_datetime < 2021-12-29
                ConstantOperator beginDatetime = ConstantOperator.createDatetime(originalDate.plusDays(0));
                ConstantOperator endDatetime = ConstantOperator.createDatetime(originalDate.plusDays(1));
                resultBinaryPredicateOperator = CompoundPredicateOperator
                        .and(BinaryPredicateOperator.ge(castChild, beginDatetime),
                                BinaryPredicateOperator.lt(castChild, endDatetime));
                break;
            default:
                resultBinaryPredicateOperator = operator;
                break;
        }
        return resultBinaryPredicateOperator;
    }
@Test
    public void testReduceDatetimeToDateCast() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        ReduceCastRule reduceCastRule = new ReduceCastRule();
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator =
                    ConstantOperator.createDate(LocalDate.parse("2021-12-28").atTime(0, 0, 0, 0));
            BinaryPredicateOperator beforeOptimize =
                    new BinaryPredicateOperator(BinaryType.GE, castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(
                    beforeOptimize,
                    null);

            Assert.assertTrue(afterOptimize instanceof BinaryPredicateOperator);
            Assert.assertEquals(BinaryType.GE,
                    ((BinaryPredicateOperator) afterOptimize).getBinaryType());
            Assert.assertTrue(afterOptimize.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(afterOptimize.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-28 00:00:00",
                    ((ConstantOperator) afterOptimize.getChild(1)).getDatetime().format(formatter));
        }
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator =
                    ConstantOperator.createDate(LocalDate.parse("2021-12-28").atTime(0, 0, 0, 0));
            BinaryPredicateOperator beforeOptimize =
                    new BinaryPredicateOperator(BinaryType.GT, castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(
                    beforeOptimize,
                    null);

            Assert.assertTrue(afterOptimize instanceof BinaryPredicateOperator);
            Assert.assertEquals(BinaryType.GE,
                    ((BinaryPredicateOperator) afterOptimize).getBinaryType());
            Assert.assertTrue(afterOptimize.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(afterOptimize.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-29 00:00:00",
                    ((ConstantOperator) afterOptimize.getChild(1)).getDatetime().format(formatter));
        }
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator =
                    ConstantOperator.createDate(LocalDate.parse("2021-12-28").atTime(0, 0, 0, 0));
            BinaryPredicateOperator beforeOptimize =
                    new BinaryPredicateOperator(BinaryType.LE, castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(
                    beforeOptimize,
                    null);

            Assert.assertTrue(afterOptimize instanceof BinaryPredicateOperator);
            Assert.assertEquals(BinaryType.LT,
                    ((BinaryPredicateOperator) afterOptimize).getBinaryType());
            Assert.assertTrue(afterOptimize.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(afterOptimize.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-29 00:00:00",
                    ((ConstantOperator) afterOptimize.getChild(1)).getDatetime().format(formatter));
        }
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator =
                    ConstantOperator.createDate(LocalDate.parse("2021-12-28").atTime(0, 0, 0, 0));
            BinaryPredicateOperator beforeOptimize =
                    new BinaryPredicateOperator(BinaryType.LT, castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(
                    beforeOptimize,
                    null);

            Assert.assertTrue(afterOptimize instanceof BinaryPredicateOperator);
            Assert.assertEquals(BinaryType.LT,
                    ((BinaryPredicateOperator) afterOptimize).getBinaryType());
            Assert.assertTrue(afterOptimize.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(afterOptimize.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-28 00:00:00",
                    ((ConstantOperator) afterOptimize.getChild(1)).getDatetime().format(formatter));
        }
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator =
                    ConstantOperator.createDate(LocalDate.parse("2021-12-28").atTime(0, 0, 0, 0));
            BinaryPredicateOperator beforeOptimize =
                    new BinaryPredicateOperator(BinaryType.EQ, castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(
                    beforeOptimize,
                    null);

            Assert.assertTrue(afterOptimize instanceof CompoundPredicateOperator);
            Assert.assertTrue(((CompoundPredicateOperator) afterOptimize).isAnd());
            ScalarOperator left = afterOptimize.getChild(0);
            ScalarOperator right = afterOptimize.getChild(1);
            Assert.assertTrue(left instanceof BinaryPredicateOperator);
            Assert.assertTrue(right instanceof BinaryPredicateOperator);
            Assert.assertEquals(BinaryType.GE,
                    ((BinaryPredicateOperator) left).getBinaryType());
            Assert.assertTrue(left.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(left.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-28 00:00:00",
                    ((ConstantOperator) left.getChild(1)).getDatetime().format(formatter));
            Assert.assertEquals(BinaryType.LT,
                    ((BinaryPredicateOperator) right).getBinaryType());
            Assert.assertTrue(right.getChild(0) instanceof ColumnRefOperator);
            Assert.assertTrue(right.getChild(1) instanceof ConstantOperator);
            Assert.assertEquals("2021-12-29 00:00:00",
                    ((ConstantOperator) right.getChild(1)).getDatetime().format(formatter));
        }
        {
            CastOperator castOperator =
                    new CastOperator(Type.DATE, new ColumnRefOperator(0, Type.DATETIME, "id_datetime", false));
            ConstantOperator constantOperator = ConstantOperator.createNull(Type.DATE);
            BinaryPredicateOperator beforeOptimize = BinaryPredicateOperator.lt(castOperator, constantOperator);
            ScalarOperator afterOptimize = reduceCastRule.apply(beforeOptimize, null);
            Assert.assertSame(beforeOptimize, afterOptimize);
        }
    }