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.temporal.IsoFields; 13 14 import hunt.time.DayOfWeek; 15 import hunt.time.temporal.ChronoField; 16 import hunt.time.temporal.ChronoUnit; 17 18 import hunt.time.Exceptions; 19 import hunt.time.Duration; 20 import hunt.time.LocalDate; 21 import hunt.time.chrono.ChronoLocalDate; 22 import hunt.time.chrono.Chronology; 23 import hunt.time.chrono.IsoChronology; 24 import hunt.time.format.ResolverStyle; 25 import hunt.time.temporal.TemporalField; 26 import hunt.time.temporal.ValueRange; 27 import hunt.time.temporal.TemporalAccessor; 28 import hunt.time.temporal.TemporalUnit; 29 import hunt.time.temporal.Temporal; 30 import hunt.time.util.Common; 31 32 import hunt.Assert; 33 import hunt.collection.Map; 34 import hunt.Integer; 35 import hunt.Long; 36 import hunt.math.Helper; 37 import hunt.Exceptions; 38 39 import hunt.util.Comparator; 40 import hunt.util.Locale; 41 // import hunt.util.ResourceBundle; 42 43 // import sun.util.locale.provider.CalendarDataUtility; 44 // import sun.util.locale.provider.LocaleProviderAdapter; 45 // import sun.util.locale.provider.LocaleResources; 46 47 /** 48 * Fields and units specific to the ISO-8601 calendar system, 49 * including quarter-of-year and week-based-year. 50 * !(p) 51 * This class defines fields and units that are specific to the ISO calendar system. 52 * 53 * !(h3)Quarter of year</h3> 54 * The ISO-8601 standard is based on the standard civic 12 month year. 55 * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4. 56 * !(p) 57 * January, February and March are _in Q1. 58 * April, May and June are _in Q2. 59 * July, August and September are _in Q3. 60 * October, November and December are _in Q4. 61 * !(p) 62 * The complete date is expressed using three fields: 63 * !(ul) 64 * !(li){@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92 65 * !(li){@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the quarter within the year, from 1 to 4 66 * !(li){@link ChronoField#YEAR YEAR} - the standard ISO year 67 * </ul> 68 * 69 * !(h3)Week based years</h3> 70 * The ISO-8601 standard was originally intended as a data interchange format, 71 * defining a string format for dates and times. However, it also defines an 72 * alternate way of expressing the date, based on the concept of week-based-year. 73 * !(p) 74 * The date is expressed using three fields: 75 * !(ul) 76 * !(li){@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} - the standard field defining the 77 * day-of-week from Monday (1) to Sunday (7) 78 * !(li){@link #WEEK_OF_WEEK_BASED_YEAR} - the week within the week-based-year 79 * !(li){@link #WEEK_BASED_YEAR WEEK_BASED_YEAR} - the week-based-year 80 * </ul> 81 * The week-based-year itself is defined relative to the standard ISO proleptic year. 82 * It differs from the standard year _in that it always starts on a Monday. 83 * !(p) 84 * The first week of a week-based-year is the first Monday-based week of the standard 85 * ISO year that has at least 4 days _in the new year. 86 * !(ul) 87 * !(li)If January 1st is Monday then week 1 starts on January 1st 88 * !(li)If January 1st is Tuesday then week 1 starts on December 31st of the previous standard year 89 * !(li)If January 1st is Wednesday then week 1 starts on December 30th of the previous standard year 90 * !(li)If January 1st is Thursday then week 1 starts on December 29th of the previous standard year 91 * !(li)If January 1st is Friday then week 1 starts on January 4th 92 * !(li)If January 1st is Saturday then week 1 starts on January 3rd 93 * !(li)If January 1st is Sunday then week 1 starts on January 2nd 94 * </ul> 95 * There are 52 weeks _in most week-based years, however on occasion there are 53 weeks. 96 * !(p) 97 * For example: 98 * 99 * <table class=striped style="text-align: left"> 100 * !(caption)Examples of Week based Years</caption> 101 * !(thead) 102 * !(tr)<th scope="col">Date</th><th scope="col">Day-of-week</th><th scope="col">Field values</th></tr> 103 * </thead> 104 * !(tbody) 105 * !(tr)<th scope="row">2008-12-28</th>!(td)Sunday</td>!(td)Week 52 of week-based-year 2008</td></tr> 106 * !(tr)<th scope="row">2008-12-29</th>!(td)Monday</td>!(td)Week 1 of week-based-year 2009</td></tr> 107 * !(tr)<th scope="row">2008-12-31</th>!(td)Wednesday</td>!(td)Week 1 of week-based-year 2009</td></tr> 108 * !(tr)<th scope="row">2009-01-01</th>!(td)Thursday</td>!(td)Week 1 of week-based-year 2009</td></tr> 109 * !(tr)<th scope="row">2009-01-04</th>!(td)Sunday</td>!(td)Week 1 of week-based-year 2009</td></tr> 110 * !(tr)<th scope="row">2009-01-05</th>!(td)Monday</td>!(td)Week 2 of week-based-year 2009</td></tr> 111 * </tbody> 112 * </table> 113 * 114 * @implSpec 115 * !(p) 116 * This class is immutable and thread-safe. 117 * 118 * @since 1.8 119 */ 120 public final class IsoFields 121 { 122 123 /** 124 * The field that represents the day-of-quarter. 125 * !(p) 126 * This field allows the day-of-quarter value to be queried and set. 127 * The day-of-quarter has values from 1 to 90 _in Q1 of a standard year, from 1 to 91 128 * _in Q1 of a leap year, from 1 to 91 _in Q2 and from 1 to 92 _in Q3 and Q4. 129 * !(p) 130 * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year 131 * are available. 132 * !(p) 133 * When setting this field, the value is allowed to be partially lenient, taking any 134 * value from 1 to 92. If the quarter has less than 92 days, then day 92, and 135 * potentially day 91, is _in the following quarter. 136 * !(p) 137 * In the resolving phase of parsing, a date can be created from a year, 138 * quarter-of-year and day-of-quarter. 139 * !(p) 140 * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are 141 * validated against their range of valid values. The day-of-quarter field 142 * is validated from 1 to 90, 91 or 92 depending on the year and quarter. 143 * !(p) 144 * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are 145 * validated against their range of valid values. The day-of-quarter field is 146 * validated between 1 and 92, ignoring the actual range based on the year and quarter. 147 * If the day-of-quarter exceeds the actual range by one day, then the resulting date 148 * is one day later. If the day-of-quarter exceeds the actual range by two days, 149 * then the resulting date is two days later. 150 * !(p) 151 * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the year is validated 152 * against the range of valid values. The resulting date is calculated equivalent to 153 * the following three stage approach. First, create a date on the first of January 154 * _in the requested year. Then take the quarter-of-year, subtract one, and add the 155 * amount _in quarters to the date. Finally, take the day-of-quarter, subtract one, 156 * and add the amount _in days to the date. 157 * !(p) 158 * This unit is an immutable and thread-safe singleton. 159 */ 160 //public __gshared TemporalField DAY_OF_QUARTER; 161 /** 162 * The field that represents the quarter-of-year. 163 * !(p) 164 * This field allows the quarter-of-year value to be queried and set. 165 * The quarter-of-year has values from 1 to 4. 166 * !(p) 167 * The quarter-of-year can only be calculated if the month-of-year is available. 168 * !(p) 169 * In the resolving phase of parsing, a date can be created from a year, 170 * quarter-of-year and day-of-quarter. 171 * See {@link #DAY_OF_QUARTER} for details. 172 * !(p) 173 * This unit is an immutable and thread-safe singleton. 174 */ 175 //public __gshared TemporalField QUARTER_OF_YEAR; 176 /** 177 * The field that represents the week-of-week-based-year. 178 * !(p) 179 * This field allows the week of the week-based-year value to be queried and set. 180 * The week-of-week-based-year has values from 1 to 52, or 53 if the 181 * week-based-year has 53 weeks. 182 * !(p) 183 * In the resolving phase of parsing, a date can be created from a 184 * week-based-year, week-of-week-based-year and day-of-week. 185 * !(p) 186 * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are 187 * validated against their range of valid values. The week-of-week-based-year 188 * field is validated from 1 to 52 or 53 depending on the week-based-year. 189 * !(p) 190 * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are 191 * validated against their range of valid values. The week-of-week-based-year 192 * field is validated between 1 and 53, ignoring the week-based-year. 193 * If the week-of-week-based-year is 53, but the week-based-year only has 194 * 52 weeks, then the resulting date is _in week 1 of the following week-based-year. 195 * !(p) 196 * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the week-based-year 197 * is validated against the range of valid values. If the day-of-week is outside 198 * the range 1 to 7, then the resulting date is adjusted by a suitable number of 199 * weeks to reduce the day-of-week to the range 1 to 7. If the week-of-week-based-year 200 * value is outside the range 1 to 52, then any excess weeks are added or subtracted 201 * from the resulting date. 202 * !(p) 203 * This unit is an immutable and thread-safe singleton. 204 */ 205 //public __gshared TemporalField WEEK_OF_WEEK_BASED_YEAR; 206 /** 207 * The field that represents the week-based-year. 208 * !(p) 209 * This field allows the week-based-year value to be queried and set. 210 * !(p) 211 * The field has a range that matches {@link LocalDate#MAX} and {@link LocalDate#MIN}. 212 * !(p) 213 * In the resolving phase of parsing, a date can be created from a 214 * week-based-year, week-of-week-based-year and day-of-week. 215 * See {@link #WEEK_OF_WEEK_BASED_YEAR} for details. 216 * !(p) 217 * This unit is an immutable and thread-safe singleton. 218 */ 219 //public __gshared TemporalField WEEK_BASED_YEAR; 220 /** 221 * The unit that represents week-based-years for the purpose of addition and subtraction. 222 * !(p) 223 * This allows a number of week-based-years to be added to, or subtracted from, a date. 224 * The unit is equal to either 52 or 53 weeks. 225 * The estimated duration of a week-based-year is the same as that of a standard ISO 226 * year at {@code 365.2425 Days}. 227 * !(p) 228 * The rules for addition add the number of week-based-years to the existing value 229 * for the week-based-year field. If the resulting week-based-year only has 52 weeks, 230 * then the date will be _in week 1 of the following week-based-year. 231 * !(p) 232 * This unit is an immutable and thread-safe singleton. 233 */ 234 //public __gshared TemporalUnit WEEK_BASED_YEARS; 235 /** 236 * Unit that represents the concept of a quarter-year. 237 * For the ISO calendar system, it is equal to 3 months. 238 * The estimated duration of a quarter-year is one quarter of {@code 365.2425 Days}. 239 * !(p) 240 * This unit is an immutable and thread-safe singleton. 241 */ 242 //public __gshared TemporalUnit QUARTER_YEARS; 243 244 // shared static this() 245 // { 246 // DAY_OF_QUARTER = Field.DAY_OF_QUARTER; 247 mixin(MakeGlobalVar!(TemporalField)("DAY_OF_QUARTER",`Field.DAY_OF_QUARTER`)); 248 // QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR; 249 mixin(MakeGlobalVar!(TemporalField)("QUARTER_OF_YEAR",`Field.QUARTER_OF_YEAR`)); 250 251 // WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR; 252 mixin(MakeGlobalVar!(TemporalField)("WEEK_OF_WEEK_BASED_YEAR",`Field.WEEK_OF_WEEK_BASED_YEAR`)); 253 254 mixin(MakeGlobalVar!(TemporalField)("WEEK_BASED_YEAR",`Field.WEEK_BASED_YEAR`)); 255 256 257 // WEEK_BASED_YEARS = Unit.WEEK_BASED_YEARS; 258 mixin(MakeGlobalVar!(TemporalUnit)("WEEK_BASED_YEARS",`Unit.WEEK_BASED_YEARS`)); 259 260 // QUARTER_YEARS = Unit.QUARTER_YEARS; 261 mixin(MakeGlobalVar!(TemporalUnit)("QUARTER_YEARS",` Unit.QUARTER_YEARS`)); 262 263 264 // } 265 266 /** 267 * Restricted constructor. 268 */ 269 private this() 270 { 271 throw new AssertionError("Not instantiable"); 272 } 273 274 //----------------------------------------------------------------------- 275 /** 276 * Implementation of the field. 277 */ 278 static class Field : TemporalField 279 { 280 // static Field DAY_OF_QUARTER; 281 // static Field QUARTER_OF_YEAR; 282 // static Field WEEK_OF_WEEK_BASED_YEAR; 283 // static Field WEEK_BASED_YEAR; 284 string getDisplayName(Locale locale){ return null;} 285 286 TemporalUnit getBaseUnit(){ return null;} 287 288 TemporalUnit getRangeUnit(){ return null;} 289 290 ValueRange range(){ return null;} 291 292 // bool isDateBased(){ return false;} 293 294 // bool isTimeBased(){ return false;} 295 296 bool isSupportedBy(TemporalAccessor temporal){ return false;} 297 298 // ValueRange rangeRefinedBy(TemporalAccessor temporal){ return null;} 299 300 long getFrom(TemporalAccessor temporal){ return long.init;} 301 302 Temporal adjustInto(Temporal temporal, long newValue){ return null;} 303 304 TemporalAccessor resolve(Map!(TemporalField, Long) fieldValues, 305 TemporalAccessor partialTemporal, ResolverStyle resolverStyle){ return null;} 306 307 override string toString(){return super.toString();} 308 309 // int opCmp(Object o){return 0;} 310 311 int opCmp(TemporalField o){return 0;} 312 // shared static this() 313 // { 314 // DAY_OF_QUARTER = new class Field 315 // { 316 // override 317 // public TemporalUnit getBaseUnit() 318 // { 319 // return ChronoUnit.DAYS; 320 // } 321 // override 322 // public TemporalUnit getRangeUnit() 323 // { 324 // return QUARTER_YEARS; 325 // } 326 // override 327 // public ValueRange range() 328 // { 329 // return ValueRange.of(1, 90, 92); 330 // } 331 // override 332 // public bool isSupportedBy(TemporalAccessor temporal) 333 // { 334 // return temporal.isSupported(ChronoField.DAY_OF_YEAR) 335 // && temporal.isSupported(ChronoField.MONTH_OF_YEAR) 336 // && temporal.isSupported(ChronoField.YEAR) && isIso(temporal); 337 // } 338 339 // override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 340 // { 341 // if (isSupportedBy(temporal) == false) 342 // { 343 // throw new Exception("Unsupported field: DayOfQuarter"); 344 // } 345 // long qoy = temporal.getLong(QUARTER_OF_YEAR); 346 // if (qoy == 1) 347 // { 348 // long year = temporal.getLong(ChronoField.YEAR); 349 // return (IsoChronology.INSTANCE.isLeapYear(year) 350 // ? ValueRange.of(1, 91) : ValueRange.of(1, 90)); 351 // } 352 // else if (qoy == 2) 353 // { 354 // return ValueRange.of(1, 91); 355 // } 356 // else if (qoy == 3 || qoy == 4) 357 // { 358 // return ValueRange.of(1, 92); 359 // } // else value not from 1 to 4, so drop through 360 // return range(); 361 // } 362 // override 363 // public long getFrom(TemporalAccessor temporal) 364 // { 365 // if (isSupportedBy(temporal) == false) 366 // { 367 // throw new Exception("Unsupported field: DayOfQuarter"); 368 // } 369 // int doy = temporal.get(ChronoField.DAY_OF_YEAR); 370 // int moy = temporal.get(ChronoField.MONTH_OF_YEAR); 371 // long year = temporal.getLong(ChronoField.YEAR); 372 // return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) 373 // ? 4 : 0)]; 374 // } 375 // /*@SuppressWarnings("unchecked")*/ 376 // override 377 // public Temporal adjustInto(Temporal temporal, long newValue) 378 // /* if (is(R : Temporal)) */ 379 // { 380 // // calls getFrom() to check if supported 381 // long curValue = getFrom(temporal); 382 // range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check 383 // return cast(Temporal) temporal._with(ChronoField.DAY_OF_YEAR, 384 // temporal.getLong(ChronoField.DAY_OF_YEAR) + (newValue - curValue)); 385 // } 386 // override 387 // public ChronoLocalDate resolve(Map!(TemporalField, Long) fieldValues, 388 // TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 389 // { 390 // Long yearLong = fieldValues.get(ChronoField.YEAR); 391 // Long qoyLong = fieldValues.get(QUARTER_OF_YEAR); 392 // if (yearLong is null || qoyLong is null) 393 // { 394 // return null; 395 // } 396 // int y = ChronoField.YEAR.checkValidIntValue(yearLong.longValue()); // always validate 397 // long doq = fieldValues.get(DAY_OF_QUARTER).longValue(); 398 // ensureIso(partialTemporal); 399 // LocalDate date; 400 // if (resolverStyle == ResolverStyle.LENIENT) 401 // { 402 // date = LocalDate.of(y, 1, 1) 403 // .plusMonths(MathHelper.multiplyExact(MathHelper.subtractExact(cast(int)(qoyLong.longValue()), 404 // 1), 3)); 405 // doq = MathHelper.subtractExact(doq, 1); 406 // } 407 // else 408 // { 409 // int qoy = QUARTER_OF_YEAR.range() 410 // .checkValidIntValue(qoyLong.longValue(), QUARTER_OF_YEAR); // validated 411 // date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1); 412 // if (doq < 1 || doq > 90) 413 // { 414 // if (resolverStyle == ResolverStyle.STRICT) 415 // { 416 // rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range 417 // } 418 // else 419 // { // SMART 420 // range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter 421 // } 422 // } 423 // doq--; 424 // } 425 // fieldValues.remove(this); 426 // fieldValues.remove(ChronoField.YEAR); 427 // fieldValues.remove(QUARTER_OF_YEAR); 428 // return date.plusDays(doq); 429 // } 430 431 // override 432 // string getDisplayName(Locale locale) 433 // { 434 // assert(locale, "locale"); 435 // return toString(); 436 // } 437 438 // override public string toString() 439 // { 440 // return "DayOfQuarter"; 441 // } 442 443 // override 444 // int opCmp(TemporalField obj) 445 // { 446 // if (cast(Field)(obj) !is null) 447 // { 448 // Field other = cast(Field) obj; 449 // return compare(toString(), other.toString()); 450 // } 451 // return 0; 452 // } 453 // }; 454 455 mixin(MakeGlobalVar!(Field)("DAY_OF_QUARTER",`new class Field 456 { 457 override 458 public TemporalUnit getBaseUnit() 459 { 460 return ChronoUnit.DAYS; 461 } 462 override 463 public TemporalUnit getRangeUnit() 464 { 465 return QUARTER_YEARS; 466 } 467 override 468 public ValueRange range() 469 { 470 return ValueRange.of(1, 90, 92); 471 } 472 override 473 public bool isSupportedBy(TemporalAccessor temporal) 474 { 475 return temporal.isSupported(ChronoField.DAY_OF_YEAR) 476 && temporal.isSupported(ChronoField.MONTH_OF_YEAR) 477 && temporal.isSupported(ChronoField.YEAR) && isIso(temporal); 478 } 479 480 override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 481 { 482 if (isSupportedBy(temporal) == false) 483 { 484 throw new Exception("Unsupported field: DayOfQuarter"); 485 } 486 long qoy = temporal.getLong(QUARTER_OF_YEAR); 487 if (qoy == 1) 488 { 489 long year = temporal.getLong(ChronoField.YEAR); 490 return (IsoChronology.INSTANCE.isLeapYear(year) 491 ? ValueRange.of(1, 91) : ValueRange.of(1, 90)); 492 } 493 else if (qoy == 2) 494 { 495 return ValueRange.of(1, 91); 496 } 497 else if (qoy == 3 || qoy == 4) 498 { 499 return ValueRange.of(1, 92); 500 } // else value not from 1 to 4, so drop through 501 return range(); 502 } 503 override 504 public long getFrom(TemporalAccessor temporal) 505 { 506 if (isSupportedBy(temporal) == false) 507 { 508 throw new Exception("Unsupported field: DayOfQuarter"); 509 } 510 int doy = temporal.get(ChronoField.DAY_OF_YEAR); 511 int moy = temporal.get(ChronoField.MONTH_OF_YEAR); 512 long year = temporal.getLong(ChronoField.YEAR); 513 return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) 514 ? 4 : 0)]; 515 } 516 /*@SuppressWarnings("unchecked")*/ 517 override 518 public Temporal adjustInto(Temporal temporal, long newValue) 519 /* if (is(R : Temporal)) */ 520 { 521 // calls getFrom() to check if supported 522 long curValue = getFrom(temporal); 523 range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check 524 return cast(Temporal) temporal._with(ChronoField.DAY_OF_YEAR, 525 temporal.getLong(ChronoField.DAY_OF_YEAR) + (newValue - curValue)); 526 } 527 override 528 public ChronoLocalDate resolve(Map!(TemporalField, Long) fieldValues, 529 TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 530 { 531 Long yearLong = fieldValues.get(ChronoField.YEAR); 532 Long qoyLong = fieldValues.get(QUARTER_OF_YEAR); 533 if (yearLong is null || qoyLong is null) 534 { 535 return null; 536 } 537 int y = ChronoField.YEAR.checkValidIntValue(yearLong.longValue()); // always validate 538 long doq = fieldValues.get(DAY_OF_QUARTER).longValue(); 539 ensureIso(partialTemporal); 540 LocalDate date; 541 if (resolverStyle == ResolverStyle.LENIENT) 542 { 543 date = LocalDate.of(y, 1, 1) 544 .plusMonths(MathHelper.multiplyExact(MathHelper.subtractExact(cast(int)(qoyLong.longValue()), 545 1), 3)); 546 doq = MathHelper.subtractExact(doq, 1); 547 } 548 else 549 { 550 int qoy = QUARTER_OF_YEAR.range() 551 .checkValidIntValue(qoyLong.longValue(), QUARTER_OF_YEAR); // validated 552 date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1); 553 if (doq < 1 || doq > 90) 554 { 555 if (resolverStyle == ResolverStyle.STRICT) 556 { 557 rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range 558 } 559 else 560 { // SMART 561 range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter 562 } 563 } 564 doq--; 565 } 566 fieldValues.remove(this); 567 fieldValues.remove(ChronoField.YEAR); 568 fieldValues.remove(QUARTER_OF_YEAR); 569 return date.plusDays(doq); 570 } 571 572 override 573 string getDisplayName(Locale locale) 574 { 575 assert(locale, "locale"); 576 return toString(); 577 } 578 579 override public string toString() 580 { 581 return "DayOfQuarter"; 582 } 583 584 override 585 int opCmp(TemporalField obj) 586 { 587 if (cast(Field)(obj) !is null) 588 { 589 Field other = cast(Field) obj; 590 return compare(toString(), other.toString()); 591 } 592 return 0; 593 } 594 }`)); 595 // QUARTER_OF_YEAR = new class Field 596 // { 597 // override 598 // public TemporalUnit getBaseUnit() 599 // { 600 // return QUARTER_YEARS; 601 // } 602 // override 603 // public TemporalUnit getRangeUnit() 604 // { 605 // return ChronoUnit.YEARS; 606 // } 607 // override 608 // public ValueRange range() 609 // { 610 // return ValueRange.of(1, 4); 611 // } 612 // override 613 // public bool isSupportedBy(TemporalAccessor temporal) 614 // { 615 // return temporal.isSupported(ChronoField.MONTH_OF_YEAR) && isIso(temporal); 616 // } 617 // override 618 // public long getFrom(TemporalAccessor temporal) 619 // { 620 // if (isSupportedBy(temporal) == false) 621 // { 622 // throw new Exception("Unsupported field: QuarterOfYear"); 623 // } 624 // long moy = temporal.getLong(ChronoField.MONTH_OF_YEAR); 625 // return ((moy + 2) / 3); 626 // } 627 628 // override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 629 // { 630 // if (isSupportedBy(temporal) == false) 631 // { 632 // throw new Exception("Unsupported field: QuarterOfYear"); 633 // } 634 // return range()/* super.rangeRefinedBy(temporal) */; 635 // } 636 // /*@SuppressWarnings("unchecked")*/ 637 // override public Temporal adjustInto(Temporal temporal, long newValue) 638 // /* if (is(R : Temporal)) */ 639 // { 640 // // calls getFrom() to check if supported 641 // long curValue = getFrom(temporal); 642 // range().checkValidValue(newValue, this); // strictly check from 1 to 4 643 // return cast(Temporal) temporal._with(ChronoField.MONTH_OF_YEAR, 644 // temporal.getLong(ChronoField.MONTH_OF_YEAR) + (newValue - curValue) * 3); 645 // } 646 647 // override 648 // string getDisplayName(Locale locale) 649 // { 650 // assert(locale, "locale"); 651 // return toString(); 652 // } 653 654 // override public string toString() 655 // { 656 // return "QuarterOfYear"; 657 // } 658 659 // override TemporalAccessor resolve(Map!(TemporalField, Long) fieldValues, 660 // TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 661 // { 662 // return null; 663 // } 664 // override 665 // int opCmp(TemporalField obj) 666 // { 667 // if (cast(Field)(obj) !is null) 668 // { 669 // Field other = cast(Field) obj; 670 // return compare(toString(), other.toString()); 671 // } 672 // return 0; 673 // } 674 // }; 675 mixin(MakeGlobalVar!(Field)("QUARTER_OF_YEAR",`new class Field 676 { 677 override 678 public TemporalUnit getBaseUnit() 679 { 680 return QUARTER_YEARS; 681 } 682 override 683 public TemporalUnit getRangeUnit() 684 { 685 return ChronoUnit.YEARS; 686 } 687 override 688 public ValueRange range() 689 { 690 return ValueRange.of(1, 4); 691 } 692 override 693 public bool isSupportedBy(TemporalAccessor temporal) 694 { 695 return temporal.isSupported(ChronoField.MONTH_OF_YEAR) && isIso(temporal); 696 } 697 override 698 public long getFrom(TemporalAccessor temporal) 699 { 700 if (isSupportedBy(temporal) == false) 701 { 702 throw new Exception("Unsupported field: QuarterOfYear"); 703 } 704 long moy = temporal.getLong(ChronoField.MONTH_OF_YEAR); 705 return ((moy + 2) / 3); 706 } 707 708 override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 709 { 710 if (isSupportedBy(temporal) == false) 711 { 712 throw new Exception("Unsupported field: QuarterOfYear"); 713 } 714 return range()/* super.rangeRefinedBy(temporal) */; 715 } 716 /*@SuppressWarnings("unchecked")*/ 717 override public Temporal adjustInto(Temporal temporal, long newValue) 718 /* if (is(R : Temporal)) */ 719 { 720 // calls getFrom() to check if supported 721 long curValue = getFrom(temporal); 722 range().checkValidValue(newValue, this); // strictly check from 1 to 4 723 return cast(Temporal) temporal._with(ChronoField.MONTH_OF_YEAR, 724 temporal.getLong(ChronoField.MONTH_OF_YEAR) + (newValue - curValue) * 3); 725 } 726 727 override 728 string getDisplayName(Locale locale) 729 { 730 assert(locale, "locale"); 731 return toString(); 732 } 733 734 override public string toString() 735 { 736 return "QuarterOfYear"; 737 } 738 739 override TemporalAccessor resolve(Map!(TemporalField, Long) fieldValues, 740 TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 741 { 742 return null; 743 } 744 override 745 int opCmp(TemporalField obj) 746 { 747 if (cast(Field)(obj) !is null) 748 { 749 Field other = cast(Field) obj; 750 return compare(toString(), other.toString()); 751 } 752 return 0; 753 } 754 }`)); 755 756 // WEEK_OF_WEEK_BASED_YEAR = new class Field 757 // { 758 // override 759 // public string getDisplayName(Locale locale) 760 // { 761 // ///@gxc 762 // // assert(locale, "locale"); 763 // // LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased() 764 // // .getLocaleResources( 765 // // CalendarDataUtility 766 // // .findRegionOverride(locale)); 767 // // ResourceBundle rb = lr.getJavaTimeFormatData(); 768 // // return rb.containsKey("field.week") ? rb.getString("field.week") : toString(); 769 // implementationMissing(); 770 // return null; 771 // } 772 773 // override 774 // public TemporalUnit getBaseUnit() 775 // { 776 // return ChronoUnit.WEEKS; 777 // } 778 // override 779 // public TemporalUnit getRangeUnit() 780 // { 781 // return WEEK_BASED_YEARS; 782 // } 783 // override 784 // public ValueRange range() 785 // { 786 // return ValueRange.of(1, 52, 53); 787 // } 788 // override 789 // public bool isSupportedBy(TemporalAccessor temporal) 790 // { 791 // return temporal.isSupported(ChronoField.EPOCH_DAY) && isIso(temporal); 792 // } 793 794 // override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 795 // { 796 // if (isSupportedBy(temporal) == false) 797 // { 798 // throw new Exception("Unsupported field: WeekOfWeekBasedYear"); 799 // } 800 // return getWeekRange(LocalDate.from(temporal)); 801 // } 802 // override 803 // public long getFrom(TemporalAccessor temporal) 804 // { 805 // if (isSupportedBy(temporal) == false) 806 // { 807 // throw new Exception("Unsupported field: WeekOfWeekBasedYear"); 808 // } 809 // return getWeek(LocalDate.from(temporal)); 810 // } 811 // /*@SuppressWarnings("unchecked")*/ 812 // override public Temporal adjustInto(Temporal temporal, long newValue) 813 // /* if (is(R : Temporal)) */ 814 // { 815 // // calls getFrom() to check if supported 816 // range().checkValidValue(newValue, this); // lenient range 817 // return cast(Temporal) temporal.plus(MathHelper.subtractExact(newValue, 818 // getFrom(temporal)), ChronoUnit.WEEKS); 819 // } 820 // override 821 // public ChronoLocalDate resolve(Map!(TemporalField, Long) fieldValues, 822 // TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 823 // { 824 // Long wbyLong = fieldValues.get(WEEK_BASED_YEAR); 825 // Long dowLong = fieldValues.get(ChronoField.DAY_OF_WEEK); 826 // if (wbyLong is null || dowLong is null) 827 // { 828 // return null; 829 // } 830 // int wby = WEEK_BASED_YEAR.range() 831 // .checkValidIntValue(wbyLong.longValue(), WEEK_BASED_YEAR); // always validate 832 // long wowby = fieldValues.get(WEEK_OF_WEEK_BASED_YEAR).longValue(); 833 // ensureIso(partialTemporal); 834 // LocalDate date = LocalDate.of(wby, 1, 4); 835 // if (resolverStyle == ResolverStyle.LENIENT) 836 // { 837 // long dow = dowLong.longValue(); // unvalidated 838 // if (dow > 7) 839 // { 840 // date = date.plusWeeks((dow - 1) / 7); 841 // dow = ((dow - 1) % 7) + 1; 842 // } 843 // else if (dow < 1) 844 // { 845 // date = date.plusWeeks(MathHelper.subtractExact(dow, 7) / 7); 846 // dow = ((dow + 6) % 7) + 1; 847 // } 848 // date = date.plusWeeks(MathHelper.subtractExact(wowby, 1)) 849 // ._with(ChronoField.DAY_OF_WEEK, dow); 850 // } 851 // else 852 // { 853 // int dow = ChronoField.DAY_OF_WEEK.checkValidIntValue(dowLong.longValue()); // validated 854 // if (wowby < 1 || wowby > 52) 855 // { 856 // if (resolverStyle == ResolverStyle.STRICT) 857 // { 858 // getWeekRange(date).checkValidValue(wowby, this); // only allow exact range 859 // } 860 // else 861 // { // SMART 862 // range().checkValidValue(wowby, this); // allow 1-53 rolling into next year 863 // } 864 // } 865 // date = date.plusWeeks(wowby - 1)._with(ChronoField.DAY_OF_WEEK, dow); 866 // } 867 // fieldValues.remove(this); 868 // fieldValues.remove(WEEK_BASED_YEAR); 869 // fieldValues.remove(ChronoField.DAY_OF_WEEK); 870 // return date; 871 // } 872 873 // /* override */ 874 // // string getDisplayName(Locale locale) 875 // // { 876 // // assert(locale, "locale"); 877 // // return toString(); 878 // // } 879 880 // override public string toString() 881 // { 882 // return "WeekOfWeekBasedYear"; 883 // } 884 885 // override int opCmp(TemporalField obj) 886 // { 887 // if (cast(Field)(obj) !is null) 888 // { 889 // Field other = cast(Field) obj; 890 // return compare(toString(), other.toString()); 891 // } 892 // return 0; 893 // } 894 // }; 895 896 mixin(MakeGlobalVar!(Field)("WEEK_OF_WEEK_BASED_YEAR",`new class Field 897 { 898 override 899 public string getDisplayName(Locale locale) 900 { 901 ///@gxc 902 // assert(locale, "locale"); 903 // LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased() 904 // .getLocaleResources( 905 // CalendarDataUtility 906 // .findRegionOverride(locale)); 907 // ResourceBundle rb = lr.getJavaTimeFormatData(); 908 // return rb.containsKey("field.week") ? rb.getString("field.week") : toString(); 909 implementationMissing(); 910 return null; 911 } 912 913 override 914 public TemporalUnit getBaseUnit() 915 { 916 return ChronoUnit.WEEKS; 917 } 918 override 919 public TemporalUnit getRangeUnit() 920 { 921 return IsoFields.WEEK_BASED_YEARS; 922 } 923 override 924 public ValueRange range() 925 { 926 return ValueRange.of(1, 52, 53); 927 } 928 override 929 public bool isSupportedBy(TemporalAccessor temporal) 930 { 931 return temporal.isSupported(ChronoField.EPOCH_DAY) && isIso(temporal); 932 } 933 934 override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 935 { 936 if (isSupportedBy(temporal) == false) 937 { 938 throw new Exception("Unsupported field: WeekOfWeekBasedYear"); 939 } 940 return getWeekRange(LocalDate.from(temporal)); 941 } 942 override 943 public long getFrom(TemporalAccessor temporal) 944 { 945 if (isSupportedBy(temporal) == false) 946 { 947 throw new Exception("Unsupported field: WeekOfWeekBasedYear"); 948 } 949 return getWeek(LocalDate.from(temporal)); 950 } 951 /*@SuppressWarnings("unchecked")*/ 952 override public Temporal adjustInto(Temporal temporal, long newValue) 953 /* if (is(R : Temporal)) */ 954 { 955 // calls getFrom() to check if supported 956 range().checkValidValue(newValue, this); // lenient range 957 return cast(Temporal) temporal.plus(MathHelper.subtractExact(newValue, 958 getFrom(temporal)), ChronoUnit.WEEKS); 959 } 960 override 961 public ChronoLocalDate resolve(Map!(TemporalField, Long) fieldValues, 962 TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 963 { 964 Long wbyLong = fieldValues.get(WEEK_BASED_YEAR); 965 Long dowLong = fieldValues.get(ChronoField.DAY_OF_WEEK); 966 if (wbyLong is null || dowLong is null) 967 { 968 return null; 969 } 970 int wby = WEEK_BASED_YEAR.range() 971 .checkValidIntValue(wbyLong.longValue(), WEEK_BASED_YEAR); // always validate 972 long wowby = fieldValues.get(WEEK_OF_WEEK_BASED_YEAR).longValue(); 973 ensureIso(partialTemporal); 974 LocalDate date = LocalDate.of(wby, 1, 4); 975 if (resolverStyle == ResolverStyle.LENIENT) 976 { 977 long dow = dowLong.longValue(); // unvalidated 978 if (dow > 7) 979 { 980 date = date.plusWeeks((dow - 1) / 7); 981 dow = ((dow - 1) % 7) + 1; 982 } 983 else if (dow < 1) 984 { 985 date = date.plusWeeks(MathHelper.subtractExact(dow, 7) / 7); 986 dow = ((dow + 6) % 7) + 1; 987 } 988 date = date.plusWeeks(MathHelper.subtractExact(wowby, 1)) 989 ._with(ChronoField.DAY_OF_WEEK, dow); 990 } 991 else 992 { 993 int dow = ChronoField.DAY_OF_WEEK.checkValidIntValue(dowLong.longValue()); // validated 994 if (wowby < 1 || wowby > 52) 995 { 996 if (resolverStyle == ResolverStyle.STRICT) 997 { 998 getWeekRange(date).checkValidValue(wowby, this); // only allow exact range 999 } 1000 else 1001 { // SMART 1002 range().checkValidValue(wowby, this); // allow 1-53 rolling into next year 1003 } 1004 } 1005 date = date.plusWeeks(wowby - 1)._with(ChronoField.DAY_OF_WEEK, dow); 1006 } 1007 fieldValues.remove(this); 1008 fieldValues.remove(WEEK_BASED_YEAR); 1009 fieldValues.remove(ChronoField.DAY_OF_WEEK); 1010 return date; 1011 } 1012 1013 /* override */ 1014 // string getDisplayName(Locale locale) 1015 // { 1016 // assert(locale, "locale"); 1017 // return toString(); 1018 // } 1019 1020 override public string toString() 1021 { 1022 return "WeekOfWeekBasedYear"; 1023 } 1024 1025 override int opCmp(TemporalField obj) 1026 { 1027 if (cast(Field)(obj) !is null) 1028 { 1029 Field other = cast(Field) obj; 1030 return compare(toString(), other.toString()); 1031 } 1032 return 0; 1033 } 1034 }`)); 1035 1036 // WEEK_BASED_YEAR = new class Field 1037 // { 1038 // override 1039 // public TemporalUnit getBaseUnit() 1040 // { 1041 // return IsoFields.WEEK_BASED_YEARS; 1042 // } 1043 // override 1044 // public TemporalUnit getRangeUnit() 1045 // { 1046 // return ChronoUnit.FOREVER; 1047 // } 1048 // override 1049 // public ValueRange range() 1050 // { 1051 // return ChronoField.YEAR.range(); 1052 // } 1053 // override 1054 // public bool isSupportedBy(TemporalAccessor temporal) 1055 // { 1056 // return temporal.isSupported(ChronoField.EPOCH_DAY) && isIso(temporal); 1057 // } 1058 // override 1059 // public long getFrom(TemporalAccessor temporal) 1060 // { 1061 // if (isSupportedBy(temporal) == false) 1062 // { 1063 // throw new Exception("Unsupported field: WeekBasedYear"); 1064 // } 1065 // return getWeekBasedYear(LocalDate.from(temporal)); 1066 // } 1067 1068 // override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 1069 // { 1070 // if (isSupportedBy(temporal) == false) 1071 // { 1072 // throw new Exception("Unsupported field: WeekBasedYear"); 1073 // } 1074 // return range()/* super.rangeRefinedBy(temporal) */; 1075 // } 1076 // /*@SuppressWarnings("unchecked")*/ 1077 // override public Temporal adjustInto(Temporal temporal, long newValue) 1078 // /* if (is(R : Temporal)) */ 1079 // { 1080 // if (isSupportedBy(temporal) == false) 1081 // { 1082 // throw new Exception("Unsupported field: WeekBasedYear"); 1083 // } 1084 // int newWby = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check 1085 // LocalDate date = LocalDate.from(temporal); 1086 // int dow = date.get(ChronoField.DAY_OF_WEEK); 1087 // int week = getWeek(date); 1088 // if (week == 53 && getWeekRange(newWby) == 52) 1089 // { 1090 // week = 52; 1091 // } 1092 // LocalDate resolved = LocalDate.of(newWby, 1, 4); // 4th is guaranteed to be _in week one 1093 // int days = (dow - resolved.get(ChronoField.DAY_OF_WEEK)) + ((week - 1) * 7); 1094 // resolved = resolved.plusDays(days); 1095 // return cast(Temporal) temporal._with(resolved); 1096 // } 1097 1098 // override 1099 // string getDisplayName(Locale locale) 1100 // { 1101 // assert(locale, "locale"); 1102 // return toString(); 1103 // } 1104 1105 // override public string toString() 1106 // { 1107 // return "WeekBasedYear"; 1108 // } 1109 1110 // override TemporalAccessor resolve(Map!(TemporalField, Long) fieldValues, 1111 // TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 1112 // { 1113 // return null; 1114 // } 1115 1116 // override int opCmp(TemporalField obj) 1117 // { 1118 // if (cast(Field)(obj) !is null) 1119 // { 1120 // Field other = cast(Field) obj; 1121 // return compare(toString(), other.toString()); 1122 // } 1123 // return 0; 1124 // } 1125 // }; 1126 mixin(MakeGlobalVar!(Field)("WEEK_BASED_YEAR",`new class Field 1127 { 1128 override 1129 public TemporalUnit getBaseUnit() 1130 { 1131 return IsoFields.WEEK_BASED_YEARS; 1132 } 1133 override 1134 public TemporalUnit getRangeUnit() 1135 { 1136 return ChronoUnit.FOREVER; 1137 } 1138 override 1139 public ValueRange range() 1140 { 1141 return ChronoField.YEAR.range(); 1142 } 1143 override 1144 public bool isSupportedBy(TemporalAccessor temporal) 1145 { 1146 return temporal.isSupported(ChronoField.EPOCH_DAY) && isIso(temporal); 1147 } 1148 override 1149 public long getFrom(TemporalAccessor temporal) 1150 { 1151 if (isSupportedBy(temporal) == false) 1152 { 1153 throw new Exception("Unsupported field: WeekBasedYear"); 1154 } 1155 return getWeekBasedYear(LocalDate.from(temporal)); 1156 } 1157 1158 override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 1159 { 1160 if (isSupportedBy(temporal) == false) 1161 { 1162 throw new Exception("Unsupported field: WeekBasedYear"); 1163 } 1164 return range()/* super.rangeRefinedBy(temporal) */; 1165 } 1166 /*@SuppressWarnings("unchecked")*/ 1167 override public Temporal adjustInto(Temporal temporal, long newValue) 1168 /* if (is(R : Temporal)) */ 1169 { 1170 if (isSupportedBy(temporal) == false) 1171 { 1172 throw new Exception("Unsupported field: WeekBasedYear"); 1173 } 1174 int newWby = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check 1175 LocalDate date = LocalDate.from(temporal); 1176 int dow = date.get(ChronoField.DAY_OF_WEEK); 1177 int week = getWeek(date); 1178 if (week == 53 && getWeekRange(newWby) == 52) 1179 { 1180 week = 52; 1181 } 1182 LocalDate resolved = LocalDate.of(newWby, 1, 4); // 4th is guaranteed to be _in week one 1183 int days = (dow - resolved.get(ChronoField.DAY_OF_WEEK)) + ((week - 1) * 7); 1184 resolved = resolved.plusDays(days); 1185 return cast(Temporal) temporal._with(resolved); 1186 } 1187 1188 override 1189 string getDisplayName(Locale locale) 1190 { 1191 assert(locale, "locale"); 1192 return toString(); 1193 } 1194 1195 override public string toString() 1196 { 1197 return "WeekBasedYear"; 1198 } 1199 1200 override TemporalAccessor resolve(Map!(TemporalField, Long) fieldValues, 1201 TemporalAccessor partialTemporal, ResolverStyle resolverStyle) 1202 { 1203 return null; 1204 } 1205 1206 override int opCmp(TemporalField obj) 1207 { 1208 if (cast(Field)(obj) !is null) 1209 { 1210 Field other = cast(Field) obj; 1211 return compare(toString(), other.toString()); 1212 } 1213 return 0; 1214 } 1215 }`)); 1216 // } 1217 1218 override public bool isDateBased() 1219 { 1220 return true; 1221 } 1222 1223 override public bool isTimeBased() 1224 { 1225 return false; 1226 } 1227 1228 override public ValueRange rangeRefinedBy(TemporalAccessor temporal) 1229 { 1230 return range(); 1231 } 1232 1233 //------------------------------------------------------------------------- 1234 enum int[] QUARTER_DAYS = [0, 90, 181, 273, 0, 91, 182, 274]; 1235 1236 static void ensureIso(TemporalAccessor temporal) 1237 { 1238 if (isIso(temporal) == false) 1239 { 1240 throw new DateTimeException("Resolve requires IsoChronology"); 1241 } 1242 } 1243 1244 static ValueRange getWeekRange(LocalDate date) 1245 { 1246 int wby = getWeekBasedYear(date); 1247 return ValueRange.of(1, getWeekRange(wby)); 1248 } 1249 1250 static int getWeekRange(int wby) 1251 { 1252 LocalDate date = LocalDate.of(wby, 1, 1); 1253 // 53 weeks if standard year starts on Thursday, or Wed _in a leap year 1254 if (date.getDayOfWeek() == DayOfWeek.THURSDAY 1255 || (date.getDayOfWeek() == DayOfWeek.WEDNESDAY && date.isLeapYear())) 1256 { 1257 return 53; 1258 } 1259 return 52; 1260 } 1261 1262 static int getWeek(LocalDate date) 1263 { 1264 int dow0 = date.getDayOfWeek().ordinal(); 1265 int doy0 = date.getDayOfYear() - 1; 1266 int doyThu0 = doy0 + (3 - dow0); // adjust to mid-week Thursday (which is 3 indexed from zero) 1267 int alignedWeek = doyThu0 / 7; 1268 int firstThuDoy0 = doyThu0 - (alignedWeek * 7); 1269 int firstMonDoy0 = firstThuDoy0 - 3; 1270 if (firstMonDoy0 < -3) 1271 { 1272 firstMonDoy0 += 7; 1273 } 1274 if (doy0 < firstMonDoy0) 1275 { 1276 return cast(int) getWeekRange(date.withDayOfYear(180).minusYears(1)).getMaximum(); 1277 } 1278 int week = ((doy0 - firstMonDoy0) / 7) + 1; 1279 if (week == 53) 1280 { 1281 if ((firstMonDoy0 == -3 || (firstMonDoy0 == -2 && date.isLeapYear())) == false) 1282 { 1283 week = 1; 1284 } 1285 } 1286 return week; 1287 } 1288 1289 static int getWeekBasedYear(LocalDate date) 1290 { 1291 int year = date.getYear(); 1292 int doy = date.getDayOfYear(); 1293 if (doy <= 3) 1294 { 1295 int dow = date.getDayOfWeek().ordinal(); 1296 if (doy - dow < -2) 1297 { 1298 year--; 1299 } 1300 } 1301 else if (doy >= 363) 1302 { 1303 int dow = date.getDayOfWeek().ordinal(); 1304 doy = doy - 363 - (date.isLeapYear() ? 1 : 0); 1305 if (doy - dow >= 0) 1306 { 1307 year++; 1308 } 1309 } 1310 return year; 1311 } 1312 } 1313 1314 //----------------------------------------------------------------------- 1315 /** 1316 * Implementation of the unit. 1317 */ 1318 static class Unit : TemporalUnit 1319 { 1320 1321 /** 1322 * Unit that represents the concept of a week-based-year. 1323 */ 1324 // static Unit WEEK_BASED_YEARS; 1325 /** 1326 * Unit that represents the concept of a quarter-year. 1327 */ 1328 // static Unit QUARTER_YEARS; 1329 1330 // shared static this() 1331 // { 1332 // WEEK_BASED_YEARS = new Unit("WeekBasedYears", Duration.ofSeconds(31556952L)); 1333 mixin(MakeGlobalVar!(Unit)("WEEK_BASED_YEARS",`new Unit("WeekBasedYears", 0, Duration.ofSeconds(31556952L))`)); 1334 // QUARTER_YEARS = new Unit("QuarterYears", Duration.ofSeconds(31556952L / 4)); 1335 mixin(MakeGlobalVar!(Unit)("QUARTER_YEARS",`new Unit("QuarterYears", 1, Duration.ofSeconds(31556952L / 4))`)); 1336 1337 // } 1338 1339 // private string name; 1340 private Duration duration; 1341 1342 protected this(string name, int ordinal, Duration estimatedDuration) 1343 { 1344 super(name, ordinal); 1345 this.duration = estimatedDuration; 1346 } 1347 1348 override public Duration getDuration() 1349 { 1350 return duration; 1351 } 1352 1353 override public bool isDurationEstimated() 1354 { 1355 return true; 1356 } 1357 1358 override public bool isDateBased() 1359 { 1360 return true; 1361 } 1362 1363 override public bool isTimeBased() 1364 { 1365 return false; 1366 } 1367 1368 override public bool isSupportedBy(Temporal temporal) 1369 { 1370 return temporal.isSupported((ChronoField.EPOCH_DAY)) && isIso(temporal); 1371 } 1372 1373 /*@SuppressWarnings("unchecked")*/ 1374 override public Temporal addTo(Temporal temporal, long amount) /* if (is(R : Temporal)) */ 1375 { 1376 auto name = this.toString(); 1377 { 1378 if(name ==Unit.WEEK_BASED_YEARS.toString) 1379 return cast(Temporal) temporal._with(WEEK_BASED_YEAR, 1380 MathHelper.addExact(temporal.get(WEEK_BASED_YEAR), amount)); 1381 if(name ==Unit.QUARTER_YEARS.toString) 1382 return cast(Temporal) temporal.plus(amount / 4, ChronoUnit.YEARS) 1383 .plus((amount % 4) * 3, ChronoUnit.MONTHS); 1384 throw new IllegalStateException("Unreachable"); 1385 } 1386 } 1387 1388 override public long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) 1389 { 1390 if (typeid(temporal1Inclusive) != typeid(temporal2Exclusive)) 1391 { 1392 return temporal1Inclusive.until(temporal2Exclusive, this); 1393 } 1394 auto name = this.toString(); 1395 { 1396 if (name == WEEK_BASED_YEARS.toString) 1397 return MathHelper.subtractExact(temporal2Exclusive.getLong(WEEK_BASED_YEAR), 1398 temporal1Inclusive.getLong(WEEK_BASED_YEAR)); 1399 if (name == QUARTER_YEARS.toString) 1400 return temporal1Inclusive.until(temporal2Exclusive, ChronoUnit.MONTHS) / 3; 1401 1402 throw new IllegalStateException("Unreachable"); 1403 } 1404 } 1405 1406 // override public string toString() 1407 // { 1408 // return name; 1409 // } 1410 1411 } 1412 1413 static bool isIso(TemporalAccessor temporal) 1414 { 1415 return Chronology.from(temporal) == (IsoChronology.INSTANCE); 1416 } 1417 }