1 /* 2 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <parsedate.h> 8 9 #include <ctype.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <OS.h> 15 16 17 #define TRACE_PARSEDATE 0 18 #if TRACE_PARSEDATE 19 # define TRACE(x) printf x ; 20 #else 21 # define TRACE(x) ; 22 #endif 23 24 25 /* The date format is as follows: 26 * 27 * a/A weekday 28 * d day of month 29 * b/B month name 30 * m month 31 * y/Y year 32 * H/I hours 33 * M minute 34 * S seconds 35 * p meridian (i.e. am/pm) 36 * T time unit: last hour, next tuesday, today, ... 37 * z/Z time zone 38 * - dash or slash 39 * 40 * Any of ",.:" is allowed and will be expected in the input string as is. 41 * You can enclose a single field with "[]" to mark it as being optional. 42 * A space stands for white space. 43 * No other character is allowed. 44 */ 45 46 static const char * const kFormatsTable[] = { 47 "[A][,] B d[,] H:M:S [p] Y[,] [Z]", 48 "[A][,] B d[,] [Y][,] H:M:S [p] [Z]", 49 "[A][,] B d[,] [Y][,] H:M [p][,] [Z]", 50 "[A][,] B d[,] [Y][,] H [p][,] [Z]", 51 "[A][,] B d[,] H:M [p][,] [Y] [Z]", 52 "[A][,] d B[,] [Y][,] H:M [p][,] [Z]", 53 "[A][,] d B[,] [Y][,] H:M:S [p][,] [Z]", 54 "[A][,] d B[,] H:M:S [Y][,] [p][,] [Z]", 55 "[A][,] d B[,] H:M [Y][,] [p][,] [Z]", 56 "d.m.y H:M:S [p] [Z]", 57 "d.m.y H:M [p] [Z]", 58 "d.m.y", 59 "[A][,] m-d-y[,] [H][:][M] [p]", 60 "[A][,] m-d[,] H:M [p]", 61 "[A][,] m-d[,] H[p]", 62 "[A][,] m-d", 63 "[A][,] B d[,] Y", 64 "[A][,] H:M [p]", 65 "[A][,] H [p]", 66 "H:M [p][,] [A]", 67 "H [p][,] [A]", 68 "[A][,] B d[,] H:M:S [p] [Z] [Y]", 69 "[A][,] B d[,] H:M [p] [Z] [Y]", 70 "[A][,] d B [,] H:M:S [p] [Z] [Y]", 71 "[A][,] d B [,] H:M [p] [Z] [Y]", 72 "[A][,] d-B-Y H:M:S [p] [Z]", 73 "[A][,] d-B-Y H:M [p] [Z]", 74 "d B Y H:M:S [Z]", 75 "d B Y H:M [Z]", 76 "y-m-d", 77 "y-m-d H:M:S [p] [Z]", 78 "m-d-y H[p]", 79 "m-d-y H:M[p]", 80 "m-d-y H:M:S[p]", 81 "H[p] m-d-y", 82 "H:M[p] m-d-y", 83 "H:M:S[p] m-d-y", 84 "A[,] H:M:S [p] [Z]", 85 "A[,] H:M [p] [Z]", 86 "H:M:S [p] [Z]", 87 "H:M [p] [Z]", 88 "A[,] [B] [d] [Y]", 89 "A[,] [d] [B] [Y]", 90 "B d[,][Y] H[p][,] [Z]", 91 "B d[,] H[p]", 92 "B d [,] H:M [p]", 93 "d B [,][Y] H [p] [Z]", 94 "d B [,] H:M [p]", 95 "B d [,][Y]", 96 "B d [,] H:M [p][,] [Y]", 97 "B d [,] H [p][,] [Y]", 98 "d B [,][Y]", 99 "H[p] [,] B d", 100 "H:M[p] [,] B d", 101 "T [T][T][T][T][T]", 102 "T H:M:S [p]", 103 "T H:M [p]", 104 "T H [p]", 105 "H:M [p] T", 106 "H [p] T", 107 "H [p]", 108 NULL 109 }; 110 static const char* const* sFormatsTable = kFormatsTable; 111 112 113 enum field_type { 114 TYPE_UNKNOWN = 0, 115 116 TYPE_DAY, 117 TYPE_MONTH, 118 TYPE_YEAR, 119 TYPE_WEEKDAY, 120 TYPE_HOUR, 121 TYPE_MINUTE, 122 TYPE_SECOND, 123 TYPE_TIME_ZONE, 124 TYPE_MERIDIAN, 125 126 TYPE_DASH, 127 TYPE_DOT, 128 TYPE_COMMA, 129 TYPE_COLON, 130 131 TYPE_UNIT, 132 TYPE_MODIFIER, 133 TYPE_END, 134 }; 135 136 #define FLAG_NONE 0 137 #define FLAG_RELATIVE 1 138 #define FLAG_NOT_MODIFIABLE 2 139 #define FLAG_NOW 4 140 #define FLAG_NEXT_LAST_THIS 8 141 #define FLAG_PLUS_MINUS 16 142 #define FLAG_HAS_DASH 32 143 144 enum units { 145 UNIT_NONE, 146 UNIT_YEAR, 147 UNIT_MONTH, 148 UNIT_DAY, 149 UNIT_SECOND, 150 }; 151 152 enum value_type { 153 VALUE_NUMERICAL, 154 VALUE_STRING, 155 VALUE_CHAR, 156 }; 157 158 enum value_modifier { 159 MODIFY_MINUS = -2, 160 MODIFY_LAST = -1, 161 MODIFY_NONE = 0, 162 MODIFY_THIS = MODIFY_NONE, 163 MODIFY_NEXT = 1, 164 MODIFY_PLUS = 2, 165 }; 166 167 struct known_identifier { 168 const char *string; 169 const char *alternate_string; 170 uint8 type; 171 uint8 flags; 172 uint8 unit; 173 int32 value; 174 }; 175 176 static const known_identifier kIdentifiers[] = { 177 {"today", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, 178 UNIT_DAY, 0}, 179 {"tomorrow", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, 180 UNIT_DAY, 1}, 181 {"yesterday", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, 182 UNIT_DAY, -1}, 183 {"now", NULL, TYPE_UNIT, 184 FLAG_RELATIVE | FLAG_NOT_MODIFIABLE | FLAG_NOW, 0}, 185 186 {"this", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, 187 MODIFY_THIS}, 188 {"next", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, 189 MODIFY_NEXT}, 190 {"last", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, 191 MODIFY_LAST}, 192 193 {"years", "year", TYPE_UNIT, FLAG_RELATIVE, UNIT_YEAR, 1}, 194 {"months", "month",TYPE_UNIT, FLAG_RELATIVE, UNIT_MONTH, 1}, 195 {"weeks", "week", TYPE_UNIT, FLAG_RELATIVE, UNIT_DAY, 7}, 196 {"days", "day", TYPE_UNIT, FLAG_RELATIVE, UNIT_DAY, 1}, 197 {"hour", NULL, TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1 * 60 * 60}, 198 {"hours", "hrs", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1 * 60 * 60}, 199 {"second", "sec", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1}, 200 {"seconds", "secs", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1}, 201 {"minute", "min", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 60}, 202 {"minutes", "mins", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 60}, 203 204 {"am", NULL, TYPE_MERIDIAN, FLAG_NOT_MODIFIABLE, UNIT_SECOND, 0}, 205 {"pm", NULL, TYPE_MERIDIAN, FLAG_NOT_MODIFIABLE, UNIT_SECOND, 206 12 * 60 * 60}, 207 208 {"sunday", "sun", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 0}, 209 {"monday", "mon", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 1}, 210 {"tuesday", "tue", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 2}, 211 {"wednesday", "wed", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 3}, 212 {"thursday", "thu", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 4}, 213 {"friday", "fri", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 5}, 214 {"saturday", "sat", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 6}, 215 216 {"january", "jan", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 1}, 217 {"february", "feb", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 2}, 218 {"march", "mar", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 3}, 219 {"april", "apr", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 4}, 220 {"may", "may", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 5}, 221 {"june", "jun", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 6}, 222 {"july", "jul", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 7}, 223 {"august", "aug", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 8}, 224 {"september", "sep", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 9}, 225 {"october", "oct", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 10}, 226 {"november", "nov", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 11}, 227 {"december", "dec", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 12}, 228 229 {"GMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0}, 230 {"UTC", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0}, 231 // the following list has been generated from info found at 232 // http://www.timegenie.com/timezones 233 {"ACDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1050 * 36}, 234 {"ACIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 235 {"ACST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 950 * 36}, 236 {"ACT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 237 {"ACWST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 875 * 36}, 238 {"ADT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 239 {"AEDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 240 {"AEST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 241 {"AFT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 450 * 36}, 242 {"AKDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -800 * 36}, 243 {"AKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -900 * 36}, 244 {"AMDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 245 {"AMST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 246 {"ANAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 247 {"ANAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 248 {"APO", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 825 * 36}, 249 {"ARDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 250 {"ART", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 251 {"AST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 252 {"AWST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 253 {"AZODT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0 * 36}, 254 {"AZOST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -100 * 36}, 255 {"AZST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 256 {"AZT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 257 {"BIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1200 * 36}, 258 {"BDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 259 {"BEST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 260 {"BIOT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 261 {"BNT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 262 {"BOT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 263 {"BRST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 264 {"BRT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 265 {"BST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 100 * 36}, 266 {"BTT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 267 {"BWDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 268 {"BWST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 269 {"CAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 270 {"CAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 271 {"CCT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 650 * 36}, 272 {"CDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 273 {"CEST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 274 {"CET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 100 * 36}, 275 {"CGST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 276 {"CGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 277 {"CHADT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1375 * 36}, 278 {"CHAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1275 * 36}, 279 {"CHST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 280 {"CIST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -800 * 36}, 281 {"CKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1000 * 36}, 282 {"CLDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 283 {"CLST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 284 {"COT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 285 {"CST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -600 * 36}, 286 {"CVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -100 * 36}, 287 {"CXT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 288 {"DAVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 289 {"DTAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 290 {"EADT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 291 {"EAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -600 * 36}, 292 {"EAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 300 * 36}, 293 {"ECT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 294 {"EDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 295 {"EEST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 300 * 36}, 296 {"EET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 297 {"EGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -100 * 36}, 298 {"EGST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0 * 36}, 299 {"EKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 300 {"EST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 301 {"FJT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 302 {"FKDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 303 {"FKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 304 {"GALT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -600 * 36}, 305 {"GET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 306 {"GFT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 307 {"GILT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 308 {"GIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -900 * 36}, 309 {"GST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 310 {"GYT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 311 {"HADT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -900 * 36}, 312 {"HAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1000 * 36}, 313 {"HKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 314 {"HMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 315 {"ICT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 316 {"IDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 300 * 36}, 317 {"IRDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 450 * 36}, 318 {"IRKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 319 {"IRKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 320 {"IRST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 350 * 36}, 321 {"IST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 322 {"JFDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 323 {"JFST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 324 {"JST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 325 {"KGST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 326 {"KGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 327 {"KRAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 328 {"KRAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 329 {"KOST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 330 {"KOVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 331 {"KOVST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 332 {"KST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 333 {"LHDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 334 {"LHST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1050 * 36}, 335 {"LINT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1400 * 36}, 336 {"LKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 337 {"MAGST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 338 {"MAGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 339 {"MAWT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 340 {"MBT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 341 {"MDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -600 * 36}, 342 {"MIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -950 * 36}, 343 {"MHT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 344 {"MMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 650 * 36}, 345 {"MNT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 346 {"MNST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 347 {"MSD", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 348 {"MSK", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 300 * 36}, 349 {"MST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -700 * 36}, 350 {"MUST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 351 {"MUT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 352 {"MVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 353 {"MYT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 354 {"NCT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 355 {"NDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -250 * 36}, 356 {"NFT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1150 * 36}, 357 {"NPT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 575 * 36}, 358 {"NRT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 359 {"NOVST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 360 {"NOVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 361 {"NST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -350 * 36}, 362 {"NUT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1100 * 36}, 363 {"NZDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 364 {"NZST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 365 {"OMSK", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 366 {"OMST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 367 {"PDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -700 * 36}, 368 {"PETST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 369 {"PET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -500 * 36}, 370 {"PETT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 371 {"PGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 372 {"PHOT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 373 {"PHT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 374 {"PIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 375 {"PKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 376 {"PKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 377 {"PMDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 378 {"PMST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 379 {"PONT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 380 {"PST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -800 * 36}, 381 {"PWT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 382 {"PYST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 383 {"PYT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 384 {"RET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 385 {"ROTT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 386 {"SAMST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 387 {"SAMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 388 {"SAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 389 {"SBT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 390 {"SCDT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 391 {"SCST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 392 {"SCT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 400 * 36}, 393 {"SGT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 394 {"SIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 395 {"SLT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -400 * 36}, 396 {"SLST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 397 {"SRT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 398 {"SST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1100 * 36}, 399 {"SYST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 300 * 36}, 400 {"SYT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 401 {"TAHT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1000 * 36}, 402 {"TFT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 403 {"TJT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 404 {"TKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -1000 * 36}, 405 {"TMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 406 {"TOT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1300 * 36}, 407 {"TPT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 408 {"TRUT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 409 {"TVT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 410 {"TWT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 411 {"UYT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -300 * 36}, 412 {"UYST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -200 * 36}, 413 {"UZT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 414 {"VLAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 415 {"VLAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 416 {"VOST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 417 {"VST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, -450 * 36}, 418 {"VUT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1100 * 36}, 419 {"WAST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 200 * 36}, 420 {"WAT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 100 * 36}, 421 {"WEST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 100 * 36}, 422 {"WET", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0 * 36}, 423 {"WFT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1200 * 36}, 424 {"WIB", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 700 * 36}, 425 {"WIT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 426 {"WITA", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 800 * 36}, 427 {"WKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 428 {"YAKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 429 {"YAKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 900 * 36}, 430 {"YAPT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 1000 * 36}, 431 {"YEKST", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 600 * 36}, 432 {"YEKT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 500 * 36}, 433 434 {NULL} 435 }; 436 437 #define MAX_ELEMENTS 32 438 439 class DateMask { 440 public: 441 DateMask() : fMask(0UL) {} 442 443 void Set(uint8 type) { fMask |= Flag(type); } 444 bool IsSet(uint8 type) { return (fMask & Flag(type)) != 0; } 445 446 bool HasTime(); 447 bool IsComplete(); 448 449 private: 450 inline uint32 Flag(uint8 type) const { return 1UL << type; } 451 452 uint32 fMask; 453 }; 454 455 456 struct parsed_element { 457 uint8 base_type; 458 uint8 type; 459 uint8 flags; 460 uint8 unit; 461 uint8 value_type; 462 int8 modifier; 463 bigtime_t value; 464 465 void SetCharType(uint8 fieldType, int8 modify = MODIFY_NONE); 466 467 void Adopt(const known_identifier& identifier); 468 void AdoptUnit(const known_identifier& identifier); 469 bool IsNextLastThis(); 470 }; 471 472 473 void 474 parsed_element::SetCharType(uint8 fieldType, int8 modify) 475 { 476 base_type = type = fieldType; 477 value_type = VALUE_CHAR; 478 modifier = modify; 479 } 480 481 482 void 483 parsed_element::Adopt(const known_identifier& identifier) 484 { 485 base_type = type = identifier.type; 486 flags = identifier.flags; 487 unit = identifier.unit; 488 489 if (identifier.type == TYPE_MODIFIER) 490 modifier = identifier.value; 491 492 value_type = VALUE_STRING; 493 value = identifier.value; 494 } 495 496 497 void 498 parsed_element::AdoptUnit(const known_identifier& identifier) 499 { 500 base_type = type = TYPE_UNIT; 501 flags = identifier.flags; 502 unit = identifier.unit; 503 value *= identifier.value; 504 } 505 506 507 inline bool 508 parsed_element::IsNextLastThis() 509 { 510 return base_type == TYPE_MODIFIER 511 && (modifier == MODIFY_NEXT || modifier == MODIFY_LAST 512 || modifier == MODIFY_THIS); 513 } 514 515 516 // #pragma mark - 517 518 519 bool 520 DateMask::HasTime() 521 { 522 // this will cause 523 return IsSet(TYPE_HOUR); 524 } 525 526 527 /*! This method checks if the date mask is complete in the 528 sense that it doesn't need to have a prefilled "struct tm" 529 when its time value is computed. 530 */ 531 bool 532 DateMask::IsComplete() 533 { 534 // mask must be absolute, at last 535 if ((fMask & Flag(TYPE_UNIT)) != 0) 536 return false; 537 538 // minimal set of flags to have a complete set 539 return !(~fMask & (Flag(TYPE_DAY) | Flag(TYPE_MONTH))); 540 } 541 542 543 // #pragma mark - 544 545 546 static status_t 547 preparseDate(const char* dateString, parsed_element* elements) 548 { 549 int32 index = 0, modify = MODIFY_NONE; 550 char c; 551 552 if (dateString == NULL) 553 return B_ERROR; 554 555 memset(&elements[0], 0, sizeof(parsed_element)); 556 557 for (; (c = dateString[0]) != '\0'; dateString++) { 558 // we don't care about spaces 559 if (isspace(c)) { 560 modify = MODIFY_NONE; 561 continue; 562 } 563 564 // if we're reached our maximum number of elements, bail out 565 if (index >= MAX_ELEMENTS) 566 return B_ERROR; 567 568 if (c == ',') { 569 elements[index].SetCharType(TYPE_COMMA); 570 } else if (c == '.') { 571 elements[index].SetCharType(TYPE_DOT); 572 } else if (c == '/') { 573 // "-" is handled differently (as a modifier) 574 elements[index].SetCharType(TYPE_DASH); 575 } else if (c == ':') { 576 elements[index].SetCharType(TYPE_COLON); 577 } else if (c == '+') { 578 modify = MODIFY_PLUS; 579 580 // this counts for the next element 581 continue; 582 } else if (c == '-') { 583 modify = MODIFY_MINUS; 584 elements[index].flags = FLAG_HAS_DASH; 585 586 // this counts for the next element 587 continue; 588 } else if (isdigit(c)) { 589 // fetch whole number 590 591 elements[index].type = TYPE_UNKNOWN; 592 elements[index].value_type = VALUE_NUMERICAL; 593 elements[index].value = atoll(dateString); 594 elements[index].modifier = modify; 595 596 // skip number 597 while (isdigit(dateString[1])) 598 dateString++; 599 600 // check for "1st", "2nd, "3rd", "4th", ... 601 602 const char* suffixes[] = {"th", "st", "nd", "rd"}; 603 const char* validSuffix = elements[index].value > 3 604 ? "th" : suffixes[elements[index].value]; 605 if (!strncasecmp(dateString + 1, validSuffix, 2) 606 && !isalpha(dateString[3])) { 607 // for now, just ignore the suffix - but we might be able 608 // to deduce some meaning out of it, since it's not really 609 // possible to put it in anywhere 610 dateString += 2; 611 } 612 } else if (isalpha(c)) { 613 // fetch whole string 614 615 const char* string = dateString; 616 while (isalpha(dateString[1])) 617 dateString++; 618 int32 length = dateString + 1 - string; 619 620 // compare with known strings 621 // ToDo: should understand other languages as well... 622 623 const known_identifier* identifier = kIdentifiers; 624 for (; identifier->string; identifier++) { 625 if (!strncasecmp(identifier->string, string, length) 626 && !identifier->string[length]) 627 break; 628 629 if (identifier->alternate_string != NULL 630 && !strncasecmp(identifier->alternate_string, string, length) 631 && !identifier->alternate_string[length]) 632 break; 633 } 634 if (identifier->string == NULL) { 635 // unknown string, we don't have to parse any further 636 return B_ERROR; 637 } 638 639 if (index > 0 && identifier->type == TYPE_UNIT) { 640 // this is just a unit, so it will give the last value a meaning 641 642 if (elements[--index].value_type != VALUE_NUMERICAL 643 && !elements[index].IsNextLastThis()) 644 return B_ERROR; 645 646 elements[index].AdoptUnit(*identifier); 647 } else if (index > 0 && elements[index - 1].IsNextLastThis()) { 648 if (identifier->type == TYPE_MONTH 649 || identifier->type == TYPE_WEEKDAY) { 650 index--; 651 652 switch (elements[index].value) { 653 case -1: 654 elements[index].modifier = MODIFY_LAST; 655 break; 656 case 0: 657 elements[index].modifier = MODIFY_THIS; 658 break; 659 case 1: 660 elements[index].modifier = MODIFY_NEXT; 661 break; 662 } 663 elements[index].Adopt(*identifier); 664 elements[index].type = TYPE_UNIT; 665 } else 666 return B_ERROR; 667 } else { 668 elements[index].Adopt(*identifier); 669 } 670 } 671 672 // see if we can join any preceding modifiers 673 674 if (index > 0 675 && elements[index - 1].type == TYPE_MODIFIER 676 && (elements[index].flags & FLAG_NOT_MODIFIABLE) == 0) { 677 // copy the current one to the last and go on 678 elements[index].modifier = elements[index - 1].modifier; 679 elements[index].value *= elements[index - 1].value; 680 elements[index].flags |= elements[index - 1].flags; 681 elements[index - 1] = elements[index]; 682 } else { 683 // we filled out one parsed_element 684 index++; 685 } 686 687 if (index < MAX_ELEMENTS) 688 memset(&elements[index], 0, sizeof(parsed_element)); 689 } 690 691 // were there any elements? 692 if (index == 0) 693 return B_ERROR; 694 695 elements[index].type = TYPE_END; 696 697 return B_OK; 698 } 699 700 701 static void 702 computeRelativeUnit(parsed_element& element, struct tm& tm, int* _flags) 703 { 704 // set the relative start depending on unit 705 706 switch (element.unit) { 707 case UNIT_YEAR: 708 tm.tm_mon = 0; // supposed to fall through 709 case UNIT_MONTH: 710 tm.tm_mday = 1; // supposed to fall through 711 case UNIT_DAY: 712 tm.tm_hour = 0; 713 tm.tm_min = 0; 714 tm.tm_sec = 0; 715 break; 716 } 717 718 // adjust value 719 720 if ((element.flags & FLAG_RELATIVE) != 0) { 721 bigtime_t value = element.value; 722 if (element.modifier == MODIFY_MINUS) 723 value = -element.value; 724 725 if (element.unit == UNIT_MONTH) 726 tm.tm_mon += value; 727 else if (element.unit == UNIT_DAY) 728 tm.tm_mday += value; 729 else if (element.unit == UNIT_SECOND) { 730 tm.tm_sec += value; 731 *_flags |= PARSEDATE_MINUTE_RELATIVE_TIME; 732 } else if (element.unit == UNIT_YEAR) 733 tm.tm_year += value; 734 } else if (element.base_type == TYPE_WEEKDAY) { 735 tm.tm_mday += element.value - tm.tm_wday; 736 737 if (element.modifier == MODIFY_NEXT) 738 tm.tm_mday += 7; 739 else if (element.modifier == MODIFY_LAST) 740 tm.tm_mday -= 7; 741 } else if (element.base_type == TYPE_MONTH) { 742 tm.tm_mon = element.value - 1; 743 744 if (element.modifier == MODIFY_NEXT) 745 tm.tm_year++; 746 else if (element.modifier == MODIFY_LAST) 747 tm.tm_year--; 748 } 749 } 750 751 752 /*! Uses the format assignment (through "format", and "optional") for the 753 parsed elements and calculates the time value with respect to "now". 754 Will also set the day/minute relative flags in "_flags". 755 */ 756 static time_t 757 computeDate(const char* format, bool* optional, parsed_element* elements, 758 time_t now, DateMask dateMask, int* _flags) 759 { 760 TRACE(("matches: %s\n", format)); 761 762 parsed_element* element = elements; 763 uint32 position = 0; 764 struct tm tm; 765 766 if (now == -1) 767 now = time(NULL); 768 769 if (dateMask.IsComplete()) 770 memset(&tm, 0, sizeof(tm)); 771 else { 772 localtime_r(&now, &tm); 773 774 if (dateMask.HasTime()) { 775 tm.tm_min = 0; 776 tm.tm_sec = 0; 777 } 778 779 *_flags = PARSEDATE_RELATIVE_TIME; 780 } 781 782 while (element->type != TYPE_END) { 783 // skip whitespace 784 while (isspace(format[0])) 785 format++; 786 787 if (format[0] == '[' && format[2] == ']') { 788 // does this optional parameter not match our date string? 789 if (!optional[position]) { 790 format += 3; 791 position++; 792 continue; 793 } 794 795 format++; 796 } 797 798 switch (element->value_type) { 799 case VALUE_CHAR: 800 // skip the single character 801 break; 802 803 case VALUE_NUMERICAL: 804 switch (format[0]) { 805 case 'd': 806 tm.tm_mday = element->value; 807 break; 808 case 'm': 809 tm.tm_mon = element->value - 1; 810 break; 811 case 'H': 812 case 'I': 813 tm.tm_hour = element->value; 814 break; 815 case 'M': 816 tm.tm_min = element->value; 817 break; 818 case 'S': 819 tm.tm_sec = element->value; 820 break; 821 case 'y': 822 case 'Y': 823 tm.tm_year = element->value; 824 if (tm.tm_year > 1900) 825 tm.tm_year -= 1900; 826 break; 827 case 'z': // time zone 828 case 'Z': 829 { 830 bigtime_t value 831 = (element->value - element->value % 100) * 36 832 + (element->value % 100) * 60; 833 if (element->modifier == MODIFY_MINUS) 834 value *= -1; 835 tm.tm_sec -= value + timezone; 836 break; 837 } 838 case 'T': 839 computeRelativeUnit(*element, tm, _flags); 840 break; 841 case '-': 842 // there is no TYPE_DASH element for this (just a flag) 843 format++; 844 continue; 845 } 846 break; 847 848 case VALUE_STRING: 849 switch (format[0]) { 850 case 'a': // weekday 851 case 'A': 852 // we'll apply this element later, if still necessary 853 if (!dateMask.IsComplete()) 854 computeRelativeUnit(*element, tm, _flags); 855 break; 856 case 'b': // month 857 case 'B': 858 tm.tm_mon = element->value - 1; 859 break; 860 case 'p': // meridian 861 tm.tm_sec += element->value; 862 break; 863 case 'z': // time zone 864 case 'Z': 865 tm.tm_sec -= element->value + timezone; 866 break; 867 case 'T': // time unit 868 if ((element->flags & FLAG_NOW) != 0) { 869 *_flags = PARSEDATE_MINUTE_RELATIVE_TIME 870 | PARSEDATE_RELATIVE_TIME; 871 break; 872 } 873 874 computeRelativeUnit(*element, tm, _flags); 875 break; 876 } 877 break; 878 } 879 880 // format matched at this point, check next element 881 format++; 882 if (format[0] == ']') 883 format++; 884 885 position++; 886 element++; 887 } 888 889 return mktime(&tm); 890 } 891 892 893 // #pragma mark - public API 894 895 896 time_t 897 parsedate_etc(const char* dateString, time_t now, int* _flags) 898 { 899 // preparse date string so that it can be easily compared to our formats 900 901 parsed_element elements[MAX_ELEMENTS]; 902 903 if (preparseDate(dateString, elements) < B_OK) { 904 *_flags = PARSEDATE_INVALID_DATE; 905 return B_ERROR; 906 } 907 908 #if TRACE_PARSEDATE 909 printf("parsedate(\"%s\", now %ld)\n", dateString, now); 910 for (int32 index = 0; elements[index].type != TYPE_END; index++) { 911 parsed_element e = elements[index]; 912 913 printf(" %ld: type = %u, base_type = %u, unit = %u, flags = %u, " 914 "modifier = %u, value = %Ld (%s)\n", index, e.type, e.base_type, 915 e.unit, e.flags, e.modifier, e.value, 916 e.value_type == VALUE_NUMERICAL ? "numerical" 917 : (e.value_type == VALUE_STRING ? "string" : "char")); 918 } 919 #endif 920 921 bool optional[MAX_ELEMENTS]; 922 923 for (int32 index = 0; sFormatsTable[index]; index++) { 924 // test if this format matches our date string 925 926 const char* format = sFormatsTable[index]; 927 uint32 position = 0; 928 DateMask dateMask; 929 930 parsed_element* element = elements; 931 while (element->type != TYPE_END) { 932 // skip whitespace 933 while (isspace(format[0])) 934 format++; 935 936 if (format[0] == '[' && format[2] == ']') { 937 optional[position] = true; 938 format++; 939 } else 940 optional[position] = false; 941 942 switch (element->value_type) { 943 case VALUE_CHAR: 944 // check the allowed single characters 945 946 switch (element->type) { 947 case TYPE_DOT: 948 if (format[0] != '.') 949 goto next_format; 950 break; 951 case TYPE_DASH: 952 if (format[0] != '-') 953 goto next_format; 954 break; 955 case TYPE_COMMA: 956 if (format[0] != ',') 957 goto next_format; 958 break; 959 case TYPE_COLON: 960 if (format[0] != ':') 961 goto next_format; 962 break; 963 default: 964 goto next_format; 965 } 966 break; 967 968 case VALUE_NUMERICAL: 969 // make sure that unit types are respected 970 if (element->type == TYPE_UNIT && format[0] != 'T') 971 goto next_format; 972 973 switch (format[0]) { 974 case 'd': 975 if (element->value > 31) 976 goto next_format; 977 978 dateMask.Set(TYPE_DAY); 979 break; 980 case 'm': 981 if (element->value > 12) 982 goto next_format; 983 984 dateMask.Set(TYPE_MONTH); 985 break; 986 case 'H': 987 case 'I': 988 if (element->value > 24) 989 goto next_format; 990 991 dateMask.Set(TYPE_HOUR); 992 break; 993 case 'M': 994 dateMask.Set(TYPE_MINUTE); 995 case 'S': 996 if (element->value > 59) 997 goto next_format; 998 999 break; 1000 case 'y': 1001 case 'Y': 1002 // accept all values 1003 break; 1004 case 'z': // time zone 1005 case 'Z': 1006 // a numerical timezone must be introduced by '+' 1007 // or '-' and it must not exceed 2399 1008 if ((element->modifier != MODIFY_MINUS 1009 && element->modifier != MODIFY_PLUS) 1010 || element->value > 2399) 1011 goto next_format; 1012 break; 1013 case 'T': 1014 dateMask.Set(TYPE_UNIT); 1015 break; 1016 case '-': 1017 if ((element->flags & FLAG_HAS_DASH) != 0) { 1018 element--; // consider this element again 1019 break; 1020 } 1021 // supposed to fall through 1022 default: 1023 goto next_format; 1024 } 1025 break; 1026 1027 case VALUE_STRING: 1028 switch (format[0]) { 1029 case 'a': // weekday 1030 case 'A': 1031 if (element->type != TYPE_WEEKDAY) 1032 goto next_format; 1033 break; 1034 case 'b': // month 1035 case 'B': 1036 if (element->type != TYPE_MONTH) 1037 goto next_format; 1038 1039 dateMask.Set(TYPE_MONTH); 1040 break; 1041 case 'p': // meridian 1042 if (element->type != TYPE_MERIDIAN) 1043 goto next_format; 1044 break; 1045 case 'z': // time zone 1046 case 'Z': 1047 if (element->type != TYPE_TIME_ZONE) 1048 goto next_format; 1049 break; 1050 case 'T': // time unit 1051 if (element->type != TYPE_UNIT) 1052 goto next_format; 1053 1054 dateMask.Set(TYPE_UNIT); 1055 break; 1056 default: 1057 goto next_format; 1058 } 1059 break; 1060 } 1061 1062 // format matched at this point, check next element 1063 if (optional[position]) 1064 format++; 1065 format++; 1066 position++; 1067 element++; 1068 continue; 1069 1070 next_format: 1071 // format didn't match element - let's see if the current 1072 // one is only optional (in which case we can continue) 1073 if (!optional[position]) 1074 goto skip_format; 1075 1076 optional[position] = false; 1077 format += 2; 1078 position++; 1079 // skip the closing ']' 1080 } 1081 1082 // check if the format is already empty (since we reached our last 1083 // element) 1084 while (format[0]) { 1085 if (format[0] == '[') 1086 format += 3; 1087 else if (isspace(format[0])) 1088 format++; 1089 else 1090 break; 1091 } 1092 if (format[0]) 1093 goto skip_format; 1094 1095 // made it here? then we seem to have found our guy 1096 1097 return computeDate(sFormatsTable[index], optional, elements, now, 1098 dateMask, _flags); 1099 1100 skip_format: 1101 // check if the next format has the same beginning as the skipped one, 1102 // and if so, skip that one, too. 1103 1104 int32 length = format + 1 - sFormatsTable[index]; 1105 1106 while (sFormatsTable[index + 1] 1107 && !strncmp(sFormatsTable[index], sFormatsTable[index + 1], length)) 1108 index++; 1109 } 1110 1111 // didn't find any matching formats 1112 return B_ERROR; 1113 } 1114 1115 1116 time_t 1117 parsedate(const char* dateString, time_t now) 1118 { 1119 int flags = 0; 1120 1121 return parsedate_etc(dateString, now, &flags); 1122 } 1123 1124 1125 void 1126 set_dateformats(const char** table) 1127 { 1128 sFormatsTable = table ? table : kFormatsTable; 1129 } 1130 1131 1132 const char** 1133 get_dateformats(void) 1134 { 1135 return const_cast<const char**>(sFormatsTable); 1136 } 1137 1138