Exemple #1
0
/* {{{ proto bool datefmt_set_calendar(IntlDateFormatter $mf, mixed $calendar)
 * Set formatter's calendar.
 */
U_CFUNC PHP_FUNCTION(datefmt_set_calendar)
{
	zval	*calendar_zv;
	DATE_FORMAT_METHOD_INIT_VARS;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
			&object, IntlDateFormatter_ce_ptr, &calendar_zv) == FAILURE) {
		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
			"datefmt_set_calendar: unable to parse input params", 0 TSRMLS_CC);
		RETURN_FALSE;
	}

	DATE_FORMAT_METHOD_FETCH_OBJECT;

	Calendar	*cal;
	long		cal_type;
	bool		cal_owned;
	Locale		locale = Locale::createFromName(dfo->requested_locale);
	// getting the actual locale from the DateFormat is not enough
	// because we would have lost modifiers such as @calendar. We
	// must store the requested locale on object creation

	if (datefmt_process_calendar_arg(calendar_zv, locale,
			"datefmt_set_calendar",	INTL_DATA_ERROR_P(dfo), cal, cal_type,
			cal_owned TSRMLS_CC) == FAILURE) {
		RETURN_FALSE;
	}

	if (cal_owned) {
		/* a non IntlCalendar was specified, we want to keep the timezone */
		TimeZone *old_timezone = fetch_datefmt(dfo)->getTimeZone().clone();
		if (old_timezone == NULL) {
			intl_errors_set(INTL_DATA_ERROR_P(dfo), U_MEMORY_ALLOCATION_ERROR,
					"datefmt_set_calendar: Out of memory when cloning calendar",
					0 TSRMLS_CC);
			delete cal;
			RETURN_FALSE;
		}
		cal->adoptTimeZone(old_timezone);
	} else {
		cal = cal->clone();
		if (cal == NULL) {
			intl_errors_set(INTL_DATA_ERROR_P(dfo), U_MEMORY_ALLOCATION_ERROR,
					"datefmt_set_calendar: Out of memory when cloning calendar",
					0 TSRMLS_CC);
			RETURN_FALSE;
		}
	}

	fetch_datefmt(dfo)->adoptCalendar(cal);

	dfo->calendar = cal_type;

	RETURN_TRUE;
}
int main(int argc, char **argv) {

    Calendar *cal;
    TimeZone *zone;
    DateFormat *fmt;
    UErrorCode status = U_ZERO_ERROR;
    UnicodeString str;
    UDate date;

    // The languages in which we will display the date
    static char* LANGUAGE[] = {
        "en", "de", "fr"
    };
    static const int32_t N_LANGUAGE = sizeof(LANGUAGE)/sizeof(LANGUAGE[0]);

    // The time zones in which we will display the time
    static char* TIMEZONE[] = {
        "America/Los_Angeles",
        "America/New_York",
        "Europe/Paris",
        "Europe/Berlin"
    };
    static const int32_t N_TIMEZONE = sizeof(TIMEZONE)/sizeof(TIMEZONE[0]);

    // Create a calendar
    cal = Calendar::createInstance(status);
    check(status, "Calendar::createInstance");
    zone = createZone("GMT"); // Create a GMT zone
    cal->adoptTimeZone(zone);
    cal->clear();
    cal->set(1999, Calendar::JUNE, 4);
    date = cal->getTime(status);
    check(status, "Calendar::getTime");

    for (int32_t i=0; i<N_LANGUAGE; ++i) {
        Locale loc(LANGUAGE[i]);

        // Create a formatter for DATE
        fmt = DateFormat::createDateInstance(DateFormat::kFull, loc);
            
        // Format the date
        str.remove();
        fmt->format(date, str, status);
        
        // Display the formatted date string
        printf("Date (%s): ", LANGUAGE[i]);
        uprintf(escape(str));
        printf("\n\n");
    }

    printf("Exiting successfully\n");
    return 0;
}
Exemple #3
0
void
CalendarLimitTest::TestCalendarExtremeLimit()
{
    UErrorCode status = U_ZERO_ERROR;
    Calendar *cal = Calendar::createInstance(status);
    if (failure(status, "Calendar::createInstance", TRUE)) return;
    cal->adoptTimeZone(TimeZone::createTimeZone("GMT"));
    DateFormat *fmt = DateFormat::createDateTimeInstance();
    if(!fmt || !cal) {
       dataerrln("can't open cal and/or fmt");
       return;
    }
    fmt->adoptCalendar(cal);
    ((SimpleDateFormat*) fmt)->applyPattern("HH:mm:ss.SSS Z, EEEE, MMMM d, yyyy G");


    // This test used to test the algorithmic limits of the dates that
    // GregorianCalendar could handle.  However, the algorithm has
    // been rewritten completely since then and the prior limits no
    // longer apply.  Instead, we now do basic round-trip testing of
    // some extreme (but still manageable) dates.
    UDate m;
    logln("checking 1e16..1e17");
    for ( m = 1e16; m < 1e17; m *= 1.1) {
        test(m, cal, fmt);
    }
    logln("checking -1e14..-1e15");
    for ( m = -1e14; m > -1e15; m *= 1.1) {
        test(m, cal, fmt);
    }

    // This is 2^52 - 1, the largest allowable mantissa with a 0
    // exponent in a 64-bit double
    UDate VERY_EARLY_MILLIS = - 4503599627370495.0;
    UDate VERY_LATE_MILLIS  =   4503599627370495.0;

    // I am removing the previousDouble and nextDouble calls below for
    // two reasons: 1. As part of jitterbug 986, I am deprecating
    // these methods and removing calls to them.  2. This test is a
    // non-critical boundary behavior test.
    test(VERY_EARLY_MILLIS, cal, fmt);
    //test(previousDouble(VERY_EARLY_MILLIS), cal, fmt);
    test(VERY_LATE_MILLIS, cal, fmt);
    //test(nextDouble(VERY_LATE_MILLIS), cal, fmt);
    delete fmt;
}
/**
 * @bug 4126678
 * CANNOT REPRODUDE
 *
 * Yet another _alleged_ bug in TimeZone::getOffset(), a method that never
 * should have been made public.  It's simply too hard to use correctly.
 *
 * The original test code failed to do the following:
 * (1) Call Calendar::setTime() before getting the fields!
 * (2) Use the right millis (as usual) for getOffset(); they were passing
 *     in the MILLIS field, instead of the STANDARD MILLIS IN DAY.
 * When you fix these two problems, the test passes, as expected.
 */
void TimeZoneRegressionTest:: Test4126678() 
{
    UErrorCode status = U_ZERO_ERROR;
    Calendar *cal = Calendar::createInstance(status);
    if(U_FAILURE(status)) {
      dataerrln("Error creating calendar %s", u_errorName(status));
      delete cal;
      return;
    }
    failure(status, "Calendar::createInstance");
    TimeZone *tz = TimeZone::createTimeZone("PST");
    cal->adoptTimeZone(tz);

    cal->set(1998, UCAL_APRIL, 5, 10, 0);
    
    if (! tz->useDaylightTime() || U_FAILURE(status))
        dataerrln("We're not in Daylight Savings Time and we should be. - %s", u_errorName(status));

    //cal.setTime(dt);
    int32_t era = cal->get(UCAL_ERA, status);
    int32_t year = cal->get(UCAL_YEAR, status);
    int32_t month = cal->get(UCAL_MONTH, status);
    int32_t day = cal->get(UCAL_DATE, status);
    int32_t dayOfWeek = cal->get(UCAL_DAY_OF_WEEK, status);
    int32_t millis = cal->get(UCAL_MILLISECOND, status) +
        (cal->get(UCAL_SECOND, status) +
         (cal->get(UCAL_MINUTE, status) +
          (cal->get(UCAL_HOUR, status) * 60) * 60) * 1000) -
        cal->get(UCAL_DST_OFFSET, status);

    failure(status, "cal->get");
    int32_t offset = tz->getOffset((uint8_t)era, year, month, day, (uint8_t)dayOfWeek, millis, status);
    int32_t raw_offset = tz->getRawOffset();

    if (offset == raw_offset)
        dataerrln("Offsets should match");

    delete cal;
}
Exemple #5
0
Calendar*
Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success)
{
  UObject* u = getService()->get(aLocale, LocaleKey::KIND_ANY, success);
  Calendar* c = NULL;

  if(U_FAILURE(success) || !u) {
    delete zone;
    if(U_SUCCESS(success)) { // Propagate some kind of err
      success = U_INTERNAL_PROGRAM_ERROR;
    }
    return NULL;
  }
  
  if(u->getDynamicClassID() == UnicodeString::getStaticClassID()) {
    // It's a unicode string telling us what type of calendar to load ("gregorian", etc)
    char tmp[200];
    const UnicodeString& str = *(UnicodeString*)u;
    // Extract a char* out of it..
    int32_t len = str.length();
    if(len > sizeof(tmp)-1) {
      len = sizeof(tmp)-1;
    }
    str.extract(0,len,tmp);
    tmp[len]=0;
    
#ifdef U_DEBUG_CALSVC
    // fprintf(stderr, "createInstance(%s) told to look at %s..\n", (const char*)aLocale.getName(), tmp);
#endif

    // Create a Locale over this string
    Locale l(tmp);

    delete u;
    u = NULL;
    
    c = (Calendar*)getService()->get(l, LocaleKey::KIND_ANY, success);

    if(U_FAILURE(success) || !c) {
      delete zone;
      if(U_SUCCESS(success)) { 
        success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err
      }
      return NULL;
    }
    
    if(c->getDynamicClassID() == UnicodeString::getStaticClassID()) {
      // recursed! Second lookup returned a UnicodeString. 
      // Perhaps DefaultCalendar{} was set to another locale.
      success = U_MISSING_RESOURCE_ERROR;  // requested a calendar type which could NOT be found.
      delete c;
      delete zone;
      return NULL;
    }
#ifdef U_DEBUG_CALSVC
    fprintf(stderr, "setting to locale %s\n", (const char*)aLocale.getName());
#endif
    c->setWeekCountData(aLocale, success);  // set the correct locale (this was an indirected calendar)
  } else {
    // a calendar was returned - we assume the factory did the right thing.
    c = (Calendar*)u;
  }

  // Now, reset calendar to default state:
  c->adoptTimeZone(zone); //  Set the correct time zone
  c->setTimeInMillis(getNow(), success); // let the new calendar have the current time.
  return c;
}
Exemple #6
0
/** Date-time artithmetic
 *
 * @param time
 * @param value
 * @param units
 * @param tz
 * @param locale
 *
 * @return POSIXst
 *
 * @version 0.5-1 (Marek Gagolewski, 2014-12-30)
 * @version 0.5-1 (Marek Gagolewski, 2015-03-06) tz arg added
 */
SEXP stri_datetime_add(SEXP time, SEXP value, SEXP units, SEXP tz, SEXP locale) {
   PROTECT(time = stri_prepare_arg_POSIXct(time, "time"));
   PROTECT(value = stri_prepare_arg_integer(value, "value"));
   if (!isNull(tz)) PROTECT(tz = stri_prepare_arg_string_1(tz, "tz"));
   else             PROTECT(tz); /* needed to set tzone attrib */

   R_len_t vectorize_length = stri__recycling_rule(true, 2, LENGTH(time), LENGTH(value));

   const char* units_val = stri__prepare_arg_string_1_notNA(units, "units");
   const char* units_opts[] = {"years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", NULL};
   int units_cur = stri__match_arg(units_val, units_opts);

   const char* locale_val = stri__prepare_arg_locale(locale, "locale", true);

   TimeZone* tz_val = stri__prepare_arg_timezone(tz, "tz", true/*allowdefault*/);

   Calendar* cal = NULL;
   STRI__ERROR_HANDLER_BEGIN(3)
   StriContainerDouble time_cont(time, vectorize_length);
   StriContainerInteger value_cont(value, vectorize_length);

   UCalendarDateFields units_field;
   switch (units_cur) {
      case 0: units_field = UCAL_YEAR;                   break;
      case 1: units_field = UCAL_MONTH;                  break;
      case 2: units_field = UCAL_WEEK_OF_YEAR;           break;
      case 3: units_field = UCAL_DAY_OF_MONTH;           break;
      case 4: units_field = UCAL_HOUR_OF_DAY;            break;
      case 5: units_field = UCAL_MINUTE;                 break;
      case 6: units_field = UCAL_SECOND;                 break;
      case 7: units_field = UCAL_MILLISECOND;            break;
      default: throw StriException(MSG__INCORRECT_MATCH_OPTION, "units");
   }

   UErrorCode status = U_ZERO_ERROR;
   cal = Calendar::createInstance(locale_val, status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   cal->adoptTimeZone(tz_val);
   tz_val = NULL; /* The Calendar takes ownership of the TimeZone. */

   SEXP ret;
   STRI__PROTECT(ret = Rf_allocVector(REALSXP, vectorize_length));
   double* ret_val = REAL(ret);
   for (R_len_t i=0; i<vectorize_length; ++i) {
      if (time_cont.isNA(i) || value_cont.isNA(i)) {
         ret_val[i] = NA_REAL;
         continue;
      }
      status = U_ZERO_ERROR;
      cal->setTime((UDate)(time_cont.get(i)*1000.0), status);
      STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

      status = U_ZERO_ERROR;
      cal->add(units_field, value_cont.get(i), status);
      STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

      status = U_ZERO_ERROR;
      ret_val[i] = ((double)cal->getTime(status))/1000.0;
      STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})
   }

   if (!isNull(tz)) Rf_setAttrib(ret, Rf_ScalarString(Rf_mkChar("tzone")), tz);
   stri__set_class_POSIXct(ret);
   if (tz_val) { delete tz_val; tz_val = NULL; }
   if (cal) { delete cal; cal = NULL; }
   STRI__UNPROTECT_ALL
   return ret;
   STRI__ERROR_HANDLER_END({
      if (tz_val) { delete tz_val; tz_val = NULL; }
      if (cal) { delete cal; cal = NULL; }
   })
}
Exemple #7
0
/**
 * Get values of date-time fields
 *
 * @param time
 * @param locale
 * @param tz
 *
 * @return list
 *
 * @version 0.5-1 (Marek Gagolewski, 2015-01-01)
 * @version 0.5-1 (Marek Gagolewski, 2015-03-03) tz arg added
 */
SEXP stri_datetime_fields(SEXP time, SEXP tz, SEXP locale) {
   PROTECT(time = stri_prepare_arg_POSIXct(time, "time"));
   const char* locale_val = stri__prepare_arg_locale(locale, "locale", true);
   if (!isNull(tz)) PROTECT(tz = stri_prepare_arg_string_1(tz, "tz"));
   else             PROTECT(tz); /* needed to set tzone attrib */

   TimeZone* tz_val = stri__prepare_arg_timezone(tz, "tz", true/*allowdefault*/);
   Calendar* cal = NULL;
   STRI__ERROR_HANDLER_BEGIN(2)
   R_len_t vectorize_length = LENGTH(time);
   StriContainerDouble time_cont(time, vectorize_length);

   UErrorCode status = U_ZERO_ERROR;
   cal = Calendar::createInstance(locale_val, status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   cal->adoptTimeZone(tz_val);
   tz_val = NULL; /* The Calendar takes ownership of the TimeZone. */

   SEXP ret;
#define STRI__FIELDS_NUM 14
   STRI__PROTECT(ret = Rf_allocVector(VECSXP, STRI__FIELDS_NUM));
   for (R_len_t j=0; j<STRI__FIELDS_NUM; ++j)
      SET_VECTOR_ELT(ret, j, Rf_allocVector(INTSXP, vectorize_length));

   for (R_len_t i=0; i<vectorize_length; ++i) {
      if (time_cont.isNA(i)) {
         for (R_len_t j=0; j<STRI__FIELDS_NUM; ++j)
            INTEGER(VECTOR_ELT(ret, j))[i] = NA_INTEGER;
         continue;
      }

      status = U_ZERO_ERROR;
      cal->setTime((UDate)(time_cont.get(i)*1000.0), status);
      STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

      for (R_len_t j=0; j<STRI__FIELDS_NUM; ++j) {
         UCalendarDateFields units_field;
         switch (j) {
            case 0:  units_field = UCAL_EXTENDED_YEAR;          break;
            case 1:  units_field = UCAL_MONTH;                  break;
            case 2:  units_field = UCAL_DAY_OF_MONTH;           break;
            case 3:  units_field = UCAL_HOUR_OF_DAY;            break;
            case 4:  units_field = UCAL_MINUTE;                 break;
            case 5:  units_field = UCAL_SECOND;                 break;
            case 6:  units_field = UCAL_MILLISECOND;            break;
            case 7:  units_field = UCAL_WEEK_OF_YEAR;           break;
            case 8:  units_field = UCAL_WEEK_OF_MONTH;          break;
            case 9:  units_field = UCAL_DAY_OF_YEAR;            break;
            case 10: units_field = UCAL_DAY_OF_WEEK;            break;
            case 11: units_field = UCAL_HOUR;                   break;
            case 12: units_field = UCAL_AM_PM;                  break;
            case 13: units_field = UCAL_ERA;                    break;
            default: throw StriException(MSG__INCORRECT_MATCH_OPTION, "units");
         }
         //UCAL_IS_LEAP_MONTH
         //UCAL_MILLISECONDS_IN_DAY -> SecondsInDay

         // UCAL_AM_PM -> "AM" or "PM" (localized? or factor?+index in stri_datetime_symbols) add arg use_symbols????
         // UCAL_DAY_OF_WEEK -> (localized? or factor?) SUNDAY, MONDAY
         // UCAL_DAY_OF_YEAR '

         // isWekend

         status = U_ZERO_ERROR;
         INTEGER(VECTOR_ELT(ret, j))[i] = cal->get(units_field, status);
         STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

         if (units_field == UCAL_MONTH)      ++INTEGER(VECTOR_ELT(ret, j))[i]; // month + 1
         else if (units_field == UCAL_AM_PM) ++INTEGER(VECTOR_ELT(ret, j))[i]; // ampm + 1
         else if (units_field == UCAL_ERA)   ++INTEGER(VECTOR_ELT(ret, j))[i]; // era + 1
      }
/**
 * Format date-time objects
 *
 * @param time
 * @param format
 * @param tz
 * @param locale
 *
 * @return character vector
 *
 * @version 0.5-1 (Marek Gagolewski, 2015-01-05)
 *
 * @version 0.5-1 (Marek Gagolewski, 2015-02-22)
 *    use tz
 */
SEXP stri_datetime_format(SEXP time, SEXP format, SEXP tz, SEXP locale) {
   PROTECT(time = stri_prepare_arg_POSIXct(time, "time"));
   const char* locale_val = stri__prepare_arg_locale(locale, "locale", true);
   const char* format_val = stri__prepare_arg_string_1_notNA(format, "format");

   // "format" may be one of:
   const char* format_opts[] = {
      "date_full", "date_long", "date_medium", "date_short",
      "date_relative_full", "date_relative_long", "date_relative_medium", "date_relative_short",
      "time_full", "time_long", "time_medium", "time_short",
      "time_relative_full", "time_relative_long", "time_relative_medium", "time_relative_short",
      "datetime_full", "datetime_long", "datetime_medium", "datetime_short",
      "datetime_relative_full", "datetime_relative_long", "datetime_relative_medium", "datetime_relative_short",
      NULL};
   int format_cur = stri__match_arg(format_val, format_opts);

   TimeZone* tz_val = stri__prepare_arg_timezone(tz, "tz", true/*allowdefault*/);
   Calendar* cal = NULL;
   DateFormat* fmt = NULL;
   STRI__ERROR_HANDLER_BEGIN(1)
   R_len_t vectorize_length = LENGTH(time);
   StriContainerDouble time_cont(time, vectorize_length);
   UnicodeString format_str(format_val);

   UErrorCode status = U_ZERO_ERROR;
   if (format_cur >= 0) {
      DateFormat::EStyle style = DateFormat::kNone;
      switch (format_cur % 8) {
         case 0:  style = DateFormat::kFull; break;
         case 1:  style = DateFormat::kLong; break;
         case 2:  style = DateFormat::kMedium; break;
         case 3:  style = DateFormat::kShort; break;
         case 4:  style = DateFormat::kFullRelative; break;
         case 5:  style = DateFormat::kLongRelative; break;
         case 6:  style = DateFormat::kMediumRelative; break;
         case 7:  style = DateFormat::kShortRelative; break;
         default: style = DateFormat::kNone; break;
      }

      /* ICU 54.1: Relative time styles are not currently supported.  */
      switch (format_cur / 8) {
         case 0:
            fmt = DateFormat::createDateInstance(style,
               Locale::createFromName(locale_val));
            break;

         case 1:
            fmt = DateFormat::createTimeInstance(
               (DateFormat::EStyle)(style & ~DateFormat::kRelative),
               Locale::createFromName(locale_val));
            break;

         case 2:
            fmt = DateFormat::createDateTimeInstance(style,
               (DateFormat::EStyle)(style & ~DateFormat::kRelative),
               Locale::createFromName(locale_val));
            break;

         default:
            fmt = NULL;
            break;

      }
   }
   else
      fmt = new SimpleDateFormat(format_str, Locale::createFromName(locale_val), status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   status = U_ZERO_ERROR;
   cal = Calendar::createInstance(locale_val, status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   cal->adoptTimeZone(tz_val);
   tz_val = NULL; /* The Calendar takes ownership of the TimeZone. */

   SEXP ret;
   STRI__PROTECT(ret = Rf_allocVector(STRSXP, vectorize_length));
   for (R_len_t i=0; i<vectorize_length; ++i) {
      if (time_cont.isNA(i)) {
         SET_STRING_ELT(ret, i, NA_STRING);
         continue;
      }

      status = U_ZERO_ERROR;
      cal->setTime((UDate)(time_cont.get(i)*1000.0), status);
      STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

      FieldPosition pos;
      UnicodeString out;
      fmt->format(*cal, out, pos);

      std::string s;
      out.toUTF8String(s);
      SET_STRING_ELT(ret, i, Rf_mkCharLenCE(s.c_str(), (int)s.length(), (cetype_t)CE_UTF8));
   }

   if (tz_val) { delete tz_val; tz_val = NULL; }
   if (fmt) { delete fmt; fmt = NULL; }
   if (cal) { delete cal; cal = NULL; }
   STRI__UNPROTECT_ALL
   return ret;
   STRI__ERROR_HANDLER_END({
      if (tz_val) { delete tz_val; tz_val = NULL; }
      if (fmt) { delete fmt; fmt = NULL; }
      if (cal) { delete cal; cal = NULL; }
   })
}
/**
 * Parse date-time objects
 *
 * @param str
 * @param format
 * @param tz
 * @param lenient
 * @param locale
 *
 * @return character vector
 *
 * @version 0.5-1 (Marek Gagolewski, 2015-01-08)
 * @version 0.5-1 (Marek Gagolewski, 2015-01-11) lenient arg added
 * @version 0.5-1 (Marek Gagolewski, 2015-02-22) use tz
 * @version 0.5-1 (Marek Gagolewski, 2015-03-01) set tzone attrib on retval
 */
SEXP stri_datetime_parse(SEXP str, SEXP format, SEXP lenient, SEXP tz, SEXP locale) {
   PROTECT(str = stri_prepare_arg_string(str, "str"));
   const char* locale_val = stri__prepare_arg_locale(locale, "locale", true);
   const char* format_val = stri__prepare_arg_string_1_notNA(format, "format");
   bool lenient_val = stri__prepare_arg_logical_1_notNA(lenient, "lenient");
   if (!isNull(tz)) PROTECT(tz = stri_prepare_arg_string_1(tz, "tz"));
   else             PROTECT(tz); /* needed to set tzone attrib */

   // "format" may be one of:
   const char* format_opts[] = {
      "date_full", "date_long", "date_medium", "date_short",
      "date_relative_full", "date_relative_long", "date_relative_medium", "date_relative_short",
      "time_full", "time_long", "time_medium", "time_short",
      "time_relative_full", "time_relative_long", "time_relative_medium", "time_relative_short",
      "datetime_full", "datetime_long", "datetime_medium", "datetime_short",
      "datetime_relative_full", "datetime_relative_long", "datetime_relative_medium", "datetime_relative_short",
      NULL};
   int format_cur = stri__match_arg(format_val, format_opts);

   TimeZone* tz_val = stri__prepare_arg_timezone(tz, "tz", true/*allowdefault*/);
   Calendar* cal = NULL;
   DateFormat* fmt = NULL;
   STRI__ERROR_HANDLER_BEGIN(2)
   R_len_t vectorize_length = LENGTH(str);
   StriContainerUTF16 str_cont(str, vectorize_length);
   UnicodeString format_str(format_val);

   UErrorCode status = U_ZERO_ERROR;
   if (format_cur >= 0) {
      DateFormat::EStyle style = DateFormat::kNone;
      switch (format_cur % 8) {
         case 0:  style = DateFormat::kFull; break;
         case 1:  style = DateFormat::kLong; break;
         case 2:  style = DateFormat::kMedium; break;
         case 3:  style = DateFormat::kShort; break;
         case 4:  style = DateFormat::kFullRelative; break;
         case 5:  style = DateFormat::kLongRelative; break;
         case 6:  style = DateFormat::kMediumRelative; break;
         case 7:  style = DateFormat::kShortRelative; break;
         default: style = DateFormat::kNone; break;
      }

      /* ICU 54.1: Relative time styles are not currently supported.  */
      switch (format_cur / 8) {
         case 0:
            fmt = DateFormat::createDateInstance(style, Locale::createFromName(locale_val));
            break;

         case 1:
            fmt = DateFormat::createTimeInstance((DateFormat::EStyle)(style & ~DateFormat::kRelative),
               Locale::createFromName(locale_val));
            break;

         case 2:
            fmt = DateFormat::createDateTimeInstance(style,
               (DateFormat::EStyle)(style & ~DateFormat::kRelative),
               Locale::createFromName(locale_val));
            break;

         default:
            fmt = NULL;
            break;

      }
   }
   else
      fmt = new SimpleDateFormat(format_str, Locale::createFromName(locale_val), status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   status = U_ZERO_ERROR;
   cal = Calendar::createInstance(locale_val, status);
   STRI__CHECKICUSTATUS_THROW(status, {/* do nothing special on err */})

   cal->adoptTimeZone(tz_val);
   tz_val = NULL; /* The Calendar takes ownership of the TimeZone. */

   cal->setLenient(lenient_val);

   SEXP ret;
   STRI__PROTECT(ret = Rf_allocVector(REALSXP, vectorize_length));
   for (R_len_t i=0; i<vectorize_length; ++i) {
      if (str_cont.isNA(i)) {
         REAL(ret)[i] = NA_REAL;
         continue;
      }

      status = U_ZERO_ERROR;
      ParsePosition pos;
      fmt->parse(str_cont.get(i), *cal, pos);

      if (pos.getErrorIndex() >= 0)
         REAL(ret)[i] = NA_REAL;
      else {
         status = U_ZERO_ERROR;
         REAL(ret)[i] = ((double)cal->getTime(status))/1000.0;
         if (U_FAILURE(status)) REAL(ret)[i] = NA_REAL;
      }
   }


   if (!isNull(tz)) Rf_setAttrib(ret, Rf_ScalarString(Rf_mkChar("tzone")), tz);
   stri__set_class_POSIXct(ret);
   if (tz_val) { delete tz_val; tz_val = NULL; }
   if (fmt) { delete fmt; fmt = NULL; }
   if (cal) { delete cal; cal = NULL; }
   STRI__UNPROTECT_ALL
   return ret;
   STRI__ERROR_HANDLER_END({
      if (tz_val) { delete tz_val; tz_val = NULL; }
      if (fmt) { delete fmt; fmt = NULL; }
      if (cal) { delete cal; cal = NULL; }
   })
}