1 /* 2 * hunt-time: A time library for D programming language. 3 * 4 * Copyright (C) 2015-2018 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.time.format.DateTimeFormatter; 13 14 import hunt.time.temporal.ChronoField; 15 16 import hunt.Exceptions; 17 // import hunt.text.FieldPosition; 18 // import hunt.text.Format; 19 // import hunt.text.ParseException; 20 // import hunt.text.ParsePosition; 21 import hunt.time.Exceptions; 22 import hunt.time.Period; 23 import hunt.time.ZoneId; 24 import hunt.time.ZoneOffset; 25 import hunt.time.chrono.ChronoLocalDateTime; 26 import hunt.time.chrono.Chronology; 27 import hunt.time.chrono.IsoChronology; 28 // import hunt.time.format.DateTimeFormatterBuilder; 29 import hunt.time.temporal.ChronoField; 30 import hunt.time.temporal.IsoFields; 31 import hunt.time.temporal.TemporalAccessor; 32 import hunt.time.temporal.TemporalField; 33 import hunt.time.temporal.TemporalQuery; 34 35 import hunt.time.text.ParsePosition; 36 import hunt.time.format.DateTimeParseException; 37 import hunt.time.format.DateTimeParseContext; 38 import hunt.time.format.Parsed; 39 import hunt.time.format.SignStyle; 40 import hunt.time.format.DateTimePrintContext; 41 import hunt.time.util.QueryHelper; 42 43 import hunt.Boolean; 44 import hunt.Long; 45 import hunt.collection; 46 import hunt.util.Appendable; 47 import hunt.util.Common; 48 import hunt.util.Locale; 49 import hunt.util.StringBuilder; 50 51 import std.algorithm.searching; 52 import std.conv; 53 import std.concurrency : initOnce; 54 55 // import sun.util.locale.provider.TimeZoneNameUtility; 56 57 /** 58 * Formatter for printing and parsing date-time objects. 59 * !(p) 60 * This class provides the main application entry point for printing and parsing 61 * and provides common implementations of {@code DateTimeFormatter}: 62 * !(ul) 63 * !(li)Using predefined constants, such as {@link #ISO_LOCAL_DATE}</li> 64 * !(li)Using pattern letters, such as {@code uuuu-MMM-dd}</li> 65 * !(li)Using localized styles, such as {@code long} or {@code medium}</li> 66 * </ul> 67 * !(p) 68 * More complex formatters are provided by 69 * {@link DateTimeFormatterBuilder DateTimeFormatterBuilder}. 70 * 71 * !(p) 72 * The main date-time classes provide two methods - one for formatting, 73 * {@code format(DateTimeFormatter formatter)}, and one for parsing, 74 * {@code parse(string text, DateTimeFormatter formatter)}. 75 * !(p)For example: 76 * !(blockquote)!(pre) 77 * LocalDate date = LocalDate.now(); 78 * string text = date.format(formatter); 79 * LocalDate parsedDate = LocalDate.parse(text, formatter); 80 * </pre></blockquote> 81 * !(p) 82 * In addition to the format, formatters can be created with desired Locale, 83 * Chronology, ZoneId, and DecimalStyle. 84 * !(p) 85 * The {@link #withLocale withLocale} method returns a new formatter that 86 * overrides the locale. The locale affects some aspects of formatting and 87 * parsing. For example, the {@link #ofLocalizedDate ofLocalizedDate} provides a 88 * formatter that uses the locale specific date format. 89 * !(p) 90 * The {@link #withChronology withChronology} method returns a new formatter 91 * that overrides the chronology. If overridden, the date-time value is 92 * converted to the chronology before formatting. During parsing the date-time 93 * value is converted to the chronology before it is returned. 94 * !(p) 95 * The {@link #withZone withZone} method returns a new formatter that overrides 96 * the zone. If overridden, the date-time value is converted to a ZonedDateTime 97 * with the requested ZoneId before formatting. During parsing the ZoneId is 98 * applied before the value is returned. 99 * !(p) 100 * The {@link #withDecimalStyle withDecimalStyle} method returns a new formatter that 101 * overrides the {@link DecimalStyle}. The DecimalStyle symbols are used for 102 * formatting and parsing. 103 * !(p) 104 * Some applications may need to use the older {@link Format java.text.Format} 105 * class for formatting. The {@link #toFormat()} method returns an 106 * implementation of {@code java.text.Format}. 107 * 108 * <h3 id="predefined">Predefined Formatters</h3> 109 * <table class="striped" style="text-align:left"> 110 * !(caption)Predefined Formatters</caption> 111 * !(thead) 112 * !(tr) 113 * <th scope="col">Formatter</th> 114 * <th scope="col">Description</th> 115 * <th scope="col">Example</th> 116 * </tr> 117 * </thead> 118 * !(tbody) 119 * !(tr) 120 * <th scope="row">{@link #ofLocalizedDate ofLocalizedDate(dateStyle)} </th> 121 * !(td) Formatter with date style from the locale </td> 122 * !(td) '2011-12-03'</td> 123 * </tr> 124 * !(tr) 125 * <th scope="row"> {@link #ofLocalizedTime ofLocalizedTime(timeStyle)} </th> 126 * !(td) Formatter with time style from the locale </td> 127 * !(td) '10:15:30'</td> 128 * </tr> 129 * !(tr) 130 * <th scope="row"> {@link #ofLocalizedDateTime ofLocalizedDateTime(dateTimeStyle)} </th> 131 * !(td) Formatter with a style for date and time from the locale</td> 132 * !(td) '3 Jun 2008 11:05:30'</td> 133 * </tr> 134 * !(tr) 135 * <th scope="row"> {@link #ofLocalizedDateTime ofLocalizedDateTime(dateStyle,timeStyle)} 136 * </th> 137 * !(td) Formatter with date and time styles from the locale </td> 138 * !(td) '3 Jun 2008 11:05'</td> 139 * </tr> 140 * !(tr) 141 * <th scope="row"> {@link #BASIC_ISO_DATE}</th> 142 * !(td)Basic ISO date </td> !(td)'20111203'</td> 143 * </tr> 144 * !(tr) 145 * <th scope="row"> {@link #ISO_LOCAL_DATE}</th> 146 * !(td) ISO Local Date </td> 147 * !(td)'2011-12-03'</td> 148 * </tr> 149 * !(tr) 150 * <th scope="row"> {@link #ISO_OFFSET_DATE}</th> 151 * !(td) ISO Date with offset </td> 152 * !(td)'2011-12-03+01:00'</td> 153 * </tr> 154 * !(tr) 155 * <th scope="row"> {@link #ISO_DATE}</th> 156 * !(td) ISO Date with or without offset </td> 157 * !(td) '2011-12-03+01:00'; '2011-12-03'</td> 158 * </tr> 159 * !(tr) 160 * <th scope="row"> {@link #ISO_LOCAL_TIME}</th> 161 * !(td) Time without offset </td> 162 * !(td)'10:15:30'</td> 163 * </tr> 164 * !(tr) 165 * <th scope="row"> {@link #ISO_OFFSET_TIME}</th> 166 * !(td) Time with offset </td> 167 * !(td)'10:15:30+01:00'</td> 168 * </tr> 169 * !(tr) 170 * <th scope="row"> {@link #ISO_TIME}</th> 171 * !(td) Time with or without offset </td> 172 * !(td)'10:15:30+01:00'; '10:15:30'</td> 173 * </tr> 174 * !(tr) 175 * <th scope="row"> {@link #ISO_LOCAL_DATE_TIME}</th> 176 * !(td) ISO Local Date and Time </td> 177 * !(td)'2011-12-03T10:15:30'</td> 178 * </tr> 179 * !(tr) 180 * <th scope="row"> {@link #ISO_OFFSET_DATE_TIME}</th> 181 * !(td) Date Time with Offset 182 * </td>!(td)'2011-12-03T10:15:30+01:00'</td> 183 * </tr> 184 * !(tr) 185 * <th scope="row"> {@link #ISO_ZONED_DATE_TIME}</th> 186 * !(td) Zoned Date Time </td> 187 * !(td)'2011-12-03T10:15:30+01:00[Europe/Paris]'</td> 188 * </tr> 189 * !(tr) 190 * <th scope="row"> {@link #ISO_DATE_TIME}</th> 191 * !(td) Date and time with ZoneId </td> 192 * !(td)'2011-12-03T10:15:30+01:00[Europe/Paris]'</td> 193 * </tr> 194 * !(tr) 195 * <th scope="row"> {@link #ISO_ORDINAL_DATE}</th> 196 * !(td) Year and day of year </td> 197 * !(td)'2012-337'</td> 198 * </tr> 199 * !(tr) 200 * <th scope="row"> {@link #ISO_WEEK_DATE}</th> 201 * !(td) Year and Week </td> 202 * !(td)'2012-W48-6'</td></tr> 203 * !(tr) 204 * <th scope="row"> {@link #ISO_INSTANT}</th> 205 * !(td) Date and Time of an Instant </td> 206 * !(td)'2011-12-03T10:15:30Z' </td> 207 * </tr> 208 * !(tr) 209 * <th scope="row"> {@link #RFC_1123_DATE_TIME}</th> 210 * !(td) RFC 1123 / RFC 822 </td> 211 * !(td)'Tue, 3 Jun 2008 11:05:30 GMT'</td> 212 * </tr> 213 * </tbody> 214 * </table> 215 * 216 * <h3 id="patterns">Patterns for Formatting and Parsing</h3> 217 * Patterns are based on a simple sequence of letters and symbols. 218 * A pattern is used to create a Formatter using the 219 * {@link #ofPattern(string)} and {@link #ofPattern(string, Locale)} methods. 220 * For example, 221 * {@code "d MMM uuuu"} will format 2011-12-03 as '3 Dec 2011'. 222 * A formatter created from a pattern can be used as many times as necessary, 223 * it is immutable and is thread-safe. 224 * !(p) 225 * For example: 226 * !(blockquote)!(pre) 227 * LocalDate date = LocalDate.now(); 228 * DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd"); 229 * string text = date.format(formatter); 230 * LocalDate parsedDate = LocalDate.parse(text, formatter); 231 * </pre></blockquote> 232 * !(p) 233 * All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. The 234 * following pattern letters are defined: 235 * <table class="striped"> 236 * !(caption)Pattern Letters and Symbols</caption> 237 * !(thead) 238 * !(tr)<th scope="col">Symbol</th> <th scope="col">Meaning</th> <th scope="col">Presentation</th> <th scope="col">Examples</th> 239 * </thead> 240 * !(tbody) 241 * !(tr)<th scope="row">G</th> !(td)era</td> !(td)text</td> !(td)AD; Anno Domini; A</td> 242 * !(tr)<th scope="row">u</th> !(td)year</td> !(td)year</td> !(td)2004; 04</td> 243 * !(tr)<th scope="row">y</th> !(td)year-of-era</td> !(td)year</td> !(td)2004; 04</td> 244 * !(tr)<th scope="row">D</th> !(td)day-of-year</td> !(td)number</td> !(td)189</td> 245 * !(tr)<th scope="row">M/L</th> !(td)month-of-year</td> !(td)number/text</td> !(td)7; 07; Jul; July; J</td> 246 * !(tr)<th scope="row">d</th> !(td)day-of-month</td> !(td)number</td> !(td)10</td> 247 * !(tr)<th scope="row">g</th> !(td)modified-julian-day</td> !(td)number</td> !(td)2451334</td> 248 * 249 * !(tr)<th scope="row">Q/q</th> !(td)quarter-of-year</td> !(td)number/text</td> !(td)3; 03; Q3; 3rd quarter</td> 250 * !(tr)<th scope="row">Y</th> !(td)week-based-year</td> !(td)year</td> !(td)1996; 96</td> 251 * !(tr)<th scope="row">w</th> !(td)week-of-week-based-year</td> !(td)number</td> !(td)27</td> 252 * !(tr)<th scope="row">W</th> !(td)week-of-month</td> !(td)number</td> !(td)4</td> 253 * !(tr)<th scope="row">E</th> !(td)day-of-week</td> !(td)text</td> !(td)Tue; Tuesday; T</td> 254 * !(tr)<th scope="row">e/c</th> !(td)localized day-of-week</td> !(td)number/text</td> !(td)2; 02; Tue; Tuesday; T</td> 255 * !(tr)<th scope="row">F</th> !(td)day-of-week-_in-month</td> !(td)number</td> !(td)3</td> 256 * 257 * !(tr)<th scope="row">a</th> !(td)am-pm-of-day</td> !(td)text</td> !(td)PM</td> 258 * !(tr)<th scope="row">h</th> !(td)clock-hour-of-am-pm (1-12)</td> !(td)number</td> !(td)12</td> 259 * !(tr)<th scope="row">K</th> !(td)hour-of-am-pm (0-11)</td> !(td)number</td> !(td)0</td> 260 * !(tr)<th scope="row">k</th> !(td)clock-hour-of-day (1-24)</td> !(td)number</td> !(td)24</td> 261 * 262 * !(tr)<th scope="row">H</th> !(td)hour-of-day (0-23)</td> !(td)number</td> !(td)0</td> 263 * !(tr)<th scope="row">m</th> !(td)minute-of-hour</td> !(td)number</td> !(td)30</td> 264 * !(tr)<th scope="row">s</th> !(td)second-of-minute</td> !(td)number</td> !(td)55</td> 265 * !(tr)<th scope="row">S</th> !(td)fraction-of-second</td> !(td)fraction</td> !(td)978</td> 266 * !(tr)<th scope="row">A</th> !(td)milli-of-day</td> !(td)number</td> !(td)1234</td> 267 * !(tr)<th scope="row">n</th> !(td)nano-of-second</td> !(td)number</td> !(td)987654321</td> 268 * !(tr)<th scope="row">N</th> !(td)nano-of-day</td> !(td)number</td> !(td)1234000000</td> 269 * 270 * !(tr)<th scope="row">V</th> !(td)time-zone ID</td> !(td)zone-id</td> !(td)America/Los_Angeles; Z; -08:30</td> 271 * !(tr)<th scope="row">v</th> !(td)generic time-zone name</td> !(td)zone-name</td> !(td)Pacific Time; PT</td> 272 * !(tr)<th scope="row">z</th> !(td)time-zone name</td> !(td)zone-name</td> !(td)Pacific Standard Time; PST</td> 273 * !(tr)<th scope="row">O</th> !(td)localized zone-offset</td> !(td)offset-O</td> !(td)GMT+8; GMT+08:00; UTC-08:00</td> 274 * !(tr)<th scope="row">X</th> !(td)zone-offset 'Z' for zero</td> !(td)offset-X</td> !(td)Z; -08; -0830; -08:30; -083015; -08:30:15</td> 275 * !(tr)<th scope="row">x</th> !(td)zone-offset</td> !(td)offset-x</td> !(td)+0000; -08; -0830; -08:30; -083015; -08:30:15</td> 276 * !(tr)<th scope="row">Z</th> !(td)zone-offset</td> !(td)offset-Z</td> !(td)+0000; -0800; -08:00</td> 277 * 278 * !(tr)<th scope="row">p</th> !(td)pad next</td> !(td)pad modifier</td> !(td)1</td> 279 * 280 * !(tr)<th scope="row">'</th> !(td)escape for text</td> !(td)delimiter</td> !(td)</td> 281 * !(tr)<th scope="row">''</th> !(td)single quote</td> !(td)literal</td> !(td)'</td> 282 * !(tr)<th scope="row">[</th> !(td)optional section start</td> !(td)</td> !(td)</td> 283 * !(tr)<th scope="row">]</th> !(td)optional section end</td> !(td)</td> !(td)</td> 284 * !(tr)<th scope="row">#</th> !(td)reserved for future use</td> !(td)</td> !(td)</td> 285 * !(tr)<th scope="row">{</th> !(td)reserved for future use</td> !(td)</td> !(td)</td> 286 * !(tr)<th scope="row">}</th> !(td)reserved for future use</td> !(td)</td> !(td)</td> 287 * </tbody> 288 * </table> 289 * !(p) 290 * The count of pattern letters determines the format. 291 * !(p) 292 * !(b)Text</b>: The text style is determined based on the number of pattern 293 * letters used. Less than 4 pattern letters will use the 294 * {@link TextStyle#SHORT short form}. Exactly 4 pattern letters will use the 295 * {@link TextStyle#FULL full form}. Exactly 5 pattern letters will use the 296 * {@link TextStyle#NARROW narrow form}. 297 * Pattern letters 'L', 'c', and 'q' specify the stand-alone form of the text styles. 298 * !(p) 299 * !(b)Number</b>: If the count of letters is one, then the value is output using 300 * the minimum number of digits and without padding. Otherwise, the count of digits 301 * is used as the width of the output field, with the value zero-padded as necessary. 302 * The following pattern letters have constraints on the count of letters. 303 * Only one letter of 'c' and 'F' can be specified. 304 * Up to two letters of 'd', 'H', 'h', 'K', 'k', 'm', and 's' can be specified. 305 * Up to three letters of 'D' can be specified. 306 * !(p) 307 * !(b)Number/Text</b>: If the count of pattern letters is 3 or greater, use the 308 * Text rules above. Otherwise use the Number rules above. 309 * !(p) 310 * !(b)Fraction</b>: Outputs the nano-of-second field as a fraction-of-second. 311 * The nano-of-second value has nine digits, thus the count of pattern letters 312 * is from 1 to 9. If it is less than 9, then the nano-of-second value is 313 * truncated, with only the most significant digits being output. 314 * !(p) 315 * !(b)Year</b>: The count of letters determines the minimum field width below 316 * which padding is used. If the count of letters is two, then a 317 * {@link DateTimeFormatterBuilder#appendValueReduced reduced} two digit form is 318 * used. For printing, this outputs the rightmost two digits. For parsing, this 319 * will parse using the base value of 2000, resulting _in a year within the range 320 * 2000 to 2099 inclusive. If the count of letters is less than four (but not 321 * two), then the sign is only output for negative years as per 322 * {@link SignStyle#NORMAL}. Otherwise, the sign is output if the pad width is 323 * exceeded, as per {@link SignStyle#EXCEEDS_PAD}. 324 * !(p) 325 * !(b)ZoneId</b>: This outputs the time-zone ID, such as 'Europe/Paris'. If the 326 * count of letters is two, then the time-zone ID is output. Any other count of 327 * letters throws {@code IllegalArgumentException}. 328 * !(p) 329 * !(b)Zone names</b>: This outputs the display name of the time-zone ID. If the 330 * pattern letter is 'z' the output is the daylight savings aware zone name. 331 * If there is insufficient information to determine whether DST applies, 332 * the name ignoring daylight savings time will be used. 333 * If the count of letters is one, two or three, then the short name is output. 334 * If the count of letters is four, then the full name is output. 335 * Five or more letters throws {@code IllegalArgumentException}. 336 * !(p) 337 * If the pattern letter is 'v' the output provides the zone name ignoring 338 * daylight savings time. If the count of letters is one, then the short name is output. 339 * If the count of letters is four, then the full name is output. 340 * Two, three and five or more letters throw {@code IllegalArgumentException}. 341 * !(p) 342 * !(b)Offset X and x</b>: This formats the offset based on the number of pattern 343 * letters. One letter outputs just the hour, such as '+01', unless the minute 344 * is non-zero _in which case the minute is also output, such as '+0130'. Two 345 * letters outputs the hour and minute, without a colon, such as '+0130'. Three 346 * letters outputs the hour and minute, with a colon, such as '+01:30'. Four 347 * letters outputs the hour and minute and optional second, without a colon, 348 * such as '+013015'. Five letters outputs the hour and minute and optional 349 * second, with a colon, such as '+01:30:15'. Six or more letters throws 350 * {@code IllegalArgumentException}. Pattern letter 'X' (upper case) will output 351 * 'Z' when the offset to be output would be zero, whereas pattern letter 'x' 352 * (lower case) will output '+00', '+0000', or '+00:00'. 353 * !(p) 354 * !(b)Offset O</b>: This formats the localized offset based on the number of 355 * pattern letters. One letter outputs the {@linkplain TextStyle#SHORT short} 356 * form of the localized offset, which is localized offset text, such as 'GMT', 357 * with hour without leading zero, optional 2-digit minute and second if 358 * non-zero, and colon, for example 'GMT+8'. Four letters outputs the 359 * {@linkplain TextStyle#FULL full} form, which is localized offset text, 360 * such as 'GMT, with 2-digit hour and minute field, optional second field 361 * if non-zero, and colon, for example 'GMT+08:00'. Any other count of letters 362 * throws {@code IllegalArgumentException}. 363 * !(p) 364 * !(b)Offset Z</b>: This formats the offset based on the number of pattern 365 * letters. One, two or three letters outputs the hour and minute, without a 366 * colon, such as '+0130'. The output will be '+0000' when the offset is zero. 367 * Four letters outputs the {@linkplain TextStyle#FULL full} form of localized 368 * offset, equivalent to four letters of Offset-O. The output will be the 369 * corresponding localized offset text if the offset is zero. Five 370 * letters outputs the hour, minute, with optional second if non-zero, with 371 * colon. It outputs 'Z' if the offset is zero. 372 * Six or more letters throws {@code IllegalArgumentException}. 373 * !(p) 374 * !(b)Optional section</b>: The optional section markers work exactly like 375 * calling {@link DateTimeFormatterBuilder#optionalStart()} and 376 * {@link DateTimeFormatterBuilder#optionalEnd()}. 377 * !(p) 378 * !(b)Pad modifier</b>: Modifies the pattern that immediately follows to be 379 * padded with spaces. The pad width is determined by the number of pattern 380 * letters. This is the same as calling 381 * {@link DateTimeFormatterBuilder#padNext(int)}. 382 * !(p) 383 * For example, 'ppH' outputs the hour-of-day padded on the left with spaces to 384 * a width of 2. 385 * !(p) 386 * Any unrecognized letter is an error. Any non-letter character, other than 387 * '[', ']', '{', '}', '#' and the single quote will be output directly. 388 * Despite this, it is recommended to use single quotes around all characters 389 * that you want to output directly to ensure that future changes do not break 390 * your application. 391 * 392 * <h3 id="resolving">Resolving</h3> 393 * Parsing is implemented as a two-phase operation. 394 * First, the text is parsed using the layout defined by the formatter, producing 395 * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}. 396 * Second, the parsed data is !(em)resolved</em>, by validating, combining and 397 * simplifying the various fields into more useful ones. 398 * !(p) 399 * Five parsing methods are supplied by this class. 400 * Four of these perform both the parse and resolve phases. 401 * The fifth method, {@link #parseUnresolved(string, ParsePosition)}, 402 * only performs the first phase, leaving the result unresolved. 403 * As such, it is essentially a low-level operation. 404 * !(p) 405 * The resolve phase is controlled by two parameters, set on this class. 406 * !(p) 407 * The {@link ResolverStyle} is an enum that offers three different approaches, 408 * strict, smart and lenient. The smart option is the default. 409 * It can be set using {@link #withResolverStyle(ResolverStyle)}. 410 * !(p) 411 * The {@link #withResolverFields(TemporalField...)} parameter allows the 412 * set of fields that will be resolved to be filtered before resolving starts. 413 * For example, if the formatter has parsed a year, month, day-of-month 414 * and day-of-year, then there are two approaches to resolve a date: 415 * (year + month + day-of-month) and (year + day-of-year). 416 * The resolver fields allows one of the two approaches to be selected. 417 * If no resolver fields are set then both approaches must result _in the same date. 418 * !(p) 419 * Resolving separate fields to form a complete date and time is a complex 420 * process with behaviour distributed across a number of classes. 421 * It follows these steps: 422 * !(ol) 423 * !(li)The chronology is determined. 424 * The chronology of the result is either the chronology that was parsed, 425 * or if no chronology was parsed, it is the chronology set on this class, 426 * or if that is null, it is {@code IsoChronology}. 427 * !(li)The {@code ChronoField} date fields are resolved. 428 * This is achieved using {@link Chronology#resolveDate(Map, ResolverStyle)}. 429 * Documentation about field resolution is located _in the implementation 430 * of {@code Chronology}. 431 * !(li)The {@code ChronoField} time fields are resolved. 432 * This is documented on {@link ChronoField} and is the same for all chronologies. 433 * !(li)Any fields that are not {@code ChronoField} are processed. 434 * This is achieved using {@link TemporalField#resolve(Map, TemporalAccessor, ResolverStyle)}. 435 * Documentation about field resolution is located _in the implementation 436 * of {@code TemporalField}. 437 * !(li)The {@code ChronoField} date and time fields are re-resolved. 438 * This allows fields _in step four to produce {@code ChronoField} values 439 * and have them be processed into dates and times. 440 * !(li)A {@code LocalTime} is formed if there is at least an hour-of-day available. 441 * This involves providing default values for minute, second and fraction of second. 442 * !(li)Any remaining unresolved fields are cross-checked against any 443 * date and/or time that was resolved. Thus, an earlier stage would resolve 444 * (year + month + day-of-month) to a date, and this stage would check that 445 * day-of-week was valid for the date. 446 * !(li)If an {@linkplain #parsedExcessDays() excess number of days} 447 * was parsed then it is added to the date if a date is available. 448 * !(li) If a second-based field is present, but {@code LocalTime} was not parsed, 449 * then the resolver ensures that milli, micro and nano second values are 450 * available to meet the contract of {@link ChronoField}. 451 * These will be set to zero if missing. 452 * !(li)If both date and time were parsed and either an offset or zone is present, 453 * the field {@link ChronoField#INSTANT_SECONDS} is created. 454 * If an offset was parsed then the offset will be combined with the 455 * {@code LocalDateTime} to form the instant, with any zone ignored. 456 * If a {@code ZoneId} was parsed without an offset then the zone will be 457 * combined with the {@code LocalDateTime} to form the instant using the rules 458 * of {@link ChronoLocalDateTime#atZone(ZoneId)}. 459 * </ol> 460 * 461 * @implSpec 462 * This class is immutable and thread-safe. 463 * 464 * @since 1.8 465 */ 466 import hunt.time.format.DecimalStyle; 467 import hunt.time.format.ResolverStyle; 468 import hunt.time.format.FormatStyle; 469 import hunt.time.format.CompositePrinterParser; 470 471 public final class DateTimeFormatter { 472 473 /** 474 * The printer and/or parser to use, not null. 475 */ 476 private CompositePrinterParser printerParser; 477 /** 478 * The locale to use for formatting, not null. 479 */ 480 private Locale locale; 481 /** 482 * The symbols to use for formatting, not null. 483 */ 484 private DecimalStyle decimalStyle; 485 /** 486 * The resolver style to use, not null. 487 */ 488 private ResolverStyle resolverStyle; 489 /** 490 * The fields to use _in resolving, null for all fields. 491 */ 492 private Set!(TemporalField) resolverFields; 493 /** 494 * The chronology to use for formatting, null for no override. 495 */ 496 private Chronology chrono; 497 /** 498 * The zone to use for formatting, null for no override. 499 */ 500 private ZoneId zone; 501 502 //----------------------------------------------------------------------- 503 504 deprecated("Using DateTimeFormatterBuilder.ofPattern instead.") 505 public static DateTimeFormatter ofPattern(string pattern) { 506 throw new Exception("Using DateTimeFormatterBuilder.ofPattern instead."); 507 } 508 509 deprecated("Using DateTimeFormatterBuilder.ofPattern instead.") 510 public static DateTimeFormatter ofPattern(string pattern, Locale locale) { 511 throw new Exception("Using DateTimeFormatterBuilder.ofPattern instead."); 512 } 513 514 deprecated("Using DateTimeFormatterBuilder.ofLocalizedDate instead.") 515 public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) { 516 throw new Exception("Using DateTimeFormatterBuilder.ofLocalizedDate instead."); 517 } 518 519 deprecated("Using DateTimeFormatterBuilder.ofLocalizedTime instead.") 520 public static DateTimeFormatter ofLocalizedTime(FormatStyle timeStyle) { 521 throw new Exception("Using DateTimeFormatterBuilder.ofLocalizedTime instead."); 522 } 523 524 deprecated("Using DateTimeFormatterBuilder.ofLocalizedDateTime instead.") 525 public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateTimeStyle) { 526 throw new Exception("Using DateTimeFormatterBuilder.ofLocalizedDateTime instead."); 527 } 528 529 deprecated("Using DateTimeFormatterBuilder.ofLocalizedDateTime instead.") 530 public static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) { 531 throw new Exception("Using DateTimeFormatterBuilder.ofLocalizedDateTime instead."); 532 } 533 534 535 536 //----------------------------------------------------------------------- 537 /** 538 * A query that provides access to the excess days that were parsed. 539 * !(p) 540 * This returns a singleton {@linkplain TemporalQuery query} that provides 541 * access to additional information from the parse. The query always returns 542 * a non-null period, with a zero period returned instead of null. 543 * !(p) 544 * There are two situations where this query may return a non-zero period. 545 * !(ul) 546 * !(li)If the {@code ResolverStyle} is {@code LENIENT} and a time is parsed 547 * without a date, then the complete result of the parse consists of a 548 * {@code LocalTime} and an excess {@code Period} _in days. 549 * 550 * !(li)If the {@code ResolverStyle} is {@code SMART} and a time is parsed 551 * without a date where the time is 24:00:00, then the complete result of 552 * the parse consists of a {@code LocalTime} of 00:00:00 and an excess 553 * {@code Period} of one day. 554 * </ul> 555 * !(p) 556 * In both cases, if a complete {@code ChronoLocalDateTime} or {@code Instant} 557 * is parsed, then the excess days are added to the date part. 558 * As a result, this query will return a zero period. 559 * !(p) 560 * The {@code SMART} behaviour handles the common "end of day" 24:00 value. 561 * Processing _in {@code LENIENT} mode also produces the same result: 562 * !(pre) 563 * Text to parse Parsed object Excess days 564 * "2012-12-03T00:00" LocalDateTime.of(2012, 12, 3, 0, 0) ZERO 565 * "2012-12-03T24:00" LocalDateTime.of(2012, 12, 4, 0, 0) ZERO 566 * "00:00" LocalTime.of(0, 0) ZERO 567 * "24:00" LocalTime.of(0, 0) Period.ofDays(1) 568 * </pre> 569 * The query can be used as follows: 570 * !(pre) 571 * TemporalAccessor parsed = formatter.parse(str); 572 * LocalTime time = parsed.query(LocalTime.from); 573 * Period extraDays = parsed.query(DateTimeFormatter.parsedExcessDays()); 574 * </pre> 575 * @return a query that provides access to the excess days that were parsed 576 */ 577 static TemporalQuery!(Period) parsedExcessDays() { 578 __gshared TemporalQuery!(Period) PARSED_EXCESS_DAYS; 579 return initOnce!PARSED_EXCESS_DAYS(createParsedExcessDays()); 580 } 581 582 private static TemporalQuery!(Period) createParsedExcessDays() { 583 return new class TemporalQuery!(Period) 584 { 585 Period queryFrom(TemporalAccessor t) 586 { 587 if (cast(Parsed)(t) !is null) 588 { 589 return (cast(Parsed) t).excessDays; 590 } 591 else 592 { 593 return Period.ZERO; 594 } 595 } 596 }; 597 } 598 599 /** 600 * A query that provides access to whether a leap-second was parsed. 601 * !(p) 602 * This returns a singleton {@linkplain TemporalQuery query} that provides 603 * access to additional information from the parse. The query always returns 604 * a non-null bool, true if parsing saw a leap-second, false if not. 605 * !(p) 606 * Instant parsing handles the special "leap second" time of '23:59:60'. 607 * Leap seconds occur at '23:59:60' _in the UTC time-zone, but at other 608 * local times _in different time-zones. To avoid this potential ambiguity, 609 * the handling of leap-seconds is limited to 610 * {@link DateTimeFormatterBuilder#appendInstant()}, as that method 611 * always parses the instant with the UTC zone offset. 612 * !(p) 613 * If the time '23:59:60' is received, then a simple conversion is applied, 614 * replacing the second-of-minute of 60 with 59. This query can be used 615 * on the parse result to determine if the leap-second adjustment was made. 616 * The query will return {@code true} if it did adjust to remove the 617 * leap-second, and {@code false} if not. Note that applying a leap-second 618 * smoothing mechanism, such as UTC-SLS, is the responsibility of the 619 * application, as follows: 620 * !(pre) 621 * TemporalAccessor parsed = formatter.parse(str); 622 * Instant instant = parsed.query(Instant::from); 623 * if (parsed.query(DateTimeFormatter.parsedLeapSecond())) { 624 * // validate leap-second is correct and apply correct smoothing 625 * } 626 * </pre> 627 * @return a query that provides access to whether a leap-second was parsed 628 */ 629 static TemporalQuery!(Boolean) parsedLeapSecond() { 630 __gshared TemporalQuery!(Boolean) PARSED_LEAP_SECOND; 631 return initOnce!PARSED_LEAP_SECOND(createParsedLeapSecond()); 632 } 633 634 private static TemporalQuery!(Boolean) createParsedLeapSecond() { 635 return new class TemporalQuery!(Boolean) 636 { 637 Boolean queryFrom(TemporalAccessor t) 638 { 639 if (cast(Parsed)(t) !is null) 640 { 641 return new Boolean((cast(Parsed) t).leapSecond); 642 } 643 else 644 { 645 return Boolean.FALSE; 646 } 647 } 648 }; 649 } 650 651 //----------------------------------------------------------------------- 652 /** 653 * Constructor. 654 * 655 * @param printerParser the printer/parser to use, not null 656 * @param locale the locale to use, not null 657 * @param decimalStyle the DecimalStyle to use, not null 658 * @param resolverStyle the resolver style to use, not null 659 * @param resolverFields the fields to use during resolving, null for all fields 660 * @param chrono the chronology to use, null for no override 661 * @param zone the zone to use, null for no override 662 */ 663 this(CompositePrinterParser printerParser, 664 Locale locale, DecimalStyle decimalStyle, 665 ResolverStyle resolverStyle, Set!(TemporalField) resolverFields, 666 Chronology chrono, ZoneId zone) { 667 this.printerParser = printerParser; 668 this.resolverFields = resolverFields; 669 this.locale = locale; 670 this.decimalStyle = decimalStyle; 671 this.resolverStyle = resolverStyle; 672 this.chrono = chrono; 673 this.zone = zone; 674 } 675 676 //----------------------------------------------------------------------- 677 /** 678 * Gets the locale to be used during formatting. 679 * !(p) 680 * This is used to lookup any part of the formatter needing specific 681 * localization, such as the text or localized pattern. 682 * 683 * @return the locale of this formatter, not null 684 */ 685 public Locale getLocale() { 686 return locale; 687 } 688 689 /** 690 * Returns a copy of this formatter with a new locale. 691 * !(p) 692 * This is used to lookup any part of the formatter needing specific 693 * localization, such as the text or localized pattern. 694 * !(p) 695 * The locale is stored as passed _in, without further processing. 696 * If the locale has <a href="../../util/Locale.html#def_locale_extension"> 697 * Unicode extensions</a>, they may be used later _in text 698 * processing. To set the chronology, time-zone and decimal style from 699 * unicode extensions, see {@link #localizedBy localizedBy()}. 700 * !(p) 701 * This instance is immutable and unaffected by this method call. 702 * 703 * @param locale the new locale, not null 704 * @return a formatter based on this formatter with the requested locale, not null 705 * @see #localizedBy(Locale) 706 */ 707 public DateTimeFormatter withLocale(Locale locale) { 708 if (this.locale == (locale)) { 709 return this; 710 } 711 return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 712 } 713 714 /** 715 * Returns a copy of this formatter with localized values of the locale, 716 * calendar, region, decimal style and/or timezone, that supercede values _in 717 * this formatter. 718 * !(p) 719 * This is used to lookup any part of the formatter needing specific 720 * localization, such as the text or localized pattern. If the locale contains the 721 * "ca" (calendar), "nu" (numbering system), "rg" (region override), and/or 722 * "tz" (timezone) 723 * <a href="../../util/Locale.html#def_locale_extension">Unicode extensions</a>, 724 * the chronology, numbering system and/or the zone are overridden. If both "ca" 725 * and "rg" are specified, the chronology from the "ca" extension supersedes the 726 * implicit one from the "rg" extension. Same is true for the "nu" extension. 727 * !(p) 728 * Unlike the {@link #withLocale withLocale} method, the call to this method may 729 * produce a different formatter depending on the order of method chaining with 730 * other withXXXX() methods. 731 * !(p) 732 * This instance is immutable and unaffected by this method call. 733 * 734 * @param locale the locale, not null 735 * @return a formatter based on this formatter with localized values of 736 * the calendar, decimal style and/or timezone, that supercede values _in this 737 * formatter. 738 * @see #withLocale(Locale) 739 * @since 10 740 */ 741 ///@gxc 742 // public DateTimeFormatter localizedBy(Locale locale) { 743 // if (this.locale == (locale)) { 744 // return this; 745 // } 746 747 // // Check for decimalStyle/chronology/timezone _in locale object 748 // Chronology c = locale.getUnicodeLocaleType("ca") !is null ? 749 // Chronology.ofLocale(locale) : chrono; 750 // DecimalStyle ds = locale.getUnicodeLocaleType("nu") !is null ? 751 // DecimalStyle.of(locale) : decimalStyle; 752 // string tzType = locale.getUnicodeLocaleType("tz"); 753 // ZoneId z = tzType !is null ? 754 // TimeZoneNameUtility.convertLDMLShortID(tzType) 755 // .map(ZoneId.of) 756 // .orElse(zone) : 757 // zone; 758 // return new DateTimeFormatter(printerParser, locale, ds, resolverStyle, resolverFields, c, z); 759 // } 760 761 //----------------------------------------------------------------------- 762 /** 763 * Gets the DecimalStyle to be used during formatting. 764 * 765 * @return the locale of this formatter, not null 766 */ 767 public DecimalStyle getDecimalStyle() { 768 return decimalStyle; 769 } 770 771 /** 772 * Returns a copy of this formatter with a new DecimalStyle. 773 * !(p) 774 * This instance is immutable and unaffected by this method call. 775 * 776 * @param decimalStyle the new DecimalStyle, not null 777 * @return a formatter based on this formatter with the requested DecimalStyle, not null 778 */ 779 public DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) { 780 if (this.decimalStyle == (decimalStyle)) { 781 return this; 782 } 783 return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 784 } 785 786 //----------------------------------------------------------------------- 787 /** 788 * Gets the overriding chronology to be used during formatting. 789 * !(p) 790 * This returns the override chronology, used to convert dates. 791 * By default, a formatter has no override chronology, returning null. 792 * See {@link #withChronology(Chronology)} for more details on overriding. 793 * 794 * @return the override chronology of this formatter, null if no override 795 */ 796 public Chronology getChronology() { 797 return chrono; 798 } 799 800 /** 801 * Returns a copy of this formatter with a new override chronology. 802 * !(p) 803 * This returns a formatter with similar state to this formatter but 804 * with the override chronology set. 805 * By default, a formatter has no override chronology, returning null. 806 * !(p) 807 * If an override is added, then any date that is formatted or parsed will be affected. 808 * !(p) 809 * When formatting, if the temporal object contains a date, then it will 810 * be converted to a date _in the override chronology. 811 * Whether the temporal contains a date is determined by querying the 812 * {@link ChronoField#EPOCH_DAY EPOCH_DAY} field. 813 * Any time or zone will be retained unaltered unless overridden. 814 * !(p) 815 * If the temporal object does not contain a date, but does contain one 816 * or more {@code ChronoField} date fields, then a {@code DateTimeException} 817 * is thrown. In all other cases, the override chronology is added to the temporal, 818 * replacing any previous chronology, but without changing the date/time. 819 * !(p) 820 * When parsing, there are two distinct cases to consider. 821 * If a chronology has been parsed directly from the text, perhaps because 822 * {@link DateTimeFormatterBuilder#appendChronologyId()} was used, then 823 * this override chronology has no effect. 824 * If no zone has been parsed, then this override chronology will be used 825 * to interpret the {@code ChronoField} values into a date according to the 826 * date resolving rules of the chronology. 827 * !(p) 828 * This instance is immutable and unaffected by this method call. 829 * 830 * @param chrono the new chronology, null if no override 831 * @return a formatter based on this formatter with the requested override chronology, not null 832 */ 833 public DateTimeFormatter withChronology(Chronology chrono) { 834 if (this.chrono !is null && (this.chrono == chrono)) { 835 return this; 836 } 837 return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 838 } 839 840 //----------------------------------------------------------------------- 841 /** 842 * Gets the overriding zone to be used during formatting. 843 * !(p) 844 * This returns the override zone, used to convert instants. 845 * By default, a formatter has no override zone, returning null. 846 * See {@link #withZone(ZoneId)} for more details on overriding. 847 * 848 * @return the override zone of this formatter, null if no override 849 */ 850 public ZoneId getZone() { 851 return zone; 852 } 853 854 /** 855 * Returns a copy of this formatter with a new override zone. 856 * !(p) 857 * This returns a formatter with similar state to this formatter but 858 * with the override zone set. 859 * By default, a formatter has no override zone, returning null. 860 * !(p) 861 * If an override is added, then any instant that is formatted or parsed will be affected. 862 * !(p) 863 * When formatting, if the temporal object contains an instant, then it will 864 * be converted to a zoned date-time using the override zone. 865 * Whether the temporal is an instant is determined by querying the 866 * {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS} field. 867 * If the input has a chronology then it will be retained unless overridden. 868 * If the input does not have a chronology, such as {@code Instant}, then 869 * the ISO chronology will be used. 870 * !(p) 871 * If the temporal object does not contain an instant, but does contain 872 * an offset then an additional check is made. If the normalized override 873 * zone is an offset that differs from the offset of the temporal, then 874 * a {@code DateTimeException} is thrown. In all other cases, the override 875 * zone is added to the temporal, replacing any previous zone, but without 876 * changing the date/time. 877 * !(p) 878 * When parsing, there are two distinct cases to consider. 879 * If a zone has been parsed directly from the text, perhaps because 880 * {@link DateTimeFormatterBuilder#appendZoneId()} was used, then 881 * this override zone has no effect. 882 * If no zone has been parsed, then this override zone will be included _in 883 * the result of the parse where it can be used to build instants and date-times. 884 * !(p) 885 * This instance is immutable and unaffected by this method call. 886 * 887 * @param zone the new override zone, null if no override 888 * @return a formatter based on this formatter with the requested override zone, not null 889 */ 890 public DateTimeFormatter withZone(ZoneId zone) { 891 if ((this.zone == zone)) { 892 return this; 893 } 894 return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 895 } 896 897 //----------------------------------------------------------------------- 898 /** 899 * Gets the resolver style to use during parsing. 900 * !(p) 901 * This returns the resolver style, used during the second phase of parsing 902 * when fields are resolved into dates and times. 903 * By default, a formatter has the {@link ResolverStyle#SMART SMART} resolver style. 904 * See {@link #withResolverStyle(ResolverStyle)} for more details. 905 * 906 * @return the resolver style of this formatter, not null 907 */ 908 public ResolverStyle getResolverStyle() { 909 return resolverStyle; 910 } 911 912 /** 913 * Returns a copy of this formatter with a new resolver style. 914 * !(p) 915 * This returns a formatter with similar state to this formatter but 916 * with the resolver style set. By default, a formatter has the 917 * {@link ResolverStyle#SMART SMART} resolver style. 918 * !(p) 919 * Changing the resolver style only has an effect during parsing. 920 * Parsing a text string occurs _in two phases. 921 * Phase 1 is a basic text parse according to the fields added to the builder. 922 * Phase 2 resolves the parsed field-value pairs into date and/or time objects. 923 * The resolver style is used to control how phase 2, resolving, happens. 924 * See {@code ResolverStyle} for more information on the options available. 925 * !(p) 926 * This instance is immutable and unaffected by this method call. 927 * 928 * @param resolverStyle the new resolver style, not null 929 * @return a formatter based on this formatter with the requested resolver style, not null 930 */ 931 public DateTimeFormatter withResolverStyle(ResolverStyle resolverStyle) { 932 assert(resolverStyle, "resolverStyle"); 933 if ((this.resolverStyle == resolverStyle)) { 934 return this; 935 } 936 return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 937 } 938 939 //----------------------------------------------------------------------- 940 /** 941 * Gets the resolver fields to use during parsing. 942 * !(p) 943 * This returns the resolver fields, used during the second phase of parsing 944 * when fields are resolved into dates and times. 945 * By default, a formatter has no resolver fields, and thus returns null. 946 * See {@link #withResolverFields(Set)} for more details. 947 * 948 * @return the immutable set of resolver fields of this formatter, null if no fields 949 */ 950 public Set!(TemporalField) getResolverFields() { 951 return resolverFields; 952 } 953 954 /** 955 * Returns a copy of this formatter with a new set of resolver fields. 956 * !(p) 957 * This returns a formatter with similar state to this formatter but with 958 * the resolver fields set. By default, a formatter has no resolver fields. 959 * !(p) 960 * Changing the resolver fields only has an effect during parsing. 961 * Parsing a text string occurs _in two phases. 962 * Phase 1 is a basic text parse according to the fields added to the builder. 963 * Phase 2 resolves the parsed field-value pairs into date and/or time objects. 964 * The resolver fields are used to filter the field-value pairs between phase 1 and 2. 965 * !(p) 966 * This can be used to select between two or more ways that a date or time might 967 * be resolved. For example, if the formatter consists of year, month, day-of-month 968 * and day-of-year, then there are two ways to resolve a date. 969 * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and 970 * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is 971 * resolved using the year and day-of-year, effectively meaning that the month 972 * and day-of-month are ignored during the resolving phase. 973 * !(p) 974 * In a similar manner, this method can be used to ignore secondary fields that 975 * would otherwise be cross-checked. For example, if the formatter consists of year, 976 * month, day-of-month and day-of-week, then there is only one way to resolve a 977 * date, but the parsed value for day-of-week will be cross-checked against the 978 * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR}, 979 * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and 980 * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is 981 * resolved correctly, but without any cross-check for the day-of-week. 982 * !(p) 983 * In implementation terms, this method behaves as follows. The result of the 984 * parsing phase can be considered to be a map of field to value. The behavior 985 * of this method is to cause that map to be filtered between phase 1 and 2, 986 * removing all fields other than those specified as arguments to this method. 987 * !(p) 988 * This instance is immutable and unaffected by this method call. 989 * 990 * @param resolverFields the new set of resolver fields, null if no fields 991 * @return a formatter based on this formatter with the requested resolver style, not null 992 */ 993 ///@gxc 994 // public DateTimeFormatter withResolverFields(TemporalField[] resolverFields...) { 995 // Set!(TemporalField) fields = null; 996 // if (resolverFields !is null) { 997 // // Set.of cannot be used because it is hostile to nulls and duplicate elements 998 // auto hs = new HashSet!TemporalField(); 999 // foreach( t ; resolverFields) 1000 // hs.add(t); 1001 // fields = Collections.unmodifiableSet(hs); 1002 // } 1003 // if ((this.resolverFields == fields)) { 1004 // return this; 1005 // } 1006 // return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone); 1007 // } 1008 1009 /** 1010 * Returns a copy of this formatter with a new set of resolver fields. 1011 * !(p) 1012 * This returns a formatter with similar state to this formatter but with 1013 * the resolver fields set. By default, a formatter has no resolver fields. 1014 * !(p) 1015 * Changing the resolver fields only has an effect during parsing. 1016 * Parsing a text string occurs _in two phases. 1017 * Phase 1 is a basic text parse according to the fields added to the builder. 1018 * Phase 2 resolves the parsed field-value pairs into date and/or time objects. 1019 * The resolver fields are used to filter the field-value pairs between phase 1 and 2. 1020 * !(p) 1021 * This can be used to select between two or more ways that a date or time might 1022 * be resolved. For example, if the formatter consists of year, month, day-of-month 1023 * and day-of-year, then there are two ways to resolve a date. 1024 * Calling this method with the arguments {@link ChronoField#YEAR YEAR} and 1025 * {@link ChronoField#DAY_OF_YEAR DAY_OF_YEAR} will ensure that the date is 1026 * resolved using the year and day-of-year, effectively meaning that the month 1027 * and day-of-month are ignored during the resolving phase. 1028 * !(p) 1029 * In a similar manner, this method can be used to ignore secondary fields that 1030 * would otherwise be cross-checked. For example, if the formatter consists of year, 1031 * month, day-of-month and day-of-week, then there is only one way to resolve a 1032 * date, but the parsed value for day-of-week will be cross-checked against the 1033 * resolved date. Calling this method with the arguments {@link ChronoField#YEAR YEAR}, 1034 * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and 1035 * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} will ensure that the date is 1036 * resolved correctly, but without any cross-check for the day-of-week. 1037 * !(p) 1038 * In implementation terms, this method behaves as follows. The result of the 1039 * parsing phase can be considered to be a map of field to value. The behavior 1040 * of this method is to cause that map to be filtered between phase 1 and 2, 1041 * removing all fields other than those specified as arguments to this method. 1042 * !(p) 1043 * This instance is immutable and unaffected by this method call. 1044 * 1045 * @param resolverFields the new set of resolver fields, null if no fields 1046 * @return a formatter based on this formatter with the requested resolver style, not null 1047 */ 1048 ///@gxc 1049 // public DateTimeFormatter withResolverFields(Set!(TemporalField) resolverFields) { 1050 // if (Objects == (this.resolverFields, resolverFields)) { 1051 // return this; 1052 // } 1053 // if (resolverFields !is null) { 1054 // resolverFields = Collections.unmodifiableSet(new HashSet!()(resolverFields)); 1055 // } 1056 // return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); 1057 // } 1058 1059 //----------------------------------------------------------------------- 1060 /** 1061 * Formats a date-time object using this formatter. 1062 * !(p) 1063 * This formats the date-time to a string using the rules of the formatter. 1064 * 1065 * @param temporal the temporal object to format, not null 1066 * @return the formatted string, not null 1067 * @throws DateTimeException if an error occurs during formatting 1068 */ 1069 public string format(TemporalAccessor temporal) { 1070 StringBuilder buf = new StringBuilder(32); 1071 formatTo(temporal, buf); 1072 return buf.toString(); 1073 } 1074 1075 //----------------------------------------------------------------------- 1076 /** 1077 * Formats a date-time object to an {@code Appendable} using this formatter. 1078 * !(p) 1079 * This outputs the formatted date-time to the specified destination. 1080 * {@link Appendable} is a general purpose interface that is implemented by all 1081 * key character output classes including {@code StringBuffer}, {@code StringBuilder}, 1082 * {@code PrintStream} and {@code Writer}. 1083 * !(p) 1084 * Although {@code Appendable} methods throw an {@code IOException}, this method does not. 1085 * Instead, any {@code IOException} is wrapped _in a runtime exception. 1086 * 1087 * @param temporal the temporal object to format, not null 1088 * @param appendable the appendable to format to, not null 1089 * @throws DateTimeException if an error occurs during formatting 1090 */ 1091 public void formatTo(TemporalAccessor temporal, Appendable appendable) { 1092 assert(temporal, "temporal"); 1093 assert(appendable, "appendable"); 1094 try { 1095 DateTimePrintContext context = new DateTimePrintContext(temporal, this); 1096 if (cast(StringBuilder)(appendable) !is null) { 1097 printerParser.format(context, cast(StringBuilder) appendable); 1098 } else { 1099 // buffer output to avoid writing to appendable _in case of error 1100 StringBuilder buf = new StringBuilder(32); 1101 printerParser.format(context, buf); 1102 appendable.append(buf.toString); 1103 } 1104 } catch (IOException ex) { 1105 throw new DateTimeException(ex.msg, ex); 1106 } 1107 } 1108 1109 //----------------------------------------------------------------------- 1110 /** 1111 * Fully parses the text producing a temporal object. 1112 * !(p) 1113 * This parses the entire text producing a temporal object. 1114 * It is typically more useful to use {@link #parse(string, TemporalQuery)}. 1115 * The result of this method is {@code TemporalAccessor} which has been resolved, 1116 * applying basic validation checks to help ensure a valid date-time. 1117 * !(p) 1118 * If the parse completes without reading the entire length of the text, 1119 * or a problem occurs during parsing or merging, then an exception is thrown. 1120 * 1121 * @param text the text to parse, not null 1122 * @return the parsed temporal object, not null 1123 * @throws DateTimeParseException if unable to parse the requested result 1124 */ 1125 public TemporalAccessor parse(string text) { 1126 assert(text, "text"); 1127 try { 1128 return parseResolved0(text, null); 1129 } catch (DateTimeParseException ex) { 1130 throw ex; 1131 } catch (RuntimeException ex) { 1132 throw createError(text, ex); 1133 } 1134 } 1135 1136 /** 1137 * Parses the text using this formatter, providing control over the text position. 1138 * !(p) 1139 * This parses the text without requiring the parse to start from the beginning 1140 * of the string or finish at the end. 1141 * The result of this method is {@code TemporalAccessor} which has been resolved, 1142 * applying basic validation checks to help ensure a valid date-time. 1143 * !(p) 1144 * The text will be parsed from the specified start {@code ParsePosition}. 1145 * The entire length of the text does not have to be parsed, the {@code ParsePosition} 1146 * will be updated with the index at the end of parsing. 1147 * !(p) 1148 * The operation of this method is slightly different to similar methods using 1149 * {@code ParsePosition} on {@code java.text.Format}. That class will return 1150 * errors using the error index on the {@code ParsePosition}. By contrast, this 1151 * method will throw a {@link DateTimeParseException} if an error occurs, with 1152 * the exception containing the error index. 1153 * This change _in behavior is necessary due to the increased complexity of 1154 * parsing and resolving dates/times _in this API. 1155 * !(p) 1156 * If the formatter parses the same field more than once with different values, 1157 * the result will be an error. 1158 * 1159 * @param text the text to parse, not null 1160 * @param position the position to parse from, updated with length parsed 1161 * and the index of any error, not null 1162 * @return the parsed temporal object, not null 1163 * @throws DateTimeParseException if unable to parse the requested result 1164 * @throws IndexOutOfBoundsException if the position is invalid 1165 */ 1166 public TemporalAccessor parse(string text, ParsePosition position) { 1167 assert(text, "text"); 1168 assert(position, "position"); 1169 try { 1170 return parseResolved0(text, position); 1171 } catch (DateTimeParseException ex) { 1172 throw ex; 1173 } catch (RuntimeException ex) { 1174 throw createError(text, ex); 1175 } 1176 } 1177 1178 //----------------------------------------------------------------------- 1179 /** 1180 * Fully parses the text producing an object of the specified type. 1181 * !(p) 1182 * Most applications should use this method for parsing. 1183 * It parses the entire text to produce the required date-time. 1184 * The query is typically a method reference to a {@code from(TemporalAccessor)} method. 1185 * For example: 1186 * !(pre) 1187 * LocalDateTime dt = parser.parse(str, LocalDateTime.from); 1188 * </pre> 1189 * If the parse completes without reading the entire length of the text, 1190 * or a problem occurs during parsing or merging, then an exception is thrown. 1191 * 1192 * @param !(T) the type of the parsed date-time 1193 * @param text the text to parse, not null 1194 * @param query the query defining the type to parse to, not null 1195 * @return the parsed date-time, not null 1196 * @throws DateTimeParseException if unable to parse the requested result 1197 */ 1198 public T parse(T)(string text, TemporalQuery!(T) query) { 1199 assert(text, "text"); 1200 assert(query, "query"); 1201 try { 1202 return QueryHelper.query!T(parseResolved0(text, null),query); 1203 } catch (DateTimeParseException ex) { 1204 throw ex; 1205 } catch (RuntimeException ex) { 1206 throw createError(text, ex); 1207 } 1208 } 1209 1210 /** 1211 * Fully parses the text producing an object of one of the specified types. 1212 * !(p) 1213 * This parse method is convenient for use when the parser can handle optional elements. 1214 * For example, a pattern of 'uuuu-MM-dd HH.mm[ VV]' can be fully parsed to a {@code ZonedDateTime}, 1215 * or partially parsed to a {@code LocalDateTime}. 1216 * The queries must be specified _in order, starting from the best matching full-parse option 1217 * and ending with the worst matching minimal parse option. 1218 * The query is typically a method reference to a {@code from(TemporalAccessor)} method. 1219 * !(p) 1220 * The result is associated with the first type that successfully parses. 1221 * Normally, applications will use {@code instanceof} to check the result. 1222 * For example: 1223 * !(pre) 1224 * TemporalAccessor dt = parser.parseBest(str, ZonedDateTime::from, LocalDateTime.from); 1225 * if (cast(ZonedDateTime)(dt) !is null) { 1226 * ... 1227 * } else { 1228 * ... 1229 * } 1230 * </pre> 1231 * If the parse completes without reading the entire length of the text, 1232 * or a problem occurs during parsing or merging, then an exception is thrown. 1233 * 1234 * @param text the text to parse, not null 1235 * @param queries the queries defining the types to attempt to parse to, 1236 * must implement {@code TemporalAccessor}, not null 1237 * @return the parsed date-time, not null 1238 * @throws IllegalArgumentException if less than 2 types are specified 1239 * @throws DateTimeParseException if unable to parse the requested result 1240 */ 1241 public TemporalAccessor parseBest(string text, TemporalQuery!(Object)[] queries...) { 1242 assert(text, "text"); 1243 assert(queries, "queries"); 1244 if (queries.length < 2) { 1245 throw new IllegalArgumentException("At least two queries must be specified"); 1246 } 1247 try { 1248 TemporalAccessor resolved = parseResolved0(text, null); 1249 foreach(TemporalQuery!(Object) query ; queries) { 1250 try { 1251 return cast(TemporalAccessor) (QueryHelper.query!Object(resolved,query)); ///@gxc 1252 } catch (RuntimeException ex) { 1253 // continue 1254 } 1255 } 1256 throw new DateTimeException("Unable to convert parsed text using any of the specified queries"); 1257 } catch (DateTimeParseException ex) { 1258 throw ex; 1259 } catch (RuntimeException ex) { 1260 throw createError(text, ex); 1261 } 1262 } 1263 1264 private DateTimeParseException createError(string text, RuntimeException ex) { 1265 string abbr; 1266 if (text.length > 64) { 1267 abbr = text[0 .. 64] ~ "..."; 1268 } else { 1269 abbr = text/* .toString() */; 1270 } 1271 return new DateTimeParseException("Text '" ~ abbr ~ "' could not be parsed: " ~ ex.msg, text, 0, ex); 1272 } 1273 1274 //----------------------------------------------------------------------- 1275 /** 1276 * Parses and resolves the specified text. 1277 * !(p) 1278 * This parses to a {@code TemporalAccessor} ensuring that the text is fully parsed. 1279 * 1280 * @param text the text to parse, not null 1281 * @param position the position to parse from, updated with length parsed 1282 * and the index of any error, null if parsing whole string 1283 * @return the resolved result of the parse, not null 1284 * @throws DateTimeParseException if the parse fails 1285 * @throws DateTimeException if an error occurs while resolving the date or time 1286 * @throws IndexOutOfBoundsException if the position is invalid 1287 */ 1288 private TemporalAccessor parseResolved0( string text, ParsePosition position) { 1289 ParsePosition pos = (position !is null ? position : new ParsePosition(0)); 1290 DateTimeParseContext context = parseUnresolved0(text, pos); 1291 if (context is null || pos.getErrorIndex() >= 0 || (position is null && pos.getIndex() < text.length)) { 1292 string abbr; 1293 if (text.length > 64) { 1294 abbr = text[0 .. 64] ~ "..."; 1295 } else { 1296 abbr = text/* .toString() */; 1297 } 1298 if (pos.getErrorIndex() >= 0) { 1299 throw new DateTimeParseException("Text '" ~ abbr ~ "' could not be parsed at index " ~ 1300 pos.getErrorIndex().to!string, text, pos.getErrorIndex()); 1301 } else { 1302 throw new DateTimeParseException("Text '" ~ abbr ~ "' could not be parsed, unparsed text found at index " ~ 1303 pos.getIndex().to!string, text, pos.getIndex()); 1304 } 1305 } 1306 return context.toResolved(resolverStyle, resolverFields); 1307 } 1308 1309 /** 1310 * Parses the text using this formatter, without resolving the result, intended 1311 * for advanced use cases. 1312 * !(p) 1313 * Parsing is implemented as a two-phase operation. 1314 * First, the text is parsed using the layout defined by the formatter, producing 1315 * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}. 1316 * Second, the parsed data is !(em)resolved</em>, by validating, combining and 1317 * simplifying the various fields into more useful ones. 1318 * This method performs the parsing stage but not the resolving stage. 1319 * !(p) 1320 * The result of this method is {@code TemporalAccessor} which represents the 1321 * data as seen _in the input. Values are not validated, thus parsing a date string 1322 * of '2012-00-65' would result _in a temporal with three fields - year of '2012', 1323 * month of '0' and day-of-month of '65'. 1324 * !(p) 1325 * The text will be parsed from the specified start {@code ParsePosition}. 1326 * The entire length of the text does not have to be parsed, the {@code ParsePosition} 1327 * will be updated with the index at the end of parsing. 1328 * !(p) 1329 * Errors are returned using the error index field of the {@code ParsePosition} 1330 * instead of {@code DateTimeParseException}. 1331 * The returned error index will be set to an index indicative of the error. 1332 * Callers must check for errors before using the result. 1333 * !(p) 1334 * If the formatter parses the same field more than once with different values, 1335 * the result will be an error. 1336 * !(p) 1337 * This method is intended for advanced use cases that need access to the 1338 * internal state during parsing. Typical application code should use 1339 * {@link #parse(string, TemporalQuery)} or the parse method on the target type. 1340 * 1341 * @param text the text to parse, not null 1342 * @param position the position to parse from, updated with length parsed 1343 * and the index of any error, not null 1344 * @return the parsed text, null if the parse results _in an error 1345 * @throws DateTimeException if some problem occurs during parsing 1346 * @throws IndexOutOfBoundsException if the position is invalid 1347 */ 1348 public TemporalAccessor parseUnresolved(string text, ParsePosition position) { 1349 DateTimeParseContext context = parseUnresolved0(text, position); 1350 if (context is null) { 1351 return null; 1352 } 1353 return context.toUnresolved(); 1354 } 1355 1356 private DateTimeParseContext parseUnresolved0(string text, ParsePosition position) { 1357 assert(text, "text"); 1358 assert(position, "position"); 1359 DateTimeParseContext context = new DateTimeParseContext(this); 1360 int pos = position.getIndex(); 1361 pos = printerParser.parse(context, text, pos); 1362 if (pos < 0) { 1363 position.setErrorIndex(~pos); // index not updated from input 1364 return null; 1365 } 1366 position.setIndex(pos); // errorIndex not updated from input 1367 return context; 1368 } 1369 1370 //----------------------------------------------------------------------- 1371 /** 1372 * Returns the formatter as a composite printer parser. 1373 * 1374 * @param optional whether the printer/parser should be optional 1375 * @return the printer/parser, not null 1376 */ 1377 CompositePrinterParser toPrinterParser(bool optional) { 1378 return printerParser.withOptional(optional); 1379 } 1380 1381 /** 1382 * Returns this formatter as a {@code java.text.Format} instance. 1383 * !(p) 1384 * The returned {@link Format} instance will format any {@link TemporalAccessor} 1385 * and parses to a resolved {@link TemporalAccessor}. 1386 * !(p) 1387 * Exceptions will follow the definitions of {@code Format}, see those methods 1388 * for details about {@code IllegalArgumentException} during formatting and 1389 * {@code ParseException} or null during parsing. 1390 * The format does not support attributing of the returned format string. 1391 * 1392 * @return this formatter as a classic format instance, not null 1393 */ 1394 ///@gxc 1395 // public Format toFormat() { 1396 // return new ClassicFormat(this, null); 1397 // } 1398 1399 /** 1400 * Returns this formatter as a {@code java.text.Format} instance that will 1401 * parse using the specified query. 1402 * !(p) 1403 * The returned {@link Format} instance will format any {@link TemporalAccessor} 1404 * and parses to the type specified. 1405 * The type must be one that is supported by {@link #parse}. 1406 * !(p) 1407 * Exceptions will follow the definitions of {@code Format}, see those methods 1408 * for details about {@code IllegalArgumentException} during formatting and 1409 * {@code ParseException} or null during parsing. 1410 * The format does not support attributing of the returned format string. 1411 * 1412 * @param parseQuery the query defining the type to parse to, not null 1413 * @return this formatter as a classic format instance, not null 1414 */ 1415 ///@gxc 1416 // public Format toFormat(TemporalQuery!(Object) parseQuery) { 1417 // assert(parseQuery, "parseQuery"); 1418 // return new ClassicFormat(this, parseQuery); 1419 // } 1420 1421 //----------------------------------------------------------------------- 1422 /** 1423 * Returns a description of the underlying formatters. 1424 * 1425 * @return a description of this formatter, not null 1426 */ 1427 override 1428 public string toString() { 1429 string pattern = printerParser.toString(); 1430 pattern = pattern.startsWith("[") ? pattern : pattern[1 .. pattern.length - 1]; 1431 return pattern; 1432 // TODO: Fix tests to not depend on toString() 1433 // return "DateTimeFormatter[" ~ locale + 1434 // (chrono !is null ? "," ~ chrono : "") + 1435 // (zone !is null ? "," ~ zone : "") + 1436 // pattern ~ "]"; 1437 } 1438 1439 //----------------------------------------------------------------------- 1440 /** 1441 * Implements the classic Java Format API. 1442 * @serial exclude 1443 */ 1444 // @SuppressWarnings("serial") // not actually serializable 1445 ///@gxc 1446 // static class ClassicFormat : Format { 1447 // /** The formatter. */ 1448 // private final DateTimeFormatter formatter; 1449 // /** The type to be parsed. */ 1450 // private final TemporalQuery!(Object) parseType; 1451 // /** Constructor. */ 1452 // public this(DateTimeFormatter formatter, TemporalQuery!(Object) parseType) { 1453 // this.formatter = formatter; 1454 // this.parseType = parseType; 1455 // } 1456 1457 // override 1458 // public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { 1459 // assert(obj, "obj"); 1460 // assert(toAppendTo, "toAppendTo"); 1461 // assert(pos, "pos"); 1462 // if ((cast(TemporalAccessor)(obj) !is null) == false) { 1463 // throw new IllegalArgumentException("Format target must implement TemporalAccessor"); 1464 // } 1465 // pos.setBeginIndex(0); 1466 // pos.setEndIndex(0); 1467 // try { 1468 // formatter.formatTo(cast(TemporalAccessor) obj, toAppendTo); 1469 // } catch (RuntimeException ex) { 1470 // throw new IllegalArgumentException(ex.getMessage(), ex); 1471 // } 1472 // return toAppendTo; 1473 // } 1474 // override 1475 // public Object parseObject(string text) /* throws ParseException */ { 1476 // assert(text, "text"); 1477 // try { 1478 // if (parseType is null) { 1479 // return formatter.parseResolved0(text, null); 1480 // } 1481 // return formatter.parse(text, parseType); 1482 // } catch (DateTimeParseException ex) { 1483 // throw new ParseException(ex.getMessage(), ex.getErrorIndex()); 1484 // } catch (RuntimeException ex) { 1485 // throw cast(ParseException) new ParseException(ex.getMessage(), 0).initCause(ex); 1486 // } 1487 // } 1488 // override 1489 // public Object parseObject(string text, ParsePosition pos) { 1490 // assert(text, "text"); 1491 // DateTimeParseContext context; 1492 // try { 1493 // context = formatter.parseUnresolved0(text, pos); 1494 // } catch (IndexOutOfBoundsException ex) { 1495 // if (pos.getErrorIndex() < 0) { 1496 // pos.setErrorIndex(0); 1497 // } 1498 // return null; 1499 // } 1500 // if (context is null) { 1501 // if (pos.getErrorIndex() < 0) { 1502 // pos.setErrorIndex(0); 1503 // } 1504 // return null; 1505 // } 1506 // try { 1507 // TemporalAccessor resolved = context.toResolved(formatter.resolverStyle, formatter.resolverFields); 1508 // if (parseType is null) { 1509 // return resolved; 1510 // } 1511 // return resolved.query(parseType); 1512 // } catch (RuntimeException ex) { 1513 // pos.setErrorIndex(0); 1514 // return null; 1515 // } 1516 // } 1517 // } 1518 1519 }