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.YearMonth;
13 
14 import hunt.time.temporal.ChronoField;
15 import hunt.time.temporal.ChronoUnit;
16 
17 import hunt.stream.DataInput;
18 import hunt.stream.DataOutput;
19 import hunt.Exceptions;
20 
21 //import hunt.io.ObjectInputStream;
22 import hunt.stream.Common;
23 import hunt.time.chrono.Chronology;
24 import hunt.time.chrono.IsoChronology;
25 // import hunt.time.format.DateTimeFormatter;
26 // import hunt.time.format.DateTimeFormatterBuilder;
27 import hunt.time.format.DateTimeParseException;
28 import hunt.time.format.SignStyle;
29 import hunt.time.temporal.ChronoField;
30 import hunt.time.temporal.ChronoUnit;
31 import hunt.time.temporal.Temporal;
32 import hunt.time.temporal.TemporalAccessor;
33 import hunt.time.temporal.TemporalAdjuster;
34 import hunt.time.temporal.TemporalAmount;
35 import hunt.time.temporal.TemporalField;
36 import hunt.time.temporal.TemporalQueries;
37 import hunt.time.temporal.TemporalQuery;
38 import hunt.time.temporal.TemporalUnit;
39 import hunt.time.Exceptions;
40 import hunt.time.temporal.ValueRange;
41 import hunt.Functions;
42 import hunt.Long;
43 import hunt.math.Helper;
44 import hunt.time.ZoneId;
45 import hunt.time.Clock;
46 import hunt.time.Month;
47 import hunt.time.LocalDate;
48 import hunt.time.Exceptions;
49 import hunt.time.Year;
50 import hunt.util.Common;
51 import hunt.time.Ser;
52 import hunt.util.StringBuilder;
53 /**
54  * A year-month _in the ISO-8601 calendar system, such as {@code 2007-12}.
55  * !(p)
56  * {@code YearMonth} is an immutable date-time object that represents the combination
57  * of a year and month. Any field that can be derived from a year and month, such as
58  * quarter-of-year, can be obtained.
59  * !(p)
60  * This class does not store or represent a day, time or time-zone.
61  * For example, the value "October 2007" can be stored _in a {@code YearMonth}.
62  * !(p)
63  * The ISO-8601 calendar system is the modern civil calendar system used today
64  * _in most of the world. It is equivalent to the proleptic Gregorian calendar
65  * system, _in which today's rules for leap years are applied for all time.
66  * For most applications written today, the ISO-8601 rules are entirely suitable.
67  * However, any application that makes use of historical dates, and requires them
68  * to be accurate will find the ISO-8601 approach unsuitable.
69  *
70  * !(p)
71  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
72  * class; use of identity-sensitive operations (including reference equality
73  * ({@code ==}), identity hash code, or synchronization) on instances of
74  * {@code YearMonth} may have unpredictable results and should be avoided.
75  * The {@code equals} method should be used for comparisons.
76  *
77  * @implSpec
78  * This class is immutable and thread-safe.
79  *
80  * @since 1.8
81  */
82 final class YearMonth
83         : Temporal, TemporalAdjuster, Comparable!(YearMonth) { // , Serializable
84 
85     /**
86      * Parser.
87      */
88     // __gshared DateTimeFormatter _PARSER ;
89 
90     /**
91      * The year.
92      */
93     private  int year;
94     /**
95      * The month-of-year, not null.
96      */
97     private  int month;
98 
99     // static ref  DateTimeFormatter PARSER()
100     //  {
101     //      if(_PARSER is null)
102     //      {
103     //          _PARSER = new DateTimeFormatterBuilder()
104     //         .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
105     //         .appendLiteral('-')
106     //         .appendValue(ChronoField.MONTH_OF_YEAR, 2)
107     //         .toFormatter();
108     //      }
109     //      return _PARSER;
110     //  }
111     // shared static this()
112     // {
113     //     PARSER = new DateTimeFormatterBuilder()
114     //     .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
115     //     .appendLiteral('-')
116     //     .appendValue(ChronoField.MONTH_OF_YEAR, 2)
117     //     .toFormatter();
118     // }
119 
120     //-----------------------------------------------------------------------
121     /**
122      * Obtains the current year-month from the system clock _in the default time-zone.
123      * !(p)
124      * This will query the {@link Clock#systemDefaultZone() system clock} _in the default
125      * time-zone to obtain the current year-month.
126      * !(p)
127      * Using this method will prevent the ability to use an alternate clock for testing
128      * because the clock is hard-coded.
129      *
130      * @return the current year-month using the system clock and default time-zone, not null
131      */
132     static YearMonth now() {
133         return now(Clock.systemDefaultZone());
134     }
135 
136     /**
137      * Obtains the current year-month from the system clock _in the specified time-zone.
138      * !(p)
139      * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current year-month.
140      * Specifying the time-zone avoids dependence on the default time-zone.
141      * !(p)
142      * Using this method will prevent the ability to use an alternate clock for testing
143      * because the clock is hard-coded.
144      *
145      * @param zone  the zone ID to use, not null
146      * @return the current year-month using the system clock, not null
147      */
148     static YearMonth now(ZoneId zone) {
149         return now(Clock.system(zone));
150     }
151 
152     /**
153      * Obtains the current year-month from the specified clock.
154      * !(p)
155      * This will query the specified clock to obtain the current year-month.
156      * Using this method allows the use of an alternate clock for testing.
157      * The alternate clock may be introduced using {@link Clock dependency injection}.
158      *
159      * @param clock  the clock to use, not null
160      * @return the current year-month, not null
161      */
162     static YearMonth now(Clock clock) {
163         LocalDate now = LocalDate.now(clock);  // called once
164         return YearMonth.of(now.getYear(), now.getMonth());
165     }
166 
167     //-----------------------------------------------------------------------
168     /**
169      * Obtains an instance of {@code YearMonth} from a year and month.
170      *
171      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
172      * @param month  the month-of-year to represent, not null
173      * @return the year-month, not null
174      * @throws DateTimeException if the year value is invalid
175      */
176     static YearMonth of(int year, Month month) {
177         assert(month, "month");
178         return of(year, month.getValue());
179     }
180 
181     /**
182      * Obtains an instance of {@code YearMonth} from a year and month.
183      *
184      * @param year  the year to represent, from MIN_YEAR to MAX_YEAR
185      * @param month  the month-of-year to represent, from 1 (January) to 12 (December)
186      * @return the year-month, not null
187      * @throws DateTimeException if either field value is invalid
188      */
189     static YearMonth of(int year, int month) {
190         ChronoField.YEAR.checkValidValue(year);
191         ChronoField.MONTH_OF_YEAR.checkValidValue(month);
192         return new YearMonth(year, month);
193     }
194 
195     //-----------------------------------------------------------------------
196     /**
197      * Obtains an instance of {@code YearMonth} from a temporal object.
198      * !(p)
199      * This obtains a year-month based on the specified temporal.
200      * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
201      * which this factory converts to an instance of {@code YearMonth}.
202      * !(p)
203      * The conversion extracts the {@link ChronoField#YEAR YEAR} and
204      * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} fields.
205      * The extraction is only permitted if the temporal object has an ISO
206      * chronology, or can be converted to a {@code LocalDate}.
207      * !(p)
208      * This method matches the signature of the functional interface {@link TemporalQuery}
209      * allowing it to be used as a query via method reference, {@code YearMonth.from}.
210      *
211      * @param temporal  the temporal object to convert, not null
212      * @return the year-month, not null
213      * @throws DateTimeException if unable to convert to a {@code YearMonth}
214      */
215     static YearMonth from(TemporalAccessor temporal) {
216         if (cast(YearMonth)(temporal) !is null) {
217             return cast(YearMonth) temporal;
218         }
219         assert(temporal, "temporal");
220         try {
221             if ((IsoChronology.INSTANCE == Chronology.from(temporal)) == false) {
222                 temporal = LocalDate.from(temporal);
223             }
224             return of(temporal.get(ChronoField.YEAR), temporal.get(ChronoField.MONTH_OF_YEAR));
225         } catch (DateTimeException ex) {
226             throw new DateTimeException("Unable to obtain YearMonth from TemporalAccessor: " ~
227                     typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
228         }
229     }
230 
231     //-----------------------------------------------------------------------
232     /**
233      * Obtains an instance of {@code YearMonth} from a text string such as {@code 2007-12}.
234      * !(p)
235      * The string must represent a valid year-month.
236      * The format must be {@code uuuu-MM}.
237      * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol.
238      *
239      * @param text  the text to parse such as "2007-12", not null
240      * @return the parsed year-month, not null
241      * @throws DateTimeParseException if the text cannot be parsed
242      */
243     // static YearMonth parse(string text) {
244     //     return parse(text, PARSER);
245     // }
246 
247     /**
248      * Obtains an instance of {@code YearMonth} from a text string using a specific formatter.
249      * !(p)
250      * The text is parsed using the formatter, returning a year-month.
251      *
252      * @param text  the text to parse, not null
253      * @param formatter  the formatter to use, not null
254      * @return the parsed year-month, not null
255      * @throws DateTimeParseException if the text cannot be parsed
256      */
257     // static YearMonth parse(string text, DateTimeFormatter formatter) {
258     //     assert(formatter, "formatter");
259     //     return formatter.parse(text, new class TemporalQuery!YearMonth{
260     //         YearMonth queryFrom(TemporalAccessor temporal)
261     //         {
262     //             if (cast(YearMonth)(temporal) !is null) {
263     //                 return cast(YearMonth) temporal;
264     //             }
265     //             assert(temporal, "temporal");
266     //             try {
267     //                 if ((IsoChronology.INSTANCE == Chronology.from(temporal)) == false) {
268     //                     temporal = LocalDate.from(temporal);
269     //                 }
270     //                 return of(temporal.get(ChronoField.YEAR), temporal.get(ChronoField.MONTH_OF_YEAR));
271     //             } catch (DateTimeException ex) {
272     //                 throw new DateTimeException("Unable to obtain YearMonth from TemporalAccessor: " ~
273     //                         typeid(temporal).name ~ " of type " ~ typeid(temporal).stringof, ex);
274     //             }
275     //         }
276     //     });
277     // }
278 
279     //-----------------------------------------------------------------------
280     /**
281      * Constructor.
282      *
283      * @param year  the year to represent, validated from MIN_YEAR to MAX_YEAR
284      * @param month  the month-of-year to represent, validated from 1 (January) to 12 (December)
285      */
286     private this(int year, int month) {
287         this.year = year;
288         this.month = month;
289     }
290 
291     /**
292      * Returns a copy of this year-month with the new year and month, checking
293      * to see if a new object is _in fact required.
294      *
295      * @param newYear  the year to represent, validated from MIN_YEAR to MAX_YEAR
296      * @param newMonth  the month-of-year to represent, validated not null
297      * @return the year-month, not null
298      */
299     private YearMonth _with(int newYear, int newMonth) {
300         if (year == newYear && month == newMonth) {
301             return this;
302         }
303         return new YearMonth(newYear, newMonth);
304     }
305 
306     //-----------------------------------------------------------------------
307     /**
308      * Checks if the specified field is supported.
309      * !(p)
310      * This checks if this year-month can be queried for the specified field.
311      * If false, then calling the {@link #range(TemporalField) range},
312      * {@link #get(TemporalField) get} and {@link #_with(TemporalField, long)}
313      * methods will throw an exception.
314      * !(p)
315      * If the field is a {@link ChronoField} then the query is implemented here.
316      * The supported fields are:
317      * !(ul)
318      * !(li){@code MONTH_OF_YEAR}
319      * !(li){@code PROLEPTIC_MONTH}
320      * !(li){@code YEAR_OF_ERA}
321      * !(li){@code YEAR}
322      * !(li){@code ERA}
323      * </ul>
324      * All other {@code ChronoField} instances will return false.
325      * !(p)
326      * If the field is not a {@code ChronoField}, then the result of this method
327      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
328      * passing {@code this} as the argument.
329      * Whether the field is supported is determined by the field.
330      *
331      * @param field  the field to check, null returns false
332      * @return true if the field is supported on this year-month, false if not
333      */
334     override
335     bool isSupported(TemporalField field) {
336         if (cast(ChronoField)(field) !is null) {
337             return field == ChronoField.YEAR || field ==ChronoField.MONTH_OF_YEAR ||
338                     field == ChronoField.PROLEPTIC_MONTH || field == ChronoField.YEAR_OF_ERA || field == ChronoField.ERA;
339         }
340         return field !is null && field.isSupportedBy(this);
341     }
342 
343     /**
344      * Checks if the specified unit is supported.
345      * !(p)
346      * This checks if the specified unit can be added to, or subtracted from, this year-month.
347      * If false, then calling the {@link #plus(long, TemporalUnit)} and
348      * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
349      * !(p)
350      * If the unit is a {@link ChronoUnit} then the query is implemented here.
351      * The supported units are:
352      * !(ul)
353      * !(li){@code MONTHS}
354      * !(li){@code YEARS}
355      * !(li){@code DECADES}
356      * !(li){@code CENTURIES}
357      * !(li){@code MILLENNIA}
358      * !(li){@code ERAS}
359      * </ul>
360      * All other {@code ChronoUnit} instances will return false.
361      * !(p)
362      * If the unit is not a {@code ChronoUnit}, then the result of this method
363      * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
364      * passing {@code this} as the argument.
365      * Whether the unit is supported is determined by the unit.
366      *
367      * @param unit  the unit to check, null returns false
368      * @return true if the unit can be added/subtracted, false if not
369      */
370     override
371     bool isSupported(TemporalUnit unit) {
372         if (cast(ChronoUnit)(unit) !is null) {
373             return unit == ChronoUnit.MONTHS || unit == ChronoUnit.YEARS || unit == ChronoUnit.DECADES || unit == ChronoUnit.CENTURIES || unit == ChronoUnit.MILLENNIA || unit == ChronoUnit.ERAS;
374         }
375         return unit !is null && unit.isSupportedBy(this);
376     }
377 
378     //-----------------------------------------------------------------------
379     /**
380      * Gets the range of valid values for the specified field.
381      * !(p)
382      * The range object expresses the minimum and maximum valid values for a field.
383      * This year-month is used to enhance the accuracy of the returned range.
384      * If it is not possible to return the range, because the field is not supported
385      * or for some other reason, an exception is thrown.
386      * !(p)
387      * If the field is a {@link ChronoField} then the query is implemented here.
388      * The {@link #isSupported(TemporalField) supported fields} will return
389      * appropriate range instances.
390      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
391      * !(p)
392      * If the field is not a {@code ChronoField}, then the result of this method
393      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessor)}
394      * passing {@code this} as the argument.
395      * Whether the range can be obtained is determined by the field.
396      *
397      * @param field  the field to query the range for, not null
398      * @return the range of valid values for the field, not null
399      * @throws DateTimeException if the range for the field cannot be obtained
400      * @throws UnsupportedTemporalTypeException if the field is not supported
401      */
402     override
403     ValueRange range(TemporalField field) {
404         if (field == ChronoField.YEAR_OF_ERA) {
405             return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE));
406         }
407         return /* Temporal. super.*/super_range(field);
408     }
409     ValueRange super_range(TemporalField field) {
410         if (cast(ChronoField)(field) !is null) {
411             if (isSupported(field)) {
412                 return field.range();
413             }
414             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ typeid(field).name);
415         }
416         assert(field, "field");
417         return field.rangeRefinedBy(this);
418     }
419     /**
420      * Gets the value of the specified field from this year-month as an {@code int}.
421      * !(p)
422      * This queries this year-month for the value of the specified field.
423      * The returned value will always be within the valid range of values for the field.
424      * If it is not possible to return the value, because the field is not supported
425      * or for some other reason, an exception is thrown.
426      * !(p)
427      * If the field is a {@link ChronoField} then the query is implemented here.
428      * The {@link #isSupported(TemporalField) supported fields} will return valid
429      * values based on this year-month, except {@code PROLEPTIC_MONTH} which is too
430      * large to fit _in an {@code int} and throw a {@code DateTimeException}.
431      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
432      * !(p)
433      * If the field is not a {@code ChronoField}, then the result of this method
434      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
435      * passing {@code this} as the argument. Whether the value can be obtained,
436      * and what the value represents, is determined by the field.
437      *
438      * @param field  the field to get, not null
439      * @return the value for the field
440      * @throws DateTimeException if a value for the field cannot be obtained or
441      *         the value is outside the range of valid values for the field
442      * @throws UnsupportedTemporalTypeException if the field is not supported or
443      *         the range of values exceeds an {@code int}
444      * @throws ArithmeticException if numeric overflow occurs
445      */
446     override  // override for Javadoc
447     int get(TemporalField field) {
448         return range(field).checkValidIntValue(getLong(field), field);
449     }
450 
451     /**
452      * Gets the value of the specified field from this year-month as a {@code long}.
453      * !(p)
454      * This queries this year-month for the value of the specified field.
455      * If it is not possible to return the value, because the field is not supported
456      * or for some other reason, an exception is thrown.
457      * !(p)
458      * If the field is a {@link ChronoField} then the query is implemented here.
459      * The {@link #isSupported(TemporalField) supported fields} will return valid
460      * values based on this year-month.
461      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
462      * !(p)
463      * If the field is not a {@code ChronoField}, then the result of this method
464      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
465      * passing {@code this} as the argument. Whether the value can be obtained,
466      * and what the value represents, is determined by the field.
467      *
468      * @param field  the field to get, not null
469      * @return the value for the field
470      * @throws DateTimeException if a value for the field cannot be obtained
471      * @throws UnsupportedTemporalTypeException if the field is not supported
472      * @throws ArithmeticException if numeric overflow occurs
473      */
474     override
475     long getLong(TemporalField field) {
476         if (cast(ChronoField)(field) !is null) {
477             auto f = cast(ChronoField) field;
478             {
479                 if( f ==  ChronoField.MONTH_OF_YEAR) return month;
480                 if( f ==  ChronoField.PROLEPTIC_MONTH) return getProlepticMonth();
481                 if( f ==  ChronoField.YEAR_OF_ERA) return (year < 1 ? 1 - year : year);
482                 if( f ==  ChronoField.YEAR) return year;
483                 if( f ==  ChronoField.ERA) return (year < 1 ? 0 : 1);
484             }
485             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ f.toString);
486         }
487         return field.getFrom(this);
488     }
489 
490     private long getProlepticMonth() {
491         return (year * 12L + month - 1);
492     }
493 
494     //-----------------------------------------------------------------------
495     /**
496      * Gets the year field.
497      * !(p)
498      * This method returns the primitive {@code int} value for the year.
499      * !(p)
500      * The year returned by this method is proleptic as per {@code get(YEAR)}.
501      *
502      * @return the year, from MIN_YEAR to MAX_YEAR
503      */
504     int getYear() {
505         return year;
506     }
507 
508     /**
509      * Gets the month-of-year field from 1 to 12.
510      * !(p)
511      * This method returns the month as an {@code int} from 1 to 12.
512      * Application code is frequently clearer if the enum {@link Month}
513      * is used by calling {@link #getMonth()}.
514      *
515      * @return the month-of-year, from 1 to 12
516      * @see #getMonth()
517      */
518     int getMonthValue() {
519         return month;
520     }
521 
522     /**
523      * Gets the month-of-year field using the {@code Month} enum.
524      * !(p)
525      * This method returns the enum {@link Month} for the month.
526      * This avoids confusion as to what {@code int} values mean.
527      * If you need access to the primitive {@code int} value then the enum
528      * provides the {@link Month#getValue() int value}.
529      *
530      * @return the month-of-year, not null
531      * @see #getMonthValue()
532      */
533     Month getMonth() {
534         return Month.of(month);
535     }
536 
537     //-----------------------------------------------------------------------
538     /**
539      * Checks if the year is a leap year, according to the ISO proleptic
540      * calendar system rules.
541      * !(p)
542      * This method applies the current rules for leap years across the whole time-line.
543      * In general, a year is a leap year if it is divisible by four without
544      * remainder. However, years divisible by 100, are not leap years, with
545      * the exception of years divisible by 400 which are.
546      * !(p)
547      * For example, 1904 is a leap year it is divisible by 4.
548      * 1900 was not a leap year as it is divisible by 100, however 2000 was a
549      * leap year as it is divisible by 400.
550      * !(p)
551      * The calculation is proleptic - applying the same rules into the far future and far past.
552      * This is historically inaccurate, but is correct for the ISO-8601 standard.
553      *
554      * @return true if the year is leap, false otherwise
555      */
556     bool isLeapYear() {
557         return IsoChronology.INSTANCE.isLeapYear(year);
558     }
559 
560     /**
561      * Checks if the day-of-month is valid for this year-month.
562      * !(p)
563      * This method checks whether this year and month and the input day form
564      * a valid date.
565      *
566      * @param dayOfMonth  the day-of-month to validate, from 1 to 31, invalid value returns false
567      * @return true if the day is valid for this year-month
568      */
569     bool isValidDay(int dayOfMonth) {
570         return dayOfMonth >= 1 && dayOfMonth <= lengthOfMonth();
571     }
572 
573     /**
574      * Returns the length of the month, taking account of the year.
575      * !(p)
576      * This returns the length of the month _in days.
577      * For example, a date _in January would return 31.
578      *
579      * @return the length of the month _in days, from 28 to 31
580      */
581     int lengthOfMonth() {
582         return getMonth().length(isLeapYear());
583     }
584 
585     /**
586      * Returns the length of the year.
587      * !(p)
588      * This returns the length of the year _in days, either 365 or 366.
589      *
590      * @return 366 if the year is leap, 365 otherwise
591      */
592     int lengthOfYear() {
593         return (isLeapYear() ? 366 : 365);
594     }
595 
596     //-----------------------------------------------------------------------
597     /**
598      * Returns an adjusted copy of this year-month.
599      * !(p)
600      * This returns a {@code YearMonth}, based on this one, with the year-month adjusted.
601      * The adjustment takes place using the specified adjuster strategy object.
602      * Read the documentation of the adjuster to understand what adjustment will be made.
603      * !(p)
604      * A simple adjuster might simply set the one of the fields, such as the year field.
605      * A more complex adjuster might set the year-month to the next month that
606      * Halley's comet will pass the Earth.
607      * !(p)
608      * The result of this method is obtained by invoking the
609      * {@link TemporalAdjuster#adjustInto(Temporal)} method on the
610      * specified adjuster passing {@code this} as the argument.
611      * !(p)
612      * This instance is immutable and unaffected by this method call.
613      *
614      * @param adjuster the adjuster to use, not null
615      * @return a {@code YearMonth} based on {@code this} with the adjustment made, not null
616      * @throws DateTimeException if the adjustment cannot be made
617      * @throws ArithmeticException if numeric overflow occurs
618      */
619     override
620     YearMonth _with(TemporalAdjuster adjuster) {
621         return cast(YearMonth) adjuster.adjustInto(this);
622     }
623 
624     /**
625      * Returns a copy of this year-month with the specified field set to a new value.
626      * !(p)
627      * This returns a {@code YearMonth}, based on this one, with the value
628      * for the specified field changed.
629      * This can be used to change any supported field, such as the year or month.
630      * If it is not possible to set the value, because the field is not supported or for
631      * some other reason, an exception is thrown.
632      * !(p)
633      * If the field is a {@link ChronoField} then the adjustment is implemented here.
634      * The supported fields behave as follows:
635      * !(ul)
636      * !(li){@code MONTH_OF_YEAR} -
637      *  Returns a {@code YearMonth} with the specified month-of-year.
638      *  The year will be unchanged.
639      * !(li){@code PROLEPTIC_MONTH} -
640      *  Returns a {@code YearMonth} with the specified proleptic-month.
641      *  This completely replaces the year and month of this object.
642      * !(li){@code YEAR_OF_ERA} -
643      *  Returns a {@code YearMonth} with the specified year-of-era
644      *  The month and era will be unchanged.
645      * !(li){@code YEAR} -
646      *  Returns a {@code YearMonth} with the specified year.
647      *  The month will be unchanged.
648      * !(li){@code ERA} -
649      *  Returns a {@code YearMonth} with the specified era.
650      *  The month and year-of-era will be unchanged.
651      * </ul>
652      * !(p)
653      * In all cases, if the new value is outside the valid range of values for the field
654      * then a {@code DateTimeException} will be thrown.
655      * !(p)
656      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
657      * !(p)
658      * If the field is not a {@code ChronoField}, then the result of this method
659      * is obtained by invoking {@code TemporalField.adjustInto(Temporal, long)}
660      * passing {@code this} as the argument. In this case, the field determines
661      * whether and how to adjust the instant.
662      * !(p)
663      * This instance is immutable and unaffected by this method call.
664      *
665      * @param field  the field to set _in the result, not null
666      * @param newValue  the new value of the field _in the result
667      * @return a {@code YearMonth} based on {@code this} with the specified field set, not null
668      * @throws DateTimeException if the field cannot be set
669      * @throws UnsupportedTemporalTypeException if the field is not supported
670      * @throws ArithmeticException if numeric overflow occurs
671      */
672     override
673     YearMonth _with(TemporalField field, long newValue) {
674         if (cast(ChronoField)(field) !is null) {
675             ChronoField f = cast(ChronoField) field;
676             f.checkValidValue(newValue);
677             {
678                 if( f== ChronoField.MONTH_OF_YEAR) return withMonth(cast(int) newValue);
679                 if( f== ChronoField.PROLEPTIC_MONTH) return plusMonths(newValue - getProlepticMonth());
680                 if( f== ChronoField.YEAR_OF_ERA) return withYear(cast(int) (year < 1 ? 1 - newValue : newValue));
681                 if( f== ChronoField.YEAR) return withYear(cast(int) newValue);
682                 if( f== ChronoField.ERA) return (getLong(ChronoField.ERA) == newValue ? this : withYear(1 - year));
683             }
684             throw new UnsupportedTemporalTypeException("Unsupported field: " ~ f.toString);
685         }
686         return cast(YearMonth)(field.adjustInto(this, newValue));
687     }
688 
689     //-----------------------------------------------------------------------
690     /**
691      * Returns a copy of this {@code YearMonth} with the year altered.
692      * !(p)
693      * This instance is immutable and unaffected by this method call.
694      *
695      * @param year  the year to set _in the returned year-month, from MIN_YEAR to MAX_YEAR
696      * @return a {@code YearMonth} based on this year-month with the requested year, not null
697      * @throws DateTimeException if the year value is invalid
698      */
699     YearMonth withYear(int year) {
700         ChronoField.YEAR.checkValidValue(year);
701         return _with(year, month);
702     }
703 
704     /**
705      * Returns a copy of this {@code YearMonth} with the month-of-year altered.
706      * !(p)
707      * This instance is immutable and unaffected by this method call.
708      *
709      * @param month  the month-of-year to set _in the returned year-month, from 1 (January) to 12 (December)
710      * @return a {@code YearMonth} based on this year-month with the requested month, not null
711      * @throws DateTimeException if the month-of-year value is invalid
712      */
713     YearMonth withMonth(int month) {
714         ChronoField.MONTH_OF_YEAR.checkValidValue(month);
715         return _with(year, month);
716     }
717 
718     //-----------------------------------------------------------------------
719     /**
720      * Returns a copy of this year-month with the specified amount added.
721      * !(p)
722      * This returns a {@code YearMonth}, based on this one, with the specified amount added.
723      * The amount is typically {@link Period} but may be any other type implementing
724      * the {@link TemporalAmount} interface.
725      * !(p)
726      * The calculation is delegated to the amount object by calling
727      * {@link TemporalAmount#addTo(Temporal)}. The amount implementation is free
728      * to implement the addition _in any way it wishes, however it typically
729      * calls back to {@link #plus(long, TemporalUnit)}. Consult the documentation
730      * of the amount implementation to determine if it can be successfully added.
731      * !(p)
732      * This instance is immutable and unaffected by this method call.
733      *
734      * @param amountToAdd  the amount to add, not null
735      * @return a {@code YearMonth} based on this year-month with the addition made, not null
736      * @throws DateTimeException if the addition cannot be made
737      * @throws ArithmeticException if numeric overflow occurs
738      */
739     override
740     YearMonth plus(TemporalAmount amountToAdd) {
741         return cast(YearMonth) amountToAdd.addTo(this);
742     }
743 
744     /**
745      * Returns a copy of this year-month with the specified amount added.
746      * !(p)
747      * This returns a {@code YearMonth}, based on this one, with the amount
748      * _in terms of the unit added. If it is not possible to add the amount, because the
749      * unit is not supported or for some other reason, an exception is thrown.
750      * !(p)
751      * If the field is a {@link ChronoUnit} then the addition is implemented here.
752      * The supported fields behave as follows:
753      * !(ul)
754      * !(li){@code MONTHS} -
755      *  Returns a {@code YearMonth} with the specified number of months added.
756      *  This is equivalent to {@link #plusMonths(long)}.
757      * !(li){@code YEARS} -
758      *  Returns a {@code YearMonth} with the specified number of years added.
759      *  This is equivalent to {@link #plusYears(long)}.
760      * !(li){@code DECADES} -
761      *  Returns a {@code YearMonth} with the specified number of decades added.
762      *  This is equivalent to calling {@link #plusYears(long)} with the amount
763      *  multiplied by 10.
764      * !(li){@code CENTURIES} -
765      *  Returns a {@code YearMonth} with the specified number of centuries added.
766      *  This is equivalent to calling {@link #plusYears(long)} with the amount
767      *  multiplied by 100.
768      * !(li){@code MILLENNIA} -
769      *  Returns a {@code YearMonth} with the specified number of millennia added.
770      *  This is equivalent to calling {@link #plusYears(long)} with the amount
771      *  multiplied by 1,000.
772      * !(li){@code ERAS} -
773      *  Returns a {@code YearMonth} with the specified number of eras added.
774      *  Only two eras are supported so the amount must be one, zero or minus one.
775      *  If the amount is non-zero then the year is changed such that the year-of-era
776      *  is unchanged.
777      * </ul>
778      * !(p)
779      * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}.
780      * !(p)
781      * If the field is not a {@code ChronoUnit}, then the result of this method
782      * is obtained by invoking {@code TemporalUnit.addTo(Temporal, long)}
783      * passing {@code this} as the argument. In this case, the unit determines
784      * whether and how to perform the addition.
785      * !(p)
786      * This instance is immutable and unaffected by this method call.
787      *
788      * @param amountToAdd  the amount of the unit to add to the result, may be negative
789      * @param unit  the unit of the amount to add, not null
790      * @return a {@code YearMonth} based on this year-month with the specified amount added, not null
791      * @throws DateTimeException if the addition cannot be made
792      * @throws UnsupportedTemporalTypeException if the unit is not supported
793      * @throws ArithmeticException if numeric overflow occurs
794      */
795     override
796     YearMonth plus(long amountToAdd, TemporalUnit unit) {
797         if (cast(ChronoUnit)(unit) !is null) {
798             auto f = cast(ChronoUnit) unit;
799             {
800                 if( f == ChronoUnit.MONTHS) return plusMonths(amountToAdd);
801                 if( f == ChronoUnit.YEARS) return plusYears(amountToAdd);
802                 if( f == ChronoUnit.DECADES) return plusYears(MathHelper.multiplyExact(amountToAdd, 10));
803                 if( f == ChronoUnit.CENTURIES) return plusYears(MathHelper.multiplyExact(amountToAdd, 100));
804                 if( f == ChronoUnit.MILLENNIA) return plusYears(MathHelper.multiplyExact(amountToAdd, 1000));
805                 if( f == ChronoUnit.ERAS) return _with(ChronoField.ERA, MathHelper.addExact(getLong(ChronoField.ERA), amountToAdd));
806             }
807             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
808         }
809         return cast(YearMonth)(unit.addTo(this, amountToAdd));
810     }
811 
812     /**
813      * Returns a copy of this {@code YearMonth} with the specified number of years added.
814      * !(p)
815      * This instance is immutable and unaffected by this method call.
816      *
817      * @param yearsToAdd  the years to add, may be negative
818      * @return a {@code YearMonth} based on this year-month with the years added, not null
819      * @throws DateTimeException if the result exceeds the supported range
820      */
821     YearMonth plusYears(long yearsToAdd) {
822         if (yearsToAdd == 0) {
823             return this;
824         }
825         int newYear = ChronoField.YEAR.checkValidIntValue(year + yearsToAdd);  // safe overflow
826         return _with(newYear, month);
827     }
828 
829     /**
830      * Returns a copy of this {@code YearMonth} with the specified number of months added.
831      * !(p)
832      * This instance is immutable and unaffected by this method call.
833      *
834      * @param monthsToAdd  the months to add, may be negative
835      * @return a {@code YearMonth} based on this year-month with the months added, not null
836      * @throws DateTimeException if the result exceeds the supported range
837      */
838     YearMonth plusMonths(long monthsToAdd) {
839         if (monthsToAdd == 0) {
840             return this;
841         }
842         long monthCount = year * 12L + (month - 1);
843         long calcMonths = monthCount + monthsToAdd;  // safe overflow
844         int newYear = ChronoField.YEAR.checkValidIntValue(MathHelper.floorDiv(calcMonths, 12));
845         int newMonth = MathHelper.floorMod(calcMonths, 12) + 1;
846         return _with(newYear, newMonth);
847     }
848 
849     //-----------------------------------------------------------------------
850     /**
851      * Returns a copy of this year-month with the specified amount subtracted.
852      * !(p)
853      * This returns a {@code YearMonth}, based on this one, with the specified amount subtracted.
854      * The amount is typically {@link Period} but may be any other type implementing
855      * the {@link TemporalAmount} interface.
856      * !(p)
857      * The calculation is delegated to the amount object by calling
858      * {@link TemporalAmount#subtractFrom(Temporal)}. The amount implementation is free
859      * to implement the subtraction _in any way it wishes, however it typically
860      * calls back to {@link #minus(long, TemporalUnit)}. Consult the documentation
861      * of the amount implementation to determine if it can be successfully subtracted.
862      * !(p)
863      * This instance is immutable and unaffected by this method call.
864      *
865      * @param amountToSubtract  the amount to subtract, not null
866      * @return a {@code YearMonth} based on this year-month with the subtraction made, not null
867      * @throws DateTimeException if the subtraction cannot be made
868      * @throws ArithmeticException if numeric overflow occurs
869      */
870     override
871     YearMonth minus(TemporalAmount amountToSubtract) {
872         return cast(YearMonth) amountToSubtract.subtractFrom(this);
873     }
874 
875     /**
876      * Returns a copy of this year-month with the specified amount subtracted.
877      * !(p)
878      * This returns a {@code YearMonth}, based on this one, with the amount
879      * _in terms of the unit subtracted. If it is not possible to subtract the amount,
880      * because the unit is not supported or for some other reason, an exception is thrown.
881      * !(p)
882      * This method is equivalent to {@link #plus(long, TemporalUnit)} with the amount negated.
883      * See that method for a full description of how addition, and thus subtraction, works.
884      * !(p)
885      * This instance is immutable and unaffected by this method call.
886      *
887      * @param amountToSubtract  the amount of the unit to subtract from the result, may be negative
888      * @param unit  the unit of the amount to subtract, not null
889      * @return a {@code YearMonth} based on this year-month with the specified amount subtracted, not null
890      * @throws DateTimeException if the subtraction cannot be made
891      * @throws UnsupportedTemporalTypeException if the unit is not supported
892      * @throws ArithmeticException if numeric overflow occurs
893      */
894     override
895     YearMonth minus(long amountToSubtract, TemporalUnit unit) {
896         return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
897     }
898 
899     /**
900      * Returns a copy of this {@code YearMonth} with the specified number of years subtracted.
901      * !(p)
902      * This instance is immutable and unaffected by this method call.
903      *
904      * @param yearsToSubtract  the years to subtract, may be negative
905      * @return a {@code YearMonth} based on this year-month with the years subtracted, not null
906      * @throws DateTimeException if the result exceeds the supported range
907      */
908     YearMonth minusYears(long yearsToSubtract) {
909         return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
910     }
911 
912     /**
913      * Returns a copy of this {@code YearMonth} with the specified number of months subtracted.
914      * !(p)
915      * This instance is immutable and unaffected by this method call.
916      *
917      * @param monthsToSubtract  the months to subtract, may be negative
918      * @return a {@code YearMonth} based on this year-month with the months subtracted, not null
919      * @throws DateTimeException if the result exceeds the supported range
920      */
921     YearMonth minusMonths(long monthsToSubtract) {
922         return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
923     }
924 
925     //-----------------------------------------------------------------------
926     /**
927      * Queries this year-month using the specified query.
928      * !(p)
929      * This queries this year-month using the specified query strategy object.
930      * The {@code TemporalQuery} object defines the logic to be used to
931      * obtain the result. Read the documentation of the query to understand
932      * what the result of this method will be.
933      * !(p)
934      * The result of this method is obtained by invoking the
935      * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the
936      * specified query passing {@code this} as the argument.
937      *
938      * @param !(R) the type of the result
939      * @param query  the query to invoke, not null
940      * @return the query result, null may be returned (defined by the query)
941      * @throws DateTimeException if unable to query (defined by the query)
942      * @throws ArithmeticException if numeric overflow occurs (defined by the query)
943      */
944     /*@SuppressWarnings("unchecked")*/
945     // override
946     R query(R)(TemporalQuery!(R) query) {
947         if (query == TemporalQueries.chronology()) {
948             return cast(R) IsoChronology.INSTANCE;
949         } else if (query == TemporalQueries.precision()) {
950             return cast(R) (ChronoUnit.MONTHS);
951         }
952         return /* Temporal. */super_query(query);
953     }
954     R super_query(R)(TemporalQuery!(R) query) {
955          if (query == TemporalQueries.zoneId()
956                  || query == TemporalQueries.chronology()
957                  || query == TemporalQueries.precision()) {
958              return null;
959          }
960          return query.queryFrom(this);
961      }
962     /**
963      * Adjusts the specified temporal object to have this year-month.
964      * !(p)
965      * This returns a temporal object of the same observable type as the input
966      * with the year and month changed to be the same as this.
967      * !(p)
968      * The adjustment is equivalent to using {@link Temporal#_with(TemporalField, long)}
969      * passing {@link ChronoField#PROLEPTIC_MONTH} as the field.
970      * If the specified temporal object does not use the ISO calendar system then
971      * a {@code DateTimeException} is thrown.
972      * !(p)
973      * In most cases, it is clearer to reverse the calling pattern by using
974      * {@link Temporal#_with(TemporalAdjuster)}:
975      * !(pre)
976      *   // these two lines are equivalent, but the second approach is recommended
977      *   temporal = thisYearMonth.adjustInto(temporal);
978      *   temporal = temporal._with(thisYearMonth);
979      * </pre>
980      * !(p)
981      * This instance is immutable and unaffected by this method call.
982      *
983      * @param temporal  the target object to be adjusted, not null
984      * @return the adjusted object, not null
985      * @throws DateTimeException if unable to make the adjustment
986      * @throws ArithmeticException if numeric overflow occurs
987      */
988     override
989     Temporal adjustInto(Temporal temporal) {
990         if ((Chronology.from(temporal) == IsoChronology.INSTANCE) == false) {
991             throw new DateTimeException("Adjustment only supported on ISO date-time");
992         }
993         return temporal._with(ChronoField.PROLEPTIC_MONTH, getProlepticMonth());
994     }
995 
996     /**
997      * Calculates the amount of time until another year-month _in terms of the specified unit.
998      * !(p)
999      * This calculates the amount of time between two {@code YearMonth}
1000      * objects _in terms of a single {@code TemporalUnit}.
1001      * The start and end points are {@code this} and the specified year-month.
1002      * The result will be negative if the end is before the start.
1003      * The {@code Temporal} passed to this method is converted to a
1004      * {@code YearMonth} using {@link #from(TemporalAccessor)}.
1005      * For example, the amount _in years between two year-months can be calculated
1006      * using {@code startYearMonth.until(endYearMonth, YEARS)}.
1007      * !(p)
1008      * The calculation returns a whole number, representing the number of
1009      * complete units between the two year-months.
1010      * For example, the amount _in decades between 2012-06 and 2032-05
1011      * will only be one decade as it is one month short of two decades.
1012      * !(p)
1013      * There are two equivalent ways of using this method.
1014      * The first is to invoke this method.
1015      * The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
1016      * !(pre)
1017      *   // these two lines are equivalent
1018      *   amount = start.until(end, MONTHS);
1019      *   amount = MONTHS.between(start, end);
1020      * </pre>
1021      * The choice should be made based on which makes the code more readable.
1022      * !(p)
1023      * The calculation is implemented _in this method for {@link ChronoUnit}.
1024      * The units {@code MONTHS}, {@code YEARS}, {@code DECADES},
1025      * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported.
1026      * Other {@code ChronoUnit} values will throw an exception.
1027      * !(p)
1028      * If the unit is not a {@code ChronoUnit}, then the result of this method
1029      * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)}
1030      * passing {@code this} as the first argument and the converted input temporal
1031      * as the second argument.
1032      * !(p)
1033      * This instance is immutable and unaffected by this method call.
1034      *
1035      * @param endExclusive  the end date, exclusive, which is converted to a {@code YearMonth}, not null
1036      * @param unit  the unit to measure the amount _in, not null
1037      * @return the amount of time between this year-month and the end year-month
1038      * @throws DateTimeException if the amount cannot be calculated, or the end
1039      *  temporal cannot be converted to a {@code YearMonth}
1040      * @throws UnsupportedTemporalTypeException if the unit is not supported
1041      * @throws ArithmeticException if numeric overflow occurs
1042      */
1043     override
1044     long until(Temporal endExclusive, TemporalUnit unit) {
1045         YearMonth end = YearMonth.from(endExclusive);
1046         if (cast(ChronoUnit)(unit) !is null) {
1047             long monthsUntil = end.getProlepticMonth() - getProlepticMonth();  // no overflow
1048             auto f = cast(ChronoUnit) unit;
1049             {
1050                 if( f == ChronoUnit.MONTHS) return monthsUntil;
1051                 if( f == ChronoUnit.YEARS) return monthsUntil / 12;
1052                 if( f == ChronoUnit.DECADES) return monthsUntil / 120;
1053                 if( f == ChronoUnit.CENTURIES) return monthsUntil / 1200;
1054                 if( f == ChronoUnit.MILLENNIA) return monthsUntil / 12000;
1055                 if( f == ChronoUnit.ERAS) return end.getLong(ChronoField.ERA) - getLong(ChronoField.ERA);
1056             }
1057             throw new UnsupportedTemporalTypeException("Unsupported unit: " ~ f.toString);
1058         }
1059         return unit.between(this, end);
1060     }
1061 
1062     /**
1063      * Formats this year-month using the specified formatter.
1064      * !(p)
1065      * This year-month will be passed to the formatter to produce a string.
1066      *
1067      * @param formatter  the formatter to use, not null
1068      * @return the formatted year-month string, not null
1069      * @throws DateTimeException if an error occurs during printing
1070      */
1071     // string format(DateTimeFormatter formatter) {
1072     //     assert(formatter, "formatter");
1073     //     return formatter.format(this);
1074     // }
1075 
1076     //-----------------------------------------------------------------------
1077     /**
1078      * Combines this year-month with a day-of-month to create a {@code LocalDate}.
1079      * !(p)
1080      * This returns a {@code LocalDate} formed from this year-month and the specified day-of-month.
1081      * !(p)
1082      * The day-of-month value must be valid for the year-month.
1083      * !(p)
1084      * This method can be used as part of a chain to produce a date:
1085      * !(pre)
1086      *  LocalDate date = year.atMonth(month).atDay(day);
1087      * </pre>
1088      *
1089      * @param dayOfMonth  the day-of-month to use, from 1 to 31
1090      * @return the date formed from this year-month and the specified day, not null
1091      * @throws DateTimeException if the day is invalid for the year-month
1092      * @see #isValidDay(int)
1093      */
1094     LocalDate atDay(int dayOfMonth) {
1095         return LocalDate.of(year, month, dayOfMonth);
1096     }
1097 
1098     /**
1099      * Returns a {@code LocalDate} at the end of the month.
1100      * !(p)
1101      * This returns a {@code LocalDate} based on this year-month.
1102      * The day-of-month is set to the last valid day of the month, taking
1103      * into account leap years.
1104      * !(p)
1105      * This method can be used as part of a chain to produce a date:
1106      * !(pre)
1107      *  LocalDate date = year.atMonth(month).atEndOfMonth();
1108      * </pre>
1109      *
1110      * @return the last valid date of this year-month, not null
1111      */
1112     LocalDate atEndOfMonth() {
1113         return LocalDate.of(year, month, lengthOfMonth());
1114     }
1115 
1116     //-----------------------------------------------------------------------
1117     /**
1118      * Compares this year-month to another year-month.
1119      * !(p)
1120      * The comparison is based first on the value of the year, then on the value of the month.
1121      * It is "consistent with equals", as defined by {@link Comparable}.
1122      *
1123      * @param other  the other year-month to compare to, not null
1124      * @return the comparator value, negative if less, positive if greater
1125      */
1126     // override
1127     int compareTo(YearMonth other) {
1128         int cmp = (year - other.year);
1129         if (cmp == 0) {
1130             cmp = (month - other.month);
1131         }
1132         return cmp;
1133     }
1134 
1135      override
1136     int opCmp(YearMonth other) {
1137         return compareTo(other);
1138     }
1139 
1140     /**
1141      * Checks if this year-month is after the specified year-month.
1142      *
1143      * @param other  the other year-month to compare to, not null
1144      * @return true if this is after the specified year-month
1145      */
1146     bool isAfter(YearMonth other) {
1147         return compareTo(other) > 0;
1148     }
1149 
1150     /**
1151      * Checks if this year-month is before the specified year-month.
1152      *
1153      * @param other  the other year-month to compare to, not null
1154      * @return true if this point is before the specified year-month
1155      */
1156     bool isBefore(YearMonth other) {
1157         return compareTo(other) < 0;
1158     }
1159 
1160     //-----------------------------------------------------------------------
1161     /**
1162      * Checks if this year-month is equal to another year-month.
1163      * !(p)
1164      * The comparison is based on the time-line position of the year-months.
1165      *
1166      * @param obj  the object to check, null returns false
1167      * @return true if this is equal to the other year-month
1168      */
1169     override
1170     bool opEquals(Object obj) {
1171         if (this is obj) {
1172             return true;
1173         }
1174         if (cast(YearMonth)(obj) !is null) {
1175             YearMonth other = cast(YearMonth) obj;
1176             return year == other.year && month == other.month;
1177         }
1178         return false;
1179     }
1180 
1181     /**
1182      * A hash code for this year-month.
1183      *
1184      * @return a suitable hash code
1185      */
1186     override
1187     size_t toHash() @trusted nothrow {
1188         return year ^ (month << 27);
1189     }
1190 
1191     //-----------------------------------------------------------------------
1192     /**
1193      * Outputs this year-month as a {@code string}, such as {@code 2007-12}.
1194      * !(p)
1195      * The output will be _in the format {@code uuuu-MM}:
1196      *
1197      * @return a string representation of this year-month, not null
1198      */
1199     override
1200     string toString() {
1201         int absYear = MathHelper.abs(year);
1202         StringBuilder buf = new StringBuilder(9);
1203         if (absYear < 1000) {
1204             if (year < 0) {
1205                 buf.append(year - 10000).deleteCharAt(1);
1206             } else {
1207                 buf.append(year + 10000).deleteCharAt(0);
1208             }
1209         } else {
1210             buf.append(year);
1211         }
1212         return buf.append(month < 10 ? "-0" : "-")
1213             .append(month)
1214             .toString();
1215     }
1216 
1217     //-----------------------------------------------------------------------
1218     /**
1219      * Writes the object using a
1220      * <a href="{@docRoot}/serialized-form.html#hunt.time.Ser">dedicated serialized form</a>.
1221      * @serialData
1222      * !(pre)
1223      *  _out.writeByte(12);  // identifies a YearMonth
1224      *  _out.writeInt(year);
1225      *  _out.writeByte(month);
1226      * </pre>
1227      *
1228      * @return the instance of {@code Ser}, not null
1229      */
1230     private Object writeReplace() {
1231         return new Ser(Ser.YEAR_MONTH_TYPE, this);
1232     }
1233 
1234     /**
1235      * Defend against malicious streams.
1236      *
1237      * @param s the stream to read
1238      * @throws InvalidObjectException always
1239      */
1240      ///@gxc
1241     // private void readObject(ObjectInputStream s) /*throws InvalidObjectException*/ {
1242     //     throw new InvalidObjectException("Deserialization via serialization delegate");
1243     // }
1244 
1245     void writeExternal(DataOutput _out) /*throws IOException*/ {
1246         _out.writeInt(year);
1247         _out.writeByte(month);
1248     }
1249 
1250     static YearMonth readExternal(DataInput _in) /*throws IOException*/ {
1251         int year = _in.readInt();
1252         byte month = _in.readByte();
1253         return YearMonth.of(year, month);
1254     }
1255 
1256 }