001/* 002 * ============================================================================ 003 * Copyright © 2002-2025 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.value; 019 020import static java.lang.String.format; 021import static java.math.BigDecimal.TEN; 022import static java.util.Arrays.stream; 023import static org.apiguardian.api.API.Status.STABLE; 024import static org.tquadrat.foundation.lang.CommonConstants.TROPICAL_YEAR; 025import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 026import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 027 028import java.math.BigDecimal; 029import java.time.Duration; 030import java.time.temporal.ChronoUnit; 031import java.time.temporal.Temporal; 032import java.time.temporal.TemporalUnit; 033import java.util.Optional; 034import java.util.concurrent.TimeUnit; 035 036import org.apiguardian.api.API; 037import org.tquadrat.foundation.annotation.ClassVersion; 038import org.tquadrat.foundation.lang.CommonConstants; 039import org.tquadrat.foundation.value.api.DimensionWithLinearConversion; 040 041/** 042 * <p>{@summary The various instances of time periods …}</p> 043 * <p>The instance of {@code Time} refers to the respective instances of 044 * {@link java.util.concurrent.TimeUnit} 045 * and 046 * {@link java.time.temporal.ChronoUnit} 047 * wherever possible.</p> 048 * 049 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 050 * @version $Id: Time.java 1151 2025-10-01 21:32:15Z tquadrat $ 051 * @since 0.1.0 052 * 053 * @UMLGraph.link 054 */ 055@SuppressWarnings( "NewClassNamingConvention" ) 056@ClassVersion( sourceVersion = "$Id: Time.java 1151 2025-10-01 21:32:15Z tquadrat $" ) 057@API( status = STABLE, since = "0.1.0" ) 058public enum Time implements DimensionWithLinearConversion 059{ 060 /*------------------*\ 061 ====** Enum Declaration **================================================= 062 \*------------------*/ 063 /** 064 * A nanosecond. 065 */ 066 NANOSECOND( new BigDecimal( "1E-9" ), "ns", TimeUnit.NANOSECONDS, ChronoUnit.NANOS ), 067 068 /** 069 * A microsecond. 070 */ 071 MICROSECOND( new BigDecimal( "1E-6" ), "µs", TimeUnit.MICROSECONDS, ChronoUnit.MICROS ), 072 073 /** 074 * A millisecond. 075 */ 076 MILLISECOND( new BigDecimal( "0.001" ), "ms", TimeUnit.MILLISECONDS, ChronoUnit.MILLIS ), 077 078 /** 079 * A second. 080 */ 081 SECOND( BigDecimal.ONE, "s", TimeUnit.SECONDS, ChronoUnit.SECONDS ), 082 083 /** 084 * A minute. 085 */ 086 MINUTE( new BigDecimal( "60" ), "min", TimeUnit.MINUTES, ChronoUnit.MINUTES ), 087 088 /** 089 * An hour. 090 */ 091 HOUR( new BigDecimal( "3600.0" ), "h", TimeUnit.HOURS, ChronoUnit.HOURS ), 092 093 /** 094 * Half a day. 095 */ 096 HALF_DAY( new BigDecimal( "43200.0" ), "d/2", null, ChronoUnit.HALF_DAYS ) 097 { 098 /** 099 * {@inheritDoc} 100 */ 101 @Override 102 public final String unitSymbolForPrinting() { return "½d"; } 103 }, 104 105 /** 106 * A day. 107 */ 108 DAY( new BigDecimal( "86400.0" ), "d", TimeUnit.DAYS, ChronoUnit.DAYS ), 109 110 /** 111 * A week (7 days). 112 */ 113 WEEK( new BigDecimal( "604800.0" ), "w", null, ChronoUnit.WEEKS ), 114 115 /** 116 * A fortnight (2 weeks or 14 days). 117 */ 118 FORTNIGHT( new BigDecimal( "1209600.0" ), "fortnight", null, null ) 119 { 120 /** 121 * {@inheritDoc} 122 */ 123 @Override 124 protected final TemporalUnit asTemporalUnit() 125 { 126 final var retValue = new TemporalUnitImpl( "Fortnights" ); 127 128 //---* Done *------------------------------------------------------ 129 return retValue; 130 } // asTemporalUnit 131 }, 132 133 /** 134 * <p>{@summary A bank month.}</p> 135 * <p>This has a fixed length of exact 30 days.</p> 136 */ 137 BANK_MONTH( new BigDecimal( "2592000.0" ), "month30", null, null ) 138 { 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 protected final TemporalUnit asTemporalUnit() 144 { 145 final var retValue = new TemporalUnitImpl( "BankMonths" ); 146 147 //---* Done *------------------------------------------------------ 148 return retValue; 149 } // asTemporalUnit 150 }, 151 152 /** 153 * <p>{@summary A month.}</p> 154 * <p>This is the average month, calculated using the average year with 155 * 365.2425 days divided by 12.</p> 156 */ 157 MONTH( new BigDecimal( "2629746.0" ), "month", null, ChronoUnit.MONTHS ), 158 159 /** 160 * <p>{@summary A bank year.}</p> 161 * <p>This has a fixed length of exact 360 days.</p> 162 */ 163 BANK_YEAR( new BigDecimal( "31104000.0" ), "yr360", null, null ) 164 { 165 /** 166 * {@inheritDoc} 167 */ 168 @Override 169 protected final TemporalUnit asTemporalUnit() 170 { 171 final var retValue = new TemporalUnitImpl( "BankYears" ); 172 173 //---* Done *------------------------------------------------------ 174 return retValue; 175 } // asTemporalUnit 176 }, 177 178 /** 179 * A tropical year, according to SI. 180 * 181 * @see CommonConstants#TROPICAL_YEAR 182 */ 183 YEAR( new BigDecimal( "86400.0" ).multiply( TROPICAL_YEAR ), "yr", null, null ) 184 { 185 /** 186 * {@inheritDoc} 187 */ 188 @Override 189 protected final TemporalUnit asTemporalUnit() 190 { 191 final var retValue = new TemporalUnitImpl( "TropicalYears" ); 192 193 //---* Done *------------------------------------------------------ 194 return retValue; 195 } // asTemporalUnit 196 }, 197 198 /** 199 * <p>{@summary A year.}</p> 200 * <p>This is calculated as the average year with 365.2425 days.</p> 201 * 202 * @see ChronoUnit#YEARS 203 */ 204 SIMPLE_YEAR( new BigDecimal( ChronoUnit.YEARS.getDuration().get( ChronoUnit.SECONDS ) ), "yrs", null, ChronoUnit.YEARS ), 205 206 /** 207 * <p>{@summary A decade.}</p> 208 * <p>This is calculated as ten average years with 365.2425 days each.</p> 209 * 210 * @see ChronoUnit#DECADES 211 */ 212 DECADE( new BigDecimal( ChronoUnit.DECADES.getDuration().get( ChronoUnit.SECONDS ) ), "decade", null, ChronoUnit.DECADES ), 213 214 /** 215 * <p>{@summary A century.}</p> 216 * <p>This is calculated as one hundred average years with 365.2425 days 217 * each.</p> 218 * 219 * @see ChronoUnit#CENTURIES 220 */ 221 CENTURY( new BigDecimal( ChronoUnit.CENTURIES.getDuration().get( ChronoUnit.SECONDS ) ).multiply( TEN ), "century", null, ChronoUnit.CENTURIES ); 222 223 /*---------------*\ 224 ====** Inner Classes **==================================================== 225 \*---------------*/ 226 /** 227 * An implementation of 228 * {@link TemporalUnit}, 229 * based on the settings for the {@code Time} instance. 230 * 231 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 232 * @version $Id: Time.java 1151 2025-10-01 21:32:15Z tquadrat $ 233 * @since 0.1.0 234 * 235 * @UMLGraph.link 236 */ 237 @ClassVersion( sourceVersion = "$Id: Time.java 1151 2025-10-01 21:32:15Z tquadrat $" ) 238 @API( status = STABLE, since = "0.1.0" ) 239 private class TemporalUnitImpl implements TemporalUnit 240 { 241 /*------------*\ 242 ====** Attributes **=================================================== 243 \*------------*/ 244 /** 245 * The duration for this temporal unit. 246 */ 247 private final Duration m_Duration; 248 249 /** 250 * The name of this temporal unit. 251 */ 252 private final String m_Name; 253 254 /*--------------*\ 255 ====** Constructors **================================================= 256 \*--------------*/ 257 /** 258 * Creates a new {@code TemporalUnitImpl} instance. 259 * 260 * @param name The name of this temporal unit. 261 */ 262 public TemporalUnitImpl( final String name ) 263 { 264 m_Duration = Duration.ofSeconds( Time.this.factor().longValue() ); 265 m_Name = name; 266 } // TemporalUnitImpl() 267 268 /*---------*\ 269 ====** Methods **====================================================== 270 \*---------*/ 271 /** 272 * {@inheritDoc} 273 */ 274 @SuppressWarnings( "unchecked" ) 275 @Override 276 public <R extends Temporal> R addTo( final R temporal, final long amount ) 277 { 278 return (R) temporal.plus( amount, this ); 279 } // addTo() 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override 285 public long between( final Temporal temporal1Inclusive, final Temporal temporal2Exclusive ) 286 { 287 return temporal1Inclusive.until( temporal2Exclusive, this ); 288 } // between() 289 290 /** 291 * {@inheritDoc} 292 */ 293 @Override 294 public final Duration getDuration() { return m_Duration; } 295 296 /** 297 * {@inheritDoc} 298 */ 299 @Override 300 public final boolean isDurationEstimated() { return true; } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public final boolean isDateBased() { return false; } 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override 312 public final boolean isTimeBased() { return false; } 313 314 /** 315 * {@inheritDoc} 316 */ 317 @Override 318 public final String toString() { return m_Name; } 319 } 320 // class TemporalUnitImpl() 321 322 /*------------*\ 323 ====** Attributes **======================================================= 324 \*------------*/ 325 /** 326 * The factor. 327 */ 328 private final BigDecimal m_Factor; 329 330 /** 331 * The unit symbol. 332 */ 333 private final String m_UnitSymbol; 334 335 /** 336 * The time unit as used by the {@code java.time} package. 337 */ 338 @SuppressWarnings( "OptionalUsedAsFieldOrParameterType" ) 339 private final Optional<TemporalUnit> m_TemporalUnit; 340 341 /** 342 * The time unit as used by the {@code java.util.concurrent} package. 343 */ 344 private final TimeUnit m_TimeUnit; 345 346 /*--------------*\ 347 ====** Constructors **===================================================== 348 \*--------------*/ 349 /** 350 * Creates a new {@code Time} instance. 351 * 352 * @param factor The factor. 353 * @param unitSymbol The unit symbol. 354 * @param timeUnit The time unit as for the 355 * {@code java.util.concurrent} package; may be {@code null}. 356 * @param temporalUnit The time unit as for the {@code java.time} 357 * package; may be {@code null}. 358 */ 359 private Time( final BigDecimal factor, final String unitSymbol, final TimeUnit timeUnit, final TemporalUnit temporalUnit ) 360 { 361 m_Factor = factor.stripTrailingZeros(); 362 m_UnitSymbol = unitSymbol; 363 m_TimeUnit = timeUnit; 364 m_TemporalUnit = Optional.ofNullable( temporalUnit ); 365 } // Time() 366 367 /*---------*\ 368 ====** Methods **========================================================== 369 \*---------*/ 370 /** 371 * Returns this instance of {@code Time} as an instance of 372 * {@link TemporalUnit}. 373 * 374 * @return {@code this} as {@code TemporalUnit}. 375 */ 376 @SuppressWarnings( "static-method" ) 377 protected TemporalUnit asTemporalUnit() { return null; } 378 379 /** 380 * {@inheritDoc} 381 */ 382 @Override 383 @SuppressWarnings( "unchecked" ) 384 public final Time baseUnit() { return SECOND; } 385 386 /** 387 * {@inheritDoc} 388 */ 389 @Override 390 public final BigDecimal factor() { return m_Factor; } 391 392 /** 393 * Returns the {@code Time} instance for the given instance of 394 * {@link ChronoUnit}. 395 * 396 * @param unit The unit. 397 * @return The respective instance. 398 * @throws IllegalArgumentException The given unit is unknown. 399 */ 400 public static final Time forUnit( final ChronoUnit unit ) throws IllegalArgumentException 401 { 402 requireNonNullArgument( unit, "unit" ); 403 404 final var retValue = stream( values() ) 405 .filter( v -> v.getTemporalUnit() == unit ) 406 .findFirst() 407 .orElseThrow( () -> new IllegalArgumentException( format( MSG_UnknownUnit, unit.name() ) ) ); 408 409 //---* Done *---------------------------------------------------------- 410 return retValue; 411 } // forUnit() 412 413 /** 414 * Returns the {@code Time} instance for the given instance of 415 * {@link TemporalUnit}. 416 * 417 * @param unit The unit. 418 * @return The respective instance. 419 * @throws IllegalArgumentException The given unit is unknown. 420 */ 421 public static final Time forUnit( final TemporalUnit unit ) throws IllegalArgumentException 422 { 423 requireNonNullArgument( unit, "unit" ); 424 425 final var retValue = stream( values() ) 426 .filter( v -> v.getTemporalUnit() == unit ) 427 .findFirst() 428 .orElseThrow( () -> new IllegalArgumentException( format( MSG_UnknownUnit, unit.toString() ) ) ); 429 430 //---* Done *---------------------------------------------------------- 431 return retValue; 432 } // forUnit() 433 434 /** 435 * Returns the {@code Time} instance for the given instance of 436 * {@link TimeUnit}. 437 * 438 * @param unit The unit. 439 * @return The respective instance. 440 * @throws IllegalArgumentException The given unit is unknown. 441 */ 442 public static final Time forUnit( final TimeUnit unit ) throws IllegalArgumentException 443 { 444 requireNonNullArgument( unit, "unit" ); 445 446 final var retValue = stream( values() ) 447 .filter( v -> v.getTimeUnit() == unit ) 448 .findFirst() 449 .orElseThrow( () -> new IllegalArgumentException( format( MSG_UnknownUnit, unit.name() ) ) ); 450 451 //---* Done *---------------------------------------------------------- 452 return retValue; 453 } // forUnit() 454 455 /** 456 * Returns the {@code Time} instance for the given unit. 457 * 458 * @param unitSymbol The unit symbol. 459 * @return The respective instance. 460 * @throws IllegalArgumentException The given unit is unknown. 461 */ 462 public static final Time forUnit( final String unitSymbol ) throws IllegalArgumentException 463 { 464 requireNotEmptyArgument( unitSymbol, "unit" ); 465 466 final var retValue = stream( values() ) 467 .filter( v -> v.unitSymbol().equals( unitSymbol ) ) 468 .findFirst() 469 .orElseThrow( () -> new IllegalArgumentException( format( MSG_UnknownUnit, unitSymbol ) ) ); 470 471 //---* Done *---------------------------------------------------------- 472 return retValue; 473 } // forUnit() 474 475 /** 476 * Returns the correspondent 477 * {@link TemporalUnit} 478 * for this instance, as used by the {@code java.time} 479 * package. Because we define here more units as in that package, the 480 * return value may be {@code null}. 481 * 482 * @return The corresponding temporal unit instance, or {@code null} if 483 * there is no corresponding time unit. 484 */ 485 @SuppressWarnings( "PublicMethodNotExposedInInterface" ) 486 public final TemporalUnit getTemporalUnit() { return m_TemporalUnit.orElse( asTemporalUnit() ); } 487 488 /** 489 * Returns the correspondent 490 * {@link TimeUnit} 491 * for this instance, as used by the {@code java.util.concurrent} 492 * package. Because we define here more units as in that package, the 493 * return value may be {@code null}. 494 * 495 * @return The corresponding time unit instance, or {@code null} if 496 * there is no corresponding time unit. 497 */ 498 @SuppressWarnings( "PublicMethodNotExposedInInterface" ) 499 public final TimeUnit getTimeUnit() { return m_TimeUnit; } 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override 505 public final String unitSymbol() { return m_UnitSymbol; } 506} 507// enum Time 508 509/* 510 * End of File 511 */