1 /*
2  * hunt-time: A time library for D programming language.
3  *
4  * Copyright (C) 2015-2018 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.time.temporal.TemporalQueries;
13 
14 import hunt.time.temporal.ChronoField;
15 
16 import hunt.time.LocalDate;
17 import hunt.time.LocalTime;
18 import hunt.time.ZoneId;
19 import hunt.time.ZoneOffset;
20 import hunt.time.chrono.Chronology;
21 import hunt.time.temporal.TemporalQuery;
22 import hunt.time.temporal.TemporalUnit;
23 import hunt.time.temporal.TemporalAccessor;
24 import hunt.time.util.QueryHelper;
25 import hunt.time.util.Common;
26 /**
27  * Common implementations of {@code TemporalQuery}.
28  * !(p)
29  * This class provides common implementations of {@link TemporalQuery}.
30  * These are defined here as they must be constants, and the definition
31  * of lambdas does not guarantee that. By assigning them once here,
32  * they become 'normal' Java constants.
33  * !(p)
34  * Queries are a key tool for extracting information from temporal objects.
35  * They exist to externalize the process of querying, permitting different
36  * approaches, as per the strategy design pattern.
37  * Examples might be a query that checks if the date is the day before February 29th
38  * _in a leap year, or calculates the number of days to your next birthday.
39  * !(p)
40  * The {@link TemporalField} interface provides another mechanism for querying
41  * temporal objects. That interface is limited to returning a {@code long}.
42  * By contrast, queries can return any type.
43  * !(p)
44  * There are two equivalent ways of using a {@code TemporalQuery}.
45  * The first is to invoke the method on this interface directly.
46  * The second is to use {@link TemporalAccessor#query(TemporalQuery)}:
47  * !(pre)
48  *   // these two lines are equivalent, but the second approach is recommended
49  *   temporal = thisQuery.queryFrom(temporal);
50  *   temporal = temporal.query(thisQuery);
51  * </pre>
52  * It is recommended to use the second approach, {@code query(TemporalQuery)},
53  * as it is a lot clearer to read _in code.
54  * !(p)
55  * The most common implementations are method references, such as
56  * {@code LocalDate.from} and {@code ZoneId::from}.
57  * Additional common queries are provided to return:
58  * !(ul)
59  * !(li) a Chronology,
60  * !(li) a LocalDate,
61  * !(li) a LocalTime,
62  * !(li) a ZoneOffset,
63  * !(li) a precision,
64  * !(li) a zone, or
65  * !(li) a zoneId.
66  * </ul>
67  *
68  * @since 1.8
69  */
70 public final class TemporalQueries
71 {
72     // note that it is vital that each method supplies a constant, not a
73     // calculated value, as they will be checked for using ==
74     // it is also vital that each constant is different (due to the == checking)
75     // as such, alterations to this code must be done with care
76 
77     /**
78      * Private constructor since this is a utility class.
79      */
80     private this()
81     {
82     }
83 
84     //-----------------------------------------------------------------------
85     // special constants should be used to extract information from a TemporalAccessor
86     // that cannot be derived _in other ways
87     // Javadoc added here, so as to pretend they are more normal than they really are
88 
89     /**
90      * A strict query for the {@code ZoneId}.
91      * !(p)
92      * This queries a {@code TemporalAccessor} for the zone.
93      * The zone is only returned if the date-time conceptually contains a {@code ZoneId}.
94      * It will not be returned if the date-time only conceptually has an {@code ZoneOffset}.
95      * Thus a {@link hunt.time.ZonedDateTime} will return the result of {@code getZone()},
96      * but an {@link hunt.time.OffsetDateTime} will return null.
97      * !(p)
98      * In most cases, applications should use {@link #zone()} as this query is too strict.
99      * !(p)
100      * The result from JDK classes implementing {@code TemporalAccessor} is as follows:!(br)
101      * {@code LocalDate} returns null!(br)
102      * {@code LocalTime} returns null!(br)
103      * {@code LocalDateTime} returns null!(br)
104      * {@code ZonedDateTime} returns the associated zone!(br)
105      * {@code OffsetTime} returns null!(br)
106      * {@code OffsetDateTime} returns null!(br)
107      * {@code ChronoLocalDate} returns null!(br)
108      * {@code ChronoLocalDateTime} returns null!(br)
109      * {@code ChronoZonedDateTime} returns the associated zone!(br)
110      * {@code Era} returns null!(br)
111      * {@code DayOfWeek} returns null!(br)
112      * {@code Month} returns null!(br)
113      * {@code Year} returns null!(br)
114      * {@code YearMonth} returns null!(br)
115      * {@code MonthDay} returns null!(br)
116      * {@code ZoneOffset} returns null!(br)
117      * {@code Instant} returns null!(br)
118      *
119      * @return a query that can obtain the zone ID of a temporal, not null
120      */
121     public static TemporalQuery!(ZoneId) zoneId()
122     {
123         return TemporalQueries.ZONE_ID;
124     }
125 
126     /**
127      * A query for the {@code Chronology}.
128      * !(p)
129      * This queries a {@code TemporalAccessor} for the chronology.
130      * If the target {@code TemporalAccessor} represents a date, or part of a date,
131      * then it should return the chronology that the date is expressed _in.
132      * As a result of this definition, objects only representing time, such as
133      * {@code LocalTime}, will return null.
134      * !(p)
135      * The result from JDK classes implementing {@code TemporalAccessor} is as follows:!(br)
136      * {@code LocalDate} returns {@code IsoChronology.INSTANCE}!(br)
137      * {@code LocalTime} returns null (does not represent a date)!(br)
138      * {@code LocalDateTime} returns {@code IsoChronology.INSTANCE}!(br)
139      * {@code ZonedDateTime} returns {@code IsoChronology.INSTANCE}!(br)
140      * {@code OffsetTime} returns null (does not represent a date)!(br)
141      * {@code OffsetDateTime} returns {@code IsoChronology.INSTANCE}!(br)
142      * {@code ChronoLocalDate} returns the associated chronology!(br)
143      * {@code ChronoLocalDateTime} returns the associated chronology!(br)
144      * {@code ChronoZonedDateTime} returns the associated chronology!(br)
145      * {@code Era} returns the associated chronology!(br)
146      * {@code DayOfWeek} returns null (shared across chronologies)!(br)
147      * {@code Month} returns {@code IsoChronology.INSTANCE}!(br)
148      * {@code Year} returns {@code IsoChronology.INSTANCE}!(br)
149      * {@code YearMonth} returns {@code IsoChronology.INSTANCE}!(br)
150      * {@code MonthDay} returns null {@code IsoChronology.INSTANCE}!(br)
151      * {@code ZoneOffset} returns null (does not represent a date)!(br)
152      * {@code Instant} returns null (does not represent a date)!(br)
153      * !(p)
154      * The method {@link hunt.time.chrono.Chronology#from(TemporalAccessor)} can be used as a
155      * {@code TemporalQuery} via a method reference, {@code Chronology::from}.
156      * That method is equivalent to this query, except that it throws an
157      * exception if a chronology cannot be obtained.
158      *
159      * @return a query that can obtain the chronology of a temporal, not null
160      */
161     public static TemporalQuery!(Chronology) chronology()
162     {
163         return TemporalQueries.CHRONO;
164     }
165 
166     /**
167      * A query for the smallest supported unit.
168      * !(p)
169      * This queries a {@code TemporalAccessor} for the time precision.
170      * If the target {@code TemporalAccessor} represents a consistent or complete date-time,
171      * date or time then this must return the smallest precision actually supported.
172      * Note that fields such as {@code NANO_OF_DAY} and {@code NANO_OF_SECOND}
173      * are defined to always return ignoring the precision, thus this is the only
174      * way to find the actual smallest supported unit.
175      * For example, were {@code GregorianCalendar} to implement {@code TemporalAccessor}
176      * it would return a precision of {@code MILLIS}.
177      * !(p)
178      * The result from JDK classes implementing {@code TemporalAccessor} is as follows:!(br)
179      * {@code LocalDate} returns {@code DAYS}!(br)
180      * {@code LocalTime} returns {@code NANOS}!(br)
181      * {@code LocalDateTime} returns {@code NANOS}!(br)
182      * {@code ZonedDateTime} returns {@code NANOS}!(br)
183      * {@code OffsetTime} returns {@code NANOS}!(br)
184      * {@code OffsetDateTime} returns {@code NANOS}!(br)
185      * {@code ChronoLocalDate} returns {@code DAYS}!(br)
186      * {@code ChronoLocalDateTime} returns {@code NANOS}!(br)
187      * {@code ChronoZonedDateTime} returns {@code NANOS}!(br)
188      * {@code Era} returns {@code ERAS}!(br)
189      * {@code DayOfWeek} returns {@code DAYS}!(br)
190      * {@code Month} returns {@code MONTHS}!(br)
191      * {@code Year} returns {@code YEARS}!(br)
192      * {@code YearMonth} returns {@code MONTHS}!(br)
193      * {@code MonthDay} returns null (does not represent a complete date or time)!(br)
194      * {@code ZoneOffset} returns null (does not represent a date or time)!(br)
195      * {@code Instant} returns {@code NANOS}!(br)
196      *
197      * @return a query that can obtain the precision of a temporal, not null
198      */
199     public static TemporalQuery!(TemporalUnit) precision()
200     {
201         return TemporalQueries.PRECISION;
202     }
203 
204     //-----------------------------------------------------------------------
205     // non-special constants are standard queries that derive information from other information
206     /**
207      * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}.
208      * !(p)
209      * This queries a {@code TemporalAccessor} for the zone.
210      * It first tries to obtain the zone, using {@link #zoneId()}.
211      * If that is not found it tries to obtain the {@link #offset()}.
212      * Thus a {@link hunt.time.ZonedDateTime} will return the result of {@code getZone()},
213      * while an {@link hunt.time.OffsetDateTime} will return the result of {@code getOffset()}.
214      * !(p)
215      * In most cases, applications should use this query rather than {@code #zoneId()}.
216      * !(p)
217      * The method {@link ZoneId#from(TemporalAccessor)} can be used as a
218      * {@code TemporalQuery} via a method reference, {@code ZoneId::from}.
219      * That method is equivalent to this query, except that it throws an
220      * exception if a zone cannot be obtained.
221      *
222      * @return a query that can obtain the zone ID or offset of a temporal, not null
223      */
224     public static TemporalQuery!(ZoneId) zone()
225     {
226         return TemporalQueries.ZONE;
227     }
228 
229     /**
230      * A query for {@code ZoneOffset} returning null if not found.
231      * !(p)
232      * This returns a {@code TemporalQuery} that can be used to query a temporal
233      * object for the offset. The query will return null if the temporal
234      * object cannot supply an offset.
235      * !(p)
236      * The query implementation examines the {@link ChronoField#OFFSET_SECONDS OFFSET_SECONDS}
237      * field and uses it to create a {@code ZoneOffset}.
238      * !(p)
239      * The method {@link hunt.time.ZoneOffset#from(TemporalAccessor)} can be used as a
240      * {@code TemporalQuery} via a method reference, {@code ZoneOffset::from}.
241      * This query and {@code ZoneOffset::from} will return the same result if the
242      * temporal object contains an offset. If the temporal object does not contain
243      * an offset, then the method reference will throw an exception, whereas this
244      * query will return null.
245      *
246      * @return a query that can obtain the offset of a temporal, not null
247      */
248     public static TemporalQuery!(ZoneOffset) offset()
249     {
250         return TemporalQueries.OFFSET;
251     }
252 
253     /**
254      * A query for {@code LocalDate} returning null if not found.
255      * !(p)
256      * This returns a {@code TemporalQuery} that can be used to query a temporal
257      * object for the local date. The query will return null if the temporal
258      * object cannot supply a local date.
259      * !(p)
260      * The query implementation examines the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
261      * field and uses it to create a {@code LocalDate}.
262      * !(p)
263      * The method {@link ZoneOffset#from(TemporalAccessor)} can be used as a
264      * {@code TemporalQuery} via a method reference, {@code LocalDate.from}.
265      * This query and {@code LocalDate.from} will return the same result if the
266      * temporal object contains a date. If the temporal object does not contain
267      * a date, then the method reference will throw an exception, whereas this
268      * query will return null.
269      *
270      * @return a query that can obtain the date of a temporal, not null
271      */
272     public static TemporalQuery!(LocalDate) localDate()
273     {
274         return TemporalQueries.LOCAL_DATE;
275     }
276 
277     /**
278      * A query for {@code LocalTime} returning null if not found.
279      * !(p)
280      * This returns a {@code TemporalQuery} that can be used to query a temporal
281      * object for the local time. The query will return null if the temporal
282      * object cannot supply a local time.
283      * !(p)
284      * The query implementation examines the {@link ChronoField#NANO_OF_DAY NANO_OF_DAY}
285      * field and uses it to create a {@code LocalTime}.
286      * !(p)
287      * The method {@link ZoneOffset#from(TemporalAccessor)} can be used as a
288      * {@code TemporalQuery} via a method reference, {@code LocalTime.from}.
289      * This query and {@code LocalTime.from} will return the same result if the
290      * temporal object contains a time. If the temporal object does not contain
291      * a time, then the method reference will throw an exception, whereas this
292      * query will return null.
293      *
294      * @return a query that can obtain the time of a temporal, not null
295      */
296     public static TemporalQuery!(LocalTime) localTime()
297     {
298         return TemporalQueries.LOCAL_TIME;
299     }
300 
301     //-----------------------------------------------------------------------
302     /**
303      * A strict query for the {@code ZoneId}.
304      */
305     // __gshared TemporalQuery!(ZoneId) ZONE_ID;
306 
307     /**
308      * A query for the {@code Chronology}.
309      */
310     //__gshared TemporalQuery!(Chronology) CHRONO;
311 
312     /**
313      * A query for the smallest supported unit.
314      */
315     //__gshared TemporalQuery!(TemporalUnit) PRECISION;
316 
317     //-----------------------------------------------------------------------
318     /**
319      * A query for {@code ZoneOffset} returning null if not found.
320      */
321     //__gshared TemporalQuery!(ZoneOffset) OFFSET;
322 
323     /**
324      * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}.
325      */
326     //__gshared TemporalQuery!(ZoneId) ZONE;
327 
328     /**
329      * A query for {@code LocalDate} returning null if not found.
330      */
331     //__gshared TemporalQuery!(LocalDate) LOCAL_DATE;
332 
333     /**
334      * A query for {@code LocalTime} returning null if not found.
335      */
336     //__gshared TemporalQuery!(LocalTime) LOCAL_TIME;
337 
338     // shared static this()
339     // {
340         // ZONE_ID = new class TemporalQuery!(ZoneId)
341         // {
342         //     override public ZoneId queryFrom(TemporalAccessor temporal)
343         //     {
344         //         return QueryHelper.query!ZoneId(temporal, TemporalQueries.ZONE_ID);
345         //     }
346 
347         //     override public string toString()
348         //     {
349         //         return "ZoneId";
350         //     }
351         // };
352 
353         mixin(MakeGlobalVar!(TemporalQuery!(ZoneId))("ZONE_ID",` new class TemporalQuery!(ZoneId)
354         {
355             override public ZoneId queryFrom(TemporalAccessor temporal)
356             {
357                 return QueryHelper.query!ZoneId(temporal, TemporalQueries.ZONE_ID);
358             }
359 
360             override public string toString()
361             {
362                 return "ZoneId";
363             }
364         }`));
365 
366         /**
367      * A query for the {@code Chronology}.
368      */
369         // CHRONO = new class TemporalQuery!(Chronology)
370         // {
371         //     override public Chronology queryFrom(TemporalAccessor temporal)
372         //     {
373         //         return QueryHelper.query!Chronology(temporal, TemporalQueries.CHRONO);
374         //     }
375 
376         //     override public string toString()
377         //     {
378         //         return "Chronology";
379         //     }
380         // };
381 
382         mixin(MakeGlobalVar!(TemporalQuery!(Chronology))("CHRONO",`new class TemporalQuery!(Chronology)
383         {
384             override public Chronology queryFrom(TemporalAccessor temporal)
385             {
386                 return QueryHelper.query!Chronology(temporal, TemporalQueries.CHRONO);
387             }
388 
389             override public string toString()
390             {
391                 return "Chronology";
392             }
393         }`));
394 
395         /**
396      * A query for the smallest supported unit.
397      */
398         // PRECISION = new class TemporalQuery!(TemporalUnit)
399         // {
400         //     override public TemporalUnit queryFrom(TemporalAccessor temporal)
401         //     {
402         //         return QueryHelper.query!TemporalUnit(temporal, TemporalQueries.PRECISION);
403         //     }
404 
405         //     override public string toString()
406         //     {
407         //         return "Precision";
408         //     }
409         // };
410 
411         mixin(MakeGlobalVar!(TemporalQuery!(TemporalUnit))("PRECISION",`new class TemporalQuery!(TemporalUnit)
412         {
413             override public TemporalUnit queryFrom(TemporalAccessor temporal)
414             {
415                 return QueryHelper.query!TemporalUnit(temporal, TemporalQueries.PRECISION);
416             }
417 
418             override public string toString()
419             {
420                 return "Precision";
421             }
422         }`));
423 
424         //-----------------------------------------------------------------------
425         /**
426      * A query for {@code ZoneOffset} returning null if not found.
427      */
428         // OFFSET = new class TemporalQuery!(ZoneOffset)
429         // {
430         //     override public ZoneOffset queryFrom(TemporalAccessor temporal)
431         //     {
432         //         if (temporal.isSupported(ChronoField.OFFSET_SECONDS))
433         //         {
434         //             return ZoneOffset.ofTotalSeconds(temporal.get(ChronoField.OFFSET_SECONDS));
435         //         }
436         //         return null;
437         //     }
438 
439         //     override public string toString()
440         //     {
441         //         return "ZoneOffset";
442         //     }
443         // };
444 
445         mixin(MakeGlobalVar!(TemporalQuery!(ZoneOffset))("OFFSET",`new class TemporalQuery!(ZoneOffset)
446         {
447             override public ZoneOffset queryFrom(TemporalAccessor temporal)
448             {
449                 if (temporal.isSupported(ChronoField.OFFSET_SECONDS))
450                 {
451                     return ZoneOffset.ofTotalSeconds(temporal.get(ChronoField.OFFSET_SECONDS));
452                 }
453                 return null;
454             }
455 
456             override public string toString()
457             {
458                 return "ZoneOffset";
459             }
460         }`));
461 
462         /**
463      * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}.
464      */
465         // ZONE = new class TemporalQuery!(ZoneId)
466         // {
467         //     override public ZoneId queryFrom(TemporalAccessor temporal)
468         //     {
469         //         ZoneId zone = QueryHelper.query!ZoneId(temporal, ZONE_ID);
470         //         return (zone !is null ? zone : QueryHelper.query!ZoneOffset(temporal, OFFSET));
471         //     }
472 
473         //     override public string toString()
474         //     {
475         //         return "Zone";
476         //     }
477         // };
478 
479         mixin(MakeGlobalVar!(TemporalQuery!(ZoneId))("ZONE",`new class TemporalQuery!(ZoneId)
480         {
481             override public ZoneId queryFrom(TemporalAccessor temporal)
482             {
483                 ZoneId zone = QueryHelper.query!ZoneId(temporal, ZONE_ID);
484                 return (zone !is null ? zone : QueryHelper.query!ZoneOffset(temporal, OFFSET));
485             }
486 
487             override public string toString()
488             {
489                 return "Zone";
490             }
491         }`));
492         /**
493      * A query for {@code LocalDate} returning null if not found.
494      */
495         // LOCAL_DATE = new class TemporalQuery!(LocalDate)
496         // {
497         //     override public LocalDate queryFrom(TemporalAccessor temporal)
498         //     {
499         //         if (temporal.isSupported(ChronoField.EPOCH_DAY))
500         //         {
501         //             return LocalDate.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
502         //         }
503         //         return null;
504         //     }
505 
506         //     override public string toString()
507         //     {
508         //         return "LocalDate";
509         //     }
510         // };
511 
512         
513         mixin(MakeGlobalVar!(TemporalQuery!(LocalDate))("LOCAL_DATE",`new class TemporalQuery!(LocalDate)
514         {
515             override public LocalDate queryFrom(TemporalAccessor temporal)
516             {
517                 if (temporal.isSupported(ChronoField.EPOCH_DAY))
518                 {
519                     return LocalDate.ofEpochDay(temporal.getLong(ChronoField.EPOCH_DAY));
520                 }
521                 return null;
522             }
523 
524             override public string toString()
525             {
526                 return "LocalDate";
527             }
528         }`));
529 
530         /**
531      * A query for {@code LocalTime} returning null if not found.
532      */
533         // LOCAL_TIME = new class TemporalQuery!(LocalTime)
534         // {
535         //     override public LocalTime queryFrom(TemporalAccessor temporal)
536         //     {
537         //         if (temporal.isSupported(ChronoField.NANO_OF_DAY))
538         //         {
539         //             return LocalTime.ofNanoOfDay(temporal.getLong(ChronoField.NANO_OF_DAY));
540         //         }
541         //         return null;
542         //     }
543 
544         //     override public string toString()
545         //     {
546         //         return "LocalTime";
547         //     }
548         // };
549         mixin(MakeGlobalVar!(TemporalQuery!(LocalTime))("LOCAL_TIME",`new class TemporalQuery!(LocalTime)
550         {
551             override public LocalTime queryFrom(TemporalAccessor temporal)
552             {
553                 if (temporal.isSupported(ChronoField.NANO_OF_DAY))
554                 {
555                     return LocalTime.ofNanoOfDay(temporal.getLong(ChronoField.NANO_OF_DAY));
556                 }
557                 return null;
558             }
559 
560             override public string toString()
561             {
562                 return "LocalTime";
563             }
564         }`));
565     // }
566 }