/** * Extended version of 'format()' with variable-length formatting codes * * Most codes reproduce the no of digits equal to the length of the code, * for example, 'YYY' will return the last 3 digits of the year, and so * the year 2007 will produce '007', and the year 89 will produce '089', * unless the no-padding code is used as in 'NPYYY', which will return * '89'. * * For negative values, the sign will be discarded, unless the 'S' code * is used in combination, but note that for positive values the value * will be padded with a leading space unless it is suppressed with * the no-padding modifier, for example for 2007: * * <code>YYYY</code> returns '2007' * <code>SYYYY</code> returns ' 2007' * <code>NPSYYYY</code> returns '2007' * * The no-padding modifier 'NP' can be used with numeric codes to * suppress leading (or trailing in the case of code 'F') noughts, and * with character-returning codes such as 'DAY' to suppress trailing * spaces, which will otherwise be padded to the maximum possible length * of the return-value of the code; for example, for Monday: * * <code>Day</code> returns 'Monday ' because the maximum length of * this code is 'Wednesday'; * <code>NPDay</code> returns 'Monday' * * N.B. this code affects the code immediately following only, and * without this code the default is always to apply padding. * * Most character-returning codes, such as 'MONTH', will * set the capitalization according to the code, so for example: * * <code>MONTH</code> returns upper-case spelling, e.g. 'JANUARY' * <code>Month</code> returns spelling with first character of each word * capitalized, e.g. 'January' * <code>month</code> returns lower-case spelling, e.g. 'january' * * Where it makes sense, numeric codes can be combined with a following * 'SP' code which spells out the number, or with a 'TH' code, which * renders the code as an ordinal ('TH' only works in English), for * example, for 31st December: * * <code>DD</code> returns '31' * <code>DDTH</code> returns '31ST' * <code>DDth</code> returns '31st' * <code>DDSP</code> returns 'THIRTY-ONE' * <code>DDSp</code> returns 'Thirty-one' * <code>DDsp</code> returns 'thirty-one' * <code>DDSPTH</code> returns 'THIRTY-FIRST' * <code>DDSpth</code> returns 'Thirty-first' * <code>DDspth</code> returns 'thirty-first' * * * All formatting options: * * <code>-</code> All punctuation and white-space is reproduced unchanged * <code>/</code> * <code>,</code> * <code>.</code> * <code>;</code> * <code>:</code> * <code> </code> * <code>"text"</code> Quoted text is reproduced unchanged (escape using * '\') * <code>AD</code> AD indicator with or without full stops; N.B. if you * are using 'Astronomical' year numbering then 'A.D./B.C.' * indicators will be out for negative years * <code>A.D.</code> * <code>AM</code> Meridian indicator with or without full stops * <code>A.M.</code> * <code>BC</code> BC indicator with or without full stops * <code>B.C.</code> * <code>BCE</code> BCE indicator with or without full stops * <code>B.C.E.</code> * <code>CC</code> Century, i.e. the year divided by 100, discarding the * remainder; 'S' prefixes negative years with a minus sign * <code>SCC</code> * <code>CE</code> CE indicator with or without full stops * <code>C.E.</code> * <code>D</code> Day of week (0-6), where 0 represents Sunday * <code>DAY</code> Name of day, padded with blanks to display width of the * widest name of day in the locale of the machine * <code>DD</code> Day of month (1-31) * <code>DDD</code> Day of year (1-366) * <code>DY</code> Abbreviated name of day * <code>FFF</code> Fractional seconds; no radix character is printed. The * no of 'F's determines the no of digits of the * part-second to return; e.g. 'HH:MI:SS.FF' * <code>F[integer]</code> The integer after 'F' specifies the number of * digits of the part-second to return. This is an * alternative to using F[integer], and 'F3' is thus * equivalent to using 'FFF'. * <code>HH</code> Hour of day (0-23) * <code>HH12</code> Hour of day (1-12) * <code>HH24</code> Hour of day (0-23) * <code>ID</code> Day of week (1-7) based on the ISO standard * <code>IW</code> Week of year (1-52 or 1-53) based on the ISO standard * <code>IYYY</code> 4-digit year based on the ISO 8601 standard; 'S' * prefixes negative years with a minus sign * <code>SIYYY</code> * <code>IYY</code> Last 3, 2, or 1 digit(s) of ISO year * <code>IY</code> * <code>I</code> * <code>J</code> Julian day - the number of days since Monday, 24th * November, 4714 B.C. (proleptic Gregorian calendar) * <code>MI</code> Minute (0-59) * <code>MM</code> Month (01-12; January = 01) * <code>MON</code> Abbreviated name of month * <code>MONTH</code> Name of month, padded with blanks to display width of * the widest name of month in the date language used for * <code>PM</code> Meridian indicator with or without full stops * <code>P.M.</code> * <code>Q</code> Quarter of year (1, 2, 3, 4; January - March = 1) * <code>RM</code> Roman numeral month (I-XII; January = I); N.B. padded * with leading spaces. * <code>SS</code> Second (0-59) * <code>SSSSS</code> Seconds past midnight (0-86399) * <code>TZC</code> Abbreviated form of time zone name, e.g. 'GMT', or the * abbreviation for Summer time if the date falls in Summer * time, e.g. 'BST'. * N.B. this is not a unique identifier - for this purpose * use the time zone region (code 'TZR'). * <code>TZH</code> Time zone hour; 'S' prefixes the hour with the correct * sign, (+/-), which otherwise is not displayed. Note * that the leading nought can be suppressed with the * no-padding code 'NP'). Also note that if you combine * with the 'SP' code, the sign will not be spelled out. * (I.e. 'STZHSp' will produce '+One', for example, and * not 'Plus One'. * 'TZH:TZM' will produce, for example, '+05:30'. (Also * see 'TZM' format code) * <code>STZH</code> * <code>TZI</code> Whether or not the date is in Summer time (daylight * saving time). Returns '1' if Summer time, else '0'. * <code>TZM</code> Time zone minute, without any +/- sign. (Also see * 'TZH' format element) * <code>TZN</code> Long form of time zone name, e.g. * 'Greenwich Mean Time', or the name of the Summer time if * the date falls in Summer time, e.g. * 'British Summer Time'. N.B. this is not a unique * identifier - for this purpose use the time zone region * (code 'TZR'). * <code>TZO</code> Time zone offset in ISO 8601 form - that is, 'Z' if * UTC, else [+/-][hh]:[mm] (which would be equivalent * to 'STZH:TZM'). Note that this result is right padded * with spaces by default, (i.e. if 'Z'). * <code>TZS</code> Time zone offset in seconds; 'S' prefixes negative * sign with minus sign '-' if negative, and no sign if * positive (i.e. -43200 to 50400). * <code>STZS</code> * <code>TZR</code> Time zone region, that is, the name or ID of the time * zone e.g. 'Europe/London'. This value is unique for * each time zone. * <code>U</code> Seconds since the Unix Epoch - * January 1 1970 00:00:00 GMT * <code>W</code> 'Absolute' week of month (1-5), counting week 1 as * 1st-7th of the year, regardless of the day * <code>W1</code> Week of year (1-54), counting week 1 as the week that * contains 1st January * <code>W4</code> Week of year (1-53), counting week 1 as the week that * contains 4th January (i.e. first week with at least 4 * days) * <code>W7</code> Week of year (1-53), counting week 1 as the week that * contains 7th January (i.e. first full week) * <code>WW</code> 'Absolute' week of year (1-53), counting week 1 as * 1st-7th of the year, regardless of the day * <code>YEAR</code> Year, spelled out; 'S' prefixes negative years with * 'MINUS'; N.B. 'YEAR' differs from 'YYYYSP' in that the * first will render 1923, for example, as 'NINETEEN * TWENTY-THREE, and the second as 'ONE THOUSAND NINE * HUNDRED TWENTY-THREE' * <code>SYEAR</code> * <code>YYYY</code> 4-digit year; 'S' prefixes negative years with a minus * sign * <code>SYYYY</code> * <code>YYY</code> Last 3, 2, or 1 digit(s) of year * <code>YY</code> * <code>Y</code> * <code>Y,YYY</code> Year with thousands-separator in this position; five * possible separators * <code>Y.YYY</code> * <code>YキYYY</code> N.B. space-dot (mid-dot, interpunct) is valid only in * ISO 8859-1 (so take care when using UTF-8 in * particular) * <code>Y'YYY</code> * <code>Y YYY</code> * * In addition the following codes can be used in combination with other * codes; * Codes that modify the next code in the format string: * * <code>NP</code> 'No Padding' - Returns a value with no trailing blanks * and no leading or trailing noughts; N.B. that the * default is to include this padding in the return string. * N.B. affects the code immediately following only. * * Codes that modify the previous code in the format string (can only * be used with integral codes such as 'MM'): * * <code>TH</code> Ordinal number * <code>SP</code> Spelled cardinal number * <code>SPTH</code> Spelled ordinal number (combination of 'SP' and 'TH' * in any order) * <code>THSP</code> * * Code 'SP' can have the following three variations (which can also be used * in combination with 'TH'): * * <code>SP</code> returns upper-case spelling, e.g. 'FOUR HUNDRED' * <code>Sp</code> returns spelling with first character of each word * capitalized, e.g. 'Four Hundred' * <code>sp</code> returns lower-case spelling, e.g. 'four hundred' * * Code 'TH' can have the following two variations (although in combination * with code 'SP', the case specification of 'SP' takes precedence): * * <code>TH</code> returns upper-case ordinal suffix, e.g. 400TH * <code>th</code> returns lower-case ordinal suffix, e.g. 400th * * @param string $ps_format format string for returned date/time * @param string $ps_locale language name abbreviation used for formatting * numbers as spelled-out words * * @return string date/time in given format * @access public * @since Method available since Release 1.5.0 */ function format2($ps_format, $ps_locale = "en_GB") { if (!preg_match('/^("([^"\\\\]|\\\\\\\\|\\\\")*"|(D{1,3}|S?C+|' . 'HH(12|24)?|I[DW]|S?IY*|J|M[IM]|Q|SS(SSS)?|S?TZ[HS]|' . 'TZM|U|W[W147]?|S?Y{1,3}([,.キ\' ]?YYY)*)(SP(TH)?|' . 'TH(SP)?)?|AD|A\\.D\\.|AM|A\\.M\\.|BCE?|B\\.C\\.(E\\.)?|CE|' . 'C\\.E\\.|DAY|DY|F(F*|[1-9][0-9]*)|MON(TH)?|NP|PM|' . 'P\\.M\\.|RM|TZ[CINOR]|S?YEAR|[^A-Z0-9"])*$/i', $ps_format)) { return PEAR::raiseError("Invalid date format '{$ps_format}'", DATE_ERROR_INVALIDFORMATSTRING); } $ret = ""; $i = 0; $hb_nopadflag = false; $hb_showsignflag = false; $hn_weekdaypad = null; $hn_monthpad = null; $hn_isoyear = null; $hn_isoweek = null; $hn_isoday = null; $hn_tzoffset = null; while ($i < strlen($ps_format)) { $hb_lower = false; if ($hb_nopadflag) { $hb_nopad = true; } else { $hb_nopad = false; } if ($hb_showsignflag) { $hb_nosign = false; } else { $hb_nosign = true; } $hb_nopadflag = false; $hb_showsignflag = false; switch ($hs_char = substr($ps_format, $i, 1)) { case "-": case "/": case ",": case ".": case ";": case ":": case " ": $ret .= $hs_char; $i += 1; break; case "\"": preg_match('/(([^"\\\\]|\\\\\\\\|\\\\")*)"/', $ps_format, $ha_matches, PREG_OFFSET_CAPTURE, $i + 1); $ret .= str_replace(array('\\\\', '\\"'), array('\\', '"'), $ha_matches[1][0]); $i += strlen($ha_matches[0][0]) + 1; break; case "a": $hb_lower = true; case "A": if (strtoupper(substr($ps_format, $i, 4)) == "A.D.") { $ret .= $this->year >= 0 ? $hb_lower ? "a.d." : "A.D." : ($hb_lower ? "b.c." : "B.C."); $i += 4; } else { if (strtoupper(substr($ps_format, $i, 2)) == "AD") { $ret .= $this->year >= 0 ? $hb_lower ? "ad" : "AD" : ($hb_lower ? "bc" : "BC"); $i += 2; } else { if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } if (strtoupper(substr($ps_format, $i, 4)) == "A.M.") { $ret .= $this->hour < 12 ? $hb_lower ? "a.m." : "A.M." : ($hb_lower ? "p.m." : "P.M."); $i += 4; } else { if (strtoupper(substr($ps_format, $i, 2)) == "AM") { $ret .= $this->hour < 12 ? $hb_lower ? "am" : "AM" : ($hb_lower ? "pm" : "PM"); $i += 2; } } } } break; case "b": $hb_lower = true; case "B": // Check for 'B.C.E.' first: // if (strtoupper(substr($ps_format, $i, 6)) == "B.C.E.") { if ($this->year >= 0) { $hs_era = $hb_lower ? "c.e." : "C.E."; $ret .= $hb_nopad ? $hs_era : str_pad($hs_era, 6, " ", STR_PAD_RIGHT); } else { $ret .= $hb_lower ? "b.c.e." : "B.C.E."; } $i += 6; } else { if (strtoupper(substr($ps_format, $i, 3)) == "BCE") { if ($this->year >= 0) { $hs_era = $hb_lower ? "ce" : "CE"; $ret .= $hb_nopad ? $hs_era : str_pad($hs_era, 3, " ", STR_PAD_RIGHT); } else { $ret .= $hb_lower ? "bce" : "BCE"; } $i += 3; } else { if (strtoupper(substr($ps_format, $i, 4)) == "B.C.") { $ret .= $this->year >= 0 ? $hb_lower ? "a.d." : "A.D." : ($hb_lower ? "b.c." : "B.C."); $i += 4; } else { if (strtoupper(substr($ps_format, $i, 2)) == "BC") { $ret .= $this->year >= 0 ? $hb_lower ? "ad" : "AD" : ($hb_lower ? "bc" : "BC"); $i += 2; } } } } break; case "c": $hb_lower = true; case "C": if (strtoupper(substr($ps_format, $i, 4)) == "C.E.") { if ($this->year >= 0) { $hs_era = $hb_lower ? "c.e." : "C.E."; $ret .= $hb_nopad ? $hs_era : str_pad($hs_era, 6, " ", STR_PAD_RIGHT); } else { $ret .= $hb_lower ? "b.c.e." : "B.C.E."; } $i += 4; } else { if (strtoupper(substr($ps_format, $i, 2)) == "CE") { if ($this->year >= 0) { $hs_era = $hb_lower ? "ce" : "CE"; $ret .= $hb_nopad ? $hs_era : str_pad($hs_era, 3, " ", STR_PAD_RIGHT); } else { $ret .= $hb_lower ? "bce" : "BCE"; } $i += 2; } else { // Code C(CCC...): // $hn_codelen = 1; while (strtoupper(substr($ps_format, $i + $hn_codelen, 1)) == "C") { ++$hn_codelen; } // Check next code is not 'CE' or 'C.E.' // if ($hn_codelen > 1 && (strtoupper(substr($ps_format, $i + $hn_codelen - 1, 4)) == "C.E." || strtoupper(substr($ps_format, $i + $hn_codelen - 1, 2)) == "CE")) { --$hn_codelen; } $hn_century = intval($this->year / 100); $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4); $hs_century = $this->_formatNumber($hn_century, $hs_numberformat, $hn_codelen, $hb_nopad, $hb_nosign, $ps_locale); if (Pear::isError($hs_century)) { return $hs_century; } $ret .= $hs_century; $i += $hn_codelen + strlen($hs_numberformat); } } break; case "d": $hb_lower = true; case "D": if (strtoupper(substr($ps_format, $i, 3)) == "DAY") { $hs_day = Date_Calc::getWeekdayFullname($this->day, $this->month, $this->year); if (!$hb_nopad) { if (is_null($hn_weekdaypad)) { // Set week-day padding variable: // $hn_weekdaypad = 0; foreach (Date_Calc::getWeekDays() as $hs_weekday) { $hn_weekdaypad = max($hn_weekdaypad, strlen($hs_weekday)); } } $hs_day = str_pad($hs_day, $hn_weekdaypad, " ", STR_PAD_RIGHT); } $ret .= $hb_lower ? strtolower($hs_day) : (substr($ps_format, $i + 1, 1) == "A" ? strtoupper($hs_day) : $hs_day); $i += 3; } else { if (strtoupper(substr($ps_format, $i, 2)) == "DY") { $hs_day = Date_Calc::getWeekdayAbbrname($this->day, $this->month, $this->year); $ret .= $hb_lower ? strtolower($hs_day) : (substr($ps_format, $i + 1, 1) == "Y" ? strtoupper($hs_day) : $hs_day); $i += 2; } else { if (strtoupper(substr($ps_format, $i, 3)) == "DDD" && strtoupper(substr($ps_format, $i + 2, 3)) != "DAY" && strtoupper(substr($ps_format, $i + 2, 2)) != "DY") { $hn_day = Date_Calc::dayOfYear($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 3, 4); $hs_day = $this->_formatNumber($hn_day, $hs_numberformat, 3, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_day)) { return $hs_day; } $ret .= $hs_day; $i += 3 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "DD" && strtoupper(substr($ps_format, $i + 1, 3)) != "DAY" && strtoupper(substr($ps_format, $i + 1, 2)) != "DY") { $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_day = $this->_formatNumber($this->day, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_day)) { return $hs_day; } $ret .= $hs_day; $i += 2 + strlen($hs_numberformat); } else { // Code 'D': // $hn_day = Date_Calc::dayOfWeek($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 1, 4); $hs_day = $this->_formatNumber($hn_day, $hs_numberformat, 1, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_day)) { return $hs_day; } $ret .= $hs_day; $i += 1 + strlen($hs_numberformat); } } } } break; case "f": case "F": if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } $hn_codelen = 1; if (is_numeric(substr($ps_format, $i + $hn_codelen, 1))) { ++$hn_codelen; while (is_numeric(substr($ps_format, $i + $hn_codelen, 1))) { ++$hn_codelen; } $hn_partsecdigits = substr($ps_format, $i + 1, $hn_codelen - 1); } else { while (strtoupper(substr($ps_format, $i + $hn_codelen, 1)) == "F") { ++$hn_codelen; } // Check next code is not F[numeric]: // if ($hn_codelen > 1 && is_numeric(substr($ps_format, $i + $hn_codelen, 1))) { --$hn_codelen; } $hn_partsecdigits = $hn_codelen; } $hs_partsec = (string) $this->partsecond; if (preg_match('/^([0-9]+)(\\.([0-9]+))?E-([0-9]+)$/i', $hs_partsec, $ha_matches)) { $hs_partsec = str_repeat("0", $ha_matches[4] - strlen($ha_matches[1])) . $ha_matches[1] . $ha_matches[3]; } else { $hs_partsec = substr($hs_partsec, 2); } $hs_partsec = substr($hs_partsec, 0, $hn_partsecdigits); // '_formatNumber() will not work for this because the // part-second is an int, and we want it to behave like a float: // if ($hb_nopad) { $hs_partsec = rtrim($hs_partsec, "0"); if ($hs_partsec == "") { $hs_partsec = "0"; } } else { $hs_partsec = str_pad($hs_partsec, $hn_partsecdigits, "0", STR_PAD_RIGHT); } $ret .= $hs_partsec; $i += $hn_codelen; break; case "h": case "H": if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } if (strtoupper(substr($ps_format, $i, 4)) == "HH12") { $hn_hour = $this->hour % 12; if ($hn_hour == 0) { $hn_hour = 12; } $hn_codelen = 4; } else { // Code 'HH' or 'HH24': // $hn_hour = $this->hour; $hn_codelen = strtoupper(substr($ps_format, $i, 4)) == "HH24" ? 4 : 2; } $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4); $hs_hour = $this->_formatNumber($hn_hour, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_hour)) { return $hs_hour; } $ret .= $hs_hour; $i += $hn_codelen + strlen($hs_numberformat); break; case "i": case "I": if (is_null($hn_isoyear)) { list($hn_isoyear, $hn_isoweek, $hn_isoday) = Date_Calc::isoWeekDate($this->day, $this->month, $this->year); } if (strtoupper(substr($ps_format, $i, 2)) == "ID" && strtoupper(substr($ps_format, $i + 1, 3)) != "DAY") { $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_isoday = $this->_formatNumber($hn_isoday, $hs_numberformat, 1, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_isoday)) { return $hs_isoday; } $ret .= $hs_isoday; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "IW") { $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_isoweek = $this->_formatNumber($hn_isoweek, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_isoweek)) { return $hs_isoweek; } $ret .= $hs_isoweek; $i += 2 + strlen($hs_numberformat); } else { // Code I(YYY...): // $hn_codelen = 1; while (strtoupper(substr($ps_format, $i + $hn_codelen, 1)) == "Y") { ++$hn_codelen; } $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4); $hs_isoyear = $this->_formatNumber($hn_isoyear, $hs_numberformat, $hn_codelen, $hb_nopad, $hb_nosign, $ps_locale); if (Pear::isError($hs_isoyear)) { return $hs_isoyear; } $ret .= $hs_isoyear; $i += $hn_codelen + strlen($hs_numberformat); } } break; case "j": case "J": $hn_jd = Date_Calc::dateToDays($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 1, 4); // Allow sign if negative; allow all digits (specify nought); // suppress padding: // $hs_jd = $this->_formatNumber($hn_jd, $hs_numberformat, 0, true, false, $ps_locale); if (Pear::isError($hs_jd)) { return $hs_jd; } $ret .= $hs_jd; $i += 1 + strlen($hs_numberformat); break; case "m": $hb_lower = true; case "M": if (strtoupper(substr($ps_format, $i, 2)) == "MI") { if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_minute = $this->_formatNumber($this->minute, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_minute)) { return $hs_minute; } $ret .= $hs_minute; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "MM") { $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_month = $this->_formatNumber($this->month, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_month)) { return $hs_month; } $ret .= $hs_month; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 5)) == "MONTH") { $hs_month = Date_Calc::getMonthFullname($this->month); if (!$hb_nopad) { if (is_null($hn_monthpad)) { // Set month padding variable: // $hn_monthpad = 0; foreach (Date_Calc::getMonthNames() as $hs_monthofyear) { $hn_monthpad = max($hn_monthpad, strlen($hs_monthofyear)); } } $hs_month = str_pad($hs_month, $hn_monthpad, " ", STR_PAD_RIGHT); } $ret .= $hb_lower ? strtolower($hs_month) : (substr($ps_format, $i + 1, 1) == "O" ? strtoupper($hs_month) : $hs_month); $i += 5; } else { if (strtoupper(substr($ps_format, $i, 3)) == "MON") { $hs_month = Date_Calc::getMonthAbbrname($this->month); $ret .= $hb_lower ? strtolower($hs_month) : (substr($ps_format, $i + 1, 1) == "O" ? strtoupper($hs_month) : $hs_month); $i += 3; } } } } break; case "n": case "N": // No-Padding rule 'NP' applies to the next code (either trailing // spaces or leading/trailing noughts): // $hb_nopadflag = true; $i += 2; break; case "p": $hb_lower = true; case "P": if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } if (strtoupper(substr($ps_format, $i, 4)) == "P.M.") { $ret .= $this->hour < 12 ? $hb_lower ? "a.m." : "A.M." : ($hb_lower ? "p.m." : "P.M."); $i += 4; } else { if (strtoupper(substr($ps_format, $i, 2)) == "PM") { $ret .= $this->hour < 12 ? $hb_lower ? "am" : "AM" : ($hb_lower ? "pm" : "PM"); $i += 2; } } break; case "q": case "Q": // N.B. Current implementation ignores the day and year, but // it is possible that a different implementation might be // desired, so pass these parameters anyway: // $hn_quarter = Date_Calc::quarterOfYear($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 1, 4); $hs_quarter = $this->_formatNumber($hn_quarter, $hs_numberformat, 1, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_quarter)) { return $hs_quarter; } $ret .= $hs_quarter; $i += 1 + strlen($hs_numberformat); break; case "r": $hb_lower = true; case "R": // Code 'RM': // switch ($this->month) { case 1: $hs_monthroman = "i"; break; case 2: $hs_monthroman = "ii"; break; case 3: $hs_monthroman = "iii"; break; case 4: $hs_monthroman = "iv"; break; case 5: $hs_monthroman = "v"; break; case 6: $hs_monthroman = "vi"; break; case 7: $hs_monthroman = "vii"; break; case 8: $hs_monthroman = "viii"; break; case 9: $hs_monthroman = "ix"; break; case 10: $hs_monthroman = "x"; break; case 11: $hs_monthroman = "xi"; break; case 12: $hs_monthroman = "xii"; break; } $hs_monthroman = $hb_lower ? $hs_monthroman : strtoupper($hs_monthroman); $ret .= $hb_nopad ? $hs_monthroman : str_pad($hs_monthroman, 4, " ", STR_PAD_LEFT); $i += 2; break; case "s": case "S": // Check for 'SSSSS' before 'SS': // if (strtoupper(substr($ps_format, $i, 5)) == "SSSSS") { if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } $hs_numberformat = substr($ps_format, $i + 5, 4); $hn_second = Date_Calc::secondsPastMidnight($this->hour, $this->minute, $this->second); $hs_second = $this->_formatNumber($hn_second, $hs_numberformat, 5, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_second)) { return $hs_second; } $ret .= $hs_second; $i += 5 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "SS") { if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_second = $this->_formatNumber($this->second, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_second)) { return $hs_second; } $ret .= $hs_second; $i += 2 + strlen($hs_numberformat); } else { // One of the following codes: // 'SC(CCC...)' // 'SY(YYY...)' // 'SIY(YYY...)' // 'STZH' // 'STZS' // 'SYEAR' // $hb_showsignflag = true; if ($hb_nopad) { $hb_nopadflag = true; } ++$i; } } break; case "t": case "T": // Code TZ[...]: // if (strtoupper(substr($ps_format, $i, 3)) == "TZR") { // This time-zone-related code can be called when the time is // invalid, but the others should return an error: // $ret .= $this->getTZID(); $i += 3; } else { if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } if (strtoupper(substr($ps_format, $i, 3)) == "TZC") { $ret .= $this->getTZShortName(); $i += 3; } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZH") { if (is_null($hn_tzoffset)) { $hn_tzoffset = $this->getTZOffset(); } $hs_numberformat = substr($ps_format, $i + 3, 4); $hn_tzh = intval($hn_tzoffset / 3600000); // Suppress sign here (it is added later): // $hs_tzh = $this->_formatNumber($hn_tzh, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_tzh)) { return $hs_tzh; } // Display sign, even if positive: // $ret .= ($hb_nosign ? "" : ($hn_tzh >= 0 ? '+' : '-')) . $hs_tzh; $i += 3 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZI") { $ret .= $this->inDaylightTime() ? '1' : '0'; $i += 3; } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZM") { if (is_null($hn_tzoffset)) { $hn_tzoffset = $this->getTZOffset(); } $hs_numberformat = substr($ps_format, $i + 3, 4); $hn_tzm = intval($hn_tzoffset % 3600000 / 60000); // Suppress sign: // $hs_tzm = $this->_formatNumber($hn_tzm, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_tzm)) { return $hs_tzm; } $ret .= $hs_tzm; $i += 3 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZN") { $ret .= $this->getTZLongName(); $i += 3; } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZO") { if (is_null($hn_tzoffset)) { $hn_tzoffset = $this->getTZOffset(); } $hn_tzh = intval(abs($hn_tzoffset) / 3600000); $hn_tzm = intval(abs($hn_tzoffset) % 3600000 / 60000); if ($hn_tzoffset == 0) { $ret .= $hb_nopad ? "Z" : "Z "; } else { // Display sign, even if positive: // $ret .= ($hn_tzoffset >= 0 ? '+' : '-') . sprintf("%02d", $hn_tzh) . ":" . sprintf("%02d", $hn_tzm); } $i += 3; } else { if (strtoupper(substr($ps_format, $i, 3)) == "TZS") { if (is_null($hn_tzoffset)) { $hn_tzoffset = $this->getTZOffset(); } $hs_numberformat = substr($ps_format, $i + 3, 4); $hn_tzs = intval($hn_tzoffset / 1000); $hs_tzs = $this->_formatNumber($hn_tzs, $hs_numberformat, 5, $hb_nopad, $hb_nosign, $ps_locale); if (Pear::isError($hs_tzs)) { return $hs_tzs; } $ret .= $hs_tzs; $i += 3 + strlen($hs_numberformat); } } } } } } } } break; case "u": case "U": if ($this->ob_invalidtime) { return $this->_getErrorInvalidTime(); } $hn_unixtime = $this->getTime(); $hs_numberformat = substr($ps_format, $i + 1, 4); // Allow sign if negative; allow all digits (specify nought); // suppress padding: // $hs_unixtime = $this->_formatNumber($hn_unixtime, $hs_numberformat, 0, true, false, $ps_locale); if (Pear::isError($hs_unixtime)) { return $hs_unixtime; } $ret .= $hs_unixtime; $i += 1 + strlen($hs_numberformat); break; case "w": case "W": // Check for 'WW' before 'W': // if (strtoupper(substr($ps_format, $i, 2)) == "WW") { $hn_week = Date_Calc::weekOfYearAbsolute($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_week = $this->_formatNumber($hn_week, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_week)) { return $hs_week; } $ret .= $hs_week; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "W1") { $hn_week = Date_Calc::weekOfYear1st($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_week = $this->_formatNumber($hn_week, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_week)) { return $hs_week; } $ret .= $hs_week; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "W4") { $ha_week = Date_Calc::weekOfYear4th($this->day, $this->month, $this->year); $hn_week = $ha_week[1]; $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_week = $this->_formatNumber($hn_week, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_week)) { return $hs_week; } $ret .= $hs_week; $i += 2 + strlen($hs_numberformat); } else { if (strtoupper(substr($ps_format, $i, 2)) == "W7") { $ha_week = Date_Calc::weekOfYear7th($this->day, $this->month, $this->year); $hn_week = $ha_week[1]; $hs_numberformat = substr($ps_format, $i + 2, 4); $hs_week = $this->_formatNumber($hn_week, $hs_numberformat, 2, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_week)) { return $hs_week; } $ret .= $hs_week; $i += 2 + strlen($hs_numberformat); } else { // Code 'W': // $hn_week = Date_Calc::weekOfMonthAbsolute($this->day, $this->month, $this->year); $hs_numberformat = substr($ps_format, $i + 1, 4); $hs_week = $this->_formatNumber($hn_week, $hs_numberformat, 1, $hb_nopad, true, $ps_locale); if (Pear::isError($hs_week)) { return $hs_week; } $ret .= $hs_week; $i += 1 + strlen($hs_numberformat); } } } } break; case "y": case "Y": // Check for 'YEAR' first: // if (strtoupper(substr($ps_format, $i, 4)) == "YEAR") { switch (substr($ps_format, $i, 2)) { case "YE": $hs_spformat = "SP"; break; case "Ye": $hs_spformat = "Sp"; break; default: $hs_spformat = "sp"; } if (($hn_yearabs = abs($this->year)) < 100 || $hn_yearabs % 100 < 10) { $hs_numberformat = $hs_spformat; // Allow all digits (specify nought); padding irrelevant: // $hs_year = $this->_formatNumber($this->year, $hs_numberformat, 0, true, $hb_nosign, $ps_locale); if (Pear::isError($hs_year)) { return $hs_year; } $ret .= $hs_year; } else { // Year is spelled 'Nineteen Twelve' rather than // 'One thousand Nine Hundred Twelve': // $hn_century = intval($this->year / 100); $hs_numberformat = $hs_spformat; // Allow all digits (specify nought); padding irrelevant: // $hs_century = $this->_formatNumber($hn_century, $hs_numberformat, 0, true, $hb_nosign, $ps_locale); if (Pear::isError($hs_century)) { return $hs_century; } $ret .= $hs_century . " "; $hs_numberformat = $hs_spformat; // Discard sign; padding irrelevant: // $hs_year = $this->_formatNumber($this->year, $hs_numberformat, 2, false, true, $ps_locale); if (Pear::isError($hs_year)) { return $hs_year; } $ret .= $hs_year; } $i += 4; } else { // Code Y(YYY...): // $hn_codelen = 1; while (strtoupper(substr($ps_format, $i + $hn_codelen, 1)) == "Y") { ++$hn_codelen; } $hs_thousandsep = null; $hn_thousandseps = 0; if ($hn_codelen <= 3) { while (preg_match('/([,.キ\' ])YYY/i', substr($ps_format, $i + $hn_codelen, 4), $ha_matches)) { $hn_codelen += 4; $hs_thousandsep = $ha_matches[1]; ++$hn_thousandseps; } } // Check next code is not 'YEAR' // if ($hn_codelen > 1 && strtoupper(substr($ps_format, $i + $hn_codelen - 1, 4)) == "YEAR") { --$hn_codelen; } $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4); $hs_year = $this->_formatNumber($this->year, $hs_numberformat, $hn_codelen - $hn_thousandseps, $hb_nopad, $hb_nosign, $ps_locale, $hs_thousandsep); if (Pear::isError($hs_year)) { return $hs_year; } $ret .= $hs_year; $i += $hn_codelen + strlen($hs_numberformat); } break; default: $ret .= $hs_char; ++$i; break; } } return $ret; }