Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
/**
5
 * SimplePie
6
 *
7
 * A PHP-Based RSS and Atom Feed Framework.
8
 * Takes the hard work out of managing a complete RSS/Atom solution.
9
 *
10
 * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
11
 * All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without modification, are
14
 * permitted provided that the following conditions are met:
15
 *
16
 * 	* Redistributions of source code must retain the above copyright notice, this list of
17
 * 	  conditions and the following disclaimer.
18
 *
19
 * 	* Redistributions in binary form must reproduce the above copyright notice, this list
20
 * 	  of conditions and the following disclaimer in the documentation and/or other materials
21
 * 	  provided with the distribution.
22
 *
23
 * 	* Neither the name of the SimplePie Team nor the names of its contributors may be used
24
 * 	  to endorse or promote products derived from this software without specific prior
25
 * 	  written permission.
26
 *
27
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
28
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
29
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
30
 * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
 * POSSIBILITY OF SUCH DAMAGE.
36
 *
37
 * @package SimplePie
38
 * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
39
 * @author Ryan Parman
40
 * @author Sam Sneddon
41
 * @author Ryan McCue
42
 * @link http://simplepie.org/ SimplePie
43
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
44
 */
45
 
46
namespace SimplePie\Parse;
47
 
48
/**
49
 * Date Parser
50
 *
51
 * @package SimplePie
52
 * @subpackage Parsing
53
 */
54
class Date
55
{
56
    /**
57
     * Input data
58
     *
59
     * @access protected
60
     * @var string
61
     */
62
    public $date;
63
 
64
    /**
65
     * List of days, calendar day name => ordinal day number in the week
66
     *
67
     * @access protected
68
     * @var array
69
     */
70
    public $day = [
71
        // English
72
        'mon' => 1,
73
        'monday' => 1,
74
        'tue' => 2,
75
        'tuesday' => 2,
76
        'wed' => 3,
77
        'wednesday' => 3,
78
        'thu' => 4,
79
        'thursday' => 4,
80
        'fri' => 5,
81
        'friday' => 5,
82
        'sat' => 6,
83
        'saturday' => 6,
84
        'sun' => 7,
85
        'sunday' => 7,
86
        // Dutch
87
        'maandag' => 1,
88
        'dinsdag' => 2,
89
        'woensdag' => 3,
90
        'donderdag' => 4,
91
        'vrijdag' => 5,
92
        'zaterdag' => 6,
93
        'zondag' => 7,
94
        // French
95
        'lundi' => 1,
96
        'mardi' => 2,
97
        'mercredi' => 3,
98
        'jeudi' => 4,
99
        'vendredi' => 5,
100
        'samedi' => 6,
101
        'dimanche' => 7,
102
        // German
103
        'montag' => 1,
104
        'mo' => 1,
105
        'dienstag' => 2,
106
        'di' => 2,
107
        'mittwoch' => 3,
108
        'mi' => 3,
109
        'donnerstag' => 4,
110
        'do' => 4,
111
        'freitag' => 5,
112
        'fr' => 5,
113
        'samstag' => 6,
114
        'sa' => 6,
115
        'sonnabend' => 6,
116
        // AFAIK no short form for sonnabend
117
        'so' => 7,
118
        'sonntag' => 7,
119
        // Italian
120
        'lunedì' => 1,
121
        'martedì' => 2,
122
        'mercoledì' => 3,
123
        'giovedì' => 4,
124
        'venerdì' => 5,
125
        'sabato' => 6,
126
        'domenica' => 7,
127
        // Spanish
128
        'lunes' => 1,
129
        'martes' => 2,
130
        'miércoles' => 3,
131
        'jueves' => 4,
132
        'viernes' => 5,
133
        'sábado' => 6,
134
        'domingo' => 7,
135
        // Finnish
136
        'maanantai' => 1,
137
        'tiistai' => 2,
138
        'keskiviikko' => 3,
139
        'torstai' => 4,
140
        'perjantai' => 5,
141
        'lauantai' => 6,
142
        'sunnuntai' => 7,
143
        // Hungarian
144
        'hétfő' => 1,
145
        'kedd' => 2,
146
        'szerda' => 3,
147
        'csütörtok' => 4,
148
        'péntek' => 5,
149
        'szombat' => 6,
150
        'vasárnap' => 7,
151
        // Greek
152
        'Δευ' => 1,
153
        'Τρι' => 2,
154
        'Τετ' => 3,
155
        'Πεμ' => 4,
156
        'Παρ' => 5,
157
        'Σαβ' => 6,
158
        'Κυρ' => 7,
159
        // Russian
160
        'Пн.' => 1,
161
        'Вт.' => 2,
162
        'Ср.' => 3,
163
        'Чт.' => 4,
164
        'Пт.' => 5,
165
        'Сб.' => 6,
166
        'Вс.' => 7,
167
    ];
168
 
169
    /**
170
     * List of months, calendar month name => calendar month number
171
     *
172
     * @access protected
173
     * @var array
174
     */
175
    public $month = [
176
        // English
177
        'jan' => 1,
178
        'january' => 1,
179
        'feb' => 2,
180
        'february' => 2,
181
        'mar' => 3,
182
        'march' => 3,
183
        'apr' => 4,
184
        'april' => 4,
185
        'may' => 5,
186
        // No long form of May
187
        'jun' => 6,
188
        'june' => 6,
189
        'jul' => 7,
190
        'july' => 7,
191
        'aug' => 8,
192
        'august' => 8,
193
        'sep' => 9,
194
        'september' => 9,
195
        'oct' => 10,
196
        'october' => 10,
197
        'nov' => 11,
198
        'november' => 11,
199
        'dec' => 12,
200
        'december' => 12,
201
        // Dutch
202
        'januari' => 1,
203
        'februari' => 2,
204
        'maart' => 3,
205
        'april' => 4,
206
        'mei' => 5,
207
        'juni' => 6,
208
        'juli' => 7,
209
        'augustus' => 8,
210
        'september' => 9,
211
        'oktober' => 10,
212
        'november' => 11,
213
        'december' => 12,
214
        // French
215
        'janvier' => 1,
216
        'février' => 2,
217
        'mars' => 3,
218
        'avril' => 4,
219
        'mai' => 5,
220
        'juin' => 6,
221
        'juillet' => 7,
222
        'août' => 8,
223
        'septembre' => 9,
224
        'octobre' => 10,
225
        'novembre' => 11,
226
        'décembre' => 12,
227
        // German
228
        'januar' => 1,
229
        'jan' => 1,
230
        'februar' => 2,
231
        'feb' => 2,
232
        'märz' => 3,
233
        'mär' => 3,
234
        'april' => 4,
235
        'apr' => 4,
236
        'mai' => 5, // no short form for may
237
        'juni' => 6,
238
        'jun' => 6,
239
        'juli' => 7,
240
        'jul' => 7,
241
        'august' => 8,
242
        'aug' => 8,
243
        'september' => 9,
244
        'sep' => 9,
245
        'oktober' => 10,
246
        'okt' => 10,
247
        'november' => 11,
248
        'nov' => 11,
249
        'dezember' => 12,
250
        'dez' => 12,
251
        // Italian
252
        'gennaio' => 1,
253
        'febbraio' => 2,
254
        'marzo' => 3,
255
        'aprile' => 4,
256
        'maggio' => 5,
257
        'giugno' => 6,
258
        'luglio' => 7,
259
        'agosto' => 8,
260
        'settembre' => 9,
261
        'ottobre' => 10,
262
        'novembre' => 11,
263
        'dicembre' => 12,
264
        // Spanish
265
        'enero' => 1,
266
        'febrero' => 2,
267
        'marzo' => 3,
268
        'abril' => 4,
269
        'mayo' => 5,
270
        'junio' => 6,
271
        'julio' => 7,
272
        'agosto' => 8,
273
        'septiembre' => 9,
274
        'setiembre' => 9,
275
        'octubre' => 10,
276
        'noviembre' => 11,
277
        'diciembre' => 12,
278
        // Finnish
279
        'tammikuu' => 1,
280
        'helmikuu' => 2,
281
        'maaliskuu' => 3,
282
        'huhtikuu' => 4,
283
        'toukokuu' => 5,
284
        'kesäkuu' => 6,
285
        'heinäkuu' => 7,
286
        'elokuu' => 8,
287
        'suuskuu' => 9,
288
        'lokakuu' => 10,
289
        'marras' => 11,
290
        'joulukuu' => 12,
291
        // Hungarian
292
        'január' => 1,
293
        'február' => 2,
294
        'március' => 3,
295
        'április' => 4,
296
        'május' => 5,
297
        'június' => 6,
298
        'július' => 7,
299
        'augusztus' => 8,
300
        'szeptember' => 9,
301
        'október' => 10,
302
        'november' => 11,
303
        'december' => 12,
304
        // Greek
305
        'Ιαν' => 1,
306
        'Φεβ' => 2,
307
        'Μάώ' => 3,
308
        'Μαώ' => 3,
309
        'Απρ' => 4,
310
        'Μάι' => 5,
311
        'Μαϊ' => 5,
312
        'Μαι' => 5,
313
        'Ιούν' => 6,
314
        'Ιον' => 6,
315
        'Ιούλ' => 7,
316
        'Ιολ' => 7,
317
        'Αύγ' => 8,
318
        'Αυγ' => 8,
319
        'Σεπ' => 9,
320
        'Οκτ' => 10,
321
        'Νοέ' => 11,
322
        'Δεκ' => 12,
323
        // Russian
324
        'Янв' => 1,
325
        'января' => 1,
326
        'Фев' => 2,
327
        'февраля' => 2,
328
        'Мар' => 3,
329
        'марта' => 3,
330
        'Апр' => 4,
331
        'апреля' => 4,
332
        'Май' => 5,
333
        'мая' => 5,
334
        'Июн' => 6,
335
        'июня' => 6,
336
        'Июл' => 7,
337
        'июля' => 7,
338
        'Авг' => 8,
339
        'августа' => 8,
340
        'Сен' => 9,
341
        'сентября' => 9,
342
        'Окт' => 10,
343
        'октября' => 10,
344
        'Ноя' => 11,
345
        'ноября' => 11,
346
        'Дек' => 12,
347
        'декабря' => 12,
348
 
349
    ];
350
 
351
    /**
352
     * List of timezones, abbreviation => offset from UTC
353
     *
354
     * @access protected
355
     * @var array
356
     */
357
    public $timezone = [
358
        'ACDT' => 37800,
359
        'ACIT' => 28800,
360
        'ACST' => 34200,
361
        'ACT' => -18000,
362
        'ACWDT' => 35100,
363
        'ACWST' => 31500,
364
        'AEDT' => 39600,
365
        'AEST' => 36000,
366
        'AFT' => 16200,
367
        'AKDT' => -28800,
368
        'AKST' => -32400,
369
        'AMDT' => 18000,
370
        'AMT' => -14400,
371
        'ANAST' => 46800,
372
        'ANAT' => 43200,
373
        'ART' => -10800,
374
        'AZOST' => -3600,
375
        'AZST' => 18000,
376
        'AZT' => 14400,
377
        'BIOT' => 21600,
378
        'BIT' => -43200,
379
        'BOT' => -14400,
380
        'BRST' => -7200,
381
        'BRT' => -10800,
382
        'BST' => 3600,
383
        'BTT' => 21600,
384
        'CAST' => 18000,
385
        'CAT' => 7200,
386
        'CCT' => 23400,
387
        'CDT' => -18000,
388
        'CEDT' => 7200,
389
        'CEST' => 7200,
390
        'CET' => 3600,
391
        'CGST' => -7200,
392
        'CGT' => -10800,
393
        'CHADT' => 49500,
394
        'CHAST' => 45900,
395
        'CIST' => -28800,
396
        'CKT' => -36000,
397
        'CLDT' => -10800,
398
        'CLST' => -14400,
399
        'COT' => -18000,
400
        'CST' => -21600,
401
        'CVT' => -3600,
402
        'CXT' => 25200,
403
        'DAVT' => 25200,
404
        'DTAT' => 36000,
405
        'EADT' => -18000,
406
        'EAST' => -21600,
407
        'EAT' => 10800,
408
        'ECT' => -18000,
409
        'EDT' => -14400,
410
        'EEST' => 10800,
411
        'EET' => 7200,
412
        'EGT' => -3600,
413
        'EKST' => 21600,
414
        'EST' => -18000,
415
        'FJT' => 43200,
416
        'FKDT' => -10800,
417
        'FKST' => -14400,
418
        'FNT' => -7200,
419
        'GALT' => -21600,
420
        'GEDT' => 14400,
421
        'GEST' => 10800,
422
        'GFT' => -10800,
423
        'GILT' => 43200,
424
        'GIT' => -32400,
425
        'GST' => 14400,
426
        'GST' => -7200,
427
        'GYT' => -14400,
428
        'HAA' => -10800,
429
        'HAC' => -18000,
430
        'HADT' => -32400,
431
        'HAE' => -14400,
432
        'HAP' => -25200,
433
        'HAR' => -21600,
434
        'HAST' => -36000,
435
        'HAT' => -9000,
436
        'HAY' => -28800,
437
        'HKST' => 28800,
438
        'HMT' => 18000,
439
        'HNA' => -14400,
440
        'HNC' => -21600,
441
        'HNE' => -18000,
442
        'HNP' => -28800,
443
        'HNR' => -25200,
444
        'HNT' => -12600,
445
        'HNY' => -32400,
446
        'IRDT' => 16200,
447
        'IRKST' => 32400,
448
        'IRKT' => 28800,
449
        'IRST' => 12600,
450
        'JFDT' => -10800,
451
        'JFST' => -14400,
452
        'JST' => 32400,
453
        'KGST' => 21600,
454
        'KGT' => 18000,
455
        'KOST' => 39600,
456
        'KOVST' => 28800,
457
        'KOVT' => 25200,
458
        'KRAST' => 28800,
459
        'KRAT' => 25200,
460
        'KST' => 32400,
461
        'LHDT' => 39600,
462
        'LHST' => 37800,
463
        'LINT' => 50400,
464
        'LKT' => 21600,
465
        'MAGST' => 43200,
466
        'MAGT' => 39600,
467
        'MAWT' => 21600,
468
        'MDT' => -21600,
469
        'MESZ' => 7200,
470
        'MEZ' => 3600,
471
        'MHT' => 43200,
472
        'MIT' => -34200,
473
        'MNST' => 32400,
474
        'MSDT' => 14400,
475
        'MSST' => 10800,
476
        'MST' => -25200,
477
        'MUT' => 14400,
478
        'MVT' => 18000,
479
        'MYT' => 28800,
480
        'NCT' => 39600,
481
        'NDT' => -9000,
482
        'NFT' => 41400,
483
        'NMIT' => 36000,
484
        'NOVST' => 25200,
485
        'NOVT' => 21600,
486
        'NPT' => 20700,
487
        'NRT' => 43200,
488
        'NST' => -12600,
489
        'NUT' => -39600,
490
        'NZDT' => 46800,
491
        'NZST' => 43200,
492
        'OMSST' => 25200,
493
        'OMST' => 21600,
494
        'PDT' => -25200,
495
        'PET' => -18000,
496
        'PETST' => 46800,
497
        'PETT' => 43200,
498
        'PGT' => 36000,
499
        'PHOT' => 46800,
500
        'PHT' => 28800,
501
        'PKT' => 18000,
502
        'PMDT' => -7200,
503
        'PMST' => -10800,
504
        'PONT' => 39600,
505
        'PST' => -28800,
506
        'PWT' => 32400,
507
        'PYST' => -10800,
508
        'PYT' => -14400,
509
        'RET' => 14400,
510
        'ROTT' => -10800,
511
        'SAMST' => 18000,
512
        'SAMT' => 14400,
513
        'SAST' => 7200,
514
        'SBT' => 39600,
515
        'SCDT' => 46800,
516
        'SCST' => 43200,
517
        'SCT' => 14400,
518
        'SEST' => 3600,
519
        'SGT' => 28800,
520
        'SIT' => 28800,
521
        'SRT' => -10800,
522
        'SST' => -39600,
523
        'SYST' => 10800,
524
        'SYT' => 7200,
525
        'TFT' => 18000,
526
        'THAT' => -36000,
527
        'TJT' => 18000,
528
        'TKT' => -36000,
529
        'TMT' => 18000,
530
        'TOT' => 46800,
531
        'TPT' => 32400,
532
        'TRUT' => 36000,
533
        'TVT' => 43200,
534
        'TWT' => 28800,
535
        'UYST' => -7200,
536
        'UYT' => -10800,
537
        'UZT' => 18000,
538
        'VET' => -14400,
539
        'VLAST' => 39600,
540
        'VLAT' => 36000,
541
        'VOST' => 21600,
542
        'VUT' => 39600,
543
        'WAST' => 7200,
544
        'WAT' => 3600,
545
        'WDT' => 32400,
546
        'WEST' => 3600,
547
        'WFT' => 43200,
548
        'WIB' => 25200,
549
        'WIT' => 32400,
550
        'WITA' => 28800,
551
        'WKST' => 18000,
552
        'WST' => 28800,
553
        'YAKST' => 36000,
554
        'YAKT' => 32400,
555
        'YAPT' => 36000,
556
        'YEKST' => 21600,
557
        'YEKT' => 18000,
558
    ];
559
 
560
    /**
561
     * Cached PCRE for Date::$day
562
     *
563
     * @access protected
564
     * @var string
565
     */
566
    public $day_pcre;
567
 
568
    /**
569
     * Cached PCRE for Date::$month
570
     *
571
     * @access protected
572
     * @var string
573
     */
574
    public $month_pcre;
575
 
576
    /**
577
     * Array of user-added callback methods
578
     *
579
     * @access private
580
     * @var array
581
     */
582
    public $built_in = [];
583
 
584
    /**
585
     * Array of user-added callback methods
586
     *
587
     * @access private
588
     * @var array
589
     */
590
    public $user = [];
591
 
592
    /**
593
     * Create new Date object, and set self::day_pcre,
594
     * self::month_pcre, and self::built_in
595
     *
596
     * @access private
597
     */
598
    public function __construct()
599
    {
600
        $this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')';
601
        $this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')';
602
 
603
        static $cache;
604
        if (!isset($cache[get_class($this)])) {
605
            $all_methods = get_class_methods($this);
606
 
607
            foreach ($all_methods as $method) {
608
                if (strtolower(substr($method, 0, 5)) === 'date_') {
609
                    $cache[get_class($this)][] = $method;
610
                }
611
            }
612
        }
613
 
614
        foreach ($cache[get_class($this)] as $method) {
615
            $this->built_in[] = $method;
616
        }
617
    }
618
 
619
    /**
620
     * Get the object
621
     *
622
     * @access public
623
     */
624
    public static function get()
625
    {
626
        static $object;
627
        if (!$object) {
628
            $object = new Date();
629
        }
630
        return $object;
631
    }
632
 
633
    /**
634
     * Parse a date
635
     *
636
     * @final
637
     * @access public
638
     * @param string $date Date to parse
639
     * @return int Timestamp corresponding to date string, or false on failure
640
     */
641
    public function parse($date)
642
    {
643
        foreach ($this->user as $method) {
644
            if (($returned = call_user_func($method, $date)) !== false) {
645
                return $returned;
646
            }
647
        }
648
 
649
        foreach ($this->built_in as $method) {
650
            if (($returned = call_user_func([$this, $method], $date)) !== false) {
651
                return $returned;
652
            }
653
        }
654
 
655
        return false;
656
    }
657
 
658
    /**
659
     * Add a callback method to parse a date
660
     *
661
     * @final
662
     * @access public
663
     * @param callable $callback
664
     */
665
    public function add_callback($callback)
666
    {
667
        if (is_callable($callback)) {
668
            $this->user[] = $callback;
669
        } else {
670
            trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
671
        }
672
    }
673
 
674
    /**
675
     * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
676
     * well as allowing any of upper or lower case "T", horizontal tabs, or
677
     * spaces to be used as the time separator (including more than one))
678
     *
679
     * @access protected
680
     * @return int Timestamp
681
     */
682
    public function date_w3cdtf($date)
683
    {
684
        $pcre = <<<'PCRE'
685
            /
686
            ^
687
            (?P<year>[0-9]{4})
688
            (?:
689
                -?
690
                (?P<month>[0-9]{2})
691
                (?:
692
                    -?
693
                    (?P<day>[0-9]{2})
694
                    (?:
695
                        [Tt\x09\x20]+
696
                        (?P<hour>[0-9]{2})
697
                        (?:
698
                            :?
699
                            (?P<minute>[0-9]{2})
700
                            (?:
701
                                :?
702
                                (?P<second>[0-9]{2})
703
                                (?:
704
                                    .
705
                                    (?P<second_fraction>[0-9]*)
706
                                )?
707
                            )?
708
                        )?
709
                        (?:
710
                            (?P<zulu>Z)
711
                            |   (?P<tz_sign>[+\-])
712
                                (?P<tz_hour>[0-9]{1,2})
713
                                :?
714
                                (?P<tz_minute>[0-9]{1,2})
715
                        )
716
                    )?
717
                )?
718
            )?
719
            $
720
            /x
721
PCRE;
722
        if (preg_match($pcre, $date, $match)) {
723
            // Fill in empty matches and convert to proper types.
724
            $year = (int) $match['year'];
725
            $month = isset($match['month']) ? (int) $match['month'] : 1;
726
            $day = isset($match['day']) ? (int) $match['day'] : 1;
727
            $hour = isset($match['hour']) ? (int) $match['hour'] : 0;
728
            $minute = isset($match['minute']) ? (int) $match['minute'] : 0;
729
            $second = isset($match['second']) ? (int) $match['second'] : 0;
730
            $second_fraction = isset($match['second_fraction']) ? ((int) $match['second_fraction']) / (10 ** strlen($match['second_fraction'])) : 0;
731
            $tz_sign = ($match['tz_sign'] ?? '') === '-' ? -1 : 1;
732
            $tz_hour = isset($match['tz_hour']) ? (int) $match['tz_hour'] : 0;
733
            $tz_minute = isset($match['tz_minute']) ? (int) $match['tz_minute'] : 0;
734
 
735
            // Numeric timezone
736
            $timezone = $tz_hour * 3600;
737
            $timezone += $tz_minute * 60;
738
            $timezone *= $tz_sign;
739
 
740
            // Convert the number of seconds to an integer, taking decimals into account
741
            $second = (int) round($second + $second_fraction);
742
 
743
            return gmmktime($hour, $minute, $second, $month, $day, $year) - $timezone;
744
        }
745
 
746
        return false;
747
    }
748
 
749
    /**
750
     * Remove RFC822 comments
751
     *
752
     * @access protected
753
     * @param string $data Data to strip comments from
754
     * @return string Comment stripped string
755
     */
756
    public function remove_rfc2822_comments($string)
757
    {
758
        $string = (string) $string;
759
        $position = 0;
760
        $length = strlen($string);
761
        $depth = 0;
762
 
763
        $output = '';
764
 
765
        while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
766
            $output .= substr($string, $position, $pos - $position);
767
            $position = $pos + 1;
768
            if ($pos === 0 || $string[$pos - 1] !== '\\') {
769
                $depth++;
770
                while ($depth && $position < $length) {
771
                    $position += strcspn($string, '()', $position);
772
                    if ($string[$position - 1] === '\\') {
773
                        $position++;
774
                        continue;
775
                    } elseif (isset($string[$position])) {
776
                        switch ($string[$position]) {
777
                            case '(':
778
                                $depth++;
779
                                break;
780
 
781
                            case ')':
782
                                $depth--;
783
                                break;
784
                        }
785
                        $position++;
786
                    } else {
787
                        break;
788
                    }
789
                }
790
            } else {
791
                $output .= '(';
792
            }
793
        }
794
        $output .= substr($string, $position);
795
 
796
        return $output;
797
    }
798
 
799
    /**
800
     * Parse RFC2822's date format
801
     *
802
     * @access protected
803
     * @return int Timestamp
804
     */
805
    public function date_rfc2822($date)
806
    {
807
        static $pcre;
808
        if (!$pcre) {
809
            $wsp = '[\x09\x20]';
810
            $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
811
            $optional_fws = $fws . '?';
812
            $day_name = $this->day_pcre;
813
            $month = $this->month_pcre;
814
            $day = '([0-9]{1,2})';
815
            $hour = $minute = $second = '([0-9]{2})';
816
            $year = '([0-9]{2,4})';
817
            $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
818
            $character_zone = '([A-Z]{1,5})';
819
            $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
820
            $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
821
        }
822
        if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) {
823
            /*
824
            Capturing subpatterns:
825
            1: Day name
826
            2: Day
827
            3: Month
828
            4: Year
829
            5: Hour
830
            6: Minute
831
            7: Second
832
            8: Timezone ±
833
            9: Timezone hours
834
            10: Timezone minutes
835
            11: Alphabetic timezone
836
            */
837
 
838
            // Find the month number
839
            $month = $this->month[strtolower($match[3])];
840
 
841
            // Numeric timezone
842
            if ($match[8] !== '') {
843
                $timezone = $match[9] * 3600;
844
                $timezone += $match[10] * 60;
845
                if ($match[8] === '-') {
846
                    $timezone = 0 - $timezone;
847
                }
848
            }
849
            // Character timezone
850
            elseif (isset($this->timezone[strtoupper($match[11])])) {
851
                $timezone = $this->timezone[strtoupper($match[11])];
852
            }
853
            // Assume everything else to be -0000
854
            else {
855
                $timezone = 0;
856
            }
857
 
858
            // Deal with 2/3 digit years
859
            if ($match[4] < 50) {
860
                $match[4] += 2000;
861
            } elseif ($match[4] < 1000) {
862
                $match[4] += 1900;
863
            }
864
 
865
            // Second is optional, if it is empty set it to zero
866
            if ($match[7] !== '') {
867
                $second = $match[7];
868
            } else {
869
                $second = 0;
870
            }
871
 
872
            return gmmktime(intval($match[5]), intval($match[6]), intval($second), intval($month), intval($match[2]), intval($match[4])) - $timezone;
873
        }
874
 
875
        return false;
876
    }
877
 
878
    /**
879
     * Parse RFC850's date format
880
     *
881
     * @access protected
882
     * @return int Timestamp
883
     */
884
    public function date_rfc850($date)
885
    {
886
        static $pcre;
887
        if (!$pcre) {
888
            $space = '[\x09\x20]+';
889
            $day_name = $this->day_pcre;
890
            $month = $this->month_pcre;
891
            $day = '([0-9]{1,2})';
892
            $year = $hour = $minute = $second = '([0-9]{2})';
893
            $zone = '([A-Z]{1,5})';
894
            $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
895
        }
896
        if (preg_match($pcre, $date, $match)) {
897
            /*
898
            Capturing subpatterns:
899
            1: Day name
900
            2: Day
901
            3: Month
902
            4: Year
903
            5: Hour
904
            6: Minute
905
            7: Second
906
            8: Timezone
907
            */
908
 
909
            // Month
910
            $month = $this->month[strtolower($match[3])];
911
 
912
            // Character timezone
913
            if (isset($this->timezone[strtoupper($match[8])])) {
914
                $timezone = $this->timezone[strtoupper($match[8])];
915
            }
916
            // Assume everything else to be -0000
917
            else {
918
                $timezone = 0;
919
            }
920
 
921
            // Deal with 2 digit year
922
            if ($match[4] < 50) {
923
                $match[4] += 2000;
924
            } else {
925
                $match[4] += 1900;
926
            }
927
 
928
            return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
929
        }
930
 
931
        return false;
932
    }
933
 
934
    /**
935
     * Parse C99's asctime()'s date format
936
     *
937
     * @access protected
938
     * @return int Timestamp
939
     */
940
    public function date_asctime($date)
941
    {
942
        static $pcre;
943
        if (!$pcre) {
944
            $space = '[\x09\x20]+';
945
            $wday_name = $this->day_pcre;
946
            $mon_name = $this->month_pcre;
947
            $day = '([0-9]{1,2})';
948
            $hour = $sec = $min = '([0-9]{2})';
949
            $year = '([0-9]{4})';
950
            $terminator = '\x0A?\x00?';
951
            $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
952
        }
953
        if (preg_match($pcre, $date, $match)) {
954
            /*
955
            Capturing subpatterns:
956
            1: Day name
957
            2: Month
958
            3: Day
959
            4: Hour
960
            5: Minute
961
            6: Second
962
            7: Year
963
            */
964
 
965
            $month = $this->month[strtolower($match[2])];
966
            return gmmktime((int) $match[4], (int) $match[5], (int) $match[6], $month, (int) $match[3], (int) $match[7]);
967
        }
968
 
969
        return false;
970
    }
971
 
972
    /**
973
     * Parse dates using strtotime()
974
     *
975
     * @access protected
976
     * @return int Timestamp
977
     */
978
    public function date_strtotime($date)
979
    {
980
        $strtotime = strtotime($date);
981
        if ($strtotime === -1 || $strtotime === false) {
982
            return false;
983
        }
984
 
985
        return $strtotime;
986
    }
987
}
988
 
989
class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date');