**Use case**

Comparison between DateTime64 and DateTime / Date types is allowed but the behavior is confusing (it looks like used numerical comparison):

* from one side for zero-precision it works as expected (values compared as datetime-based ones): 

```sql
SELECT toTypeName(dt64) AS dt64_typename, dt64 = dt
FROM 
(
    SELECT 
        toDateTime64(toStartOfInterval(now(), toIntervalSecond(1), 'UTC'), 0, 'UTC') AS dt64,
        toStartOfInterval(now(), toIntervalSecond(1), 'UTC') AS dt
)

/*
┌─dt64_typename────────┬─equals(dt64, dt)─┐
│ DateTime64(0, 'UTC') │                1 │
└──────────────────────┴──────────────────┘
*/
```
* from another side for non-zero-precision its behavior is unexpected if not used explicit type conversion:

```sql
SELECT 
    toTypeName(dt64) AS dt64_typename,
    dt64 = dt,
    toDateTime(dt64) = dt,
    dt64 = toDateTime64(dt, 1, 'UTC')
FROM 
(
    SELECT 
        toDateTime64(toStartOfInterval(now(), toIntervalSecond(1), 'UTC'), 1, 'UTC') AS dt64,
        toStartOfInterval(now(), toIntervalSecond(1), 'UTC') AS dt
)
/*
┌─dt64_typename────────┬─equals(dt64, dt)─┬─equals(toDateTime(dt64), dt)─┬─equals(dt64, toDateTime64(dt, 1, 'UTC'))─┐
│ DateTime64(1, 'UTC') │                0 │                            1 │                                        1 │
└──────────────────────┴──────────────────┴──────────────────────────────┴──────────────────────────────────────────┘
*/
```

**Describe the solution you'd like**
I think this comparison either:
- should be not allowed and throw an exception to ask for user uses explicit conversion
- or make valid implicit conversion relying on datetime/date essence of values

{
        return name;
    }
    size_t getNumberOfArguments() const override { return 2; }
    
    DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
    {
        WhichDataType left(arguments[0].get());
        WhichDataType right(arguments[1].get());
        const DataTypeTuple * left_tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].get());
        const DataTypeTuple * right_tuple = checkAndGetDataType<DataTypeTuple>(arguments[1].get());
        bool both_represented_by_number = arguments[0]->isValueRepresentedByNumber() && arguments[1]->isValueRepresentedByNumber();
        bool has_date = left.isDate() || right.isDate();
        if (!((both_represented_by_number && !has_date)   
            || (left.isStringOrFixedString() || right.isStringOrFixedString())  
            
            || (left.isDateOrDateTime() && right.isDateOrDateTime() && left.idx == right.idx) 
            || (left.isUUID() && right.isUUID())
            || (left.isEnum() && right.isEnum() && arguments[0]->getName() == arguments[1]->getName()) 
            || (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size())
            || (arguments[0]->equals(*arguments[1]))))
        {
            try
            {
                getLeastSupertype(arguments);
            }
            catch (const Exception &)