{"record":{"id":"1885858e70c1daee","repo":"apache/iceberg","slug":"expected-value-to-be-timestamp","errorCode":null,"errorMessage":"Expected value to be timestamp: ","messagePattern":"Expected value to be timestamp: ","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/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.1/spark/src/main/java/org/apache/iceberg/spark/functions/HoursFunction.java#L28-L64","documentation":"Bind-time type validation in HoursFunction.doBind: the argument to system.hours is neither TimestampType nor TimestampNTZType — the hour transform only applies to timestamp-like inputs. The appended value is the actual type; binding fails before execution.","triggerScenarios":"Calling system.hours(<expr>) with a date, string, or numeric expression instead of a timestamp, e.g. system.hours(d) where d is DATE.","commonSituations":"Applying hours() to date partition columns; passing string timestamps without casting; applying hours() to epoch-millis bigints.","solutions":["Cast a date/string to timestamp: system.hours(CAST(d AS TIMESTAMP)) or system.hours(to_timestamp(s))","Use system.days or system.months for date-only columns instead of hours","Convert epoch values: system.hours(timestamp_millis(col))"],"exampleFix":"// before\nSELECT * FROM t WHERE system.hours(event_date) >= 100;   -- DATE\n// after\nSELECT * FROM t WHERE system.hours(CAST(event_date AS TIMESTAMP)) >= 100;","handlingStrategy":"type-guard","validationCode":"if (!(value.dataType() instanceof TimestampType) && !(value.dataType() instanceof TimestampNTZType)) {\n  throw new IllegalArgumentException(\"hours() requires timestamp, got: \" + value.dataType());\n}","typeGuard":"boolean isTimestampLike(DataType t) {\n  return t instanceof TimestampType || t instanceof TimestampNTZType;\n}","tryCatchPattern":"try {\n  bound = hoursFn.doBind(valueType);\n} catch (UnsupportedOperationException e) {\n  throw new IllegalArgumentException(\"Cast the expression to TIMESTAMP\", e);\n}","preventionTips":["Use days()/months() for DATE columns instead of hours()","Cast date or string values to timestamp before hours()","Convert epoch values via timestamp_millis/timestamp_micros"],"tags":["spark","sql-function","date-transform","type-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}