apache/iceberg · error · UnsupportedOperationException

Unsupported time unit:

Error message

Unsupported time unit: 

What it means

Dates.DateToYearsTransform (and sibling date transforms) apply only to a fixed set of granularities: YEARS, MONTHS, DAYS. If apply() encounters any other ChronoUnit, Iceberg throws UnsupportedOperationException. This indicates the transform was constructed or dispatched with a granularity the date transform does not implement.

Source

Thrown at api/src/main/java/org/apache/iceberg/transforms/Dates.java:61

    Apply(ChronoUnit granularity) {
      this.granularity = granularity;
    }

    @Override
    public Integer apply(Integer days) {
      if (days == null) {
        return null;
      }

      switch (granularity) {
        case YEARS:
          return DateTimeUtil.daysToYears(days);
        case MONTHS:
          return DateTimeUtil.daysToMonths(days);
        case DAYS:
          return days;
        default:
          throw new UnsupportedOperationException("Unsupported time unit: " + granularity);
      }
    }
  }

  private final ChronoUnit granularity;
  private final String name;
  private final Apply apply;

  Dates(ChronoUnit granularity, String name) {
    this.granularity = granularity;
    this.name = name;
    this.apply = new Apply(granularity);
  }

  /**
   * Transforms a value to its corresponding partition value.
   *
   * @param days a source value

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use the correct transform family: Dates for day-granularity sources, Hours for hour sources, or a timestamp transform for finer units.
  2. Validate granularity is one of ChronoUnit.YEARS/MONTHS/DAYS before constructing/calling the date transform.
  3. Catch UnsupportedOperationException at dispatch boundaries when translating unknown granularities.

Example fix

// before
Transform<Integer, Integer> t = Dates.from(granularity); // granularity = HOURS
Integer out = t.apply(days);
// after
Preconditions.checkArgument(granularity == ChronoUnit.YEARS || granularity == ChronoUnit.MONTHS || granularity == ChronoUnit.DAYS, "Invalid date granularity: %s", granularity);
Transform<Integer, Integer> t = Dates.from(granularity);
Defensive patterns

Strategy: validation

Validate before calling

if (granularity != ChronoUnit.YEARS && granularity != ChronoUnit.MONTHS && granularity != ChronoUnit.DAYS) { throw new IllegalArgumentException("Invalid date granularity: " + granularity); }

Type guard

boolean isDateGranularity(ChronoUnit u) { return u == ChronoUnit.YEARS || u == ChronoUnit.MONTHS || u == ChronoUnit.DAYS; }

Try / catch

try { return dateTransform.apply(days); } catch (UnsupportedOperationException e) { throw new IllegalArgumentException("Granularity not supported by date transform", e); }

Prevention

When it happens

Trigger: Building a Dates transform (e.g. via TransformUtil or parsing) with a granularity outside YEARS/MONTHS/DAYS (e.g. HOURS, MINUTES), then calling apply(days).

Common situations: Parsing partition field transforms from strings or specs where an invalid granularity slipped in; generic transform-dispatch code reusing a date transform for hour-level source data (which belongs to the Hours/ timestamps transforms).

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/fcb49093474a47d9. Report an issue: GitHub.