@Override
  public Coupon toDerivative(
      final ZonedDateTime date,
      final DoubleTimeSeries<ZonedDateTime> priceIndexTimeSeries,
      final String... yieldCurveNames) {
    ArgumentChecker.notNull(date, "date");
    ArgumentChecker.notNull(yieldCurveNames, "yield curve names");
    ArgumentChecker.isTrue(yieldCurveNames.length > 0, "at least one curve required");
    ArgumentChecker.isTrue(!date.isAfter(getPaymentDate()), "date is after payment date");
    final double lastKnownFixingTime =
        TimeCalculator.getTimeBetween(date, getLastKnownFixingDate());
    final LocalDate dayConversion = date.toLocalDate();
    final String discountingCurveName = yieldCurveNames[0];
    final double paymentTime = TimeCalculator.getTimeBetween(date, getPaymentDate());
    final LocalDate dayFixing = getReferenceEndDate()[1].toLocalDate();
    if (dayConversion.isAfter(dayFixing)) {
      final Double fixedEndIndex1 = priceIndexTimeSeries.getValue(getReferenceEndDate()[1]);

      if (fixedEndIndex1 != null) {
        final Double fixedEndIndex0 = priceIndexTimeSeries.getValue(getReferenceEndDate()[0]);
        final Double fixedEndIndex =
            getWeight() * fixedEndIndex0 + (1 - getWeight()) * fixedEndIndex1;
        final Double fixedRate = (fixedEndIndex / getIndexStartValue() - 1.0);
        return new CouponFixed(
            getCurrency(),
            paymentTime,
            discountingCurveName,
            getPaymentYearFraction(),
            getNotional(),
            payOff(fixedRate));
      }
    }
    final double[] referenceEndTime = new double[2];
    referenceEndTime[0] = TimeCalculator.getTimeBetween(date, _referenceEndDate[0]);
    referenceEndTime[1] = TimeCalculator.getTimeBetween(date, _referenceEndDate[1]);
    final ZonedDateTime naturalPaymentDate =
        getPaymentDate().minusMonths(_monthLag - _conventionalMonthLag);
    final double naturalPaymentEndTime = TimeCalculator.getTimeBetween(date, naturalPaymentDate);
    return new CapFloorInflationZeroCouponInterpolation(
        getCurrency(),
        paymentTime,
        getPaymentYearFraction(),
        getNotional(),
        getPriceIndex(),
        lastKnownFixingTime,
        _indexStartValue,
        referenceEndTime,
        naturalPaymentEndTime,
        _maturity,
        _weight,
        _strike,
        _isCap);
  }
 /**
  * {@inheritDoc} If the fixing date is strictly before the conversion date and the fixing rate is
  * not available, an exception is thrown; if the fixing rate is available a fixed coupon is
  * returned. If the fixing date is equal to the conversion date, if the fixing rate is available a
  * fixed coupon is returned, if not a coupon Ibor with spread is returned. If the fixing date is
  * strictly after the conversion date, a coupon Ibor is returned. All the comparisons are between
  * dates without time.
  */
 @Override
 public Coupon toDerivative(
     final ZonedDateTime dateTime, final DoubleTimeSeries<ZonedDateTime> indexFixingTimeSeries) {
   ArgumentChecker.notNull(dateTime, "date");
   final LocalDate dayConversion = dateTime.toLocalDate();
   ArgumentChecker.notNull(indexFixingTimeSeries, "Index fixing time series");
   ArgumentChecker.isTrue(
       !dayConversion.isAfter(getPaymentDate().toLocalDate()), "date is after payment date");
   final double paymentTime = TimeCalculator.getTimeBetween(dateTime, getPaymentDate());
   final LocalDate dayFixing = getFixingDate().toLocalDate();
   if (dayConversion.equals(
       dayFixing)) { // The fixing is on the reference date; if known the fixing is used and if
                     // not, the floating coupon is created.
     final Double fixedRate = indexFixingTimeSeries.getValue(getFixingDate());
     if (fixedRate != null) {
       return new CouponFixed(
           getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), fixedRate);
     }
   }
   if (dayConversion.isAfter(dayFixing)) { // The fixing is required
     final ZonedDateTime rezonedFixingDate =
         getFixingDate().toLocalDate().atStartOfDay(ZoneOffset.UTC);
     final Double fixedRate =
         indexFixingTimeSeries.getValue(rezonedFixingDate); // TODO: remove time from fixing date.
     if (fixedRate == null) {
       throw new OpenGammaRuntimeException(
           "Could not get fixing value for date " + getFixingDate());
     }
     return new CouponFixed(
         getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), fixedRate);
   }
   final double fixingTime = TimeCalculator.getTimeBetween(dateTime, getFixingDate());
   final double fixingPeriodStartTime =
       TimeCalculator.getTimeBetween(dateTime, getFixingPeriodStartDate());
   final double fixingPeriodEndTime =
       TimeCalculator.getTimeBetween(dateTime, getFixingPeriodEndDate());
   return new CouponIbor(
       getCurrency(),
       paymentTime,
       getPaymentYearFraction(),
       getNotional(),
       fixingTime,
       getIndex(),
       fixingPeriodStartTime,
       fixingPeriodEndTime,
       getFixingPeriodAccrualFactor());
 }