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.TemporalUnit; 13 14 15 16 import hunt.time.Exceptions; 17 import hunt.time.Duration; 18 // import hunt.time.LocalTime; 19 import hunt.time.Period; 20 import hunt.time.chrono.ChronoLocalDate; 21 import hunt.time.chrono.ChronoLocalDateTime; 22 import hunt.time.chrono.ChronoZonedDateTime; 23 import hunt.time.temporal.Temporal; 24 import hunt.time.Exceptions; 25 26 import hunt.Enum; 27 import hunt.Exceptions; 28 /** 29 * A unit of date-time, such as Days or Hours. 30 * !(p) 31 * Measurement of time is built on units, such as years, months, days, hours, minutes and seconds. 32 * Implementations of this interface represent those units. 33 * !(p) 34 * An instance of this interface represents the unit itself, rather than an amount of the unit. 35 * See {@link Period} for a class that represents an amount _in terms of the common units. 36 * !(p) 37 * The most commonly used units are defined _in {@link ChronoUnit}. 38 * Further units are supplied _in {@link IsoFields}. 39 * Units can also be written by application code by implementing this interface. 40 * !(p) 41 * The unit works using double dispatch. Client code calls methods on a date-time like 42 * {@code LocalDateTime} which check if the unit is a {@code ChronoUnit}. 43 * If it is, then the date-time must handle it. 44 * Otherwise, the method call is re-dispatched to the matching method _in this interface. 45 * 46 * @implSpec 47 * This interface must be implemented with care to ensure other classes operate correctly. 48 * All implementations that can be instantiated must be final, immutable and thread-safe. 49 * It is recommended to use an enum where possible. 50 * 51 */ 52 abstract class TemporalUnit : AbstractEnum!TemporalUnit { 53 54 protected this(string name, int ordinal) { 55 super(name, ordinal); 56 } 57 58 /** 59 * Gets the duration of this unit, which may be an estimate. 60 * !(p) 61 * All units return a duration measured _in standard nanoseconds from this method. 62 * The duration will be positive and non-zero. 63 * For example, an hour has a duration of {@code 60 * 60 * 1,000,000,000ns}. 64 * !(p) 65 * Some units may return an accurate duration while others return an estimate. 66 * For example, days have an estimated duration due to the possibility of 67 * daylight saving time changes. 68 * To determine if the duration is an estimate, use {@link #isDurationEstimated()}. 69 * 70 * @return the duration of this unit, which may be an estimate, not null 71 */ 72 Duration getDuration(); 73 74 /** 75 * Checks if the duration of the unit is an estimate. 76 * !(p) 77 * All units have a duration, however the duration is not always accurate. 78 * For example, days have an estimated duration due to the possibility of 79 * daylight saving time changes. 80 * This method returns true if the duration is an estimate and false if it is 81 * accurate. Note that accurate/estimated ignores leap seconds. 82 * 83 * @return true if the duration is estimated, false if accurate 84 */ 85 bool isDurationEstimated(); 86 87 //----------------------------------------------------------------------- 88 /** 89 * Checks if this unit represents a component of a date. 90 * !(p) 91 * A date is time-based if it can be used to imply meaning from a date. 92 * It must have a {@linkplain #getDuration() duration} that is an integral 93 * multiple of the length of a standard day. 94 * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()} 95 * to return false, such as when representing a unit like 36 hours. 96 * 97 * @return true if this unit is a component of a date 98 */ 99 bool isDateBased(); 100 101 /** 102 * Checks if this unit represents a component of a time. 103 * !(p) 104 * A unit is time-based if it can be used to imply meaning from a time. 105 * It must have a {@linkplain #getDuration() duration} that divides into 106 * the length of a standard day without remainder. 107 * Note that it is valid for both {@code isDateBased()} and {@code isTimeBased()} 108 * to return false, such as when representing a unit like 36 hours. 109 * 110 * @return true if this unit is a component of a time 111 */ 112 bool isTimeBased(); 113 114 //----------------------------------------------------------------------- 115 /** 116 * Checks if this unit is supported by the specified temporal object. 117 * !(p) 118 * This checks that the implementing date-time can add/subtract this unit. 119 * This can be used to avoid throwing an exception. 120 * !(p) 121 * This implementation derives the value using 122 * {@link Temporal#plus(long, TemporalUnit)}. 123 * 124 * @param temporal the temporal object to check, not null 125 * @return true if the unit is supported 126 */ 127 bool isSupportedBy(Temporal temporal) { 128 // TODO: Tasks pending completion -@zhangxueping at 4/4/2019, 2:35:59 PM 129 // 130 // if (cast(LocalTime)(temporal) !is null) { 131 // return isTimeBased(); 132 // } 133 if (cast(ChronoLocalDate)(temporal) !is null) { 134 return isDateBased(); 135 } 136 if (cast(ChronoLocalDateTime!ChronoLocalDate)(temporal) !is null || 137 cast(ChronoZonedDateTime!ChronoLocalDate)(temporal) !is null) { 138 return true; 139 } 140 try { 141 temporal.plus(1, this); 142 return true; 143 } catch (UnsupportedTemporalTypeException ex) { 144 return false; 145 } catch (RuntimeException ex) { 146 try { 147 temporal.plus(-1, this); 148 return true; 149 } catch (RuntimeException ex2) { 150 return false; 151 } 152 } 153 } 154 155 /** 156 * Returns a copy of the specified temporal object with the specified period added. 157 * !(p) 158 * The period added is a multiple of this unit. For example, this method 159 * could be used to add "3 days" to a date by calling this method on the 160 * instance representing "days", passing the date and the period "3". 161 * The period to be added may be negative, which is equivalent to subtraction. 162 * !(p) 163 * There are two equivalent ways of using this method. 164 * The first is to invoke this method directly. 165 * The second is to use {@link Temporal#plus(long, TemporalUnit)}: 166 * !(pre) 167 * // these two lines are equivalent, but the second approach is recommended 168 * temporal = thisUnit.addTo(temporal); 169 * temporal = temporal.plus(thisUnit); 170 * </pre> 171 * It is recommended to use the second approach, {@code plus(TemporalUnit)}, 172 * as it is a lot clearer to read _in code. 173 * !(p) 174 * Implementations should perform any queries or calculations using the units 175 * available _in {@link ChronoUnit} or the fields available _in {@link ChronoField}. 176 * If the unit is not supported an {@code UnsupportedTemporalTypeException} must be thrown. 177 * !(p) 178 * Implementations must not alter the specified temporal object. 179 * Instead, an adjusted copy of the original must be returned. 180 * This provides equivalent, safe behavior for immutable and mutable implementations. 181 * 182 * @param !(R) the type of the Temporal object 183 * @param temporal the temporal object to adjust, not null 184 * @param amount the amount of this unit to add, positive or negative 185 * @return the adjusted temporal object, not null 186 * @throws DateTimeException if the amount cannot be added 187 * @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal 188 */ 189 Temporal addTo(Temporal temporal, long amount); 190 191 //----------------------------------------------------------------------- 192 /** 193 * Calculates the amount of time between two temporal objects. 194 * !(p) 195 * This calculates the amount _in terms of this unit. The start and end 196 * points are supplied as temporal objects and must be of compatible types. 197 * The implementation will convert the second type to be an instance of the 198 * first type before the calculating the amount. 199 * The result will be negative if the end is before the start. 200 * For example, the amount _in hours between two temporal objects can be 201 * calculated using {@code HOURS.between(startTime, endTime)}. 202 * !(p) 203 * The calculation returns a whole number, representing the number of 204 * complete units between the two temporals. 205 * For example, the amount _in hours between the times 11:30 and 13:29 206 * will only be one hour as it is one minute short of two hours. 207 * !(p) 208 * There are two equivalent ways of using this method. 209 * The first is to invoke this method directly. 210 * The second is to use {@link Temporal#until(Temporal, TemporalUnit)}: 211 * !(pre) 212 * // these two lines are equivalent 213 * between = thisUnit.between(start, end); 214 * between = start.until(end, thisUnit); 215 * </pre> 216 * The choice should be made based on which makes the code more readable. 217 * !(p) 218 * For example, this method allows the number of days between two dates to 219 * be calculated: 220 * !(pre) 221 * long daysBetween = DAYS.between(start, end); 222 * // or alternatively 223 * long daysBetween = start.until(end, DAYS); 224 * </pre> 225 * !(p) 226 * Implementations should perform any queries or calculations using the units 227 * available _in {@link ChronoUnit} or the fields available _in {@link ChronoField}. 228 * If the unit is not supported an {@code UnsupportedTemporalTypeException} must be thrown. 229 * Implementations must not alter the specified temporal objects. 230 * 231 * @implSpec 232 * Implementations must begin by checking to if the two temporals have the 233 * same type using {@code getClass()}. If they do not, then the result must be 234 * obtained by calling {@code temporal1Inclusive.until(temporal2Exclusive, this)}. 235 * 236 * @param temporal1Inclusive the base temporal object, not null 237 * @param temporal2Exclusive the other temporal object, exclusive, not null 238 * @return the amount of time between temporal1Inclusive and temporal2Exclusive 239 * _in terms of this unit; positive if temporal2Exclusive is later than 240 * temporal1Inclusive, negative if earlier 241 * @throws DateTimeException if the amount cannot be calculated, or the end 242 * temporal cannot be converted to the same type as the start temporal 243 * @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal 244 * @throws ArithmeticException if numeric overflow occurs 245 */ 246 long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive); 247 248 //----------------------------------------------------------------------- 249 /** 250 * Gets a descriptive name for the unit. 251 * !(p) 252 * This should be _in the plural and upper-first camel case, such as 'Days' or 'Minutes'. 253 * 254 * @return the name of this unit, not null 255 */ 256 // override 257 // string toString(); 258 259 }