docs: @Override
      public Object mapSequence(Object sequence) {
          List<Long> longs = (List<Long>) sequence;
          List<List<Object>> ret = new ArrayList<>();
          for (Long l : longs)
              ret.add((List<Object>) map(l));
          return ret;
      }
  
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder();
          sb.append("DeriveColumnsFromTimeTransform(timeColumn=\"").append(columnName).append("\",insertAfter=\"")
                          .append(insertAfter).append("\",derivedColumns=(");
  
          boolean first = true;
          for (DerivedColumn d : derivedColumns) {
              if (!first)
                  sb.append(",");
              sb.append(d);
              first = false;
          }
  
          sb.append("))");
  
          return sb.toString();
      }
  
      /**
       * The output column name
       * after the operation has been applied
       *
       * @return the output column name
       */
      @Override
      public String outputColumnName() {
          return outputColumnNames()[0];
      }
  
      /**
       * The output column names
       * This will often be the same as the input
       *
       * @return the output column names
       */
      @Override
      public String[] outputColumnNames() {
          String[] ret = new String[derivedColumns.size()];
          for (int i = 0; i < ret.length; i++)
              ret[i] = derivedColumns.get(i).columnName;
          return ret;
      }
  
      /**
       * Returns column names
       * this op is meant to run on
       *
       * @return
       */
      @Override
      public String[] columnNames() {
          return new String[] {columnName()};
      }
  
      /**
       * Returns a singular column name
       * this op is meant to run on
       *
       * @return
       */
      @Override
      public String columnName() {
          return columnName;
      }
  
      public static class Builder {
  
          private final String columnName;
          private String insertAfter;
          private final List<DerivedColumn> derivedColumns = new ArrayList<>();
  
  
          /**
           * @param timeColumnName The name of the time column from which to derive the new values
           */
          public Builder(String timeColumnName) {
              this.columnName = timeColumnName;
              this.insertAfter = timeColumnName;
          }
  
          /**
           * Where should the new columns be inserted?
           * By default, they will be inserted after the source column
           *
           * @param columnName Name of the column to insert the derived columns after
           */
          public Builder insertAfter(String columnName) {
              this.insertAfter = columnName;
              return this;
          }
  
          /**
           * Add a String column (for example, human readable format), derived from the time
           *
           * @param columnName Name of the new/derived column
           * @param format     Joda time format, as per <a href="http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html">http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html</a>
           * @param timeZone   Timezone to use for formatting
           */
          public Builder addStringDerivedColumn(String columnName, String format, DateTimeZone timeZone) {
              derivedColumns.add(new DerivedColumn(columnName, ColumnType.String, format, timeZone, null));
              return this;
          }
  
          /**
           * Add an integer derived column - for example, the hour of day, etc. Uses timezone from the time column metadata
           *
           * @param columnName Name of the column
           * @param type       Type of field (for example, DateTimeFieldType.hourOfDay() etc)
           */
          public Builder addIntegerDerivedColumn(String columnName, DateTimeFieldType type) {
              derivedColumns.add(new DerivedColumn(columnName, ColumnType.Integer, null, null, type));
              return this;
          }
  
          /**
           * Create the transform instance
           */
          public DeriveColumnsFromTimeTransform build() {
              return new DeriveColumnsFromTimeTransform(this);
          }
      }
  
      @JsonInclude(JsonInclude.Include.NON_NULL)
      @EqualsAndHashCode(exclude = "dateTimeFormatter")
      @Data
      @JsonIgnoreProperties({"dateTimeFormatter"})
      public static class DerivedColumn implements Serializable {
          private final String columnName;
          private final ColumnType columnType;
          private final String format;
          private final DateTimeZone dateTimeZone;
          @JsonSerialize(using = DateTimeFieldTypeSerializer.class)
          @JsonDeserialize(using = DateTimeFieldTypeDeserializer.class)
          private final DateTimeFieldType fieldType;
          private transient DateTimeFormatter dateTimeFormatter;
  
          //        public DerivedColumn(String columnName, ColumnType columnType, String format, DateTimeZone dateTimeZone, DateTimeFieldType fieldType) {
          public DerivedColumn(@JsonProperty("columnName") String columnName,
                          @JsonProperty("columnType") ColumnType columnType, @JsonProperty("format") String format,
                          @JsonProperty("dateTimeZone") DateTimeZone dateTimeZone,
                          @JsonProperty("fieldType") DateTimeFieldType fieldType) {
              this.columnName = columnName;
              this.columnType = columnType;
              this.format = format;
              this.dateTimeZone = dateTimeZone;
              this.fieldType = fieldType;
              if (format != null)
                  dateTimeFormatter = DateTimeFormat.forPattern(this.format).withZone(dateTimeZone);
          }
  
          @Override
          public String toString() {
              return "(name=" + columnName + ",type=" + columnType + ",derived=" + (format != null ? format : fieldType)
                              + ")";
          }
  
          //Custom serialization methods, because Joda Time doesn't allow DateTimeFormatter objects to be serialized :(
          private void writeObject(ObjectOutputStream out) throws IOException {
              out.defaultWriteObject();
          }
  
          private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
              in.defaultReadObject();
              if (format != null)
                  dateTimeFormatter = DateTimeFormat.forPattern(format).withZone(dateTimeZone);
          }
      }
  }