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.TemporalAccessor;
13 
14 import hunt.time.Exceptions;
15 import hunt.time.temporal.TemporalField;
16 import hunt.time.temporal.ValueRange;
17 import hunt.time.temporal.TemporalQuery;
18 
19 /**
20  * Framework-level interface defining read-only access to a temporal object,
21  * such as a date, time, offset or some combination of these.
22  * !(p)
23  * This is the base interface type for date, time and offset objects.
24  * It is implemented by those classes that can provide information
25  * as {@linkplain TemporalField fields} or {@linkplain TemporalQuery queries}.
26  * !(p)
27  * Most date and time information can be represented as a number.
28  * These are modeled using {@code TemporalField} with the number held using
29  * a {@code long} to handle large values. Year, month and day-of-month are
30  * simple examples of fields, but they also include instant and offsets.
31  * See {@link ChronoField} for the standard set of fields.
32  * !(p)
33  * Two pieces of date/time information cannot be represented by numbers,
34  * the {@linkplain hunt.time.chrono.Chronology chronology} and the
35  * {@linkplain hunt.time.ZoneId time-zone}.
36  * These can be accessed via {@linkplain #query(TemporalQuery) queries} using
37  * the static methods defined on {@link TemporalQuery}.
38  * !(p)
39  * A sub-interface, {@link Temporal}, extends this definition to one that also
40  * supports adjustment and manipulation on more complete temporal objects.
41  * !(p)
42  * This interface is a framework-level interface that should not be widely
43  * used _in application code. Instead, applications should create and pass
44  * around instances of concrete types, such as {@code LocalDate}.
45  * There are many reasons for this, part of which is that implementations
46  * of this interface may be _in calendar systems other than ISO.
47  * See {@link hunt.time.chrono.ChronoLocalDate} for a fuller discussion of the issues.
48  *
49  * @implSpec
50  * This interface places no restrictions on the mutability of implementations,
51  * however immutability is strongly recommended.
52  *
53  * @since 1.8
54  */
55 public interface TemporalAccessor {
56 
57     /**
58      * Checks if the specified field is supported.
59      * !(p)
60      * This checks if the date-time can be queried for the specified field.
61      * If false, then calling the {@link #range(TemporalField) range} and {@link #get(TemporalField) get}
62      * methods will throw an exception.
63      *
64      * @implSpec
65      * Implementations must check and handle all fields defined _in {@link ChronoField}.
66      * If the field is supported, then true must be returned, otherwise false must be returned.
67      * !(p)
68      * If the field is not a {@code ChronoField}, then the result of this method
69      * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
70      * passing {@code this} as the argument.
71      * !(p)
72      * Implementations must ensure that no observable state is altered when this
73      * read-only method is invoked.
74      *
75      * @param field  the field to check, null returns false
76      * @return true if this date-time can be queried for the field, false if not
77      */
78     bool isSupported(TemporalField field);
79 
80     /**
81      * Gets the range of valid values for the specified field.
82      * !(p)
83      * All fields can be expressed as a {@code long} integer.
84      * This method returns an object that describes the valid range for that value.
85      * The value of this temporal object is used to enhance the accuracy of the returned range.
86      * If the date-time cannot return the range, because the field is unsupported or for
87      * some other reason, an exception will be thrown.
88      * !(p)
89      * Note that the result only describes the minimum and maximum valid values
90      * and it is important not to read too much into them. For example, there
91      * could be values within the range that are invalid for the field.
92      *
93      * @implSpec
94      * Implementations must check and handle all fields defined _in {@link ChronoField}.
95      * If the field is supported, then the range of the field must be returned.
96      * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
97      * !(p)
98      * If the field is not a {@code ChronoField}, then the result of this method
99      * is obtained by invoking {@code TemporalField.rangeRefinedBy(TemporalAccessorl)}
100      * passing {@code this} as the argument.
101      * !(p)
102      * Implementations must ensure that no observable state is altered when this
103      * read-only method is invoked.
104      * !(p)
105      * The  implementation must behave equivalent to this code:
106      * !(pre)
107      *  if (cast(ChronoField)(field) !is null) {
108      *    if (isSupported(field)) {
109      *      return field.range();
110      *    }
111      *    throw new UnsupportedTemporalTypeException("Unsupported field: " ~ field);
112      *  }
113      *  return field.rangeRefinedBy(this);
114      * </pre>
115      *
116      * @param field  the field to query the range for, not null
117      * @return the range of valid values for the field, not null
118      * @throws DateTimeException if the range for the field cannot be obtained
119      * @throws UnsupportedTemporalTypeException if the field is not supported
120      */
121      ValueRange range(TemporalField field);
122     //   ValueRange range(TemporalField field) {
123     //     if (cast(ChronoField)(field) !is null) {
124     //         if (isSupported(field)) {
125     //             return field.range();
126     //         }
127     //         throw new UnsupportedTemporalTypeException("Unsupported field: " ~ field);
128     //     }
129     //     assert(field, "field");
130     //     return field.rangeRefinedBy(this);
131     // }
132 
133     /**
134      * Gets the value of the specified field as an {@code int}.
135      * !(p)
136      * This queries the date-time for the value of the specified field.
137      * The returned value will always be within the valid range of values for the field.
138      * If the date-time cannot return the value, because the field is unsupported or for
139      * some other reason, an exception will be thrown.
140      *
141      * @implSpec
142      * Implementations must check and handle all fields defined _in {@link ChronoField}.
143      * If the field is supported and has an {@code int} range, then the value of
144      * the field must be returned.
145      * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
146      * !(p)
147      * If the field is not a {@code ChronoField}, then the result of this method
148      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
149      * passing {@code this} as the argument.
150      * !(p)
151      * Implementations must ensure that no observable state is altered when this
152      * read-only method is invoked.
153      * !(p)
154      * The  implementation must behave equivalent to this code:
155      * !(pre)
156      *  if (range(field).isIntValue()) {
157      *    return range(field).checkValidIntValue(getLong(field), field);
158      *  }
159      *  throw new UnsupportedTemporalTypeException("Invalid field " ~ field ~ " ~ for get() method, use getLong() instead");
160      * </pre>
161      *
162      * @param field  the field to get, not null
163      * @return the value for the field, within the valid range of values
164      * @throws DateTimeException if a value for the field cannot be obtained or
165      *         the value is outside the range of valid values for the field
166      * @throws UnsupportedTemporalTypeException if the field is not supported or
167      *         the range of values exceeds an {@code int}
168      * @throws ArithmeticException if numeric overflow occurs
169      */
170      int get(TemporalField field);
171     //  int get(TemporalField field) {
172     //     ValueRange range = range(field);
173     //     if (range.isIntValue() == false) {
174     //         throw new UnsupportedTemporalTypeException("Invalid field " ~ field ~ " for get() method, use getLong() instead");
175     //     }
176     //     long value = getLong(field);
177     //     if (range.isValidValue(value) == false) {
178     //         throw new DateTimeException("Invalid value for " ~ field ~ " (valid values " ~ range ~ "): " ~ value);
179     //     }
180     //     return cast(int) value;
181     // }
182 
183     /**
184      * Gets the value of the specified field as a {@code long}.
185      * !(p)
186      * This queries the date-time for the value of the specified field.
187      * The returned value may be outside the valid range of values for the field.
188      * If the date-time cannot return the value, because the field is unsupported or for
189      * some other reason, an exception will be thrown.
190      *
191      * @implSpec
192      * Implementations must check and handle all fields defined _in {@link ChronoField}.
193      * If the field is supported, then the value of the field must be returned.
194      * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown.
195      * !(p)
196      * If the field is not a {@code ChronoField}, then the result of this method
197      * is obtained by invoking {@code TemporalField.getFrom(TemporalAccessor)}
198      * passing {@code this} as the argument.
199      * !(p)
200      * Implementations must ensure that no observable state is altered when this
201      * read-only method is invoked.
202      *
203      * @param field  the field to get, not null
204      * @return the value for the field
205      * @throws DateTimeException if a value for the field cannot be obtained
206      * @throws UnsupportedTemporalTypeException if the field is not supported
207      * @throws ArithmeticException if numeric overflow occurs
208      */
209     long getLong(TemporalField field);
210 
211     /**
212      * Queries this date-time.
213      * !(p)
214      * This queries this date-time using the specified query strategy object.
215      * !(p)
216      * Queries are a key tool for extracting information from date-times.
217      * They exists to externalize the process of querying, permitting different
218      * approaches, as per the strategy design pattern.
219      * Examples might be a query that checks if the date is the day before February 29th
220      * _in a leap year, or calculates the number of days to your next birthday.
221      * !(p)
222      * The most common query implementations are method references, such as
223      * {@code LocalDate::from} and {@code ZoneId::from}.
224      * Additional implementations are provided as static methods on {@link TemporalQuery}.
225      *
226      * @implSpec
227      * The  implementation must behave equivalent to this code:
228      * !(pre)
229      *  if (query == TemporalQueries.zoneId() ||
230      *        query == TemporalQueries.chronology() || query == TemporalQueries.precision()) {
231      *    return null;
232      *  }
233      *  return query.queryFrom(this);
234      * </pre>
235      * Future versions are permitted to add further queries to the if statement.
236      * !(p)
237      * All classes implementing this interface and overriding this method must call
238      * {@code TemporalAccessor.super.query(query)}. JDK classes may avoid calling
239      * super if they provide behavior equivalent to the  behaviour, however
240      * non-JDK classes may not utilize this optimization and must call {@code super}.
241      * !(p)
242      * If the implementation can supply a value for one of the queries listed _in the
243      * if statement of the  implementation, then it must do so.
244      * For example, an application-defined {@code HourMin} class storing the hour
245      * and minute must override this method as follows:
246      * !(pre)
247      *  if (query == TemporalQueries.precision()) {
248      *    return MINUTES;
249      *  }
250      *  return TemporalAccessor.super.query(query);
251      * </pre>
252      * !(p)
253      * Implementations must ensure that no observable state is altered when this
254      * read-only method is invoked.
255      *
256      * @param !(R) the type of the result
257      * @param query  the query to invoke, not null
258      * @return the query result, null may be returned (defined by the query)
259      * @throws DateTimeException if unable to query
260      * @throws ArithmeticException if numeric overflow occurs
261      */
262      R query(R)(TemporalQuery!(R) query);
263     //   R query(R)(TemporalQuery!(R) query) {
264     //     if (query == TemporalQueries.zoneId()
265     //             || query == TemporalQueries.chronology()
266     //             || query == TemporalQueries.precision()) {
267     //         return null;
268     //     }
269     //     return query.queryFrom(this);
270     // }
271     string toString();
272 }