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.LocalDate;
13 
14 import hunt.time.LocalTime;
15 
16 import hunt.stream.DataInput;
17 import hunt.stream.DataOutput;
18 import hunt.Exceptions;
19 import hunt.Long;
20 import hunt.stream.Common;
21 import hunt.time.chrono.ChronoLocalDate;
22 import hunt.time.chrono.IsoEra;
23 import hunt.time.chrono.IsoChronology;
24 // import hunt.time.format.DateTimeFormatter;
25 import hunt.time.format.DateTimeParseException;
26 import hunt.time.temporal.ChronoField;
27 import hunt.time.temporal.ChronoUnit;
28 import hunt.time.temporal.Temporal;
29 import hunt.time.temporal.TemporalAccessor;
30 import hunt.time.temporal.TemporalAdjuster;
31 import hunt.time.temporal.TemporalAmount;
32 import hunt.time.temporal.TemporalField;
33 import hunt.time.temporal.TemporalQueries;
34 import hunt.time.temporal.TemporalQuery;
35 import hunt.time.temporal.TemporalUnit;
36 import hunt.time.Exceptions;
37 import hunt.time.temporal.ValueRange;
38 import hunt.time.zone.ZoneOffsetTransition;
39 import hunt.time.zone.ZoneRules;
40 import hunt.time.ZoneId;
41 import hunt.time.Clock;
42 import hunt.time.Month;
43 import hunt.time.Instant;
44 import hunt.time.DayOfWeek;
45 import hunt.time.Period;
46 import hunt.time.LocalDateTime;
47 import hunt.time.OffsetDateTime;
48 import hunt.time.OffsetTime;
49 import hunt.time.ZonedDateTime;
50 import hunt.time.ZoneOffset;
51 import hunt.time.Year;
52 import hunt.time.Exceptions;
53 import hunt.util.StringBuilder;
54 import hunt.math.Helper;
55 import hunt.time.Ser;
56 import hunt.util.Comparator;
57 import hunt.time.chrono.ChronoLocalDateTime;
58 import hunt.time.chrono.ChronoLocalDateTimeImpl;
59 import hunt.time.util.QueryHelper;
60 import hunt.logging;
61 // import hunt.time.util.Common;
62 
63 // import hunt.util.stream.LongStream;
64 // import hunt.util.stream.Stream;
65 
66 import hunt.util.Common;
67 // import hunt.serialization.JsonSerializer;
68 
69 import std.algorithm.comparison;
70 import std.conv;
71 import std.concurrency : initOnce;
72 
73 /**
74  * A date without a time-zone _in the ISO-8601 calendar system,
75  * such as {@code 2007-12-03}.
76  * !(p)
77  * {@code LocalDate} is an immutable date-time object that represents a date,
78  * often viewed as year-month-day. Other date fields, such as day-of-year,
79  * day-of-week and week-of-year, can also be accessed.
80  * For example, the value "2nd October 2007" can be stored _in a {@code LocalDate}.
81  * !(p)
82  * This class does not store or represent a time or time-zone.
83  * Instead, it is a description of the date, as used for birthdays.
84  * It cannot represent an instant on the time-line without additional information
85  * such as an offset or time-zone.
86  * !(p)
87  * The ISO-8601 calendar system is the modern civil calendar system used today
88  * _in most of the world. It is equivalent to the proleptic Gregorian calendar
89  * system, _in which today's rules for leap years are applied for all time.
90  * For most applications written today, the ISO-8601 rules are entirely suitable.
91  * However, any application that makes use of historical dates, and requires them
92  * to be accurate will find the ISO-8601 approach unsuitable.
93  *
94  * !(p)
95  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
96  * class; use of identity-sensitive operations (including reference equality
97  * ({@code ==}), identity hash code, or synchronization) on instances of
98  * {@code LocalDate} may have unpredictable results and should be avoided.
99  * The {@code equals} method should be used for comparisons.
100  *
101  * @implSpec
102  * This class is immutable and thread-safe.
103  *
104  * @since 1.8
105  */
106 
107 
108 public  class LocalDate
109         : Temporal, TemporalAdjuster, ChronoLocalDate { // , Serializable
110 
111     /**
112      * The minimum supported {@code LocalDate}, '-999999999-01-01'.
113      * This could be used by an application as a "far past" date.
114      */
115     static LocalDate MIN() {
116         __gshared LocalDate _MIN;
117         return initOnce!(_MIN)(LocalDate.of(Year.MIN_VALUE, 1, 1));
118     }
119 
120     /**
121      * The maximum supported {@code LocalDate}, '+999999999-12-31'.
122      * This could be used by an application as a "far future" date.
123      */
124     static LocalDate MAX() {
125         __gshared LocalDate _MAX;
126         return initOnce!(_MAX)(LocalDate.of(Year.MAX_VALUE, 1, 1));
127     }
128     
129     /**
130      * The epoch year {@code LocalDate}, '1970-01-01'.
131      */
132     static LocalDate EPOCH() {
133         __gshared LocalDate _EPOCH;
134         return initOnce!(_EPOCH)(LocalDate.of(1970, 1, 1));
135     }
136 
137     /**
138      * The number of days _in a 400 year cycle.
139      */
140     private enum int DAYS_PER_CYCLE = 146097;
141     /**
142      * The number of days from year zero to year 1970.
143      * There are five 400 year cycles from year zero to 2000.
144      * There are 7 leap years from 1970 to 2000.
145      */
146     enum long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);
147 
148     /**
149      * The year.
150      */
151     private  int year;
152     /**
153      * The month-of-year.
154      */
155     private  short month;
156     /**
157      * The day-of-month.
158      */
159     private  short day;
160 
161     //-----------------------------------------------------------------------
162     /**
163      * Obtains the current date from the system clock _in the default time-zone.
164      * !(p)
165      * This will query the {@link Clock#systemDefaultZone() system clock} _in the default
166      * time-zone to obtain the current date.
167      * !(p)
168      * Using this method will prevent the ability to use an alternate clock for testing
169      * because the clock is hard-coded.
170      *
171      * @return the current date using the system clock and default time-zone, not null
172      */
173     public static LocalDate now() {
174         return now(Clock.systemDefaultZone());
175     }
176 
177     /**
178      * Obtains the current date from the system clock _in the specified time-zone.
179      * !(p)
180      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
181      * Specifying the time-zone avoids dependence on the default time-zone.
182      * !(p)
183      * Using this method will prevent the ability to use an alternate clock for testing
184      * because the clock is hard-coded.
185      *
186      * @param zone  the zone ID to use, not null
187      * @return the current date using the system clock, not null
188      */
189     public static LocalDate now(ZoneId zone) {
190         return now(Clock.system(zone));
191     }
192 
193     /**
194      * Obtains the current date from the specified clock.
195      * !(p)
196      * This will query the specified clock to obtain the current date - today.
197      * Using this method allows the use of an alternate clock for testing.
198      * The alternate clock may be introduced using {@link Clock dependency injection}.
199      *
200      * @param clock  the clock to use, not null
201      * @return the current date, not null
202      */
203     public static LocalDate now(Clock clock) {
204         assert(clock, "clock");
205         Instant now = clock.instant();  // called once
206         return ofInstant(now, clock.getZone());
207     }
208 
209     //-----------------------------------------------------------------------
210     /**
211      * Obtains an instance of {@code LocalDate} from a year, month and day.
212      * !(p)
213      * This returns a {@code LocalDate} with the specified year, month and day-of-month.
214      * The day must be valid for the year and month, otherwise an exception will be thrown.
215      *
216      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
217      * @param month  the month-of-year to represent, not null
218      * @param dayOfMonth  the day-of-month to represent, from 1 to 31
219      * @return the local date, not null
220      * @throws DateTimeException if the value of any field is _out of range,
221      *  or if the day-of-month is invalid for the month-year
222      */
223     public static LocalDate of(int year, Month month, int dayOfMonth) {
224         ChronoField.YEAR.checkValidValue(year);
225         assert(month, "month");
226         ChronoField.DAY_OF_MONTH.checkValidValue(dayOfMonth);
227         return create(year, month.getValue(), dayOfMonth);
228     }
229 
230     /**
231      * Obtains an instance of {@code LocalDate} from a year, month and day.
232      * !(p)
233      * This returns a {@code LocalDate} with the specified year, month and day-of-month.
234      * The day must be valid for the year and month, otherwise an exception will be thrown.
235      *
236      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
237      * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
238      * @param dayOfMonth  the day-of-month to represent, from 1 to 31
239      * @return the local date, not null
240      * @throws DateTimeException if the value of any field is _out of range,
241      *  or if the day-of-month is invalid for the month-year
242      */
243     public static LocalDate of(int year, int month, int dayOfMonth) {
244         if(ChronoField.YEAR is null)
245         {
246             error("ChronoField.YEAR is null");
247         }
248         ChronoField.YEAR.checkValidValue(year);
249         ChronoField.MONTH_OF_YEAR.checkValidValue(month);
250         ChronoField.DAY_OF_MONTH.checkValidValue(dayOfMonth);
251         return create(year, month, dayOfMonth);
252     }
253 
254     //-----------------------------------------------------------------------
255     /**
256      * Obtains an instance of {@code LocalDate} from a year and day-of-year.
257      * !(p)
258      * This returns a {@code LocalDate} with the specified year and day-of-year.
259      * The day-of-year must be valid for the year, otherwise an exception will be thrown.
260      *
261      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
262      * @param dayOfYear  the day-of-year to represent, from 1 to 366
263      * @return the local date, not null
264      * @throws DateTimeException if the value of any field is _out of range,
265      *  or if the day-of-year is invalid for the year
266      */
267     public static LocalDate ofYearDay(int year, int dayOfYear) {
268         ChronoField.YEAR.checkValidValue(year);
269         ChronoField.DAY_OF_YEAR.checkValidValue(dayOfYear);
270         bool leap = IsoChronology.INSTANCE.isLeapYear(year);
271         if (dayOfYear == 366 && leap == false) {
272             throw new DateTimeException("Invalid date 'DayOfYear 366' as '" ~ year.to!string ~ "' is not a leap year");
273         }
274         Month moy = Month.of((dayOfYear - 1) / 31 + 1);
275         int monthEnd = moy.firstDayOfYear(leap) + moy.length(leap) - 1;
276         if (dayOfYear > monthEnd) {
277             moy = moy.plus(1);
278         }
279         int dom = dayOfYear - moy.firstDayOfYear(leap) + 1;
280         return new LocalDate(year, moy.getValue(), dom);
281     }
282 
283     //-----------------------------------------------------------------------
284     /**
285      * Obtains an instance of {@code LocalDate} from an {@code Instant} and zone ID.
286      * !(p)
287      * This creates a local date based on the specified instant.
288      * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
289      * which is simple as there is only one valid offset for each instant.
290      * Then, the instant and offset are used to calculate the local date.
291      *
292      * @param instant  the instant to create the date from, not null
293      * @param zone  the time-zone, which may be an offset, not null
294      * @return the local date, not null
295      * @throws DateTimeException if the result exceeds the supported range
296      * @since 9
297      */
298     public static LocalDate ofInstant(Instant instant, ZoneId zone) {
299         assert(instant, "instant");
300         assert(zone, "zone");
301         ZoneRules rules = zone.getRules();
302         ZoneOffset offset = rules.getOffset(instant);
303         long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
304         long localEpochDay = MathHelper.floorDiv(localSecond , LocalTime.SECONDS_PER_DAY);
305         return ofEpochDay(localEpochDay);
306     }
307 
308     //-----------------------------------------------------------------------
309     /**
310      * Obtains an instance of {@code LocalDate} from the epoch day count.
311      * !(p)
312      * This returns a {@code LocalDate} with the specified epoch-day.
313      * The {@link ChronoField#EPOCH_DAY EPOCH_DAY} is a simple incrementing count
314      * of days where day 0 is 1970-01-01. Negative numbers represent earlier days.
315      *
316      * @param epochDay  the Epoch Day to convert, based on the epoch 1970-01-01
317      * @return the local date, not null
318      * @throws DateTimeException if the epoch day exceeds the supported date range
319      */
320     public static LocalDate ofEpochDay(long epochDay) {
321         ChronoField.EPOCH_DAY.checkValidValue(epochDay);
322         long zeroDay = epochDay + DAYS_0000_TO_1970;
323         // find the march-based year
324         zeroDay -= 60;  // adjust to 0000-03-01 so leap day is at end of four year cycle
325         long adjust = 0;
326         if (zeroDay < 0) {
327             // adjust negative years to positive for calculation
328             long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
329             adjust = adjustCycles * 400;
330             zeroDay += -adjustCycles * DAYS_PER_CYCLE;
331         }
332         long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
333         long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
334         if (doyEst < 0) {
335             // fix estimate
336             yearEst--;
337             doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
338         }
339         yearEst += adjust;  // reset any negative year
340         int marchDoy0 = cast(int) doyEst;
341 
342         // convert march-based values back to january-based
343         int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
344         int month = (marchMonth0 + 2) % 12 + 1;
345         int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1;
346         yearEst += marchMonth0 / 10;
347 
348         // check year now we are certain it is correct
349         int year = ChronoField.YEAR.checkValidIntValue(yearEst);
350         return new LocalDate(year, month, dom);
351     }
352 
353     //-----------------------------------------------------------------------
354     /**
355      * Obtains an instance of {@code LocalDate} from a temporal object.
356      * !(p)
357      * This obtains a local date based on the specified temporal.
358      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
359      * which this factory converts to an instance of {@code LocalDate}.
360      * !(p)
361      * The conversion uses the {@link TemporalQueries#localDate()} query, which relies
362      * on extracting the {@link ChronoField#EPOCH_DAY EPOCH_DAY} field.
363      * !(p)
364      * This method matches the signature of the functional interface {@link TemporalQuery}
365      * allowing it to be used as a query via method reference, {@code LocalDate.from}.
366      *
367      * @param temporal  the temporal object to convert, not null
368      * @return the local date, not null
369      * @throws DateTimeException if unable to convert to a {@code LocalDate}
370      */
371     public static LocalDate from(TemporalAccessor temporal) {
372         assert(temporal !is null, "temporal is null");
373         LocalDate date = queryFrom(temporal); // QueryHelper.query!LocalDate(temporal, TemporalQueries.localDate());
374         if (date is null) {
375             throw new DateTimeException("Unable to obtain LocalDate from TemporalAccessor: " ~
376                     (cast(Object)temporal).stringof ~ " of type " ~ typeid(cast(Object)temporal).name);
377         }
378         return date;
379     }
380 
381     private static LocalDate queryFrom(TemporalAccessor temporal) {
382         if (temporal.isSupported(ChronoField.EPOCH_DAY)) {
383                 return LocalDate.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
384         }
385         return null;
386     }
387 
388     //-----------------------------------------------------------------------
389     /**
390      * Obtains an instance of {@code LocalDate} from a text string such as {@code 2007-12-03}.
391      * !(p)
392      * The string must represent a valid date and is parsed using
393      * {@link hunt.time.format.DateTimeFormatter#ISO_LOCAL_DATE}.
394      *
395      * @param text  the text to parse such as "2007-12-03", not null
396      * @return the parsed local date, not null
397      * @throws DateTimeParseException if the text cannot be parsed
398      */
399     // public static LocalDate parse(string text) {
400     //     return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
401     // }
402 
403     /**
404      * Obtains an instance of {@code LocalDate} from a text string using a specific formatter.
405      * !(p)
406      * The text is parsed using the formatter, returning a date.
407      *
408      * @param text  the text to parse, not null
409      * @param formatter  the formatter to use, not null
410      * @return the parsed local date, not null
411      * @throws DateTimeParseException if the text cannot be parsed
412      */
413     // public static LocalDate parse(string text, DateTimeFormatter formatter) {
414     //     assert(formatter, "formatter");
415     //     return formatter.parse(text, new class TemporalQuery!LocalDate{
416     //         LocalDate queryFrom(TemporalAccessor temporal)
417     //         {
418     //             assert(temporal, "temporal");
419     //             LocalDate date = QueryHelper.query!LocalDate(temporal , TemporalQueries.localDate());
420     //             if (date is null) {
421     //                 throw new DateTimeException("Unable to obtain LocalDate from TemporalAccessor: " ~
422     //                         typeid(temporal).stringof ~ " of type " ~ typeid(temporal).stringof);
423     //             }
424     //             return date;
425     //         }
426     //     });
427             
428     // }
429 
430     //-----------------------------------------------------------------------
431     /**
432      * Creates a local date from the year, month and day fields.
433      *
434      * @param year  the year to represent, validated from MIN_YEAR to MAX_YEAR
435      * @param month  the month-of-year to represent, from 1 to 12, validated
436      * @param dayOfMonth  the day-of-month to represent, validated from 1 to 31
437      * @return the local date, not null
438      * @throws DateTimeException if the day-of-month is invalid for the month-year
439      */
440     private static LocalDate create(int year, int month, int dayOfMonth) {
441         if (dayOfMonth > 28) {
442             int dom = 31;
443             switch (month) {
444                 case 2:
445                     dom = (IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
446                     break;
447                 case 4:
448                 case 6:
449                 case 9:
450                 case 11:
451                     dom = 30;
452                     break;
453                 default:
454                     break;
455             }
456             if (dayOfMonth > dom) {
457                 if (dayOfMonth == 29) {
458                     throw new DateTimeException("Invalid date 'February 29' as '" ~ year.to!string ~ "' is not a leap year");
459                 } else {
460                     throw new DateTimeException("Invalid date '" ~ Month.of(month).name() ~ " " ~ dayOfMonth.to!string ~ "'");
461                 }
462             }
463         }
464         return new LocalDate(year, month, dayOfMonth);
465     }
466 
467     /**
468      * Resolves the date, resolving days past the end of month.
469      *
470      * @param year  the year to represent, validated from MIN_YEAR to MAX_YEAR
471      * @param month  the month-of-year to represent, validated from 1 to 12
472      * @param day  the day-of-month to represent, validated from 1 to 31
473      * @return the resolved date, not null
474      */
475     private static LocalDate resolvePreviousValid(int year, int month, int day) {
476         switch (month) {
477             case 2:
478                 day = /* MathHelper. */min(day, IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
479                 break;
480             case 4:
481             case 6:
482             case 9:
483             case 11:
484                 day = /* MathHelper. */min(day, 30);
485                 break;
486             default:break;
487         }
488         return new LocalDate(year, month, day);
489     }
490 
491     /**
492      * Constructor, previously validated.
493      *
494      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
495      * @param month  the month-of-year to represent, not null
496      * @param dayOfMonth  the day-of-month to represent, valid for year-month, from 1 to 31
497      */
498     private this(int year, int month, int dayOfMonth) {
499         this.year = year;
500         this.month = cast(short) month;
501         this.day = cast(short) dayOfMonth;
502     }
503 
504     //-----------------------------------------------------------------------
505     /**
506      * Checks if the specified field is supported.
507      * !(p)
508      * This checks if this date can be queried for the specified field.
509      * If false, then calling the {@link #range(TemporalField) range},
510      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
511      * methods will throw an exception.
512      * !(p)
513      * If the field is a {@link ChronoField} then the query is implemented here.
514      * The supported fields are:
515      * !(ul)
516      * !(li){@code DAY_OF_WEEK}
517      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_MONTH}
518      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_YEAR}
519      * !(li){@code DAY_OF_MONTH}
520      * !(li){@code DAY_OF_YEAR}
521      * !(li){@code EPOCH_DAY}
522      * !(li){@code ALIGNED_WEEK_OF_MONTH}
523      * !(li){@code ALIGNED_WEEK_OF_YEAR}
524      * !(li){@code MONTH_OF_YEAR}
525      * !(li){@code PROLEPTIC_MONTH}
526      * !(li){@code YEAR_OF_ERA}
527      * !(li){@code YEAR}
528      * !(li){@code ERA}
529      * </ul>
530      * All other {@code ChronoField} instances will return false.
531      * !(p)
532      * If the field is not a {@code ChronoField}, then the result of this method
533      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
534      * passing {@code this} as the argument.
535      * Whether the field is supported is determined by the field.
536      *
537      * @param field  the field to check, null returns false
538      * @return true if the field is supported on this date, false if not
539      */
540     override  // override for Javadoc
541     public bool isSupported(TemporalField field) {
542         return /* ChronoLocalDate. super.*/super_isSupported(field);
543     }
544     bool super_isSupported(TemporalField field) {
545         if (cast(ChronoField)(field) !is null) {
546             return field.isDateBased();
547         }
548         return field !is null && field.isSupportedBy(this);
549     }
550     /**
551      * Checks if the specified unit is supported.
552      * !(p)
553      * This checks if the specified unit can be added to, or subtracted from, this date.
554      * If false, then calling the {@link #plus(long, TemporalUnit)} and
555      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
556      * !(p)
557      * If the unit is a {@link ChronoUnit} then the query is implemented here.
558      * The supported units are:
559      * !(ul)
560      * !(li){@code DAYS}
561      * !(li){@code WEEKS}
562      * !(li){@code MONTHS}
563      * !(li){@code YEARS}
564      * !(li){@code DECADES}
565      * !(li){@code CENTURIES}
566      * !(li){@code MILLENNIA}
567      * !(li){@code ERAS}
568      * </ul>
569      * All other {@code ChronoUnit} instances will return false.
570      * !(p)
571      * If the unit is not a {@code ChronoUnit}, then the result of this method
572      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
573      * passing {@code this} as the argument.
574      * Whether the unit is supported is determined by the unit.
575      *
576      * @param unit  the unit to check, null returns false
577      * @return true if the unit can be added/subtracted, false if not
578      */
579     override  // override for Javadoc
580     public bool isSupported(TemporalUnit unit) {
581         return /* ChronoLocalDate.super. */super_isSupported(unit);
582     }
583     bool super_isSupported(TemporalUnit unit) {
584         if (cast(ChronoUnit)(unit) !is null) {
585             return unit.isDateBased();
586         }
587         return unit !is null && unit.isSupportedBy(this);
588     }
589 
590     //-----------------------------------------------------------------------
591     /**
592      * Gets the range of valid values for the specified field.
593      * !(p)
594      * The range object expresses the minimum and maximum valid values for a field.
595      * This date is used to enhance the accuracy of the returned range.
596      * If it is not possible to return the range, because the field is not supported
597      * or for some other reason, an exception is thrown.
598      * !(p)
599      * If the field is a {@link ChronoField} then the query is implemented here.
600      * The {@link #isSupported(TemporalField) supported fields} will return
601      * appropriate range instances.
602      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
603      * !(p)
604      * If the field is not a {@code ChronoField}, then the result of this method
605      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
606      * passing {@code this} as the argument.
607      * Whether the range can be obtained is determined by the field.
608      *
609      * @param field  the field to query the range for, not null
610      * @return the range of valid values for the field, not null
611      * @throws DateTimeException if the range for the field cannot be obtained
612      * @throws UnsupportedTemporalTypeException if the field is not supported
613      */
614     override
615     public ValueRange range(TemporalField field) {
616         if (cast(ChronoField)(field) !is null) {
617             ChronoField f = cast(ChronoField) field;
618             if (f.isDateBased()) {
619                 auto name = f.toString;
620                 {
621                     if(name == ChronoField.DAY_OF_MONTH.toString) return ValueRange.of(1, lengthOfMonth());
622                     if(name == ChronoField.DAY_OF_YEAR.toString) return ValueRange.of(1, lengthOfYear());
623                     if(name == ChronoField.ALIGNED_WEEK_OF_MONTH.toString) return ValueRange.of(1, getMonth() == Month.FEBRUARY && isLeapYear() == false ? 4 : 5);
624                     if(name == ChronoField.YEAR_OF_ERA.toString)
625                         return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE));
626                 }
627                 return field.range();
628             }
629             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ f.toString);
630         }
631         return field.rangeRefinedBy(this);
632     }
633 
634     /**
635      * Gets the value of the specified field from this date as an {@code int}.
636      * !(p)
637      * This queries this date for the value of the specified field.
638      * The returned value will always be within the valid range of values for the field.
639      * If it is not possible to return the value, because the field is not supported
640      * or for some other reason, an exception is thrown.
641      * !(p)
642      * If the field is a {@link ChronoField} then the query is implemented here.
643      * The {@link #isSupported(TemporalField) supported fields} will return valid
644      * values based on this date, except {@code EPOCH_DAY} and {@code PROLEPTIC_MONTH}
645      * which are too large to fit _in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
646      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
647      * !(p)
648      * If the field is not a {@code ChronoField}, then the result of this method
649      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
650      * passing {@code this} as the argument. Whether the value can be obtained,
651      * and what the value represents, is determined by the field.
652      *
653      * @param field  the field to get, not null
654      * @return the value for the field
655      * @throws DateTimeException if a value for the field cannot be obtained or
656      *         the value is outside the range of valid values for the field
657      * @throws UnsupportedTemporalTypeException if the field is not supported or
658      *         the range of values exceeds an {@code int}
659      * @throws ArithmeticException if numeric overflow occurs
660      */
661     override  // override for Javadoc and performance
662     public int get(TemporalField field) {
663         if (cast(ChronoField)(field) !is null) {
664             return get0(field);
665         }
666         return /* ChronoLocalDate. super.*/super_get(field);
667     }
668      int super_get(TemporalField field) {
669         ValueRange range = range(field);
670         if (range.isIntValue() == false) {
671             throw new UnsupportedTemporalTypeException("Invalid field " ~ range.toString ~ " for get() method, use getLong() instead");
672         }
673         long value = getLong(field);
674         if (range.isValidValue(value) == false) {
675             throw new DateTimeException("Invalid value for " ~ typeid(field).stringof ~ " (valid values " ~ range.toString ~ "): " ~ value.to!string);
676         }
677         return cast(int) value;
678     }
679 
680     /**
681      * Gets the value of the specified field from this date as a {@code long}.
682      * !(p)
683      * This queries this date for the value of the specified field.
684      * If it is not possible to return the value, because the field is not supported
685      * or for some other reason, an exception is thrown.
686      * !(p)
687      * If the field is a {@link ChronoField} then the query is implemented here.
688      * The {@link #isSupported(TemporalField) supported fields} will return valid
689      * values based on this date.
690      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
691      * !(p)
692      * If the field is not a {@code ChronoField}, then the result of this method
693      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
694      * passing {@code this} as the argument. Whether the value can be obtained,
695      * and what the value represents, is determined by the field.
696      *
697      * @param field  the field to get, not null
698      * @return the value for the field
699      * @throws DateTimeException if a value for the field cannot be obtained
700      * @throws UnsupportedTemporalTypeException if the field is not supported
701      * @throws ArithmeticException if numeric overflow occurs
702      */
703     override
704     public long getLong(TemporalField field) {
705         if (cast(ChronoField)(field) !is null) {
706             if (field == ChronoField.EPOCH_DAY) {
707                 return toEpochDay();
708             }
709             if (field == ChronoField.PROLEPTIC_MONTH) {
710                 return getProlepticMonth();
711             }
712             return get0(field);
713         }
714         return field.getFrom(this);
715     }
716 
717     private int get0(TemporalField field) {
718         auto f = cast(ChronoField) field;
719          {
720             if(f ==  ChronoField.DAY_OF_WEEK) return getDayOfWeek().getValue();
721             if(f ==  ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH) return ((day - 1) % 7) + 1;
722             if(f ==  ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR) return ((getDayOfYear() - 1) % 7) + 1;
723             if(f ==  ChronoField.DAY_OF_MONTH) return day;
724             if(f ==  ChronoField.DAY_OF_YEAR) return getDayOfYear();
725             if(f ==  ChronoField.EPOCH_DAY) throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
726             if(f ==  ChronoField.ALIGNED_WEEK_OF_MONTH) return ((day - 1) / 7) + 1;
727             if(f ==  ChronoField.ALIGNED_WEEK_OF_YEAR) return ((getDayOfYear() - 1) / 7) + 1;
728             if(f ==  ChronoField.MONTH_OF_YEAR) return month;
729             if(f ==  ChronoField.PROLEPTIC_MONTH) throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
730             if(f ==  ChronoField.YEAR_OF_ERA) return (year >= 1 ? year : 1 - year);
731             if(f ==  ChronoField.YEAR) return year;
732             if(f ==  ChronoField.ERA) return (year >= 1 ? 1 : 0);
733         }
734         throw new UnsupportedTemporalTypeException("Unsupported field: " ~ typeid(field).stringof);
735     }
736 
737     private long getProlepticMonth() {
738         return (year * 12L + month - 1);
739     }
740 
741     //-----------------------------------------------------------------------
742     /**
743      * Gets the chronology of this date, which is the ISO calendar system.
744      * !(p)
745      * The {@code Chronology} represents the calendar system _in use.
746      * The ISO-8601 calendar system is the modern civil calendar system used today
747      * _in most of the world. It is equivalent to the proleptic Gregorian calendar
748      * system, _in which today's rules for leap years are applied for all time.
749      *
750      * @return the ISO chronology, not null
751      */
752     override
753     public IsoChronology getChronology() {
754         return IsoChronology.INSTANCE;
755     }
756 
757     /**
758      * Gets the era applicable at this date.
759      * !(p)
760      * The official ISO-8601 standard does not define eras, however {@code IsoChronology} does.
761      * It defines two eras, 'CE' from year one onwards and 'BCE' from year zero backwards.
762      * Since dates before the Julian-Gregorian cutover are not _in line with history,
763      * the cutover between 'BCE' and 'CE' is also not aligned with the commonly used
764      * eras, often referred to using 'BC' and 'AD'.
765      * !(p)
766      * Users of this class should typically ignore this method as it exists primarily
767      * to fulfill the {@link ChronoLocalDate} contract where it is necessary to support
768      * the Japanese calendar system.
769      *
770      * @return the IsoEra applicable at this date, not null
771      */
772     override // override for Javadoc
773     public IsoEra getEra() {
774         return (getYear() >= 1 ? IsoEra.CE : IsoEra.BCE);
775     }
776 
777     /**
778      * Gets the year field.
779      * !(p)
780      * This method returns the primitive {@code int} value for the year.
781      * !(p)
782      * The year returned by this method is proleptic as per {@code get(YEAR)}.
783      * To obtain the year-of-era, use {@code get(YEAR_OF_ERA)}.
784      *
785      * @return the year, from MIN_YEAR to MAX_YEAR
786      */
787     public int getYear() nothrow {
788         return year;
789     }
790 
791     /**
792      * Gets the month-of-year field from 1 to 12.
793      * !(p)
794      * This method returns the month as an {@code int} from 1 to 12.
795      * Application code is frequently clearer if the enum {@link Month}
796      * is used by calling {@link #getMonth()}.
797      *
798      * @return the month-of-year, from 1 to 12
799      * @see #getMonth()
800      */
801     public int getMonthValue() nothrow {
802         return month;
803     }
804 
805     /**
806      * Gets the month-of-year field using the {@code Month} enum.
807      * !(p)
808      * This method returns the enum {@link Month} for the month.
809      * This avoids confusion as to what {@code int} values mean.
810      * If you need access to the primitive {@code int} value then the enum
811      * provides the {@link Month#getValue() int value}.
812      *
813      * @return the month-of-year, not null
814      * @see #getMonthValue()
815      */
816     public Month getMonth() nothrow {
817         try {
818             return Month.of(month);
819         } catch (Exception) {
820             return null;
821         }
822     }
823 
824     /**
825      * Gets the day-of-month field.
826      * !(p)
827      * This method returns the primitive {@code int} value for the day-of-month.
828      *
829      * @return the day-of-month, from 1 to 31
830      */
831     public int getDayOfMonth() nothrow {
832         return day;
833     }
834 
835     /**
836      * Gets the day-of-year field.
837      * !(p)
838      * This method returns the primitive {@code int} value for the day-of-year.
839      *
840      * @return the day-of-year, from 1 to 365, or 366 _in a leap year
841      */
842     public int getDayOfYear() {
843         return getMonth().firstDayOfYear(isLeapYear()) + day - 1;
844     }
845 
846     /**
847      * Gets the day-of-week field, which is an enum {@code DayOfWeek}.
848      * !(p)
849      * This method returns the enum {@link DayOfWeek} for the day-of-week.
850      * This avoids confusion as to what {@code int} values mean.
851      * If you need access to the primitive {@code int} value then the enum
852      * provides the {@link DayOfWeek#getValue() int value}.
853      * !(p)
854      * Additional information can be obtained from the {@code DayOfWeek}.
855      * This includes textual names of the values.
856      *
857      * @return the day-of-week, not null
858      */
859     public DayOfWeek getDayOfWeek() {
860         int dow0 = MathHelper.floorMod((toEpochDay() + 3), 7);
861         return DayOfWeek.of(dow0 + 1);
862     }
863 
864     //-----------------------------------------------------------------------
865     /**
866      * Checks if the year is a leap year, according to the ISO proleptic
867      * calendar system rules.
868      * !(p)
869      * This method applies the current rules for leap years across the whole time-line.
870      * In general, a year is a leap year if it is divisible by four without
871      * remainder. However, years divisible by 100, are not leap years, with
872      * the exception of years divisible by 400 which are.
873      * !(p)
874      * For example, 1904 is a leap year it is divisible by 4.
875      * 1900 was not a leap year as it is divisible by 100, however 2000 was a
876      * leap year as it is divisible by 400.
877      * !(p)
878      * The calculation is proleptic - applying the same rules into the far future and far past.
879      * This is historically inaccurate, but is correct for the ISO-8601 standard.
880      *
881      * @return true if the year is leap, false otherwise
882      */
883     override // override for Javadoc and performance
884     public bool isLeapYear() {
885         return IsoChronology.INSTANCE.isLeapYear(year);
886     }
887 
888     /**
889      * Returns the length of the month represented by this date.
890      * !(p)
891      * This returns the length of the month _in days.
892      * For example, a date _in January would return 31.
893      *
894      * @return the length of the month _in days
895      */
896     override
897     public int lengthOfMonth() {
898         switch (month) {
899             case 2:
900                 return (isLeapYear() ? 29 : 28);
901             case 4:
902             case 6:
903             case 9:
904             case 11:
905                 return 30;
906             default:
907                 return 31;
908         }
909     }
910 
911     /**
912      * Returns the length of the year represented by this date.
913      * !(p)
914      * This returns the length of the year _in days, either 365 or 366.
915      *
916      * @return 366 if the year is leap, 365 otherwise
917      */
918     override // override for Javadoc and performance
919     public int lengthOfYear() {
920         return (isLeapYear() ? 366 : 365);
921     }
922 
923     //-----------------------------------------------------------------------
924     /**
925      * Returns an adjusted copy of this date.
926      * !(p)
927      * This returns a {@code LocalDate}, based on this one, with the date adjusted.
928      * The adjustment takes place using the specified adjuster strategy object.
929      * Read the documentation of the adjuster to understand what adjustment will be made.
930      * !(p)
931      * A simple adjuster might simply set the one of the fields, such as the year field.
932      * A more complex adjuster might set the date to the last day of the month.
933      * !(p)
934      * A selection of common adjustments is provided _in
935      * {@link hunt.time.temporal.TemporalAdjusters TemporalAdjusters}.
936      * These include finding the "last day of the month" and "next Wednesday".
937      * Key date-time classes also implement the {@code TemporalAdjuster} interface,
938      * such as {@link Month} and {@link hunt.time.MonthDay MonthDay}.
939      * The adjuster is responsible for handling special cases, such as the varying
940      * lengths of month and leap years.
941      * !(p)
942      * For example this code returns a date on the last day of July:
943      * !(pre)
944      *  import hunt.time.Month.*;
945      *  import hunt.time.temporal.TemporalAdjusters.*;
946      *
947      *  result = localDate._with(JULY)._with(lastDayOfMonth());
948      * </pre>
949      * !(p)
950      * The result of this method is obtained by invoking the
951      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
952      * specified adjuster passing {@code this} as the argument.
953      * !(p)
954      * This instance is immutable and unaffected by this method call.
955      *
956      * @param adjuster the adjuster to use, not null
957      * @return a {@code LocalDate} based on {@code this} with the adjustment made, not null
958      * @throws DateTimeException if the adjustment cannot be made
959      * @throws ArithmeticException if numeric overflow occurs
960      */
961     override
962     public LocalDate _with(TemporalAdjuster adjuster) {
963         // optimizations
964         if (cast(LocalDate)(adjuster) !is null) {
965             return cast(LocalDate) adjuster;
966         }
967         return cast(LocalDate) adjuster.adjustInto(this);
968     }
969 
970     /**
971      * Returns a copy of this date with the specified field set to a new value.
972      * !(p)
973      * This returns a {@code LocalDate}, based on this one, with the value
974      * for the specified field changed.
975      * This can be used to change any supported field, such as the year, month or day-of-month.
976      * If it is not possible to set the value, because the field is not supported or for
977      * some other reason, an exception is thrown.
978      * !(p)
979      * In some cases, changing the specified field can cause the resulting date to become invalid,
980      * such as changing the month from 31st January to February would make the day-of-month invalid.
981      * In cases like this, the field is responsible for resolving the date. Typically it will choose
982      * the previous valid date, which would be the last valid day of February _in this example.
983      * !(p)
984      * If the field is a {@link ChronoField} then the adjustment is implemented here.
985      * The supported fields behave as follows:
986      * !(ul)
987      * !(li){@code DAY_OF_WEEK} -
988      *  Returns a {@code LocalDate} with the specified day-of-week.
989      *  The date is adjusted up to 6 days forward or backward within the boundary
990      *  of a Monday to Sunday week.
991      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
992      *  Returns a {@code LocalDate} with the specified aligned-day-of-week.
993      *  The date is adjusted to the specified month-based aligned-day-of-week.
994      *  Aligned weeks are counted such that the first week of a given month starts
995      *  on the first day of that month.
996      *  This may cause the date to be moved up to 6 days into the following month.
997      * !(li){@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
998      *  Returns a {@code LocalDate} with the specified aligned-day-of-week.
999      *  The date is adjusted to the specified year-based aligned-day-of-week.
1000      *  Aligned weeks are counted such that the first week of a given year starts
1001      *  on the first day of that year.
1002      *  This may cause the date to be moved up to 6 days into the following year.
1003      * !(li){@code DAY_OF_MONTH} -
1004      *  Returns a {@code LocalDate} with the specified day-of-month.
1005      *  The month and year will be unchanged. If the day-of-month is invalid for the
1006      *  year and month, then a {@code DateTimeException} is thrown.
1007      * !(li){@code DAY_OF_YEAR} -
1008      *  Returns a {@code LocalDate} with the specified day-of-year.
1009      *  The year will be unchanged. If the day-of-year is invalid for the
1010      *  year, then a {@code DateTimeException} is thrown.
1011      * !(li){@code EPOCH_DAY} -
1012      *  Returns a {@code LocalDate} with the specified epoch-day.
1013      *  This completely replaces the date and is equivalent to {@link #ofEpochDay(long)}.
1014      * !(li){@code ALIGNED_WEEK_OF_MONTH} -
1015      *  Returns a {@code LocalDate} with the specified aligned-week-of-month.
1016      *  Aligned weeks are counted such that the first week of a given month starts
1017      *  on the first day of that month.
1018      *  This adjustment moves the date _in whole week chunks to match the specified week.
1019      *  The result will have the same day-of-week as this date.
1020      *  This may cause the date to be moved into the following month.
1021      * !(li){@code ALIGNED_WEEK_OF_YEAR} -
1022      *  Returns a {@code LocalDate} with the specified aligned-week-of-year.
1023      *  Aligned weeks are counted such that the first week of a given year starts
1024      *  on the first day of that year.
1025      *  This adjustment moves the date _in whole week chunks to match the specified week.
1026      *  The result will have the same day-of-week as this date.
1027      *  This may cause the date to be moved into the following year.
1028      * !(li){@code MONTH_OF_YEAR} -
1029      *  Returns a {@code LocalDate} with the specified month-of-year.
1030      *  The year will be unchanged. The day-of-month will also be unchanged,
1031      *  unless it would be invalid for the new month and year. In that case, the
1032      *  day-of-month is adjusted to the maximum valid value for the new month and year.
1033      * !(li){@code PROLEPTIC_MONTH} -
1034      *  Returns a {@code LocalDate} with the specified proleptic-month.
1035      *  The day-of-month will be unchanged, unless it would be invalid for the new month
1036      *  and year. In that case, the day-of-month is adjusted to the maximum valid value
1037      *  for the new month and year.
1038      * !(li){@code YEAR_OF_ERA} -
1039      *  Returns a {@code LocalDate} with the specified year-of-era.
1040      *  The era and month will be unchanged. The day-of-month will also be unchanged,
1041      *  unless it would be invalid for the new month and year. In that case, the
1042      *  day-of-month is adjusted to the maximum valid value for the new month and year.
1043      * !(li){@code YEAR} -
1044      *  Returns a {@code LocalDate} with the specified year.
1045      *  The month will be unchanged. The day-of-month will also be unchanged,
1046      *  unless it would be invalid for the new month and year. In that case, the
1047      *  day-of-month is adjusted to the maximum valid value for the new month and year.
1048      * !(li){@code ERA} -
1049      *  Returns a {@code LocalDate} with the specified era.
1050      *  The year-of-era and month will be unchanged. The day-of-month will also be unchanged,
1051      *  unless it would be invalid for the new month and year. In that case, the
1052      *  day-of-month is adjusted to the maximum valid value for the new month and year.
1053      * </ul>
1054      * !(p)
1055      * In all cases, if the new value is outside the valid range of values for the field
1056      * then a {@code DateTimeException} will be thrown.
1057      * !(p)
1058      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
1059      * !(p)
1060      * If the field is not a {@code ChronoField}, then the result of this method
1061      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
1062      * passing {@code this} as the argument. In this case, the field determines
1063      * whether and how to adjust the instant.
1064      * !(p)
1065      * This instance is immutable and unaffected by this method call.
1066      *
1067      * @param field  the field to set _in the result, not null
1068      * @param newValue  the new value of the field _in the result
1069      * @return a {@code LocalDate} based on {@code this} with the specified field set, not null
1070      * @throws DateTimeException if the field cannot be set
1071      * @throws UnsupportedTemporalTypeException if the field is not supported
1072      * @throws ArithmeticException if numeric overflow occurs
1073      */
1074     override
1075     public LocalDate _with(TemporalField field, long newValue) {
1076         if (cast(ChronoField)(field) !is null) {
1077             ChronoField f = cast(ChronoField) field;
1078             f.checkValidValue(newValue);
1079              {
1080                 if( f == ChronoField.DAY_OF_WEEK) return plusDays(newValue - getDayOfWeek().getValue());
1081                 if( f == ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH) return plusDays(newValue - getLong(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH));
1082                 if( f == ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR) return plusDays(newValue - getLong(ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR));
1083                 if( f == ChronoField.DAY_OF_MONTH) return withDayOfMonth(cast(int) newValue);
1084                 if( f == ChronoField.DAY_OF_YEAR) return withDayOfYear(cast(int) newValue);
1085                 if( f == ChronoField.EPOCH_DAY) return LocalDate.ofEpochDay(newValue);
1086                 if( f == ChronoField.ALIGNED_WEEK_OF_MONTH) return plusWeeks(newValue - getLong(ChronoField.ALIGNED_WEEK_OF_MONTH));
1087                 if( f == ChronoField.ALIGNED_WEEK_OF_YEAR) return plusWeeks(newValue - getLong(ChronoField.ALIGNED_WEEK_OF_YEAR));
1088                 if( f == ChronoField.MONTH_OF_YEAR) return withMonth(cast(int) newValue);
1089                 if( f == ChronoField.PROLEPTIC_MONTH) return plusMonths(newValue - getProlepticMonth());
1090                 if( f == ChronoField.YEAR_OF_ERA) return withYear(cast(int) (year >= 1 ? newValue : 1 - newValue));
1091                 if( f == ChronoField.YEAR) return withYear(cast(int) newValue);
1092                 if( f == ChronoField.ERA) return (getLong(ChronoField.ERA) == newValue ? this : withYear(1 - year));
1093             }
1094             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ f.toString);
1095         }
1096         return cast(LocalDate)(field.adjustInto(this, newValue));
1097     }
1098 
1099     //-----------------------------------------------------------------------
1100     /**
1101      * Returns a copy of this {@code LocalDate} with the year altered.
1102      * !(p)
1103      * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
1104      * !(p)
1105      * This instance is immutable and unaffected by this method call.
1106      *
1107      * @param year  the year to set _in the result, from MIN_YEAR to MAX_YEAR
1108      * @return a {@code LocalDate} based on this date with the requested year, not null
1109      * @throws DateTimeException if the year value is invalid
1110      */
1111     public LocalDate withYear(int year) {
1112         if (this.year == year) {
1113             return this;
1114         }
1115         ChronoField.YEAR.checkValidValue(year);
1116         return resolvePreviousValid(year, month, day);
1117     }
1118 
1119     /**
1120      * Returns a copy of this {@code LocalDate} with the month-of-year altered.
1121      * !(p)
1122      * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month.
1123      * !(p)
1124      * This instance is immutable and unaffected by this method call.
1125      *
1126      * @param month  the month-of-year to set _in the result, from 1 (January) to 12 (December)
1127      * @return a {@code LocalDate} based on this date with the requested month, not null
1128      * @throws DateTimeException if the month-of-year value is invalid
1129      */
1130     public LocalDate withMonth(int month) {
1131         if (this.month == month) {
1132             return this;
1133         }
1134         ChronoField.MONTH_OF_YEAR.checkValidValue(month);
1135         return resolvePreviousValid(year, month, day);
1136     }
1137 
1138     /**
1139      * Returns a copy of this {@code LocalDate} with the day-of-month altered.
1140      * !(p)
1141      * If the resulting date is invalid, an exception is thrown.
1142      * !(p)
1143      * This instance is immutable and unaffected by this method call.
1144      *
1145      * @param dayOfMonth  the day-of-month to set _in the result, from 1 to 28-31
1146      * @return a {@code LocalDate} based on this date with the requested day, not null
1147      * @throws DateTimeException if the day-of-month value is invalid,
1148      *  or if the day-of-month is invalid for the month-year
1149      */
1150     public LocalDate withDayOfMonth(int dayOfMonth) {
1151         if (this.day == dayOfMonth) {
1152             return this;
1153         }
1154         return of(year, month, dayOfMonth);
1155     }
1156 
1157     /**
1158      * Returns a copy of this {@code LocalDate} with the day-of-year altered.
1159      * !(p)
1160      * If the resulting date is invalid, an exception is thrown.
1161      * !(p)
1162      * This instance is immutable and unaffected by this method call.
1163      *
1164      * @param dayOfYear  the day-of-year to set _in the result, from 1 to 365-366
1165      * @return a {@code LocalDate} based on this date with the requested day, not null
1166      * @throws DateTimeException if the day-of-year value is invalid,
1167      *  or if the day-of-year is invalid for the year
1168      */
1169     public LocalDate withDayOfYear(int dayOfYear) {
1170         if (this.getDayOfYear() == dayOfYear) {
1171             return this;
1172         }
1173         return ofYearDay(year, dayOfYear);
1174     }
1175 
1176     //-----------------------------------------------------------------------
1177     /**
1178      * Returns a copy of this date with the specified amount added.
1179      * !(p)
1180      * This returns a {@code LocalDate}, based on this one, with the specified amount added.
1181      * The amount is typically {@link Period} but may be any other type implementing
1182      * the {@link TemporalAmount} interface.
1183      * !(p)
1184      * The calculation is delegated to the amount object by calling
1185      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
1186      * to implement the addition _in any way it wishes, however it typically
1187      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
1188      * of the amount implementation to determine if it can be successfully added.
1189      * !(p)
1190      * This instance is immutable and unaffected by this method call.
1191      *
1192      * @param amountToAdd  the amount to add, not null
1193      * @return a {@code LocalDate} based on this date with the addition made, not null
1194      * @throws DateTimeException if the addition cannot be made
1195      * @throws ArithmeticException if numeric overflow occurs
1196      */
1197     override
1198     public LocalDate plus(TemporalAmount amountToAdd) {
1199         if (cast(Period)(amountToAdd) !is null) {
1200             Period periodToAdd = cast(Period) amountToAdd;
1201             return plusMonths(periodToAdd.toTotalMonths()).plusDays(periodToAdd.getDays());
1202         }
1203         assert(amountToAdd, "amountToAdd");
1204         return cast(LocalDate) amountToAdd.addTo(this);
1205     }
1206 
1207     /**
1208      * Returns a copy of this date with the specified amount added.
1209      * !(p)
1210      * This returns a {@code LocalDate}, based on this one, with the amount
1211      * _in terms of the unit added. If it is not possible to add the amount, because the
1212      * unit is not supported or for some other reason, an exception is thrown.
1213      * !(p)
1214      * In some cases, adding the amount can cause the resulting date to become invalid.
1215      * For example, adding one month to 31st January would result _in 31st February.
1216      * In cases like this, the unit is responsible for resolving the date.
1217      * Typically it will choose the previous valid date, which would be the last valid
1218      * day of February _in this example.
1219      * !(p)
1220      * If the field is a {@link ChronoUnit} then the addition is implemented here.
1221      * The supported fields behave as follows:
1222      * !(ul)
1223      * !(li){@code DAYS} -
1224      *  Returns a {@code LocalDate} with the specified number of days added.
1225      *  This is equivalent to {@link #plusDays(long)}.
1226      * !(li){@code WEEKS} -
1227      *  Returns a {@code LocalDate} with the specified number of weeks added.
1228      *  This is equivalent to {@link #plusWeeks(long)} and uses a 7 day week.
1229      * !(li){@code MONTHS} -
1230      *  Returns a {@code LocalDate} with the specified number of months added.
1231      *  This is equivalent to {@link #plusMonths(long)}.
1232      *  The day-of-month will be unchanged unless it would be invalid for the new
1233      *  month and year. In that case, the day-of-month is adjusted to the maximum
1234      *  valid value for the new month and year.
1235      * !(li){@code YEARS} -
1236      *  Returns a {@code LocalDate} with the specified number of years added.
1237      *  This is equivalent to {@link #plusYears(long)}.
1238      *  The day-of-month will be unchanged unless it would be invalid for the new
1239      *  month and year. In that case, the day-of-month is adjusted to the maximum
1240      *  valid value for the new month and year.
1241      * !(li){@code DECADES} -
1242      *  Returns a {@code LocalDate} with the specified number of decades added.
1243      *  This is equivalent to calling {@link #plusYears(long)} with the amount
1244      *  multiplied by 10.
1245      *  The day-of-month will be unchanged unless it would be invalid for the new
1246      *  month and year. In that case, the day-of-month is adjusted to the maximum
1247      *  valid value for the new month and year.
1248      * !(li){@code CENTURIES} -
1249      *  Returns a {@code LocalDate} with the specified number of centuries added.
1250      *  This is equivalent to calling {@link #plusYears(long)} with the amount
1251      *  multiplied by 100.
1252      *  The day-of-month will be unchanged unless it would be invalid for the new
1253      *  month and year. In that case, the day-of-month is adjusted to the maximum
1254      *  valid value for the new month and year.
1255      * !(li){@code MILLENNIA} -
1256      *  Returns a {@code LocalDate} with the specified number of millennia added.
1257      *  This is equivalent to calling {@link #plusYears(long)} with the amount
1258      *  multiplied by 1,000.
1259      *  The day-of-month will be unchanged unless it would be invalid for the new
1260      *  month and year. In that case, the day-of-month is adjusted to the maximum
1261      *  valid value for the new month and year.
1262      * !(li){@code ERAS} -
1263      *  Returns a {@code LocalDate} with the specified number of eras added.
1264      *  Only two eras are supported so the amount must be one, zero or minus one.
1265      *  If the amount is non-zero then the year is changed such that the year-of-era
1266      *  is unchanged.
1267      *  The day-of-month will be unchanged unless it would be invalid for the new
1268      *  month and year. In that case, the day-of-month is adjusted to the maximum
1269      *  valid value for the new month and year.
1270      * </ul>
1271      * !(p)
1272      * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
1273      * !(p)
1274      * If the field is not a {@code ChronoUnit}, then the result of this method
1275      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
1276      * passing {@code this} as the argument. In this case, the unit determines
1277      * whether and how to perform the addition.
1278      * !(p)
1279      * This instance is immutable and unaffected by this method call.
1280      *
1281      * @param amountToAdd  the amount of the unit to add to the result, may be negative
1282      * @param unit  the unit of the amount to add, not null
1283      * @return a {@code LocalDate} based on this date with the specified amount added, not null
1284      * @throws DateTimeException if the addition cannot be made
1285      * @throws UnsupportedTemporalTypeException if the unit is not supported
1286      * @throws ArithmeticException if numeric overflow occurs
1287      */
1288     override
1289     public LocalDate plus(long amountToAdd, TemporalUnit unit) {
1290         if (cast(ChronoUnit)(unit) !is null) {
1291             ChronoUnit f = cast(ChronoUnit) unit;
1292             {
1293                 if( f ==  ChronoUnit.DAYS) return plusDays(amountToAdd);
1294                 if( f ==  ChronoUnit.WEEKS) return plusWeeks(amountToAdd);
1295                 if( f ==  ChronoUnit.MONTHS) return plusMonths(amountToAdd);
1296                 if( f ==  ChronoUnit.YEARS) return plusYears(amountToAdd);
1297                 if( f ==  ChronoUnit.DECADES) return plusYears(MathHelper.multiplyExact(amountToAdd, 10));
1298                 if( f ==  ChronoUnit.CENTURIES) return plusYears(MathHelper.multiplyExact(amountToAdd , 100));
1299                 if( f ==  ChronoUnit.MILLENNIA) return plusYears(MathHelper.multiplyExact(amountToAdd , 1000));
1300                 if( f ==  ChronoUnit.ERAS) return _with(ChronoField.ERA, MathHelper.addExact(getLong(ChronoField.ERA) , amountToAdd));
1301             }
1302             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
1303         }
1304         return cast(LocalDate)(unit.addTo(this, amountToAdd));
1305     }
1306 
1307     //-----------------------------------------------------------------------
1308     /**
1309      * Returns a copy of this {@code LocalDate} with the specified number of years added.
1310      * !(p)
1311      * This method adds the specified amount to the years field _in three steps:
1312      * !(ol)
1313      * !(li)Add the input years to the year field</li>
1314      * !(li)Check if the resulting date would be invalid</li>
1315      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1316      * </ol>
1317      * !(p)
1318      * For example, 2008-02-29 (leap year) plus one year would result _in the
1319      * invalid date 2009-02-29 (standard year). Instead of returning an invalid
1320      * result, the last valid day of the month, 2009-02-28, is selected instead.
1321      * !(p)
1322      * This instance is immutable and unaffected by this method call.
1323      *
1324      * @param yearsToAdd  the years to add, may be negative
1325      * @return a {@code LocalDate} based on this date with the years added, not null
1326      * @throws DateTimeException if the result exceeds the supported date range
1327      */
1328     public LocalDate plusYears(long yearsToAdd) {
1329         if (yearsToAdd == 0) {
1330             return this;
1331         }
1332         int newYear = ChronoField.YEAR.checkValidIntValue(year + yearsToAdd);  // safe overflow
1333         return resolvePreviousValid(newYear, month, day);
1334     }
1335 
1336     /**
1337      * Returns a copy of this {@code LocalDate} with the specified number of months added.
1338      * !(p)
1339      * This method adds the specified amount to the months field _in three steps:
1340      * !(ol)
1341      * !(li)Add the input months to the month-of-year field</li>
1342      * !(li)Check if the resulting date would be invalid</li>
1343      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1344      * </ol>
1345      * !(p)
1346      * For example, 2007-03-31 plus one month would result _in the invalid date
1347      * 2007-04-31. Instead of returning an invalid result, the last valid day
1348      * of the month, 2007-04-30, is selected instead.
1349      * !(p)
1350      * This instance is immutable and unaffected by this method call.
1351      *
1352      * @param monthsToAdd  the months to add, may be negative
1353      * @return a {@code LocalDate} based on this date with the months added, not null
1354      * @throws DateTimeException if the result exceeds the supported date range
1355      */
1356     public LocalDate plusMonths(long monthsToAdd) {
1357         if (monthsToAdd == 0) {
1358             return this;
1359         }
1360         long monthCount = year * 12L + (month - 1);
1361         long calcMonths = monthCount + monthsToAdd;  // safe overflow
1362         int newYear = ChronoField.YEAR.checkValidIntValue(MathHelper.floorDiv(calcMonths , 12));
1363         int newMonth = MathHelper.floorMod(calcMonths , 12) + 1;
1364         return resolvePreviousValid(newYear, newMonth, day);
1365     }
1366 
1367     /**
1368      * Returns a copy of this {@code LocalDate} with the specified number of weeks added.
1369      * !(p)
1370      * This method adds the specified amount _in weeks to the days field incrementing
1371      * the month and year fields as necessary to ensure the result remains valid.
1372      * The result is only invalid if the maximum/minimum year is exceeded.
1373      * !(p)
1374      * For example, 2008-12-31 plus one week would result _in 2009-01-07.
1375      * !(p)
1376      * This instance is immutable and unaffected by this method call.
1377      *
1378      * @param weeksToAdd  the weeks to add, may be negative
1379      * @return a {@code LocalDate} based on this date with the weeks added, not null
1380      * @throws DateTimeException if the result exceeds the supported date range
1381      */
1382     public LocalDate plusWeeks(long weeksToAdd) {
1383         return plusDays(MathHelper.multiplyExact(weeksToAdd , 7));
1384     }
1385 
1386     /**
1387      * Returns a copy of this {@code LocalDate} with the specified number of days added.
1388      * !(p)
1389      * This method adds the specified amount to the days field incrementing the
1390      * month and year fields as necessary to ensure the result remains valid.
1391      * The result is only invalid if the maximum/minimum year is exceeded.
1392      * !(p)
1393      * For example, 2008-12-31 plus one day would result _in 2009-01-01.
1394      * !(p)
1395      * This instance is immutable and unaffected by this method call.
1396      *
1397      * @param daysToAdd  the days to add, may be negative
1398      * @return a {@code LocalDate} based on this date with the days added, not null
1399      * @throws DateTimeException if the result exceeds the supported date range
1400      */
1401     public LocalDate plusDays(long daysToAdd) {
1402         if (daysToAdd == 0) {
1403             return this;
1404         }
1405         long dom = day + daysToAdd;
1406         if (dom > 0) {
1407             if (dom <= 28) {
1408                 return new LocalDate(year, month, cast(int) dom);
1409             } else if (dom <= 59) { // 59th Jan is 28th Feb, 59th Feb is 31st Mar
1410                 long monthLen = lengthOfMonth();
1411                 if (dom <= monthLen) {
1412                     return new LocalDate(year, month, cast(int) dom);
1413                 } else if (month < 12) {
1414                     return new LocalDate(year, month + 1, cast(int) (dom - monthLen));
1415                 } else {
1416                     ChronoField.YEAR.checkValidValue(year + 1);
1417                     return new LocalDate(year + 1, 1, cast(int) (dom - monthLen));
1418                 }
1419             }
1420         }
1421 
1422         long mjDay = MathHelper.addExact(toEpochDay() , daysToAdd);
1423         return LocalDate.ofEpochDay(mjDay);
1424     }
1425 
1426     //-----------------------------------------------------------------------
1427     /**
1428      * Returns a copy of this date with the specified amount subtracted.
1429      * !(p)
1430      * This returns a {@code LocalDate}, based on this one, with the specified amount subtracted.
1431      * The amount is typically {@link Period} but may be any other type implementing
1432      * the {@link TemporalAmount} interface.
1433      * !(p)
1434      * The calculation is delegated to the amount object by calling
1435      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
1436      * to implement the subtraction _in any way it wishes, however it typically
1437      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
1438      * of the amount implementation to determine if it can be successfully subtracted.
1439      * !(p)
1440      * This instance is immutable and unaffected by this method call.
1441      *
1442      * @param amountToSubtract  the amount to subtract, not null
1443      * @return a {@code LocalDate} based on this date with the subtraction made, not null
1444      * @throws DateTimeException if the subtraction cannot be made
1445      * @throws ArithmeticException if numeric overflow occurs
1446      */
1447     override
1448     public LocalDate minus(TemporalAmount amountToSubtract) {
1449         if (cast(Period)(amountToSubtract) !is null) {
1450             Period periodToSubtract = cast(Period) amountToSubtract;
1451             return minusMonths(periodToSubtract.toTotalMonths()).minusDays(periodToSubtract.getDays());
1452         }
1453         assert(amountToSubtract, "amountToSubtract");
1454         return cast(LocalDate) amountToSubtract.subtractFrom(this);
1455     }
1456 
1457     /**
1458      * Returns a copy of this date with the specified amount subtracted.
1459      * !(p)
1460      * This returns a {@code LocalDate}, based on this one, with the amount
1461      * _in terms of the unit subtracted. If it is not possible to subtract the amount,
1462      * because the unit is not supported or for some other reason, an exception is thrown.
1463      * !(p)
1464      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
1465      * See that method for a full description of how addition, and thus subtraction, works.
1466      * !(p)
1467      * This instance is immutable and unaffected by this method call.
1468      *
1469      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
1470      * @param unit  the unit of the amount to subtract, not null
1471      * @return a {@code LocalDate} based on this date with the specified amount subtracted, not null
1472      * @throws DateTimeException if the subtraction cannot be made
1473      * @throws UnsupportedTemporalTypeException if the unit is not supported
1474      * @throws ArithmeticException if numeric overflow occurs
1475      */
1476     override
1477     public LocalDate minus(long amountToSubtract, TemporalUnit unit) {
1478         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
1479     }
1480 
1481     //-----------------------------------------------------------------------
1482     /**
1483      * Returns a copy of this {@code LocalDate} with the specified number of years subtracted.
1484      * !(p)
1485      * This method subtracts the specified amount from the years field _in three steps:
1486      * !(ol)
1487      * !(li)Subtract the input years from the year field</li>
1488      * !(li)Check if the resulting date would be invalid</li>
1489      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1490      * </ol>
1491      * !(p)
1492      * For example, 2008-02-29 (leap year) minus one year would result _in the
1493      * invalid date 2007-02-29 (standard year). Instead of returning an invalid
1494      * result, the last valid day of the month, 2007-02-28, is selected instead.
1495      * !(p)
1496      * This instance is immutable and unaffected by this method call.
1497      *
1498      * @param yearsToSubtract  the years to subtract, may be negative
1499      * @return a {@code LocalDate} based on this date with the years subtracted, not null
1500      * @throws DateTimeException if the result exceeds the supported date range
1501      */
1502     public LocalDate minusYears(long yearsToSubtract) {
1503         return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
1504     }
1505 
1506     /**
1507      * Returns a copy of this {@code LocalDate} with the specified number of months subtracted.
1508      * !(p)
1509      * This method subtracts the specified amount from the months field _in three steps:
1510      * !(ol)
1511      * !(li)Subtract the input months from the month-of-year field</li>
1512      * !(li)Check if the resulting date would be invalid</li>
1513      * !(li)Adjust the day-of-month to the last valid day if necessary</li>
1514      * </ol>
1515      * !(p)
1516      * For example, 2007-03-31 minus one month would result _in the invalid date
1517      * 2007-02-31. Instead of returning an invalid result, the last valid day
1518      * of the month, 2007-02-28, is selected instead.
1519      * !(p)
1520      * This instance is immutable and unaffected by this method call.
1521      *
1522      * @param monthsToSubtract  the months to subtract, may be negative
1523      * @return a {@code LocalDate} based on this date with the months subtracted, not null
1524      * @throws DateTimeException if the result exceeds the supported date range
1525      */
1526     public LocalDate minusMonths(long monthsToSubtract) {
1527         return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
1528     }
1529 
1530     /**
1531      * Returns a copy of this {@code LocalDate} with the specified number of weeks subtracted.
1532      * !(p)
1533      * This method subtracts the specified amount _in weeks from the days field decrementing
1534      * the month and year fields as necessary to ensure the result remains valid.
1535      * The result is only invalid if the maximum/minimum year is exceeded.
1536      * !(p)
1537      * For example, 2009-01-07 minus one week would result _in 2008-12-31.
1538      * !(p)
1539      * This instance is immutable and unaffected by this method call.
1540      *
1541      * @param weeksToSubtract  the weeks to subtract, may be negative
1542      * @return a {@code LocalDate} based on this date with the weeks subtracted, not null
1543      * @throws DateTimeException if the result exceeds the supported date range
1544      */
1545     public LocalDate minusWeeks(long weeksToSubtract) {
1546         return (weeksToSubtract == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeksToSubtract));
1547     }
1548 
1549     /**
1550      * Returns a copy of this {@code LocalDate} with the specified number of days subtracted.
1551      * !(p)
1552      * This method subtracts the specified amount from the days field decrementing the
1553      * month and year fields as necessary to ensure the result remains valid.
1554      * The result is only invalid if the maximum/minimum year is exceeded.
1555      * !(p)
1556      * For example, 2009-01-01 minus one day would result _in 2008-12-31.
1557      * !(p)
1558      * This instance is immutable and unaffected by this method call.
1559      *
1560      * @param daysToSubtract  the days to subtract, may be negative
1561      * @return a {@code LocalDate} based on this date with the days subtracted, not null
1562      * @throws DateTimeException if the result exceeds the supported date range
1563      */
1564     public LocalDate minusDays(long daysToSubtract) {
1565         return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
1566     }
1567 
1568     //-----------------------------------------------------------------------
1569     /**
1570      * Queries this date using the specified query.
1571      * !(p)
1572      * This queries this date using the specified query strategy object.
1573      * The {@code TemporalQuery} object defines the logic to be used to
1574      * obtain the result. Read the documentation of the query to understand
1575      * what the result of this method will be.
1576      * !(p)
1577      * The result of this method is obtained by invoking the
1578      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
1579      * specified query passing {@code this} as the argument.
1580      *
1581      * @param !(R) the type of the result
1582      * @param query  the query to invoke, not null
1583      * @return the query result, null may be returned (defined by the query)
1584      * @throws DateTimeException if unable to query (defined by the query)
1585      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
1586      */
1587     /*@SuppressWarnings("unchecked")*/
1588     // override
1589     public R query(R)(TemporalQuery!(R) query) {
1590         if (query == TemporalQueries.localDate()) {
1591             return cast(R) this;
1592         }
1593         return /* ChronoLocalDate. */super_query(query);
1594     }
1595 
1596     R super_query(R)(TemporalQuery!(R) query) {
1597          if (query == TemporalQueries.zoneId()
1598                  || query == TemporalQueries.chronology()
1599                  || query == TemporalQueries.precision()) {
1600              return null;
1601          }
1602          return query.queryFrom(this);
1603      }
1604 
1605     /**
1606      * Adjusts the specified temporal object to have the same date as this object.
1607      * !(p)
1608      * This returns a temporal object of the same observable type as the input
1609      * with the date changed to be the same as this.
1610      * !(p)
1611      * The adjustment is equivalent to using {@link Temporal#_with(TemporalField, long)}
1612      * passing {@link ChronoField#EPOCH_DAY} as the field.
1613      * !(p)
1614      * In most cases, it is clearer to reverse the calling pattern by using
1615      * {@link Temporal#_with(TemporalAdjuster)}:
1616      * !(pre)
1617      *   // these two lines are equivalent, but the second approach is recommended
1618      *   temporal = thisLocalDate.adjustInto(temporal);
1619      *   temporal = temporal._with(thisLocalDate);
1620      * </pre>
1621      * !(p)
1622      * This instance is immutable and unaffected by this method call.
1623      *
1624      * @param temporal  the target object to be adjusted, not null
1625      * @return the adjusted object, not null
1626      * @throws DateTimeException if unable to make the adjustment
1627      * @throws ArithmeticException if numeric overflow occurs
1628      */
1629     override  // override for Javadoc
1630     public Temporal adjustInto(Temporal temporal) {
1631         return /* ChronoLocalDate. super.*/super_adjustInto(temporal);
1632     }
1633 
1634      Temporal super_adjustInto(Temporal temporal) {
1635         return temporal._with(ChronoField.EPOCH_DAY, toEpochDay());
1636     }
1637 
1638     /**
1639      * Calculates the amount of time until another date _in terms of the specified unit.
1640      * !(p)
1641      * This calculates the amount of time between two {@code LocalDate}
1642      * objects _in terms of a single {@code TemporalUnit}.
1643      * The start and end points are {@code this} and the specified date.
1644      * The result will be negative if the end is before the start.
1645      * The {@code Temporal} passed to this method is converted to a
1646      * {@code LocalDate} using {@link #from(TemporalAccessor)}.
1647      * For example, the amount _in days between two dates can be calculated
1648      * using {@code startDate.until(endDate, DAYS)}.
1649      * !(p)
1650      * The calculation returns a whole number, representing the number of
1651      * complete units between the two dates.
1652      * For example, the amount _in months between 2012-06-15 and 2012-08-14
1653      * will only be one month as it is one day short of two months.
1654      * !(p)
1655      * There are two equivalent ways of using this method.
1656      * The first is to invoke this method.
1657      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1658      * !(pre)
1659      *   // these two lines are equivalent
1660      *   amount = start.until(end, MONTHS);
1661      *   amount = MONTHS.between(start, end);
1662      * </pre>
1663      * The choice should be made based on which makes the code more readable.
1664      * !(p)
1665      * The calculation is implemented _in this method for {@link ChronoUnit}.
1666      * The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS},
1667      * {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS}
1668      * are supported. Other {@code ChronoUnit} values will throw an exception.
1669      * !(p)
1670      * If the unit is not a {@code ChronoUnit}, then the result of this method
1671      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1672      * passing {@code this} as the first argument and the converted input temporal
1673      * as the second argument.
1674      * !(p)
1675      * This instance is immutable and unaffected by this method call.
1676      *
1677      * @param endExclusive  the end date, exclusive, which is converted to a {@code LocalDate}, not null
1678      * @param unit  the unit to measure the amount _in, not null
1679      * @return the amount of time between this date and the end date
1680      * @throws DateTimeException if the amount cannot be calculated, or the end
1681      *  temporal cannot be converted to a {@code LocalDate}
1682      * @throws UnsupportedTemporalTypeException if the unit is not supported
1683      * @throws ArithmeticException if numeric overflow occurs
1684      */
1685     override
1686     public long until(Temporal endExclusive, TemporalUnit unit) {
1687         LocalDate end = LocalDate.from(endExclusive);
1688         if (cast(ChronoUnit)(unit) !is null) {
1689             auto f = cast(ChronoUnit) unit;
1690             {
1691                 if( f == ChronoUnit.DAYS) return daysUntil(end);
1692                 if( f == ChronoUnit.WEEKS) return daysUntil(end) / 7;
1693                 if( f == ChronoUnit.MONTHS) return monthsUntil(end);
1694                 if( f == ChronoUnit.YEARS) return monthsUntil(end) / 12;
1695                 if( f == ChronoUnit.DECADES) return monthsUntil(end) / 120;
1696                 if( f == ChronoUnit.CENTURIES) return monthsUntil(end) / 1200;
1697                 if( f == ChronoUnit.MILLENNIA) return monthsUntil(end) / 12000;
1698                 if( f == ChronoUnit.ERAS) return end.getLong(ChronoField.ERA) - getLong(ChronoField.ERA);
1699             }
1700             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
1701         }
1702         return unit.between(this, end);
1703     }
1704 
1705     long daysUntil(LocalDate end) {
1706         return end.toEpochDay() - toEpochDay();  // no overflow
1707     }
1708 
1709     private long monthsUntil(LocalDate end) {
1710         long packed1 = getProlepticMonth() * 32L + getDayOfMonth();  // no overflow
1711         long packed2 = end.getProlepticMonth() * 32L + end.getDayOfMonth();  // no overflow
1712         return (packed2 - packed1) / 32;
1713     }
1714 
1715     /**
1716      * Calculates the period between this date and another date as a {@code Period}.
1717      * !(p)
1718      * This calculates the period between two dates _in terms of years, months and days.
1719      * The start and end points are {@code this} and the specified date.
1720      * The result will be negative if the end is before the start.
1721      * The negative sign will be the same _in each of year, month and day.
1722      * !(p)
1723      * The calculation is performed using the ISO calendar system.
1724      * If necessary, the input date will be converted to ISO.
1725      * !(p)
1726      * The start date is included, but the end date is not.
1727      * The period is calculated by removing complete months, then calculating
1728      * the remaining number of days, adjusting to ensure that both have the same sign.
1729      * The number of months is then normalized into years and months based on a 12 month year.
1730      * A month is considered to be complete if the end day-of-month is greater
1731      * than or equal to the start day-of-month.
1732      * For example, from {@code 2010-01-15} to {@code 2011-03-18} is "1 year, 2 months and 3 days".
1733      * !(p)
1734      * There are two equivalent ways of using this method.
1735      * The first is to invoke this method.
1736      * The second is to use {@link Period#between(LocalDate, LocalDate)}:
1737      * !(pre)
1738      *   // these two lines are equivalent
1739      *   period = start.until(end);
1740      *   period = Period.between(start, end);
1741      * </pre>
1742      * The choice should be made based on which makes the code more readable.
1743      *
1744      * @param endDateExclusive  the end date, exclusive, which may be _in any chronology, not null
1745      * @return the period between this date and the end date, not null
1746      */
1747     override
1748     public Period until(ChronoLocalDate endDateExclusive) {
1749         LocalDate end = LocalDate.from(endDateExclusive);
1750         long totalMonths = end.getProlepticMonth() - this.getProlepticMonth();  // safe
1751         int days = end.day - this.day;
1752         if (totalMonths > 0 && days < 0) {
1753             totalMonths--;
1754             LocalDate calcDate = this.plusMonths(totalMonths);
1755             days = cast(int) (end.toEpochDay() - calcDate.toEpochDay());  // safe
1756         } else if (totalMonths < 0 && days > 0) {
1757             totalMonths++;
1758             days -= end.lengthOfMonth();
1759         }
1760         long years = totalMonths / 12;  // safe
1761         int months = cast(int) (totalMonths % 12);  // safe
1762         return Period.of(/* MathHelper.toIntExact */cast(int)(years), months, days);
1763     }
1764 
1765     /**
1766      * Returns a sequential ordered stream of dates. The returned stream starts from this date
1767      * (inclusive) and goes to {@code endExclusive} (exclusive) by an incremental step of 1 day.
1768      * !(p)
1769      * This method is equivalent to {@code datesUntil(endExclusive, Period.ofDays(1))}.
1770      *
1771      * @param endExclusive  the end date, exclusive, not null
1772      * @return a sequential {@code Stream} for the range of {@code LocalDate} values
1773      * @throws IllegalArgumentException if end date is before this date
1774      * @since 9
1775      */
1776      ///@gxc
1777     // public Stream!(LocalDate) datesUntil(LocalDate endExclusive) {
1778     //     long end = endExclusive.toEpochDay();
1779     //     long start = toEpochDay();
1780     //     if (end < start) {
1781     //         throw new IllegalArgumentException(endExclusive ~ " < " ~ this);
1782     //     }
1783     //     return LongStream.range(start, end).mapToObj(LocalDate.ofEpochDay);
1784     // }
1785 
1786     /**
1787      * Returns a sequential ordered stream of dates by given incremental step. The returned stream
1788      * starts from this date (inclusive) and goes to {@code endExclusive} (exclusive).
1789      * !(p)
1790      * The n-th date which appears _in the stream is equal to {@code this.plus(step.multipliedBy(n))}
1791      * (but the result of step multiplication never overflows). For example, if this date is
1792      * {@code 2015-01-31}, the end date is {@code 2015-05-01} and the step is 1 month, then the
1793      * stream contains {@code 2015-01-31}, {@code 2015-02-28}, {@code 2015-03-31}, and
1794      * {@code 2015-04-30}.
1795      *
1796      * @param endExclusive  the end date, exclusive, not null
1797      * @param step  the non-zero, non-negative {@code Period} which represents the step.
1798      * @return a sequential {@code Stream} for the range of {@code LocalDate} values
1799      * @throws IllegalArgumentException if step is zero, or {@code step.getDays()} and
1800      *             {@code step.toTotalMonths()} have opposite sign, or end date is before this date
1801      *             and step is positive, or end date is after this date and step is negative
1802      * @since 9
1803      */
1804      ///@gxc
1805     // public Stream!(LocalDate) datesUntil(LocalDate endExclusive, Period step) {
1806     //     if (step.isZero()) {
1807     //         throw new IllegalArgumentException("step is zero");
1808     //     }
1809     //     long end = endExclusive.toEpochDay();
1810     //     long start = toEpochDay();
1811     //     long until = end - start;
1812     //     long months = step.toTotalMonths();
1813     //     long days = step.getDays();
1814     //     if ((months < 0 && days > 0) || (months > 0 && days < 0)) {
1815     //         throw new IllegalArgumentException("period months and days are of opposite sign");
1816     //     }
1817     //     if (until == 0) {
1818     //         return Stream.empty();
1819     //     }
1820     //     int sign = months > 0 || days > 0 ? 1 : -1;
1821     //     if ((sign < 0) ^ (until < 0)) {
1822     //         throw new IllegalArgumentException(endExclusive + (sign < 0 ? " > " : " < ") + this);
1823     //     }
1824     //     if (months == 0) {
1825     //         long steps = (until - sign) / days; // non-negative
1826     //         return LongStream.rangeClosed(0, steps).mapToObj(
1827     //                 n => LocalDate.ofEpochDay(start + n * days));
1828     //     }
1829     //     // 48699/1600 = 365.2425/12, no overflow, non-negative result
1830     //     long steps = until * 1600 / (months * 48699 + days * 1600) + 1;
1831     //     long addMonths = months * steps;
1832     //     long addDays = days * steps;
1833     //     long maxAddMonths = months > 0 ? MAX.getProlepticMonth() - getProlepticMonth()
1834     //             : getProlepticMonth() - MIN.getProlepticMonth();
1835     //     // adjust steps estimation
1836     //     if (addMonths * sign > maxAddMonths
1837     //             || (plusMonths(addMonths).toEpochDay() + addDays) * sign >= end * sign) {
1838     //         steps--;
1839     //         addMonths -= months;
1840     //         addDays -= days;
1841     //         if (addMonths * sign > maxAddMonths
1842     //                 || (plusMonths(addMonths).toEpochDay() + addDays) * sign >= end * sign) {
1843     //             steps--;
1844     //         }
1845     //     }
1846     //     return LongStream.rangeClosed(0, steps).mapToObj(
1847     //             n => this.plusMonths(months * n).plusDays(days * n));
1848     // }
1849 
1850     /**
1851      * Formats this date using the specified formatter.
1852      * !(p)
1853      * This date will be passed to the formatter to produce a string.
1854      *
1855      * @param formatter  the formatter to use, not null
1856      * @return the formatted date string, not null
1857      * @throws DateTimeException if an error occurs during printing
1858      */
1859     // override  // override for Javadoc and performance
1860     // public string format(DateTimeFormatter formatter) {
1861     //     assert(formatter, "formatter");
1862     //     return formatter.format(this);
1863     // }
1864 
1865     //-----------------------------------------------------------------------
1866     /**
1867      * Combines this date with a time to create a {@code LocalDateTime}.
1868      * !(p)
1869      * This returns a {@code LocalDateTime} formed from this date at the specified time.
1870      * All possible combinations of date and time are valid.
1871      *
1872      * @param time  the time to combine with, not null
1873      * @return the local date-time formed from this date and the specified time, not null
1874      */
1875     // override
1876     public LocalDateTime atTime_s(LocalTime time) {
1877         return LocalDateTime.of(this, time);
1878     }
1879     override 
1880      ChronoLocalDateTime!(ChronoLocalDate) atTime(LocalTime localTime) {
1881         return ChronoLocalDateTimeImpl!ChronoLocalDate.of!ChronoLocalDate(this, localTime);
1882     }
1883     /**
1884      * Combines this date with a time to create a {@code LocalDateTime}.
1885      * !(p)
1886      * This returns a {@code LocalDateTime} formed from this date at the
1887      * specified hour and minute.
1888      * The seconds and nanosecond fields will be set to zero.
1889      * The individual time fields must be within their valid range.
1890      * All possible combinations of date and time are valid.
1891      *
1892      * @param hour  the hour-of-day to use, from 0 to 23
1893      * @param minute  the minute-of-hour to use, from 0 to 59
1894      * @return the local date-time formed from this date and the specified time, not null
1895      * @throws DateTimeException if the value of any field is _out of range
1896      */
1897     public LocalDateTime atTime(int hour, int minute) {
1898         return atTime_s(LocalTime.of(hour, minute));
1899     }
1900 
1901     /**
1902      * Combines this date with a time to create a {@code LocalDateTime}.
1903      * !(p)
1904      * This returns a {@code LocalDateTime} formed from this date at the
1905      * specified hour, minute and second.
1906      * The nanosecond field will be set to zero.
1907      * The individual time fields must be within their valid range.
1908      * All possible combinations of date and time are valid.
1909      *
1910      * @param hour  the hour-of-day to use, from 0 to 23
1911      * @param minute  the minute-of-hour to use, from 0 to 59
1912      * @param second  the second-of-minute to represent, from 0 to 59
1913      * @return the local date-time formed from this date and the specified time, not null
1914      * @throws DateTimeException if the value of any field is _out of range
1915      */
1916     public LocalDateTime atTime(int hour, int minute, int second) {
1917         return atTime_s(LocalTime.of(hour, minute, second));
1918     }
1919 
1920     /**
1921      * Combines this date with a time to create a {@code LocalDateTime}.
1922      * !(p)
1923      * This returns a {@code LocalDateTime} formed from this date at the
1924      * specified hour, minute, second and nanosecond.
1925      * The individual time fields must be within their valid range.
1926      * All possible combinations of date and time are valid.
1927      *
1928      * @param hour  the hour-of-day to use, from 0 to 23
1929      * @param minute  the minute-of-hour to use, from 0 to 59
1930      * @param second  the second-of-minute to represent, from 0 to 59
1931      * @param nanoOfSecond  the nano-of-second to represent, from 0 to 999,999,999
1932      * @return the local date-time formed from this date and the specified time, not null
1933      * @throws DateTimeException if the value of any field is _out of range
1934      */
1935     public LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond) {
1936         return atTime_s(LocalTime.of(hour, minute, second, nanoOfSecond));
1937     }
1938 
1939     /**
1940      * Combines this date with an offset time to create an {@code OffsetDateTime}.
1941      * !(p)
1942      * This returns an {@code OffsetDateTime} formed from this date at the specified time.
1943      * All possible combinations of date and time are valid.
1944      *
1945      * @param time  the time to combine with, not null
1946      * @return the offset date-time formed from this date and the specified time, not null
1947      */
1948     public OffsetDateTime atTime(OffsetTime time) {
1949         return OffsetDateTime.of(LocalDateTime.of(this, time.toLocalTime()), time.getOffset());
1950     }
1951 
1952     /**
1953      * Combines this date with the time of midnight to create a {@code LocalDateTime}
1954      * at the start of this date.
1955      * !(p)
1956      * This returns a {@code LocalDateTime} formed from this date at the time of
1957      * midnight, 00:00, at the start of this date.
1958      *
1959      * @return the local date-time of midnight at the start of this date, not null
1960      */
1961     public LocalDateTime atStartOfDay() {
1962         return LocalDateTime.of(this, LocalTime.MIDNIGHT);
1963     }
1964 
1965     /**
1966      * Returns a zoned date-time from this date at the earliest valid time according
1967      * to the rules _in the time-zone.
1968      * !(p)
1969      * Time-zone rules, such as daylight savings, mean that not every local date-time
1970      * is valid for the specified zone, thus the local date-time may not be midnight.
1971      * !(p)
1972      * In most cases, there is only one valid offset for a local date-time.
1973      * In the case of an overlap, there are two valid offsets, and the earlier one is used,
1974      * corresponding to the first occurrence of midnight on the date.
1975      * In the case of a gap, the zoned date-time will represent the instant just after the gap.
1976      * !(p)
1977      * If the zone ID is a {@link ZoneOffset}, then the result always has a time of midnight.
1978      * !(p)
1979      * To convert to a specific time _in a given time-zone call {@link #atTime(LocalTime)}
1980      * followed by {@link LocalDateTime#atZone(ZoneId)}.
1981      *
1982      * @param zone  the zone ID to use, not null
1983      * @return the zoned date-time formed from this date and the earliest valid time for the zone, not null
1984      */
1985     public ZonedDateTime atStartOfDay(ZoneId zone) {
1986         assert(zone, "zone");
1987         // need to handle case where there is a gap from 11:30 to 00:30
1988         // standard ZDT factory would result _in 01:00 rather than 00:30
1989         LocalDateTime ldt = atTime_s(LocalTime.MIDNIGHT);
1990         if ((cast(ZoneOffset)(zone) !is null) == false) {
1991             ZoneRules rules = zone.getRules();
1992             ZoneOffsetTransition trans = rules.getTransition(ldt);
1993             if (trans !is null && trans.isGap()) {
1994                 ldt = trans.getDateTimeAfter();
1995             }
1996         }
1997         return ZonedDateTime.of(ldt, zone);
1998     }
1999 
2000     //-----------------------------------------------------------------------
2001     override
2002     public long toEpochDay() {
2003         long y = year;
2004         long m = month;
2005         long total = 0;
2006         total += 365 * y;
2007         if (y >= 0) {
2008             total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400;
2009         } else {
2010             total -= y / -4 - y / -100 + y / -400;
2011         }
2012         total += ((367 * m - 362) / 12);
2013         total += day - 1;
2014         if (m > 2) {
2015             total--;
2016             if (isLeapYear() == false) {
2017                 total--;
2018             }
2019         }
2020         return total - DAYS_0000_TO_1970;
2021     }
2022 
2023     /**
2024      * Converts this {@code LocalDate} to the number of seconds since the epoch
2025      * of 1970-01-01T00:00:00Z.
2026      * !(p)
2027      * This combines this local date with the specified time and
2028      * offset to calculate the epoch-second value, which is the
2029      * number of elapsed seconds from 1970-01-01T00:00:00Z.
2030      * Instants on the time-line after the epoch are positive, earlier
2031      * are negative.
2032      *
2033      * @param time the local time, not null
2034      * @param offset the zone offset, not null
2035      * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
2036      * @since 9
2037      */
2038     public long toEpochSecond(LocalTime time, ZoneOffset offset) {
2039         assert(time, "time");
2040         assert(offset, "offset");
2041         long secs = toEpochDay() * LocalTime.SECONDS_PER_DAY + time.toSecondOfDay();
2042         secs -= offset.getTotalSeconds();
2043         return secs;
2044     }
2045 
2046     //-----------------------------------------------------------------------
2047     /**
2048      * Compares this date to another date.
2049      * !(p)
2050      * The comparison is primarily based on the date, from earliest to latest.
2051      * It is "consistent with equals", as defined by {@link Comparable}.
2052      * !(p)
2053      * If all the dates being compared are instances of {@code LocalDate},
2054      * then the comparison will be entirely based on the date.
2055      * If some dates being compared are _in different chronologies, then the
2056      * chronology is also considered, see {@link hunt.time.chrono.ChronoLocalDate#compareTo}.
2057      *
2058      * @param other  the other date to compare to, not null
2059      * @return the comparator value, negative if less, positive if greater
2060      */
2061     override  // override for Javadoc and performance
2062     public int compareTo(ChronoLocalDate other) {
2063         if (cast(LocalDate)(other) !is null) {
2064             return compareTo0(cast(LocalDate) other);
2065         }
2066         return /* ChronoLocalDate.super.*/super_compareTo(other);
2067     }
2068      int super_compareTo(ChronoLocalDate other) {
2069         int cmp = compare(toEpochDay(), other.toEpochDay());
2070         if (cmp == 0) {
2071             cmp = getChronology().compareTo(other.getChronology());
2072         }
2073         return cmp;
2074     }
2075 
2076     int compareTo0(LocalDate otherDate) {
2077         int cmp = (year - otherDate.year);
2078         if (cmp == 0) {
2079             cmp = (month - otherDate.month);
2080             if (cmp == 0) {
2081                 cmp = (day - otherDate.day);
2082             }
2083         }
2084         return cmp;
2085     }
2086     override  // override for Javadoc and performance
2087     public int opCmp(ChronoLocalDate other) {
2088         if (cast(LocalDate)(other) !is null) {
2089             return compareTo0(cast(LocalDate) other);
2090         }
2091         return /* ChronoLocalDate.super.*/super_compareTo(other);
2092     }
2093     /**
2094      * Checks if this date is after the specified date.
2095      * !(p)
2096      * This checks to see if this date represents a point on the
2097      * local time-line after the other date.
2098      * !(pre)
2099      *   LocalDate a = LocalDate.of(2012, 6, 30);
2100      *   LocalDate b = LocalDate.of(2012, 7, 1);
2101      *   a.isAfter(b) == false
2102      *   a.isAfter(a) == false
2103      *   b.isAfter(a) == true
2104      * </pre>
2105      * !(p)
2106      * This method only considers the position of the two dates on the local time-line.
2107      * It does not take into account the chronology, or calendar system.
2108      * This is different from the comparison _in {@link #compareTo(ChronoLocalDate)},
2109      * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
2110      *
2111      * @param other  the other date to compare to, not null
2112      * @return true if this date is after the specified date
2113      */
2114     override  // override for Javadoc and performance
2115     public bool isAfter(ChronoLocalDate other) {
2116         if (cast(LocalDate)(other) !is null) {
2117             return compareTo0(cast(LocalDate) other) > 0;
2118         }
2119         return /* ChronoLocalDate. super.*/super_isAfter(other);
2120     }
2121 
2122      bool super_isAfter(ChronoLocalDate other) {
2123         return this.toEpochDay() > other.toEpochDay();
2124     }
2125 
2126     /**
2127      * Checks if this date is before the specified date.
2128      * !(p)
2129      * This checks to see if this date represents a point on the
2130      * local time-line before the other date.
2131      * !(pre)
2132      *   LocalDate a = LocalDate.of(2012, 6, 30);
2133      *   LocalDate b = LocalDate.of(2012, 7, 1);
2134      *   a.isBefore(b) == true
2135      *   a.isBefore(a) == false
2136      *   b.isBefore(a) == false
2137      * </pre>
2138      * !(p)
2139      * This method only considers the position of the two dates on the local time-line.
2140      * It does not take into account the chronology, or calendar system.
2141      * This is different from the comparison _in {@link #compareTo(ChronoLocalDate)},
2142      * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
2143      *
2144      * @param other  the other date to compare to, not null
2145      * @return true if this date is before the specified date
2146      */
2147     override  // override for Javadoc and performance
2148     public bool isBefore(ChronoLocalDate other) {
2149         if (cast(LocalDate)(other) !is null) {
2150             return compareTo0(cast(LocalDate) other) < 0;
2151         }
2152         return /* ChronoLocalDate. super.*/super_isBefore(other);
2153     }
2154 
2155      bool super_isBefore(ChronoLocalDate other) {
2156         return this.toEpochDay() < other.toEpochDay();
2157     }
2158 
2159     /**
2160      * Checks if this date is equal to the specified date.
2161      * !(p)
2162      * This checks to see if this date represents the same point on the
2163      * local time-line as the other date.
2164      * !(pre)
2165      *   LocalDate a = LocalDate.of(2012, 6, 30);
2166      *   LocalDate b = LocalDate.of(2012, 7, 1);
2167      *   a.isEqual(b) == false
2168      *   a.isEqual(a) == true
2169      *   b.isEqual(a) == false
2170      * </pre>
2171      * !(p)
2172      * This method only considers the position of the two dates on the local time-line.
2173      * It does not take into account the chronology, or calendar system.
2174      * This is different from the comparison _in {@link #compareTo(ChronoLocalDate)}
2175      * but is the same approach as {@link ChronoLocalDate#timeLineOrder()}.
2176      *
2177      * @param other  the other date to compare to, not null
2178      * @return true if this date is equal to the specified date
2179      */
2180     override  // override for Javadoc and performance
2181     public bool isEqual(ChronoLocalDate other) {
2182         if (cast(LocalDate)(other) !is null) {
2183             return compareTo0(cast(LocalDate) other) == 0;
2184         }
2185         return /* ChronoLocalDate. super.*/super_isEqual(other);
2186     }
2187      bool super_isEqual(ChronoLocalDate other) {
2188         return this.toEpochDay() == other.toEpochDay();
2189     }
2190 
2191     //-----------------------------------------------------------------------
2192     /**
2193      * Checks if this date is equal to another date.
2194      * !(p)
2195      * Compares this {@code LocalDate} with another ensuring that the date is the same.
2196      * !(p)
2197      * Only objects of type {@code LocalDate} are compared, other types return false.
2198      * To compare the dates of two {@code TemporalAccessor} instances, including dates
2199      * _in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
2200      *
2201      * @param obj  the object to check, null returns false
2202      * @return true if this is equal to the other date
2203      */
2204     override
2205     public bool opEquals(Object obj) {
2206         if (this is obj) {
2207             return true;
2208         }
2209         if (cast(LocalDate)(obj) !is null) {
2210             return compareTo0(cast(LocalDate) obj) == 0;
2211         }
2212         return false;
2213     }
2214 
2215     /**
2216      * A hash code for this date.
2217      *
2218      * @return a suitable hash code
2219      */
2220     override
2221     public size_t toHash() @trusted nothrow {
2222         int yearValue = year;
2223         int monthValue = month;
2224         int dayValue = day;
2225         return (yearValue & 0xFFFFF800) ^ ((yearValue << 11) + (monthValue << 6) + (dayValue));
2226     }
2227 
2228     //-----------------------------------------------------------------------
2229     /**
2230      * Outputs this date as a {@code string}, such as {@code 2007-12-03}.
2231      * !(p)
2232      * The output will be _in the ISO-8601 format {@code uuuu-MM-dd}.
2233      *
2234      * @return a string representation of this date, not null
2235      */
2236     override
2237     public string toString() {
2238         import std.math : abs;
2239         int yearValue = year;
2240         int monthValue = month;
2241         int dayValue = day;
2242         int absYear = /* MathHelper. */abs(yearValue);
2243         StringBuilder buf = new StringBuilder(10);
2244         if (absYear < 1000) {
2245             if (yearValue < 0) {
2246                 buf.append(yearValue - 10000).deleteCharAt(1);
2247             } else {
2248                 buf.append(yearValue + 10000).deleteCharAt(0);
2249             }
2250         } else {
2251             if (yearValue > 9999) {
2252                 buf.append('+');
2253             }
2254             buf.append(yearValue);
2255         }
2256         return buf.append(monthValue < 10 ? "-0" : "-")
2257             .append(monthValue)
2258             .append(dayValue < 10 ? "-0" : "-")
2259             .append(dayValue)
2260             .toString();
2261     }
2262 
2263     //-----------------------------------------------------------------------
2264     /**
2265      * Writes the object using a
2266      * <a href="{@docRoot}/serialized-form.html#hunt.time.Ser">dedicated serialized form</a>.
2267      * @serialData
2268      * !(pre)
2269      *  _out.writeByte(3);  // identifies a LocalDate
2270      *  _out.writeInt(year);
2271      *  _out.writeByte(month);
2272      *  _out.writeByte(day);
2273      * </pre>
2274      *
2275      * @return the instance of {@code Ser}, not null
2276      */
2277     private Object writeReplace() {
2278         return new Ser(Ser.LOCAL_DATE_TYPE, this);
2279     }
2280 
2281     /**
2282      * Defend against malicious streams.
2283      *
2284      * @param s the stream to read
2285      * @throws InvalidObjectException always
2286      */
2287      ///@gxc
2288     // private void readObject(ObjectInputStream s) /*throws InvalidObjectException*/ {
2289     //     throw new InvalidObjectException("Deserialization via serialization delegate");
2290     // }
2291 
2292     void writeExternal(DataOutput _out) /*throws IOException*/ {
2293         _out.writeInt(year);
2294         _out.writeByte(month);
2295         _out.writeByte(day);
2296     }
2297 
2298     static LocalDate readExternal(DataInput _in) /*throws IOException*/ {
2299         int year = _in.readInt();
2300         int month = _in.readByte();
2301         int dayOfMonth = _in.readByte();
2302         return LocalDate.of(year, month, dayOfMonth);
2303     }
2304     
2305     // mixin SerializationMember!(typeof(this));
2306 
2307 }