protected static function joinInternal($list, $keys, $locale) { $result = ''; if (is_array($list)) { $list = array_values($list); $n = count($list); switch ($n) { case 0: break; case 1: $result = strval($list[0]); break; default: $allData = \Punic\Data::get('listPatterns', $locale); $data = null; if (!empty($keys)) { foreach ($keys as $key) { if (array_key_exists($key, $allData)) { $data = $allData[$key]; break; } } } if (is_null($data)) { $data = $allData['standard']; } if (array_key_exists($n, $data)) { $result = vsprintf($data[$n], $list); } else { $result = sprintf($data['end'], $list[$n - 2], $list[$n - 1]); if ($n > 2) { for ($index = $n - 3; $index > 0; $index--) { $result = sprintf($data['middle'], $list[$index], $result); } $result = sprintf($data['start'], $list[0], $result); } } break; } } return $result; }
/** * Format a unit string * @param int|float|string $number The unit amount * @param string $unit The unit identifier (eg 'duration/millisecond' or 'millisecond') * @param string $width = 'short' The format name; it can be 'long' (eg '3 milliseconds'), 'short' (eg '3 ms') or 'narrow' (eg '3ms'). You can also add a precision specifier ('long,2' or just '2') * @param string $locale = '' The locale to use. If empty we'll use the default locale set in \Punic\Data */ public static function format($number, $unit, $width = 'short', $locale = '') { $data = \Punic\Data::get('units', $locale); $precision = null; if (is_int($width)) { $precision = $width; $width = 'short'; } elseif (is_string($width) && preg_match('/^(?:(.*),)?([+\\-]?\\d+)$/', $width, $m)) { $precision = intval($m[2]); $width = $m[1]; if (!strlen($width)) { $width = 'short'; } } if (strpos($width, '_') === 0 || !array_key_exists($width, $data)) { $widths = array(); foreach (array_keys($data) as $w) { if (strpos($w, '_') !== 0) { $widths[] = $w; } } throw new Exception\ValueNotInList($width, $widths); } $data = $data[$width]; if (strpos($unit, '/') === false) { $unitCategory = null; $unitID = null; foreach (array_keys($data) as $c) { if (strpos($c, '_') === false) { if (array_key_exists($unit, $data[$c])) { if (is_null($unitCategory)) { $unitCategory = $c; $unitID = $unit; } else { $unitCategory = null; break; } } } } } else { list($unitCategory, $unitID) = explode('/', $unit, 2); } $rules = null; if (strpos($unit, '_') === false && !is_null($unitCategory) && !is_null($unitID) && array_key_exists($unitCategory, $data) && array_key_exists($unitID, $data[$unitCategory])) { $rules = $data[$unitCategory][$unitID]; } if (is_null($rules)) { $units = array(); foreach ($data as $c => $us) { if (strpos($c, '_') === false) { foreach (array_keys($us) as $u) { if (strpos($c, '_') === false) { $units[] = "{$c}/{$u}"; } } } } throw new \Punic\Exception\ValueNotInList($unit, $units); } $pluralRule = \Punic\Plural::getRule($number, $locale); //@codeCoverageIgnoreStart // These checks aren't necessary since $pluralRule should always be in $rules, but they don't hurt ;) if (!array_key_exists($pluralRule, $rules)) { if (array_key_exists('other', $rules)) { $pluralRule = 'other'; } else { $availableRules = array_keys($rules); $pluralRule = $availableRules[0]; } } //@codeCoverageIgnoreEnd return sprintf($rules[$pluralRule], \Punic\Number::format($number, $precision, $locale)); }
/** * Describe an relative interval from $dateStart to $dateEnd (eg '2 days ago'). * Only the largest differing unit is described, and the next smaller unit will be used * for rounding. * * @param \DateTime $dateEnd The terminal date * @param \DateTime|null $dateStart The anchor date, defaults to now. (if it has a timezone different than * $dateEnd, we'll use the one of $dateEnd) * @param string $width The format name; it can be '', 'short' or 'narrow' * @param string $locale The locale to use. If empty we'll use the default locale set in \Punic\Data * * @return string * * @throws \InvalidArgumentException */ public static function describeRelativeInterval($dateEnd, $dateStart = null, $width = '', $locale = '') { if (!is_a($dateEnd, '\\DateTime')) { throw new \InvalidArgumentException('Not a DateTime object'); } if (empty($dateStart) && $dateStart !== 0 && $dateStart !== '0') { $dateStart = new \DateTime('now'); } elseif (!is_a($dateStart, '\\DateTime')) { throw new \InvalidArgumentException('Not a DateTime object'); } else { $dateStart = clone $dateStart; } $dateStart->setTimezone($dateEnd->getTimezone()); //$utc = new \DateTimeZone('UTC'); //$dateEndUTC = new \DateTime($dateEnd->format('Y-m-d H:i:s'), $utc); //$dateStartUTC = new \DateTime($dateStart->format('Y-m-d H:i:s'), $utc); $parts = array(); $data = Data::get('dateFields', $locale); $diff = $dateStart->diff($dateEnd, false); $past = (bool) $diff->invert; $value = 0; $key = ''; if ($diff->y != 0) { $key = 'year'; $value = $diff->y + ($diff->m > 6 ? 1 : 0); } elseif ($diff->m != 0) { $key = 'month'; $value = $diff->m + ($diff->d > 15 ? 1 : 0); } elseif ($diff->d != 0) { $key = 'day'; $value = $diff->d + ($diff->h > 12 ? 1 : 0); } elseif ($diff->h != 0) { $key = 'hour'; $value = $diff->h + ($diff->i > 30 ? 1 : 0); } elseif ($diff->i != 0) { $key = 'minute'; $value = $diff->i + ($diff->s > 30 ? 1 : 0); } elseif ($diff->s != 0) { $key = 'second'; $value = $diff->s; } if ($value == 0) { $key = 'second'; $relKey = 'relative-type-0'; $relPattern = null; } elseif ($key === 'day' && $value > 1 && $value < 7) { $dow = $dateEnd->format('N') - 1; $days = array('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'); $key = $days[$dow]; $relKey = $past ? "relative-type--1" : "relative-type-1"; $relPattern = null; } else { if ($value == 1 && isset($data[$key]['relative-type--1'])) { $relKey = $past ? 'relative-type--1' : 'relative-type-1'; $relPattern = null; } else { $relKey = $past ? 'relativeTime-type-past' : 'relativeTime-type-future'; $rule = Plural::getRule($value, $locale); $relPattern = 'relativeTimePattern-count-' . $rule; } } if (!empty($width) && array_key_exists($key . '-' . $width, $data)) { $key .= '-' . $width; } if (empty($relPattern)) { $relativeString = $data[$key][$relKey]; } else { $tempString = $data[$key][$relKey][$relPattern]; $tempString = str_replace('{0}', '%d', $tempString); $relativeString = sprintf($tempString, $value); } return $relativeString; }
/** * Convert a localized representation of a number to a number (for instance, converts the string '1,234' to 1234 in case of English and to 1.234 in case of Italian) * @param string $value The string value to convert * @param string $locale = '' The locale to use. If empty we'll use the default locale set in \Punic\Data * @return int|float|null Returns null if $value is not valid, the numeric value otherwise */ public static function unformat($value, $locale = '') { $result = null; if (is_int($value) || is_float($value)) { $result = $value; } elseif (is_string($value) && strlen($value)) { $data = \Punic\Data::get('numbers', $locale); $plus = $data['symbols']['plusSign']; $plusQ = preg_quote($plus); $minus = $data['symbols']['minusSign']; $minusQ = preg_quote($minus); $decimal = $data['symbols']['decimal']; $decimalQ = preg_quote($decimal); $group = $data['symbols']['group']; $groupQ = preg_quote($group); $ok = true; if (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*)" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = null; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*){$decimalQ}" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = ''; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?(\\d+(?:{$groupQ}\\d+)*){$decimalQ}(\\d+)" . '$/', $value, $m)) { $sign = $m[1]; $int = $m[2]; $float = $m[3]; } elseif (preg_match('/^' . "({$plusQ}|{$minusQ})?{$decimalQ}(\\d+)" . '$/', $value, $m)) { $sign = $m[1]; $int = '0'; $float = $m[2]; } else { $ok = false; } if ($ok) { if ($sign === $minus) { $sign = '-'; } else { $sign = ''; } $int = str_replace($group, '', $int); if (is_null($float)) { $result = intval("{$sign}{$int}"); } else { $result = floatval("{$sign}{$int}.{$float}"); } } } return $result; }
protected static function decodeTimezoneDelta(\DateTime $value, $count, $locale) { $offset = $value->getOffset(); $sign = $offset < 0 ? '-' : '+'; $seconds = abs($offset); $hours = intval(floor($seconds / 3600)); $seconds -= $hours * 3600; $minutes = intval(floor($seconds / 60)); $seconds -= $minutes * 60; $partsWithoutSeconds = array(); $partsWithoutSeconds[] = $sign . substr('0' . strval($hours), -2); $partsWithoutSeconds[] = substr('0' . strval($minutes), -2); $partsMaybeWithSeconds = $partsWithoutSeconds; /* @TZWS if ($seconds > 0) { $partsMaybeWithSeconds[] = substr('0' . strval($seconds), -2); } */ switch ($count) { case 1: case 2: case 3: return implode('', $partsMaybeWithSeconds); case 4: $data = \Punic\Data::get('timeZoneNames', $locale); $format = array_key_exists('gmtFormat', $data) ? $data['gmtFormat'] : 'GMT%1$s'; return sprintf($format, implode(':', $partsWithoutSeconds)); case 5: return implode(':', $partsMaybeWithSeconds); default: throw new Exception\ValueNotInList($count, array(1, 2, 3, 4, 5)); } }