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 }