Overview

Namespaces

  • Peach
    • DF
    • DT
    • Http
      • Body
      • Header
    • Markup
    • Util

Classes

  • Peach\DF\Base64Codec
  • Peach\DF\CodecChain
  • Peach\DF\JsonCodec
  • Peach\DF\SerializationCodec
  • Peach\DF\Utf8Codec
  • Peach\DT\AbstractTime
  • Peach\DT\Clock
  • Peach\DT\Date
  • Peach\DT\Datetime
  • Peach\DT\DefaultClock
  • Peach\DT\FixedClock
  • Peach\DT\FormatWrapper
  • Peach\DT\HttpDateFormat
  • Peach\DT\OffsetClock
  • Peach\DT\ShiftFormat
  • Peach\DT\SimpleFormat
  • Peach\DT\TimeEquator
  • Peach\DT\Timestamp
  • Peach\DT\TimeWrapper
  • Peach\DT\UnixTimeFormat
  • Peach\DT\Util
  • Peach\DT\W3cDatetimeFormat
  • Peach\Http\Body
  • Peach\Http\Body\CodecRenderer
  • Peach\Http\Body\StringRenderer
  • Peach\Http\DefaultEndpoint
  • Peach\Http\Header\CookieItem
  • Peach\Http\Header\CookieOptions
  • Peach\Http\Header\HttpDate
  • Peach\Http\Header\NoField
  • Peach\Http\Header\QualityValues
  • Peach\Http\Header\Raw
  • Peach\Http\Header\SetCookie
  • Peach\Http\Header\Status
  • Peach\Http\Request
  • Peach\Http\Response
  • Peach\Http\Util
  • Peach\Markup\AbstractHelper
  • Peach\Markup\AbstractRenderer
  • Peach\Markup\BaseHelper
  • Peach\Markup\BreakControlWrapper
  • Peach\Markup\Builder
  • Peach\Markup\Code
  • Peach\Markup\Comment
  • Peach\Markup\ContainerElement
  • Peach\Markup\Context
  • Peach\Markup\DebugBuilder
  • Peach\Markup\DebugContext
  • Peach\Markup\DefaultBreakControl
  • Peach\Markup\DefaultBuilder
  • Peach\Markup\DefaultContext
  • Peach\Markup\Element
  • Peach\Markup\EmptyElement
  • Peach\Markup\HelperObject
  • Peach\Markup\HtmlHelper
  • Peach\Markup\Indent
  • Peach\Markup\MinimalBreakControl
  • Peach\Markup\NameBreakControl
  • Peach\Markup\NameValidator
  • Peach\Markup\NodeList
  • Peach\Markup\None
  • Peach\Markup\SgmlRenderer
  • Peach\Markup\Text
  • Peach\Markup\XmlRenderer
  • Peach\Util\AbstractMapEntry
  • Peach\Util\ArrayMap
  • Peach\Util\ArrayMapEntry
  • Peach\Util\Arrays
  • Peach\Util\DefaultComparator
  • Peach\Util\DefaultEquator
  • Peach\Util\HashMap
  • Peach\Util\HashMapEntry
  • Peach\Util\Strings
  • Peach\Util\Values

Interfaces

  • Peach\DF\Codec
  • Peach\DT\Format
  • Peach\DT\Time
  • Peach\Http\BodyRenderer
  • Peach\Http\Endpoint
  • Peach\Http\HeaderField
  • Peach\Http\MultiHeaderField
  • Peach\Http\SingleHeaderField
  • Peach\Markup\BreakControl
  • Peach\Markup\Component
  • Peach\Markup\Container
  • Peach\Markup\Helper
  • Peach\Markup\Node
  • Peach\Markup\Renderer
  • Peach\Util\Comparable
  • Peach\Util\Comparator
  • Peach\Util\Equator
  • Peach\Util\Map
  • Peach\Util\MapEntry
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /*
  3:  * Copyright (c) 2014 @trashtoy
  4:  * https://github.com/trashtoy/
  5:  * 
  6:  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  7:  * this software and associated documentation files (the "Software"), to deal in
  8:  * the Software without restriction, including without limitation the rights to use,
  9:  * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 10:  * Software, and to permit persons to whom the Software is furnished to do so,
 11:  * subject to the following conditions:
 12:  * 
 13:  * The above copyright notice and this permission notice shall be included in all
 14:  * copies or substantial portions of the Software.
 15:  * 
 16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 18:  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 19:  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 20:  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 21:  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 22:  */
 23: /**
 24:  * PHP class file.
 25:  * @auhtor trashtoy
 26:  * @since  2.0.0
 27:  */
 28: namespace Peach\DT;
 29: 
 30: /**
 31:  * {@link http://www.w3.org/TR/NOTE-datetime W3CDTF} と時間オブジェクトの相互変換を行うフォーマットです.
 32:  * 本来の W3CDTF は日付と時刻の間に "T" が入りますが (例: "YYYY-MM-DDThh:mm:ss")
 33:  * このクラスは SQL などで用いられる慣用表現 (例: "YYYY-MM-DD hh:mm:ss") のパースもできます.
 34:  * 
 35:  * parse メソッドは, 文字列の先頭が W3CDTF の書式にマッチするかどうかで妥当性をチェックします.
 36:  * "2012-05-21T07:30:00" のような文字列は
 37:  * parseDate, parseDatetime, parseTimestamp のすべてのメソッドで受け付けますが,
 38:  * "2012-05-21" のような文字列は parseDate だけしか受け付けません.
 39:  * 
 40:  * このクラスはタイムゾーンを適切に扱うことが出来ます.
 41:  * parse 系メソッドでは, 時間オブジェクトに変換する際に内部のタイムゾーンに合わせて時刻を調整します.
 42:  * 例えばシステム時刻が UTC+9 として, GMT の時間文字列 "2012-05-20T22:30Z" を parse した場合
 43:  * 9 時間進ませた時間オブジェクト (new Datetime(2012, 5, 21, 7, 30) に等価) を生成します.
 44:  * 
 45:  * 時間オブジェクトを書式化する際は, 出力用の時差に合わせた文字列を生成します.
 46:  * 例えば内部のタイムゾーンが UTC+9, 出力用のタイムゾーンが UTC+5 に設定されているものとします.
 47:  * この場合, 内部のタイムゾーンと出力用のタイムゾーンの時差が -4 時間となるため,
 48:  * new Datetime(2012, 5, 21, 7, 30) の時間オブジェクトを書式化した結果は
 49:  * "2012-05-21T03:30+5:00" となります.
 50:  */
 51: class W3cDatetimeFormat implements Format
 52: {
 53:     /**
 54:      * タイムゾーンを扱うのみ TRUE となります
 55:      * @var bool
 56:      */
 57:     private $usingTz;
 58:     
 59:     /**
 60:      * システム時刻の時差です (単位は分)
 61:      * @var int
 62:      */
 63:     private $internalOffset;
 64:     
 65:     /**
 66:      * フォーマットの時差です (単位は分)
 67:      * @var int
 68:      */
 69:     private $externalOffset;
 70:     
 71:     /**
 72:      * 日付文字列のパターンです.
 73:      * "YYYY-MM-DD" のような形式にマッチします.
 74:      * 
 75:      * @var string
 76:      */
 77:     private static $datePattern = "[0-9]{4}[^0-9][0-9]{2}[^0-9][0-9]{2}";
 78:     
 79:     /**
 80:      * タイムゾーンの文字列のパターンです. 以下の形式にマッチします.
 81:      * 
 82:      * - "Z"
 83:      * - "-hh:mm"
 84:      * - "+hh:mm"
 85:      * - 空文字列
 86:      * 
 87:      * @var string
 88:      */
 89:     private static $timeZonePattern = "(Z|[\\-\\+][0-9]{2}:[0-9]{2})?";
 90:     
 91:     /**
 92:      * 指定されたタイムゾーンの条件で, 新しいフォーマットを構築します.
 93:      * タイムゾーンを扱わない場合は, コンストラクタの代わりに
 94:      * {@link W3cDatetimeFormat::getInstance} を使って下さい.
 95:      * 
 96:      * 引数 $externalOffset を指定することで, 
 97:      * 書式化する際のタイムゾーンの値を設定することが出来ます.
 98:      * (デフォルトはシステム時刻のタイムゾーンとなります)
 99:      * さらに $internalOffset を指定することで,
100:      * システム時刻のタイムゾーンとして任意の値を指定することも出来ます.
101:      * 
102:      * $externalOffset と $internalOffset を省略した場合, デフォルト値としてシステム時刻の設定
103:      * ({@link Util::getTimeZoneOffset}) と同じ値が指定されます.
104:      * UTC+1 以上の時差を設定する場合は負の値,
105:      * UTC-1 以下の時差を設定する場合は正の値を指定してください.
106:      * 
107:      * 以下に, $externalOffset と $internalOffset の組み合わせによる動作例を示します.
108:      * 
109:      * <code>
110:      * // 書式・システム時刻共に UTC+9
111:      * $f1 = new W3cDatetimeFormat(-540, -540);
112:      * 
113:      * // 書式は GMT, システム時刻は UTC+9
114:      * $f2 = new W3cDatetimeFormat(0,    -540);
115:      * 
116:      * // 書式は UTC+9, システム時刻は GMT
117:      * $f3 = new W3cDatetimeFormat(-540,    0);
118:      * 
119:      * // 2012-05-21T07:30 の時間オブジェクトを生成
120:      * $d = new Datetime(2012, 5, 21, 7, 30);
121:      * var_dump($d->format($f1)); // "2012-05-21T07:30+09:00"
122:      * var_dump($d->format($f2)); // "2012-05-20T22:30Z"      (UTC+9 の時刻を GMT として書式化)
123:      * var_dump($d->format($f3)); // "2012-05-21T16:30+09:00" (GMT の時刻を UTC+9 として書式化)
124:      * </code>
125:      * 
126:      * @param int  $externalOffset フォーマットの時差 (単位は分, 省略した場合はシステム設定の値を使用)
127:      * @param int  $internalOffset システム時刻の時差 (単位は分, 省略した場合はシステム設定の値を使用)
128:      */
129:     public function __construct($externalOffset = null, $internalOffset = null)
130:     {
131:         // @codeCoverageIgnoreStart
132:         if ($externalOffset === false) {
133:             $this->usingTz        = false;
134:             $this->externalOffset = null;
135:             $this->internalOffset = null;
136:             return;
137:         }
138:         // @codeCoverageIgnoreEnd
139:         
140:         $this->usingTz        = true;
141:         $this->externalOffset = Util::cleanTimeZoneOffset($externalOffset);
142:         $this->internalOffset = Util::cleanTimeZoneOffset($internalOffset);
143:     }
144:     
145:     /**
146:      * デフォルトのインスタンスを返します.
147:      * このメソッドから生成されたオブジェクトは, parse の際にタイムゾーンを一切考慮しません.
148:      * また, 書式化する際にタイムゾーン文字列を付与しません.
149:      * 
150:      * @return W3cDatetimeFormat タイムゾーンに対応しないインスタンス
151:      * @codeCoverageIgnore
152:      */
153:     public static function getInstance()
154:     {
155:         static $instance = null;
156:         if ($instance === null) {
157:             $instance = new self(false);
158:         }
159:         return $instance;
160:     }
161:     
162:     /**
163:      * "YYYY-MM-DD" 形式の文字列を解析します.
164:      * 
165:      * @param  string $format 解析対象の文字列
166:      * @return Date           解析結果
167:      * @see Format::parseDate()
168:      */
169:     public function parseDate($format)
170:     {
171:         $dp = self::$datePattern;
172:         if (preg_match("/^{$dp}/", $format)) {
173:             $year  = substr($format, 0, 4);
174:             $month = substr($format, 5, 2);
175:             $date  = substr($format, 8, 2);
176:             return new Date($year, $month, $date);
177:         } else {
178:             throw $this->createFormatException($format, "YYYY-MM-DD");
179:         }
180:     }
181:     
182:     /**
183:      * "YYYY-MM-DDThh:mm" 形式の文字列を解析します.
184:      * 文字列の末尾にタイムゾーン (+hh:mm や -hh:mm など) を含む場合は, システム時刻への変換を行います.
185:      * 
186:      * @param  string $format 解析対象の文字列
187:      * @return Datetime       解析結果
188:      * @see Format::parseDatetime()
189:      */
190:     public function parseDatetime($format)
191:     {
192:         $dp     = self::$datePattern;
193:         $tzp    = self::$timeZonePattern;
194:         $result = null;
195:         if (!preg_match("/^{$dp}[^0-9][0-9]{2}:[0-9]{2}{$tzp}/", $format, $result)) {
196:             throw $this->createFormatException($format, "YYYY-MM-DD hh:mm[timezone]");
197:         }
198:         
199:         $year   = substr($format, 0,  4);
200:         $month  = substr($format, 5,  2);
201:         $date   = substr($format, 8,  2);
202:         $hour   = substr($format, 11, 2);
203:         $min    = substr($format, 14, 2);
204:         $obj    = new Datetime($year, $month, $date, $hour, $min);
205:         return ($this->usingTz && isset($result[1])) ?
206:             $this->adjustFromParse($obj, $result[1]) : $obj;
207:     }
208:     
209:     /**
210:      * "YYYY-MM-DDThh:mm:ss" 形式の文字列を解析します.
211:      * 
212:      * @param  string $format 解析対象の文字列
213:      * @return Timestamp      解析結果
214:      * @see Format::parseTimestamp()
215:      */
216:     public function parseTimestamp($format)
217:     {
218:         $dp     = self::$datePattern;
219:         $tzp    = self::$timeZonePattern;
220:         $result = null;
221:         if (!preg_match("/^{$dp}[^0-9][0-9]{2}:[0-9]{2}:[0-9]{2}{$tzp}/", $format, $result)) {
222:             throw $this->createFormatException($format, "YYYY-MM-DD hh:mm:ss");
223:         }
224:         
225:         $year   = substr($format, 0,  4);
226:         $month  = substr($format, 5,  2);
227:         $date   = substr($format, 8,  2);
228:         $hour   = substr($format, 11, 2);
229:         $min    = substr($format, 14, 2);
230:         $sec    = substr($format, 17, 2);
231:         $obj    = new Timestamp($year, $month, $date, $hour, $min, $sec);
232:         return ($this->usingTz && isset($result[1])) ?
233:             $this->adjustFromParse($obj, $result[1]) : $obj;
234:     }
235:     
236:     /**
237:      * 指定された時間オブジェクトを "YYYY-MM-DD" 形式の文字列に変換します.
238:      * 
239:      * @param  Date $d 解析対象の Date オブジェクト
240:      * @return string  "YYYY-MM-DD" 形式の文字列
241:      * @see    Format::formatDate()
242:      */
243:     public function formatDate(Date $d)
244:     {
245:         return $d->__toString();
246:     }
247:     
248:     /**
249:      * 指定された時間オブジェクトを "YYYY-MM-DDThh:mm" 形式の文字列に変換します.
250:      * このインスタンスがタイムゾーンに対応している場合, 末尾にタイムゾーン文字列も付加します.
251:      * 
252:      * @param  Datetime $d 変換対象の Datetime オブジェクト
253:      * @return string      "YYYY-MM-DDThh:mm" 形式の文字列
254:      * @see    Format::formatDatetime()
255:      */
256:     public function formatDatetime(Datetime $d)
257:     {
258:         $a = $this->adjustFromFormat($d, false);
259:         return $this->formatDate($a->toDate()) . "T" . $a->formatTime() . $this->formatTimezone();
260:     }
261:     
262:     /**
263:      * 指定された時間オブジェクトを "YYYY-MM-DDThh:mm:ss" 形式の文字列に変換します.
264:      * このインスタンスがタイムゾーンに対応している場合, 末尾にタイムゾーン文字列も付加します.
265:      * 
266:      * @param  Timestamp $d 変換対象の Timestamp オブジェクト
267:      * @return string       "YYYY-MM-DDThh:mm:ss" 形式の文字列
268:      * @see    Format::formatTimestamp()
269:      */
270:     public function formatTimestamp(Timestamp $d)
271:     {
272:         return $this->formatDatetime($d);
273:     }
274:     
275:     /**
276:      * 指定された文字列が想定されたフォーマットでないことをあらわす
277:      * InvalidArgumentException を生成します.
278:      * 
279:      * @param string $format
280:      * @param string $expected
281:      * @return \InvalidArgumentException
282:      */
283:     private function createFormatException($format, $expected)
284:     {
285:         return new \InvalidArgumentException("Illegal format({$format}). Expected: {$expected}");
286:     }
287:     
288:     /**
289:      * タイムゾーン文字列をパースして, 時差を分単位で返します.
290:      * NULL が指定された場合 (タイムゾーン文字列がなかった場合),
291:      * このオブジェクトに設定されている externalOffset の値を返します.
292:      * 
293:      * @param  string $tz NULL, "Z", または "+h:mm", "-h:mm" 形式の文字列
294:      * @return int
295:      */
296:     private function parseTimezone($tz)
297:     {
298:         if ($tz === "Z") {
299:             return 0;
300:         }
301:         
302:         $coronIndex = strpos($tz, ":");
303:         $sign       = substr($tz, 0, 1);
304:         $hour       = substr($tz, 1, $coronIndex - 1);
305:         $minute     = substr($tz, $coronIndex + 1);
306:         $offset     = $hour * 60 + $minute;
307:         return ($sign === "-") ? $offset : - $offset;
308:     }
309:     
310:     /**
311:      * フォーマットのタイムゾーンと, 時間オブジェクトのタイムゾーンを元に
312:      * 指定された時間オブジェクトの補正処理を行います.
313:      * 
314:      * @param  Datetime $d  補正対象の時間オブジェクト
315:      * @param  string   $tz タイムゾーン文字列
316:      * @return Datetime     補正結果の時間オブジェクト
317:      */
318:     private function adjustFromParse(Datetime $d, $tz)
319:     {
320:         $externalOffset = $this->parseTimezone($tz);
321:         return $d->add("minute", $externalOffset - $this->internalOffset);
322:     }
323:     
324:     /**
325:      * このオブジェクトがタイムゾーンを利用する場合は,
326:      * 引数の時間オブジェクトを補正した結果を返します.
327:      * タイムゾーンを利用しない場合は引数をそのまま返します.
328:      * このメソッドは各種 format メソッドから呼ばれます.
329:      * 
330:      * @param  Datetime $d 補正対象の時間オブジェクト
331:      * @return Datetime    補正結果
332:      */
333:     private function adjustFromFormat(Datetime $d)
334:     {
335:         return $this->usingTz ? $d->add("minute", $this->internalOffset - $this->externalOffset) : $d;
336:     }
337:     
338:     /**
339:      * タイムゾーンを書式化します.
340:      * @return string
341:      */
342:     private function formatTimezone()
343:     {
344:         if (!$this->usingTz) {
345:             return "";
346:         }
347:         if ($this->externalOffset === 0) {
348:             return "Z";
349:         }
350:         
351:         $ext    = $this->externalOffset;
352:         $format = (0 < $ext) ? "-" : "+";
353:         $offset = abs($ext);
354:         $hour   = intval($offset / 60);
355:         $minute = intval($offset % 60);
356:         $format .= str_pad($hour,   2, '0', STR_PAD_LEFT);
357:         $format .= ':';
358:         $format .= str_pad($minute, 2, '0', STR_PAD_LEFT);
359:         return $format;
360:     }
361: }
362: 
PEACH2 API documentation generated by ApiGen