public function humanReadable(array $opt = array())
{
if (self::$intl_loaded === null) {
self::$intl_loaded = extension_loaded('intl');
}
// attempt to detect default locale
if (self::$intl_loaded) {
$locale = \Locale::getDefault();
} else {
$locale = setlocale(LC_MESSAGES, 0);
if ($locale == 'C') {
$locale = 'en';
}
}
$default_opt = array('locale' => $locale, 'date_formatter' => null, 'fallback' => 'en');
if (self::$intl_loaded) {
$default_opt['date_format'] = \IntlDateFormatter::SHORT;
if ($this->freq >= self::SECONDLY || not_empty($this->rule['BYSECOND'])) {
$default_opt['time_format'] = \IntlDateFormatter::LONG;
} elseif ($this->freq >= self::HOURLY || not_empty($this->rule['BYHOUR']) || not_empty($this->rule['BYMINUTE'])) {
$default_opt['time_format'] = \IntlDateFormatter::SHORT;
} else {
$default_opt['time_format'] = \IntlDateFormatter::NONE;
}
}
$opt = array_merge($default_opt, $opt);
if ($opt['date_formatter'] && !is_callable($opt['date_formatter'])) {
throw new \InvalidArgumentException('The option date_formatter must callable');
}
if (!$opt['date_formatter']) {
if (self::$intl_loaded) {
$formatter = \IntlDateFormatter::create($opt['locale'], $opt['date_format'], $opt['time_format'], $this->dtstart->getTimezone()->getName());
$opt['date_formatter'] = function ($date) use($formatter) {
return $formatter->format($date);
};
} else {
$opt['date_formatter'] = function ($date) {
return $date->format('Y-m-d H:i:s');
};
}
}
$i18n = self::i18nLoad($opt['locale'], $opt['fallback']);
$parts = array('freq' => '', 'byweekday' => '', 'bymonth' => '', 'byweekno' => '', 'byyearday' => '', 'bymonthday' => '', 'byhour' => '', 'byminute' => '', 'bysecond' => '', 'bysetpos' => '');
// Every (INTERVAL) FREQ...
$freq_str = strtolower(array_search($this->freq, self::$frequencies));
$parts['freq'] = strtr(self::i18nSelect($i18n[$freq_str], $this->interval), array('%{interval}' => $this->interval));
// BYXXX rules
if (not_empty($this->rule['BYMONTH'])) {
$tmp = $this->bymonth;
foreach ($tmp as &$value) {
$value = $i18n['months'][$value];
}
$parts['bymonth'] = strtr(self::i18nSelect($i18n['bymonth'], count($tmp)), array('%{months}' => self::i18nList($tmp, $i18n['and'])));
}
if (not_empty($this->rule['BYWEEKNO'])) {
// XXX negative week number are not great here
$tmp = $this->byweekno;
foreach ($tmp as &$value) {
$value = strtr($i18n['nth_weekno'], array('%{n}' => $value));
}
$parts['byweekno'] = strtr(self::i18nSelect($i18n['byweekno'], count($this->byweekno)), array('%{weeks}' => self::i18nList($tmp, $i18n['and'])));
}
if (not_empty($this->rule['BYYEARDAY'])) {
$tmp = $this->byyearday;
foreach ($tmp as &$value) {
$value = strtr(self::i18nSelect($i18n[$value > 0 ? 'nth_yearday' : '-nth_yearday'], $value), array('%{n}' => abs($value)));
}
$tmp = strtr(self::i18nSelect($i18n['byyearday'], count($tmp)), array('%{yeardays}' => self::i18nList($tmp, $i18n['and'])));
// ... of the month
$tmp = strtr(self::i18nSelect($i18n['x_of_the_y'], 'yearly'), array('%{x}' => $tmp));
$parts['byyearday'] = $tmp;
}
if (not_empty($this->rule['BYMONTHDAY'])) {
$parts['bymonthday'] = array();
if ($this->bymonthday) {
$tmp = $this->bymonthday;
foreach ($tmp as &$value) {
$value = strtr(self::i18nSelect($i18n['nth_monthday'], $value), array('%{n}' => $value));
}
$tmp = strtr(self::i18nSelect($i18n['bymonthday'], count($tmp)), array('%{monthdays}' => self::i18nList($tmp, $i18n['and'])));
// ... of the month
$tmp = strtr(self::i18nSelect($i18n['x_of_the_y'], 'monthly'), array('%{x}' => $tmp));
$parts['bymonthday'][] = $tmp;
}
if ($this->bymonthday_negative) {
$tmp = $this->bymonthday_negative;
foreach ($tmp as &$value) {
$value = strtr(self::i18nSelect($i18n['-nth_monthday'], $value), array('%{n}' => -$value));
}
$tmp = strtr(self::i18nSelect($i18n['bymonthday'], count($tmp)), array('%{monthdays}' => self::i18nList($tmp, $i18n['and'])));
// ... of the month
$tmp = strtr(self::i18nSelect($i18n['x_of_the_y'], 'monthly'), array('%{x}' => $tmp));
$parts['bymonthday'][] = $tmp;
}
$parts['bymonthday'] = implode(' ' . $i18n['and'], $parts['bymonthday']);
}
if (not_empty($this->rule['BYDAY'])) {
$parts['byweekday'] = array();
if ($this->byweekday) {
$tmp = $this->byweekday;
foreach ($tmp as &$value) {
$value = $i18n['weekdays'][$value];
}
$parts['byweekday'][] = strtr(self::i18nSelect($i18n['byweekday'], count($tmp)), array('%{weekdays}' => self::i18nList($tmp, $i18n['and'])));
}
if ($this->byweekday_nth) {
$tmp = $this->byweekday_nth;
foreach ($tmp as &$value) {
list($day, $n) = $value;
$value = strtr(self::i18nSelect($i18n[$n > 0 ? 'nth_weekday' : '-nth_weekday'], $n), array('%{weekday}' => $i18n['weekdays'][$day], '%{n}' => abs($n)));
}
$tmp = strtr(self::i18nSelect($i18n['byweekday'], count($tmp)), array('%{weekdays}' => self::i18nList($tmp, $i18n['and'])));
// ... of the year|month
$tmp = strtr(self::i18nSelect($i18n['x_of_the_y'], $freq_str), array('%{x}' => $tmp));
$parts['byweekday'][] = $tmp;
}
$parts['byweekday'] = implode(' ' . $i18n['and'], $parts['byweekday']);
}
if (not_empty($this->rule['BYHOUR'])) {
$tmp = $this->byhour;
foreach ($tmp as &$value) {
$value = strtr($i18n['nth_hour'], array('%{n}' => $value));
}
$parts['byhour'] = strtr(self::i18nSelect($i18n['byhour'], count($tmp)), array('%{hours}' => self::i18nList($tmp, $i18n['and'])));
}
if (not_empty($this->rule['BYMINUTE'])) {
$tmp = $this->byminute;
foreach ($tmp as &$value) {
$value = strtr($i18n['nth_minute'], array('%{n}' => $value));
}
$parts['byminute'] = strtr(self::i18nSelect($i18n['byminute'], count($tmp)), array('%{minutes}' => self::i18nList($tmp, $i18n['and'])));
}
if (not_empty($this->rule['BYSECOND'])) {
$tmp = $this->bysecond;
foreach ($tmp as &$value) {
$value = strtr($i18n['nth_second'], array('%{n}' => $value));
}
$parts['bysecond'] = strtr(self::i18nSelect($i18n['bysecond'], count($tmp)), array('%{seconds}' => self::i18nList($tmp, $i18n['and'])));
}
if ($this->bysetpos) {
$tmp = $this->bysetpos;
foreach ($tmp as &$value) {
$value = strtr(self::i18nSelect($i18n[$value > 0 ? 'nth_setpos' : '-nth_setpos'], $value), array('%{n}' => abs($value)));
}
$tmp = strtr(self::i18nSelect($i18n['bysetpos'], count($tmp)), array('%{setpos}' => self::i18nList($tmp, $i18n['and'])));
$parts['bysetpos'] = $tmp;
}
// from X
$parts['start'] = strtr($i18n['dtstart'], array('%{date}' => $opt['date_formatter']($this->dtstart)));
// to X, or N times, or indefinitely
if (!$this->until && !$this->count) {
$parts['end'] = $i18n['infinite'];
} elseif ($this->until) {
$parts['end'] = strtr($i18n['until'], array('%{date}' => $opt['date_formatter']($this->until)));
} elseif ($this->count) {
$parts['end'] = strtr(self::i18nSelect($i18n['count'], $this->count), array('%{count}' => $this->count));
}
// $str = strtr('%{frequency}%{byday}%{start}%{end}', array(
// '%{frequency}' => $parts['frequency'],
// '%{start}' => $parts['start'],
// '%{end}' => $parts['end'],
// '%{byday}' => $parts['byday'],
// ));
$parts = array_filter($parts);
$str = implode('', $parts);
return $str;
}