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