1 /** 2 * Boost Software License - Version 1.0 - August 17th, 2003 3 * 4 * Permission is hereby granted, free of charge, to any person or organization 5 * obtaining a copy of the software and accompanying documentation covered by 6 * this license (the "Software") to use, reproduce, display, distribute, 7 * execute, and transmit the Software, and to prepare derivative works of the 8 * Software, and to permit third-parties to whom the Software is furnished to 9 * do so, all subject to the following: 10 * 11 * The copyright notices in the Software and this entire statement, including 12 * the above license grant, this restriction and the following disclaimer, 13 * must be included in all copies of the Software, in whole or in part, and 14 * all derivative works of the Software, unless such copies or derivative 15 * works are solely in the form of machine-executable object code generated by 16 * a source language processor. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 21 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 22 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 */ 26 27 module dateparser.parserinfo; 28 29 import std.traits; 30 import std.range; 31 import dateparser.parseresult; 32 33 package: 34 35 // dfmt off 36 // m from a.m/p.m, t from ISO T separator, order doesn't 37 // matter here, just a presence check 38 enum jumpDefault = [ 39 "and":9, "'":6, 40 "at":7, "/":5, 41 "st":14, 42 ";":3, " ":0, 43 "of":13, "nd":15, 44 "rd":16, ".":1, 45 "th":17, "on":8, 46 "m":11, ",":2, 47 "ad":10, "-":4, "t":12 48 ]; 49 enum weekdaysDefault = [ 50 "mon":0, "monday":0, 51 "tue":1, "tuesday":1, 52 "wed":2, "wednesday":2, 53 "thu":3, "thursday":3, 54 "fri":4, "friday":4, 55 "sat":5, "saturday":5, 56 "sun":6, "sunday":6, 57 ]; 58 enum monthsDefault = [ 59 "jan":0, "january":0, 60 "feb":1, "february":1, 61 "mar":2, "march":2, 62 "apr":3, "april":3, 63 "may":4, 64 "jun":5, "june":5, 65 "jul":6, "july":6, 66 "aug":7, "august":7, 67 "sep":8, "sept":8, "september":8, 68 "oct":9, "october":9, 69 "nov":10, "november":10, 70 "dec":11, "december":11 71 ]; 72 enum hmsDefault = [ 73 "h":0, "hour":0, "hours":0, 74 "m":1, "minute":1, "minutes":1, 75 "s":2, "second":2, "seconds":2 76 ]; 77 enum ampmDefault = [ 78 "am":0, "a":0, 79 "pm":1, "p":1 80 ]; 81 enum utcDefault = [ 82 "UTC":0, "GMT":0, "Z":0 83 ]; 84 enum pertainDefault = [ 85 "of":0 86 ]; 87 // dfmt on 88 89 /** 90 * If the century isn't specified, e.g. `"'07"`, then assume that the year 91 * is in the current century and return it as such. Otherwise do nothing 92 * 93 * Params: 94 * convertYear = year to be converted 95 * centurySpecified = is the century given in the year 96 * 97 * Returns: 98 * the converted year 99 */ 100 int convertYear(int convertYear, bool centurySpecified = false) @safe 101 { 102 import std.math : abs; 103 import std.datetime : Clock; 104 105 immutable year = Clock.currTime.year; 106 immutable century = (year / 100) * 100; 107 108 if (convertYear < 100 && !centurySpecified) 109 { 110 convertYear += century; 111 if (abs(convertYear - year) >= 50) 112 { 113 if (convertYear < year) 114 convertYear += 100; 115 else 116 convertYear -= 100; 117 } 118 } 119 120 return convertYear; 121 } 122 123 public: 124 125 /** 126 Class which handles what inputs are accepted. Subclass this to customize 127 the language and acceptable values for each parameter. 128 129 Params: 130 dayFirst = Whether to interpret the first value in an ambiguous 3-integer date 131 (e.g. 01/05/09) as the day (`true`) or month (`false`). If 132 `yearFirst` is set to `true`, this distinguishes between YDM 133 and YMD. Default is `false`. 134 yearFirst = Whether to interpret the first value in an ambiguous 3-integer date 135 (e.g. 01/05/09) as the year. If `true`, the first number is taken 136 to be the year, otherwise the last number is taken to be the year. 137 Default is `false`. 138 */ 139 class ParserInfo 140 { 141 private: 142 bool dayFirst; 143 bool yearFirst; 144 145 package: 146 /** 147 * Takes and Result and converts it year and checks if the timezone is UTC 148 */ 149 final void validate(ref ParseResult res) @safe const 150 { 151 //move to info 152 if (!res.year.isNull) 153 res.year = convertYear(res.year, res.centurySpecified); 154 155 if ((!res.tzoffset.isNull && res.tzoffset == 0) 156 && (res.tzname.length == 0 || res.tzname == "Z")) 157 { 158 res.tzname = "UTC"; 159 res.tzoffset = 0; 160 } 161 else if (!res.tzoffset.isNull && res.tzoffset != 0 && res.tzname.length > 0 162 && this.utczone(res.tzname)) 163 res.tzoffset = 0; 164 } 165 166 public: 167 /** 168 * AAs used for matching strings to calendar numbers, e.g. Jan is 1. 169 * 170 * `jumpAA`, `utczoneAA`, and `pertainAA` are only used to check the 171 * presence of a key; the value of the key doesn't matter. 172 */ 173 int[string] jumpAA; 174 ///ditto 175 int[string] weekdaysAA; 176 ///ditto 177 int[string] monthsAA; 178 ///ditto 179 int[string] hmsAA; 180 ///ditto 181 int[string] ampmAA; 182 ///ditto 183 int[string] utczoneAA; 184 ///ditto 185 int[string] pertainAA; 186 187 /** 188 * Take a range of character ranges or a range of ranges of character 189 * ranges and converts it to an associative array that the internal 190 * parser info methods can use. 191 * 192 * Use this method in order to override the default parser info field 193 * values. See the example on the $(REF parse). 194 * 195 * Params: 196 * list = a range of character ranges 197 * 198 * Returns: 199 * An associative array of `int`s accessed by strings 200 */ 201 static int[string] convert(Range)(Range list) if (isInputRange!Range 202 && isSomeChar!(ElementEncodingType!(ElementEncodingType!(Range))) 203 || isSomeChar!( 204 ElementEncodingType!(ElementEncodingType!(ElementEncodingType!(Range))))) 205 { 206 import std.array : array; 207 import std.conv : to; 208 import std.uni : asLowerCase; 209 210 int[string] dictionary; 211 212 foreach (int i, value; list) 213 { 214 // tuple of strings or multidimensional string array 215 static if (isInputRange!(ElementType!(ElementType!(Range)))) 216 foreach (item; value) 217 dictionary[item.asLowerCase.array.to!string] = i; 218 else 219 dictionary[value.asLowerCase.array.to!string] = i; 220 } 221 222 return dictionary; 223 } 224 225 /// Ctor 226 this(bool dayFirst = false, bool yearFirst = false) @safe 227 { 228 dayFirst = dayFirst; 229 yearFirst = yearFirst; 230 231 jumpAA = jumpDefault; 232 weekdaysAA = weekdaysDefault; 233 monthsAA = monthsDefault; 234 hmsAA = hmsDefault; 235 ampmAA = ampmDefault; 236 utczoneAA = utcDefault; 237 pertainAA = pertainDefault; 238 } 239 240 /// Tests for presence of `name` in each of the AAs 241 final bool jump(S)(const S name) const if (isSomeString!S) 242 { 243 import std.uni : toLower; 244 return name.toLower() in jumpAA ? true : false; 245 } 246 247 /// ditto 248 final int weekday(S)(const S name) const if (isSomeString!S) 249 { 250 import std.uni : toLower; 251 252 auto key = name.toLower(); 253 if (key in weekdaysAA) 254 return weekdaysAA[key]; 255 else 256 return -1; 257 } 258 259 /// ditto 260 final int month(S)(const S name) const if (isSomeString!S) 261 { 262 import std.uni : toLower; 263 264 auto key = name.toLower(); 265 if (key in monthsAA) 266 return monthsAA[key] + 1; 267 else 268 return -1; 269 } 270 271 /// ditto 272 final int hms(S)(const S name) const if (isSomeString!S) 273 { 274 import std.uni : toLower; 275 276 auto key = name.toLower(); 277 if (key in hmsAA) 278 return hmsAA[key]; 279 else 280 return -1; 281 } 282 283 /// ditto 284 final int ampm(S)(const S name) const if (isSomeString!S) 285 { 286 import std.uni : toLower; 287 288 auto key = name.toLower(); 289 if (key in ampmAA) 290 return ampmAA[key]; 291 else 292 return -1; 293 } 294 295 /// ditto 296 final bool pertain(S)(const S name) const if (isSomeString!S) 297 { 298 import std.uni : toLower; 299 300 return name.toLower() in pertainAA ? true : false; 301 } 302 303 /// ditto 304 final bool utczone(S)(const S name) const if (isSomeString!S) 305 { 306 import std.uni : toLower; 307 308 return name.toLower() in utczoneAA ? true : false; 309 } 310 }