class TimestampType extends DataType<Long>
    implements FixedWidthType, Streamer<Long> {

    public static final int ID_WITH_TZ = 11;
    public static final int ID_WITHOUT_TZ = 15;

    public static final TimestampType INSTANCE_WITH_TZ = new TimestampType(
        ID_WITH_TZ,
        "timestamp with time zone",
        TimestampType::parseTimestamp,
        Precedence.TIMESTAMP_WITH_TIME_ZONE);

    public static final TimestampType INSTANCE_WITHOUT_TZ = new TimestampType(
        ID_WITHOUT_TZ,
        "timestamp without time zone",
        TimestampType::parseTimestampIgnoreTimeZone,
        Precedence.TIMESTAMP);

    private static final StorageSupport<Long> STORAGE = new StorageSupport<>(true, true, new LongEqQuery()) {

        @Override
        public ValueIndexer<Long> valueIndexer(RelationName table,
                                               Reference ref,
                                               Function<String, FieldType> getFieldType,
                                               Function<ColumnIdent, Reference> getRef) {
            return new LongIndexer(ref, getFieldType.apply(ref.storageIdent()));
        }
    };

    private final int id;
    private final String name;
    private final Function<String, Long> parse;
    private final Precedence precedence;

    private TimestampType(int id, String name, Function<String, Long> parse, Precedence precedence) {
        this.id = id;
        this.name = name;
        this.parse = parse;
        this.precedence = precedence;
    }

    @Override
    public int id() {
        return id;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Precedence precedence() {
        return precedence;
    }

    @Override
    public Streamer<Long> streamer() {
        return this;
    }

    @Override
    public Long implicitCast(Object value) throws IllegalArgumentException, ClassCastException {<FILL_FUNCTION_BODY>}

    @Override
    public Long sanitizeValue(Object value) {
        if (value == null) {
            return null;
        } else if (value instanceof Long l) {
            return l;
        } else {
            return ((Number) value).longValue();
        }
    }

    @Override
    public int compare(Long val1, Long val2) {
        return Long.compare(val1, val2);
    }

    @Override
    public Long readValueFrom(StreamInput in) throws IOException {
        return in.readBoolean() ? null : in.readLong();
    }

    @Override
    public void writeValueTo(StreamOutput out, Long v) throws IOException {
        out.writeBoolean(v == null);
        if (v != null) {
            out.writeLong(v);
        }
    }

    @Override
    public int fixedSize() {
        return LongType.LONG_SIZE;
    }


    static long parseTimestamp(String timestamp) {
        long[] out = StringUtils.PARSE_LONG_BUFFER.get();
        if (StringUtils.tryParseLong(timestamp, out)) {
            return out[0];
        }
        TemporalAccessor dt;
        try {
            dt = TIMESTAMP_PARSER.parseBest(
                timestamp, OffsetDateTime::from, LocalDateTime::from, LocalDate::from);
        } catch (DateTimeParseException e1) {
            throw new IllegalArgumentException(e1.getMessage());
        }
        if (dt instanceof LocalDateTime) {
            LocalDateTime localDateTime = LocalDateTime.from(dt);
            return localDateTime.toInstant(UTC).toEpochMilli();
        } else if (dt instanceof LocalDate) {
            LocalDate localDate = LocalDate.from(dt);
            return localDate.atStartOfDay(UTC).toInstant().toEpochMilli();
        }
        OffsetDateTime offsetDateTime = OffsetDateTime.from(dt);
        return offsetDateTime.toInstant().toEpochMilli();
    }

    public static long parseTimestampIgnoreTimeZone(String timestamp) {
        long[] out = StringUtils.PARSE_LONG_BUFFER.get();
        if (StringUtils.tryParseLong(timestamp, out)) {
            return out[0];
        }
        TemporalAccessor dt;
        try {
            dt = TIMESTAMP_PARSER.parseBest(
                timestamp, LocalDateTime::from, LocalDate::from);
        } catch (DateTimeParseException e1) {
            throw new IllegalArgumentException(e1.getMessage());
        }

        if (dt instanceof LocalDate) {
            LocalDate localDate = LocalDate.from(dt);
            return localDate.atStartOfDay(UTC).toInstant().toEpochMilli();
        }

        LocalDateTime localDateTime = LocalDateTime.from(dt);
        return localDateTime.toInstant(UTC).toEpochMilli();
    }

    public static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .append(ISO_LOCAL_DATE)
        .optionalStart()
            .padNext(1)
                .optionalStart()
                    .appendLiteral('T')
                .optionalEnd()
            .append(ISO_LOCAL_TIME)
        .optionalStart()
        .appendPattern("[Z][VV][x][xx][xxx]")
        .toFormatter(Locale.ENGLISH).withResolverStyle(ResolverStyle.STRICT);


    @Override
    public StorageSupport<Long> storageSupport() {
        return STORAGE;
    }

    @Override
    public long valueBytes(Long value) {
        return LongType.LONG_SIZE;
    }

    @Override
    public void addMappingOptions(Map<String, Object> mapping) {
        mapping.put("format", "epoch_millis||strict_date_optional_time");
        if (id == ID_WITHOUT_TZ) {
            mapping.put("ignore_timezone", true);
        }
    }

    @Override
    public ColumnStatsSupport<Long> columnStatsSupport() {
        return ColumnStatsSupport.singleValued(Long.class, TimestampType.this);
    }
}

        if (value == null) {
            return null;
        } else if (value instanceof Long l) {
            return l;
        } else if (value instanceof String str) {
            return parse.apply(str);
        } else if (value instanceof Double) {
            // we treat float and double values as seconds with milliseconds as fractions
            // see timestamp documentation
            return ((Number) (((Double) value) * 1000)).longValue();
        } else if (value instanceof Float) {
            return ((Number) (((Float) value) * 1000)).longValue();
        } else if (value instanceof Number number) {
            return number.longValue();
        } else {
            throw new ClassCastException("Can't cast '" + value + "' to " + getName());
        }
    
<methods>public non-sealed void <init>() ,public void addMappingOptions(Map<java.lang.String,java.lang.Object>) ,public java.lang.Integer characterMaximumLength() ,public ColumnStatsSupport<java.lang.Long> columnStatsSupport() ,public int compareTo(DataType<?>) ,public boolean equals(java.lang.Object) ,public java.lang.Long explicitCast(java.lang.Object, io.crate.metadata.settings.SessionSettings) throws java.lang.IllegalArgumentException, java.lang.ClassCastException,public abstract java.lang.String getName() ,public List<DataType<?>> getTypeParameters() ,public io.crate.types.TypeSignature getTypeSignature() ,public int hashCode() ,public abstract int id() ,public java.lang.Long implicitCast(java.lang.Object) throws java.lang.IllegalArgumentException, java.lang.ClassCastException,public boolean isConvertableTo(DataType<?>, boolean) ,public java.lang.Integer numericPrecision() ,public abstract io.crate.types.DataType.Precedence precedence() ,public boolean precedes(DataType<?>) ,public long ramBytesUsed() ,public abstract java.lang.Long sanitizeValue(java.lang.Object) ,public StorageSupport<? super java.lang.Long> storageSupport() ,public final StorageSupport<? super java.lang.Long> storageSupportSafe() ,public abstract Streamer<java.lang.Long> streamer() ,public ColumnType<io.crate.sql.tree.Expression> toColumnType(io.crate.sql.tree.ColumnPolicy, Supplier<List<ColumnDefinition<io.crate.sql.tree.Expression>>>) ,public java.lang.String toString() ,public abstract long valueBytes(java.lang.Long) ,public java.lang.Long valueForInsert(java.lang.Long) ,public final ValueIndexer<? super java.lang.Long> valueIndexer(io.crate.metadata.RelationName, io.crate.metadata.Reference, Function<java.lang.String,FieldType>, Function<io.crate.metadata.ColumnIdent,io.crate.metadata.Reference>) ,public void writeTo(org.elasticsearch.common.io.stream.StreamOutput) throws java.io.IOException<variables>