{"record":{"id":"4a548365e18c29ef","repo":"apache/iceberg","slug":"expected-value-to-be-timestamp-valuetype-catalo-4a5483","errorCode":null,"errorMessage":"Expected value to be timestamp: ${valueType.catalogString()}","messagePattern":"Expected value to be timestamp: (.+?)","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/functions/HoursFunction.java","lineNumber":46,"sourceCode":"import org.apache.spark.sql.types.DataTypes;\nimport org.apache.spark.sql.types.TimestampNTZType;\nimport org.apache.spark.sql.types.TimestampType;\n\n/**\n * A Spark function implementation for the Iceberg hour transform.\n *\n * <p>Example usage: {@code SELECT system.hours('source_col')}.\n */\npublic class HoursFunction extends UnaryUnboundFunction {\n\n  @Override\n  protected BoundFunction doBind(DataType valueType) {\n    if (valueType instanceof TimestampType) {\n      return new TimestampToHoursFunction();\n    } else if (valueType instanceof TimestampNTZType) {\n      return new TimestampNtzToHoursFunction();\n    } else {\n      throw new UnsupportedOperationException(\n          \"Expected value to be timestamp: \" + valueType.catalogString());\n    }\n  }\n\n  @Override\n  public String description() {\n    return name()\n        + \"(col) - Call Iceberg's hour transform\\n\"\n        + \"  col :: source column (must be timestamp)\";\n  }\n\n  @Override\n  public String name() {\n    return \"hours\";\n  }\n\n  public abstract static class BaseToHourFunction extends BaseScalarFunction<Integer>\n      implements ReducibleFunction<Integer, Integer> {","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/functions/HoursFunction.java#L28-L64","documentation":"Iceberg's hours(x) Spark transform function only accepts timestamp or timestamp_ntz inputs — there is no hour bucketing for plain dates. Binding any other type (date, string, numeric) throws this UnsupportedOperationException with the offending type's catalogString appended. Thrown during query analysis via doBind.","triggerScenarios":"Calling hours(value) where value is a DateType, StringType, or numeric column, e.g. hours(d) where d is DATE, or hours(ts_string).","commonSituations":"Applying hours() to a date column assuming it works like days(); passing string timestamps from log tables; partition spec definitions using hours on date columns.","solutions":["Cast the date column to timestamp: hours(CAST(d AS TIMESTAMP)).","Parse string timestamps: hours(to_timestamp(ts_str)).","If hourly granularity on a date is intended, use days(d) instead — dates have no time component.","Verify the column type with DESCRIBE TABLE and use a timestamp column."],"exampleFix":"// before\nSELECT hours(d) FROM t  -- d is DATE\n// after\nSELECT hours(CAST(d AS TIMESTAMP)) FROM t","handlingStrategy":"validation","validationCode":"// Spark Scala\nval dt = df.schema(\"value_col\").dataType\nrequire(dt.typeName.startsWith(\"timestamp\"),\n  s\"hours() requires TIMESTAMP or TIMESTAMP_NTZ, got: ${dt.catalogString}\")","typeGuard":"def isTimestampLike(dt: org.apache.spark.sql.types.DataType): Boolean =\n  dt.isInstanceOf[org.apache.spark.sql.types.TimestampType] || dt.typeName == \"timestamp_ntz\"","tryCatchPattern":"try {\n  df.select(expr(\"hours(ts_col)\"))\n} catch {\n  case e: UnsupportedOperationException if e.getMessage.startsWith(\"Expected value to be timestamp\") =>\n    throw new IllegalArgumentException(\"hours() needs TIMESTAMP/TIMESTAMP_NTZ; cast DATE with CAST(col AS TIMESTAMP)\", e)\n}","preventionTips":["Use days() for DATE columns; hours() only accepts timestamps","Cast DATE to TIMESTAMP when sub-day granularity is truly needed","Parse string timestamps with to_timestamp() before hours()","Validate partition spec column types at table creation"],"tags":["spark","sql-function","type-mismatch","temporal"],"backgroundTag":"type-mismatch","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}