1 /* Copyright 2002-2024 CS GROUP 2 * Licensed to CS GROUP (CS) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * CS licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.orekit.time; 18 19 import java.io.Serializable; 20 import java.time.Instant; 21 import java.time.LocalDateTime; 22 import java.time.ZoneOffset; 23 import java.time.format.DateTimeFormatter; 24 import java.util.Date; 25 import java.util.TimeZone; 26 27 import java.util.concurrent.TimeUnit; 28 import org.hipparchus.util.FastMath; 29 import org.hipparchus.util.MathUtils; 30 import org.hipparchus.util.MathUtils.SumAndResidual; 31 import org.orekit.annotation.DefaultDataContext; 32 import org.orekit.data.DataContext; 33 import org.orekit.errors.OrekitIllegalArgumentException; 34 import org.orekit.utils.Constants; 35 36 37 /** This class represents a specific instant in time. 38 39 * <p>Instances of this class are considered to be absolute in the sense 40 * that each one represent the occurrence of some event and can be compared 41 * to other instances or located in <em>any</em> {@link TimeScale time scale}. In 42 * other words the different locations of an event with respect to two different 43 * time scales (say {@link TAIScale TAI} and {@link UTCScale UTC} for example) are 44 * simply different perspective related to a single object. Only one 45 * <code>AbsoluteDate</code> instance is needed, both representations being available 46 * from this single instance by specifying the time scales as parameter when calling 47 * the ad-hoc methods.</p> 48 * 49 * <p>Since an instance is not bound to a specific time-scale, all methods related 50 * to the location of the date within some time scale require to provide the time 51 * scale as an argument. It is therefore possible to define a date in one time scale 52 * and to use it in another one. An example of such use is to read a date from a file 53 * in UTC and write it in another file in TAI. This can be done as follows:</p> 54 * <pre> 55 * DateTimeComponents utcComponents = readNextDate(); 56 * AbsoluteDate date = new AbsoluteDate(utcComponents, TimeScalesFactory.getUTC()); 57 * writeNextDate(date.getComponents(TimeScalesFactory.getTAI())); 58 * </pre> 59 * 60 * <p>Two complementary views are available:</p> 61 * <ul> 62 * <li><p>location view (mainly for input/output or conversions)</p> 63 * <p>locations represent the coordinate of one event with respect to a 64 * {@link TimeScale time scale}. The related methods are {@link 65 * #AbsoluteDate(DateComponents, TimeComponents, TimeScale)}, {@link 66 * #AbsoluteDate(int, int, int, int, int, double, TimeScale)}, {@link 67 * #AbsoluteDate(int, int, int, TimeScale)}, {@link #AbsoluteDate(Date, 68 * TimeScale)}, {@link #parseCCSDSCalendarSegmentedTimeCode(byte, byte[])}, 69 * {@link #toDate(TimeScale)}, {@link #toString(TimeScale) toString(timeScale)}, 70 * {@link #toString()}, and {@link #timeScalesOffset}.</p> 71 * </li> 72 * <li><p>offset view (mainly for physical computation)</p> 73 * <p>offsets represent either the flow of time between two events 74 * (two instances of the class) or durations. They are counted in seconds, 75 * are continuous and could be measured using only a virtually perfect stopwatch. 76 * The related methods are {@link #AbsoluteDate(AbsoluteDate, double)}, 77 * {@link #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate)}, 78 * {@link #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents)}, 79 * {@link #durationFrom(AbsoluteDate)}, {@link #compareTo(AbsoluteDate)}, {@link #equals(Object)} 80 * and {@link #hashCode()}.</p> 81 * </li> 82 * </ul> 83 * <p> 84 * A few reference epochs which are commonly used in space systems have been defined. These 85 * epochs can be used as the basis for offset computation. The supported epochs are: 86 * {@link #JULIAN_EPOCH}, {@link #MODIFIED_JULIAN_EPOCH}, {@link #FIFTIES_EPOCH}, 87 * {@link #CCSDS_EPOCH}, {@link #GALILEO_EPOCH}, {@link #GPS_EPOCH}, {@link #QZSS_EPOCH} 88 * {@link #J2000_EPOCH}, {@link #JAVA_EPOCH}. 89 * There are also two factory methods {@link #createJulianEpoch(double)} 90 * and {@link #createBesselianEpoch(double)} that can be used to compute other reference 91 * epochs like J1900.0 or B1950.0. 92 * In addition to these reference epochs, two other constants are defined for convenience: 93 * {@link #PAST_INFINITY} and {@link #FUTURE_INFINITY}, which can be used either as dummy 94 * dates when a date is not yet initialized, or for initialization of loops searching for 95 * a min or max date. 96 * </p> 97 * <p> 98 * Instances of the <code>AbsoluteDate</code> class are guaranteed to be immutable. 99 * </p> 100 * @author Luc Maisonobe 101 * @author Evan Ward 102 * @see TimeScale 103 * @see TimeStamped 104 * @see ChronologicalComparator 105 */ 106 public class AbsoluteDate 107 implements TimeStamped, TimeShiftable<AbsoluteDate>, Comparable<AbsoluteDate>, Serializable { 108 109 /** Reference epoch for julian dates: -4712-01-01T12:00:00 Terrestrial Time. 110 * <p>Both <code>java.util.Date</code> and {@link DateComponents} classes 111 * follow the astronomical conventions and consider a year 0 between 112 * years -1 and +1, hence this reference date lies in year -4712 and not 113 * in year -4713 as can be seen in other documents or programs that obey 114 * a different convention (for example the <code>convcal</code> utility).</p> 115 * 116 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 117 * 118 * @see TimeScales#getJulianEpoch() 119 */ 120 @DefaultDataContext 121 public static final AbsoluteDate JULIAN_EPOCH = 122 DataContext.getDefault().getTimeScales().getJulianEpoch(); 123 124 /** Reference epoch for modified julian dates: 1858-11-17T00:00:00 Terrestrial Time. 125 * 126 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 127 * 128 * @see TimeScales#getModifiedJulianEpoch() 129 */ 130 @DefaultDataContext 131 public static final AbsoluteDate MODIFIED_JULIAN_EPOCH = 132 DataContext.getDefault().getTimeScales().getModifiedJulianEpoch(); 133 134 /** Reference epoch for 1950 dates: 1950-01-01T00:00:00 Terrestrial Time. 135 * 136 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 137 * 138 * @see TimeScales#getFiftiesEpoch() 139 */ 140 @DefaultDataContext 141 public static final AbsoluteDate FIFTIES_EPOCH = 142 DataContext.getDefault().getTimeScales().getFiftiesEpoch(); 143 144 /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4): 145 * 1958-01-01T00:00:00 International Atomic Time (<em>not</em> UTC). 146 * 147 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 148 * 149 * @see TimeScales#getCcsdsEpoch() 150 */ 151 @DefaultDataContext 152 public static final AbsoluteDate CCSDS_EPOCH = 153 DataContext.getDefault().getTimeScales().getCcsdsEpoch(); 154 155 /** Reference epoch for Galileo System Time: 1999-08-22T00:00:00 GST. 156 * 157 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 158 * 159 * @see TimeScales#getGalileoEpoch() 160 */ 161 @DefaultDataContext 162 public static final AbsoluteDate GALILEO_EPOCH = 163 DataContext.getDefault().getTimeScales().getGalileoEpoch(); 164 165 /** Reference epoch for GPS weeks: 1980-01-06T00:00:00 GPS time. 166 * 167 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 168 * 169 * @see TimeScales#getGpsEpoch() 170 */ 171 @DefaultDataContext 172 public static final AbsoluteDate GPS_EPOCH = 173 DataContext.getDefault().getTimeScales().getGpsEpoch(); 174 175 /** Reference epoch for QZSS weeks: 1980-01-06T00:00:00 QZSS time. 176 * 177 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 178 * 179 * @see TimeScales#getQzssEpoch() 180 */ 181 @DefaultDataContext 182 public static final AbsoluteDate QZSS_EPOCH = 183 DataContext.getDefault().getTimeScales().getQzssEpoch(); 184 185 /** Reference epoch for IRNSS weeks: 1999-08-22T00:00:00 IRNSS time. 186 * 187 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 188 * 189 * @see TimeScales#getIrnssEpoch() 190 */ 191 @DefaultDataContext 192 public static final AbsoluteDate IRNSS_EPOCH = 193 DataContext.getDefault().getTimeScales().getIrnssEpoch(); 194 195 /** Reference epoch for BeiDou weeks: 2006-01-01T00:00:00 UTC. 196 * 197 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 198 * 199 * @see TimeScales#getBeidouEpoch() 200 */ 201 @DefaultDataContext 202 public static final AbsoluteDate BEIDOU_EPOCH = 203 DataContext.getDefault().getTimeScales().getBeidouEpoch(); 204 205 /** Reference epoch for GLONASS four-year interval number: 1996-01-01T00:00:00 GLONASS time. 206 * <p>By convention, TGLONASS = UTC + 3 hours.</p> 207 * 208 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 209 * 210 * @see TimeScales#getGlonassEpoch() 211 */ 212 @DefaultDataContext 213 public static final AbsoluteDate GLONASS_EPOCH = 214 DataContext.getDefault().getTimeScales().getGlonassEpoch(); 215 216 /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (<em>not</em> UTC). 217 * 218 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 219 * 220 * @see #createJulianEpoch(double) 221 * @see #createBesselianEpoch(double) 222 * @see TimeScales#getJ2000Epoch() 223 */ 224 @DefaultDataContext 225 public static final AbsoluteDate J2000_EPOCH = // TODO 226 DataContext.getDefault().getTimeScales().getJ2000Epoch(); 227 228 /** Java Reference epoch: 1970-01-01T00:00:00 Universal Time Coordinate. 229 * <p> 230 * Between 1968-02-01 and 1972-01-01, UTC-TAI = 4.213 170 0s + (MJD - 39 126) x 0.002 592s. 231 * As on 1970-01-01 MJD = 40587, UTC-TAI = 8.000082s 232 * </p> 233 * 234 * <p>This constant uses the {@link DataContext#getDefault() default data context}. 235 * 236 * @see TimeScales#getJavaEpoch() 237 */ 238 @DefaultDataContext 239 public static final AbsoluteDate JAVA_EPOCH = 240 DataContext.getDefault().getTimeScales().getJavaEpoch(); 241 242 /** 243 * An arbitrary finite date. Uses when a non-null date is needed but its value doesn't 244 * matter. 245 */ 246 public static final AbsoluteDate ARBITRARY_EPOCH = new AbsoluteDate(0, 0); 247 248 /** Dummy date at infinity in the past direction. 249 * @see TimeScales#getPastInfinity() 250 */ 251 public static final AbsoluteDate PAST_INFINITY = ARBITRARY_EPOCH.shiftedBy(Double.NEGATIVE_INFINITY); 252 253 /** Dummy date at infinity in the future direction. 254 * @see TimeScales#getFutureInfinity() 255 */ 256 public static final AbsoluteDate FUTURE_INFINITY = ARBITRARY_EPOCH.shiftedBy(Double.POSITIVE_INFINITY); 257 258 /** Serializable UID. */ 259 private static final long serialVersionUID = 617061803741806846L; 260 261 /** Reference epoch in seconds from 2000-01-01T12:00:00 TAI. 262 * <p>Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT.</p> */ 263 private final long epoch; 264 265 /** Offset from the reference epoch in seconds. */ 266 private final double offset; 267 268 /** Create an instance with a default value ({@link #J2000_EPOCH}). 269 * 270 * <p>This constructor uses the {@link DataContext#getDefault() default data context}. 271 * 272 * @see #AbsoluteDate(DateTimeComponents, TimeScale) 273 */ 274 @DefaultDataContext 275 public AbsoluteDate() { 276 epoch = J2000_EPOCH.epoch; 277 offset = J2000_EPOCH.offset; 278 } 279 280 /** Build an instance from a location (parsed from a string) in a {@link TimeScale time scale}. 281 * <p> 282 * The supported formats for location are mainly the ones defined in ISO-8601 standard, 283 * the exact subset is explained in {@link DateTimeComponents#parseDateTime(String)}, 284 * {@link DateComponents#parseDate(String)} and {@link TimeComponents#parseTime(String)}. 285 * </p> 286 * <p> 287 * As CCSDS ASCII calendar segmented time code is a trimmed down version of ISO-8601, 288 * it is also supported by this constructor. 289 * </p> 290 * @param location location in the time scale, must be in a supported format 291 * @param timeScale time scale 292 * @exception IllegalArgumentException if location string is not in a supported format 293 */ 294 public AbsoluteDate(final String location, final TimeScale timeScale) { 295 this(DateTimeComponents.parseDateTime(location), timeScale); 296 } 297 298 /** Build an instance from a location in a {@link TimeScale time scale}. 299 * @param location location in the time scale 300 * @param timeScale time scale 301 */ 302 public AbsoluteDate(final DateTimeComponents location, final TimeScale timeScale) { 303 this(location.getDate(), location.getTime(), timeScale); 304 } 305 306 /** Build an instance from a location in a {@link TimeScale time scale}. 307 * @param date date location in the time scale 308 * @param time time location in the time scale 309 * @param timeScale time scale 310 */ 311 public AbsoluteDate(final DateComponents date, final TimeComponents time, 312 final TimeScale timeScale) { 313 314 final double seconds = time.getSecond(); 315 final double tsOffset = timeScale.offsetToTAI(date, time); 316 317 // Use 2Sum for high precision. 318 final SumAndResidual sumAndResidual = MathUtils.twoSum(seconds, tsOffset); 319 final long dl = (long) FastMath.floor(sumAndResidual.getSum()); 320 final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual(); 321 322 if (regularOffset >= 0) { 323 // regular case, the offset is between 0.0 and 1.0 324 offset = regularOffset; 325 epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + 326 time.getMinute() - time.getMinutesFromUTC() - 720L) + dl; 327 } else { 328 // very rare case, the offset is just before a whole second 329 // we will loose some bits of accuracy when adding 1 second 330 // but this will ensure the offset remains in the [0.0; 1.0] interval 331 offset = 1.0 + regularOffset; 332 epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + 333 time.getMinute() - time.getMinutesFromUTC() - 720L) + dl - 1; 334 } 335 336 } 337 338 /** Build an instance from a location in a {@link TimeScale time scale}. 339 * @param year year number (may be 0 or negative for BC years) 340 * @param month month number from 1 to 12 341 * @param day day number from 1 to 31 342 * @param hour hour number from 0 to 23 343 * @param minute minute number from 0 to 59 344 * @param second second number from 0.0 to 60.0 (excluded) 345 * @param timeScale time scale 346 * @exception IllegalArgumentException if inconsistent arguments 347 * are given (parameters out of range) 348 */ 349 public AbsoluteDate(final int year, final int month, final int day, 350 final int hour, final int minute, final double second, 351 final TimeScale timeScale) throws IllegalArgumentException { 352 this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale); 353 } 354 355 /** Build an instance from a location in a {@link TimeScale time scale}. 356 * @param year year number (may be 0 or negative for BC years) 357 * @param month month enumerate 358 * @param day day number from 1 to 31 359 * @param hour hour number from 0 to 23 360 * @param minute minute number from 0 to 59 361 * @param second second number from 0.0 to 60.0 (excluded) 362 * @param timeScale time scale 363 * @exception IllegalArgumentException if inconsistent arguments 364 * are given (parameters out of range) 365 */ 366 public AbsoluteDate(final int year, final Month month, final int day, 367 final int hour, final int minute, final double second, 368 final TimeScale timeScale) throws IllegalArgumentException { 369 this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale); 370 } 371 372 /** Build an instance from a location in a {@link TimeScale time scale}. 373 * <p>The hour is set to 00:00:00.000.</p> 374 * @param date date location in the time scale 375 * @param timeScale time scale 376 * @exception IllegalArgumentException if inconsistent arguments 377 * are given (parameters out of range) 378 */ 379 public AbsoluteDate(final DateComponents date, final TimeScale timeScale) 380 throws IllegalArgumentException { 381 this(date, TimeComponents.H00, timeScale); 382 } 383 384 /** Build an instance from a location in a {@link TimeScale time scale}. 385 * <p>The hour is set to 00:00:00.000.</p> 386 * @param year year number (may be 0 or negative for BC years) 387 * @param month month number from 1 to 12 388 * @param day day number from 1 to 31 389 * @param timeScale time scale 390 * @exception IllegalArgumentException if inconsistent arguments 391 * are given (parameters out of range) 392 */ 393 public AbsoluteDate(final int year, final int month, final int day, 394 final TimeScale timeScale) throws IllegalArgumentException { 395 this(new DateComponents(year, month, day), TimeComponents.H00, timeScale); 396 } 397 398 /** Build an instance from a location in a {@link TimeScale time scale}. 399 * <p>The hour is set to 00:00:00.000.</p> 400 * @param year year number (may be 0 or negative for BC years) 401 * @param month month enumerate 402 * @param day day number from 1 to 31 403 * @param timeScale time scale 404 * @exception IllegalArgumentException if inconsistent arguments 405 * are given (parameters out of range) 406 */ 407 public AbsoluteDate(final int year, final Month month, final int day, 408 final TimeScale timeScale) throws IllegalArgumentException { 409 this(new DateComponents(year, month, day), TimeComponents.H00, timeScale); 410 } 411 412 /** Build an instance from a location in a {@link TimeScale time scale}. 413 * @param location location in the time scale 414 * @param timeScale time scale 415 */ 416 public AbsoluteDate(final Date location, final TimeScale timeScale) { 417 this(new DateComponents(DateComponents.JAVA_EPOCH, 418 (int) (location.getTime() / 86400000L)), 419 millisToTimeComponents((int) (location.getTime() % 86400000L)), 420 timeScale); 421 } 422 423 /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}. 424 * 425 * @deprecated Use {@link AbsoluteDate#AbsoluteDate(Instant, UTCScale)} or {@link AbsoluteDate#AbsoluteDate(Instant)} instead 426 * @param instant instant in the time scale 427 * @param timeScale time scale 428 * @since 12.0 429 */ 430 @Deprecated 431 public AbsoluteDate(final Instant instant, final TimeScale timeScale) { 432 this(new DateComponents(DateComponents.JAVA_EPOCH, 433 (int) (instant.getEpochSecond() / 86400L)), 434 instantToTimeComponents(instant), 435 timeScale); 436 } 437 438 /** Build an instance from an {@link Instant instant} in utc time scale. 439 * @param instant instant in the time scale 440 * @since 12.1 441 */ 442 @DefaultDataContext 443 public AbsoluteDate(final Instant instant) { 444 this(instant, TimeScalesFactory.getUTC()); 445 } 446 447 /** Build an instance from an {@link Instant instant} in the {@link UTCScale time scale}. 448 * @param instant instant in the time scale 449 * @param utcScale utc time scale 450 * @since 12.1 451 */ 452 public AbsoluteDate(final Instant instant, final UTCScale utcScale) { 453 this(new DateComponents(DateComponents.JAVA_EPOCH, 454 (int) (instant.getEpochSecond() / 86400l)), 455 instantToTimeComponents(instant), 456 utcScale); 457 } 458 459 /** Build an instance from an elapsed duration since to another instant. 460 * <p>It is important to note that the elapsed duration is <em>not</em> 461 * the difference between two readings on a time scale. As an example, 462 * the duration between the two instants leading to the readings 463 * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC} 464 * time scale is <em>not</em> 1 second, but a stop watch would have measured 465 * an elapsed duration of 2 seconds between these two instances because a leap 466 * second was introduced at the end of 2005 in this time scale.</p> 467 * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate)} 468 * method.</p> 469 * @param since start instant of the measured duration 470 * @param elapsedDuration physically elapsed duration from the <code>since</code> 471 * instant, as measured in a regular time scale 472 * @see #durationFrom(AbsoluteDate) 473 */ 474 public AbsoluteDate(final AbsoluteDate since, final double elapsedDuration) { 475 // Use 2Sum for high precision. 476 final SumAndResidual sumAndResidual = MathUtils.twoSum(since.offset, elapsedDuration); 477 if (Double.isInfinite(sumAndResidual.getSum())) { 478 offset = sumAndResidual.getSum(); 479 epoch = (sumAndResidual.getSum() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE; 480 } else { 481 final long dl = (long) FastMath.floor(sumAndResidual.getSum()); 482 final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual(); 483 if (regularOffset >= 0) { 484 // regular case, the offset is between 0.0 and 1.0 485 offset = regularOffset; 486 epoch = since.epoch + dl; 487 } else { 488 // very rare case, the offset is just before a whole second 489 // we will loose some bits of accuracy when adding 1 second 490 // but this will ensure the offset remains in the [0.0; 1.0] interval 491 offset = 1.0 + regularOffset; 492 epoch = since.epoch + dl - 1; 493 } 494 } 495 } 496 497 /** Build an instance from an elapsed duration since to another instant. 498 * <p>It is important to note that the elapsed duration is <em>not</em> 499 * the difference between two readings on a time scale. As an example, 500 * the duration between the two instants leading to the readings 501 * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC} 502 * time scale is <em>not</em> 1 second, but a stop watch would have measured 503 * an elapsed duration of 2 seconds between these two instances because a leap 504 * second was introduced at the end of 2005 in this time scale.</p> 505 * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate, TimeUnit)} 506 * method.</p> 507 * @param since start instant of the measured duration 508 * @param elapsedDuration physically elapsed duration from the <code>since</code> 509 * instant, as measured in a regular time scale 510 * @param timeUnit {@link TimeUnit} of the elapsedDuration 511 * @see #durationFrom(AbsoluteDate, TimeUnit) 512 * @since 12.1 513 */ 514 public AbsoluteDate(final AbsoluteDate since, final long elapsedDuration, final TimeUnit timeUnit) { 515 final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(elapsedDuration, timeUnit); 516 final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1); 517 final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1); 518 final double newOffset = since.offset + deltaOffset; 519 if (newOffset >= 1.0) { 520 // newOffset is in [1.0, 2.0] 521 epoch = since.epoch + deltaEpoch + 1L; 522 offset = newOffset - 1.0; 523 } else if (newOffset < 0) { 524 epoch = since.epoch + deltaEpoch - 1L; 525 offset = 1.0 + newOffset; 526 } else { 527 epoch = since.epoch + deltaEpoch; 528 offset = newOffset; 529 } 530 } 531 532 /** Build an instance from an apparent clock offset with respect to another 533 * instant <em>in the perspective of a specific {@link TimeScale time scale}</em>. 534 * <p>It is important to note that the apparent clock offset <em>is</em> the 535 * difference between two readings on a time scale and <em>not</em> an elapsed 536 * duration. As an example, the apparent clock offset between the two instants 537 * leading to the readings 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the 538 * {@link UTCScale UTC} time scale is 1 second, but the elapsed duration is 2 539 * seconds because a leap second has been introduced at the end of 2005 in this 540 * time scale.</p> 541 * <p>This constructor is the reverse of the {@link #offsetFrom(AbsoluteDate, 542 * TimeScale)} method.</p> 543 * @param reference reference instant 544 * @param apparentOffset apparent clock offset from the reference instant 545 * (difference between two readings in the specified time scale) 546 * @param timeScale time scale with respect to which the offset is defined 547 * @see #offsetFrom(AbsoluteDate, TimeScale) 548 */ 549 public AbsoluteDate(final AbsoluteDate reference, final double apparentOffset, 550 final TimeScale timeScale) { 551 this(new DateTimeComponents(reference.getComponents(timeScale), apparentOffset), 552 timeScale); 553 } 554 555 /** Build a date from its internal components. 556 * <p> 557 * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}). 558 * </p> 559 * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI. 560 * (beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT) 561 * @param offset offset from the reference epoch in seconds (must be 562 * between 0.0 included and 1.0 excluded) 563 * @since 9.0 564 */ 565 AbsoluteDate(final long epoch, final double offset) { 566 this.epoch = epoch; 567 this.offset = offset; 568 } 569 570 /** Extract time components from a number of milliseconds within the day. 571 * @param millisInDay number of milliseconds within the day 572 * @return time components 573 */ 574 private static TimeComponents millisToTimeComponents(final int millisInDay) { 575 return new TimeComponents(millisInDay / 1000, 0.001 * (millisInDay % 1000)); 576 } 577 578 /** Extract time components from an instant within the day. 579 * @param instant instant to extract the number of seconds within the day 580 * @return time components 581 */ 582 private static TimeComponents instantToTimeComponents(final Instant instant) { 583 final int secInDay = (int) (instant.getEpochSecond() % 86400L); 584 return new TimeComponents(secInDay, 1.0e-9 * instant.getNano()); 585 } 586 587 /** Get the reference epoch in seconds from 2000-01-01T12:00:00 TAI. 588 * <p> 589 * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}). 590 * </p> 591 * <p> 592 * Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT. 593 * </p> 594 * @return reference epoch in seconds from 2000-01-01T12:00:00 TAI 595 * @since 9.0 596 */ 597 long getEpoch() { 598 return epoch; 599 } 600 601 /** Get the offset from the reference epoch in seconds. 602 * <p> 603 * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}). 604 * </p> 605 * @return offset from the reference epoch in seconds 606 * @since 9.0 607 */ 608 double getOffset() { 609 return offset; 610 } 611 612 /** Build an instance from a CCSDS Unsegmented Time Code (CUC). 613 * <p> 614 * CCSDS Unsegmented Time Code is defined in the blue book: 615 * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 616 * </p> 617 * <p> 618 * If the date to be parsed is formatted using version 3 of the standard 619 * (CCSDS 301.0-B-3 published in 2002) or if the extension of the preamble 620 * field introduced in version 4 of the standard is not used, then the 621 * {@code preambleField2} parameter can be set to 0. 622 * </p> 623 * 624 * <p>This method uses the {@link DataContext#getDefault() default data context} if 625 * the CCSDS epoch is used. 626 * 627 * @param preambleField1 first byte of the field specifying the format, often 628 * not transmitted in data interfaces, as it is constant for a given data interface 629 * @param preambleField2 second byte of the field specifying the format 630 * (added in revision 4 of the CCSDS standard in 2010), often not transmitted in data 631 * interfaces, as it is constant for a given data interface (value ignored if presence 632 * not signaled in {@code preambleField1}) 633 * @param timeField byte array containing the time code 634 * @param agencyDefinedEpoch reference epoch, ignored if the preamble field 635 * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence 636 * may be null in this case) 637 * @return an instance corresponding to the specified date 638 * @see #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate, AbsoluteDate) 639 */ 640 @DefaultDataContext 641 public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(final byte preambleField1, 642 final byte preambleField2, 643 final byte[] timeField, 644 final AbsoluteDate agencyDefinedEpoch) { 645 return parseCCSDSUnsegmentedTimeCode(preambleField1, preambleField2, timeField, 646 agencyDefinedEpoch, 647 DataContext.getDefault().getTimeScales().getCcsdsEpoch()); 648 } 649 650 /** 651 * Build an instance from a CCSDS Unsegmented Time Code (CUC). 652 * <p> 653 * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format 654 * (CCSDS 301.0-B-4) published in November 2010 655 * </p> 656 * <p> 657 * If the date to be parsed is formatted using version 3 of the standard (CCSDS 658 * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced 659 * in version 4 of the standard is not used, then the {@code preambleField2} parameter 660 * can be set to 0. 661 * </p> 662 * 663 * @param preambleField1 first byte of the field specifying the format, often not 664 * transmitted in data interfaces, as it is constant for a 665 * given data interface 666 * @param preambleField2 second byte of the field specifying the format (added in 667 * revision 4 of the CCSDS standard in 2010), often not 668 * transmitted in data interfaces, as it is constant for a 669 * given data interface (value ignored if presence not 670 * signaled in {@code preambleField1}) 671 * @param timeField byte array containing the time code 672 * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies 673 * the {@link #CCSDS_EPOCH CCSDS reference epoch} is used 674 * (and hence may be null in this case) 675 * @param ccsdsEpoch reference epoch, ignored if the preamble field specifies 676 * the agency epoch is used. 677 * @return an instance corresponding to the specified date 678 * @since 10.1 679 */ 680 public static AbsoluteDate parseCCSDSUnsegmentedTimeCode( 681 final byte preambleField1, 682 final byte preambleField2, 683 final byte[] timeField, 684 final AbsoluteDate agencyDefinedEpoch, 685 final AbsoluteDate ccsdsEpoch) { 686 final CcsdsUnsegmentedTimeCode<AbsoluteDate> timeCode = 687 new CcsdsUnsegmentedTimeCode<>(preambleField1, preambleField2, timeField, 688 agencyDefinedEpoch, ccsdsEpoch); 689 return new AbsoluteDate(timeCode.getEpoch(), timeCode.getSeconds()). 690 shiftedBy(timeCode.getSubSecond()); 691 692 } 693 694 /** Build an instance from a CCSDS Day Segmented Time Code (CDS). 695 * <p> 696 * CCSDS Day Segmented Time Code is defined in the blue book: 697 * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 698 * </p> 699 * 700 * <p>This method uses the {@link DataContext#getDefault() default data context}. 701 * 702 * @param preambleField field specifying the format, often not transmitted in 703 * data interfaces, as it is constant for a given data interface 704 * @param timeField byte array containing the time code 705 * @param agencyDefinedEpoch reference epoch, ignored if the preamble field 706 * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence 707 * may be null in this case) 708 * @return an instance corresponding to the specified date 709 * @see #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents, TimeScale) 710 */ 711 @DefaultDataContext 712 public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, final byte[] timeField, 713 final DateComponents agencyDefinedEpoch) { 714 return parseCCSDSDaySegmentedTimeCode(preambleField, timeField, 715 agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC()); 716 } 717 718 /** Build an instance from a CCSDS Day Segmented Time Code (CDS). 719 * <p> 720 * CCSDS Day Segmented Time Code is defined in the blue book: 721 * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 722 * </p> 723 * @param preambleField field specifying the format, often not transmitted in 724 * data interfaces, as it is constant for a given data interface 725 * @param timeField byte array containing the time code 726 * @param agencyDefinedEpoch reference epoch, ignored if the preamble field 727 * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence 728 * may be null in this case) 729 * @param utc time scale used to compute date and time components. 730 * @return an instance corresponding to the specified date 731 * @since 10.1 732 */ 733 public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, 734 final byte[] timeField, 735 final DateComponents agencyDefinedEpoch, 736 final TimeScale utc) { 737 738 final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField, 739 agencyDefinedEpoch); 740 return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc). 741 shiftedBy(timeCode.getSubSecond()); 742 } 743 744 /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS). 745 * <p> 746 * CCSDS Calendar Segmented Time Code is defined in the blue book: 747 * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 748 * </p> 749 * 750 * <p>This method uses the {@link DataContext#getDefault() default data context}. 751 * 752 * @param preambleField field specifying the format, often not transmitted in 753 * data interfaces, as it is constant for a given data interface 754 * @param timeField byte array containing the time code 755 * @return an instance corresponding to the specified date 756 * @see #parseCCSDSCalendarSegmentedTimeCode(byte, byte[], TimeScale) 757 */ 758 @DefaultDataContext 759 public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField) { 760 return parseCCSDSCalendarSegmentedTimeCode(preambleField, timeField, 761 DataContext.getDefault().getTimeScales().getUTC()); 762 } 763 764 /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS). 765 * <p> 766 * CCSDS Calendar Segmented Time Code is defined in the blue book: 767 * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 768 * </p> 769 * @param preambleField field specifying the format, often not transmitted in 770 * data interfaces, as it is constant for a given data interface 771 * @param timeField byte array containing the time code 772 * @param utc time scale used to compute date and time components. 773 * @return an instance corresponding to the specified date 774 * @since 10.1 775 */ 776 public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, 777 final byte[] timeField, 778 final TimeScale utc) { 779 final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField); 780 return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc). 781 shiftedBy(timeCode.getSubSecond()); 782 } 783 784 /** Build an instance corresponding to a Julian Day date. 785 * @param jd Julian day 786 * @param secondsSinceNoon seconds in the Julian day 787 * (BEWARE, Julian days start at noon, so 0.0 is noon) 788 * @param timeScale time scale in which the seconds in day are defined 789 * @return a new instant 790 */ 791 public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon, 792 final TimeScale timeScale) { 793 return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jd), 794 TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); 795 } 796 797 /** Build an instance corresponding to a Julian Day date. 798 * <p> 799 * This function should be preferred to {@link #createMJDDate(int, double, TimeScale)} when the target time scale 800 * has a non-constant offset with respect to TAI. 801 * <p> 802 * The idea is to introduce a pivot time scale that is close to the target time scale but has a constant bias with TAI. 803 * <p> 804 * For example, to get a date from an MJD in TDB time scale, it's advised to use the TT time scale 805 * as a pivot scale. TT is very close to TDB and has constant offset to TAI. 806 * @param jd Julian day 807 * @param secondsSinceNoon seconds in the Julian day 808 * (BEWARE, Julian days start at noon, so 0.0 is noon) 809 * @param timeScale timescale in which the seconds in day are defined 810 * @param pivotTimeScale pivot timescale used as intermediate timescale 811 * @return a new instant 812 */ 813 public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon, 814 final TimeScale timeScale, 815 final TimeScale pivotTimeScale) { 816 // Get the date in pivot timescale 817 final AbsoluteDate dateInPivotTimeScale = createJDDate(jd, secondsSinceNoon, pivotTimeScale); 818 819 // Compare offsets to TAI of the two time scales 820 final double offsetFromTAI = timeScale.offsetFromTAI(dateInPivotTimeScale) - pivotTimeScale.offsetFromTAI(dateInPivotTimeScale); 821 822 // Return date in desired timescale 823 return dateInPivotTimeScale.shiftedBy(-offsetFromTAI); 824 } 825 826 /** Build an instance corresponding to a Modified Julian Day date. 827 * @param mjd modified Julian day 828 * @param secondsInDay seconds in the day 829 * @param timeScale time scale in which the seconds in day are defined 830 * @return a new instant 831 * @exception OrekitIllegalArgumentException if seconds number is out of range 832 */ 833 public static AbsoluteDate createMJDDate(final int mjd, final double secondsInDay, 834 final TimeScale timeScale) 835 throws OrekitIllegalArgumentException { 836 final DateComponents dc = new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd); 837 final TimeComponents tc; 838 if (secondsInDay >= Constants.JULIAN_DAY) { 839 // check we are really allowed to use this number of seconds 840 final int secondsA = 86399; // 23:59:59, i.e. 59s in the last minute of the day 841 final double secondsB = secondsInDay - secondsA; 842 final TimeComponents safeTC = new TimeComponents(secondsA, 0.0); 843 final AbsoluteDate safeDate = new AbsoluteDate(dc, safeTC, timeScale); 844 if (timeScale.minuteDuration(safeDate) > 59 + secondsB) { 845 // we are within the last minute of the day, the number of seconds is OK 846 return safeDate.shiftedBy(secondsB); 847 } else { 848 // let TimeComponents trigger an OrekitIllegalArgumentException 849 // for the wrong number of seconds 850 tc = new TimeComponents(secondsA, secondsB); 851 } 852 } else { 853 tc = new TimeComponents(secondsInDay); 854 } 855 856 // create the date 857 return new AbsoluteDate(dc, tc, timeScale); 858 859 } 860 861 /** Build an instance corresponding to a Julian Epoch (JE). 862 * <p>According to Lieske paper: <a 863 * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf."> 864 * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics, 865 * vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is related to Julian Ephemeris Date as:</p> 866 * <pre> 867 * JE = 2000.0 + (JED - 2451545.0) / 365.25 868 * </pre> 869 * <p> 870 * This method reverts the formula above and computes an {@code AbsoluteDate} from the Julian Epoch. 871 * </p> 872 * 873 * <p>This method uses the {@link DataContext#getDefault() default data context}. 874 * 875 * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference J2000.0 876 * @return a new instant 877 * @see #J2000_EPOCH 878 * @see #createBesselianEpoch(double) 879 * @see TimeScales#createJulianEpoch(double) 880 */ 881 @DefaultDataContext 882 public static AbsoluteDate createJulianEpoch(final double julianEpoch) { 883 return DataContext.getDefault().getTimeScales().createJulianEpoch(julianEpoch); 884 } 885 886 /** Build an instance corresponding to a Besselian Epoch (BE). 887 * <p>According to Lieske paper: <a 888 * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf."> 889 * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics, 890 * vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch is related to Julian Ephemeris Date as:</p> 891 * <pre> 892 * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781 893 * </pre> 894 * <p> 895 * This method reverts the formula above and computes an {@code AbsoluteDate} from the Besselian Epoch. 896 * </p> 897 * 898 * <p>This method uses the {@link DataContext#getDefault() default data context}. 899 * 900 * @param besselianEpoch Besselian epoch, like 1950 for defining the classical reference B1950.0 901 * @return a new instant 902 * @see #createJulianEpoch(double) 903 * @see TimeScales#createBesselianEpoch(double) 904 */ 905 @DefaultDataContext 906 public static AbsoluteDate createBesselianEpoch(final double besselianEpoch) { 907 return DataContext.getDefault().getTimeScales() 908 .createBesselianEpoch(besselianEpoch); 909 } 910 911 /** Get a time-shifted date. 912 * <p> 913 * Calling this method is equivalent to call <code>new AbsoluteDate(this, dt)</code>. 914 * </p> 915 * @param dt time shift in seconds 916 * @return a new date, shifted with respect to instance (which is immutable) 917 * @see org.orekit.utils.PVCoordinates#shiftedBy(double) 918 * @see org.orekit.attitudes.Attitude#shiftedBy(double) 919 * @see org.orekit.orbits.Orbit#shiftedBy(double) 920 * @see org.orekit.propagation.SpacecraftState#shiftedBy(double) 921 */ 922 public AbsoluteDate shiftedBy(final double dt) { 923 return new AbsoluteDate(this, dt); 924 } 925 926 /** Get a time-shifted date. 927 * <p> 928 * Calling this method is equivalent to call <code>new AbsoluteDate(this, shift, timeUnit)</code>. 929 * </p> 930 * @param dt time shift in time units 931 * @param timeUnit {@link TimeUnit} of the shift 932 * @return a new date, shifted with respect to instance (which is immutable) 933 * @since 12.1 934 */ 935 public AbsoluteDate shiftedBy(final long dt, final TimeUnit timeUnit) { 936 return new AbsoluteDate(this, dt, timeUnit); 937 } 938 939 /** Compute the physically elapsed duration between two instants. 940 * <p>The returned duration is the number of seconds physically 941 * elapsed between the two instants, measured in a regular time 942 * scale with respect to surface of the Earth (i.e either the {@link 943 * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link 944 * GPSScale GPS scale}). It is the only method that gives a 945 * duration with a physical meaning.</p> 946 * <p>This method gives the same result (with less computation) 947 * as calling {@link #offsetFrom(AbsoluteDate, TimeScale)} 948 * with a second argument set to one of the regular scales cited 949 * above.</p> 950 * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate, 951 * double)} constructor.</p> 952 * @param instant instant to subtract from the instance 953 * @return offset in seconds between the two instants (positive 954 * if the instance is posterior to the argument) 955 * @see #offsetFrom(AbsoluteDate, TimeScale) 956 * @see #AbsoluteDate(AbsoluteDate, double) 957 */ 958 public double durationFrom(final AbsoluteDate instant) { 959 return (epoch - instant.epoch) + (offset - instant.offset); 960 } 961 962 /** Compute the physically elapsed duration between two instants. 963 * <p>The returned duration is the duration physically 964 * elapsed between the two instants, using the given time unit and rounded to the nearest integer, measured in a regular time 965 * scale with respect to surface of the Earth (i.e either the {@link 966 * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link 967 * GPSScale GPS scale}). It is the only method that gives a 968 * duration with a physical meaning.</p> 969 * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate, 970 * long, TimeUnit)} constructor.</p> 971 * @param instant instant to subtract from the instance 972 * @param timeUnit {@link TimeUnit} precision for the offset 973 * @return offset in the given timeunit between the two instants (positive 974 * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit} 975 * @see #AbsoluteDate(AbsoluteDate, long, TimeUnit) 976 * @since 12.1 977 */ 978 public long durationFrom(final AbsoluteDate instant, final TimeUnit timeUnit) { 979 final long deltaEpoch = timeUnit.convert(epoch - instant.epoch, TimeUnit.SECONDS); 980 981 final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS); 982 final long deltaOffset = FastMath.round((offset - instant.offset) * multiplier); 983 984 return deltaEpoch + deltaOffset; 985 } 986 987 /** Compute the apparent clock offset between two instant <em>in the 988 * perspective of a specific {@link TimeScale time scale}</em>. 989 * <p>The offset is the number of seconds counted in the given 990 * time scale between the locations of the two instants, with 991 * all time scale irregularities removed (i.e. considering all 992 * days are exactly 86400 seconds long). This method will give 993 * a result that may not have a physical meaning if the time scale 994 * is irregular. For example since a leap second was introduced at 995 * the end of 2005, the apparent offset between 2005-12-31T23:59:59 996 * and 2006-01-01T00:00:00 is 1 second, but the physical duration 997 * of the corresponding time interval as returned by the {@link 998 * #durationFrom(AbsoluteDate)} method is 2 seconds.</p> 999 * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate, 1000 * double, TimeScale)} constructor.</p> 1001 * @param instant instant to subtract from the instance 1002 * @param timeScale time scale with respect to which the offset should 1003 * be computed 1004 * @return apparent clock offset in seconds between the two instants 1005 * (positive if the instance is posterior to the argument) 1006 * @see #durationFrom(AbsoluteDate) 1007 * @see #AbsoluteDate(AbsoluteDate, double, TimeScale) 1008 */ 1009 public double offsetFrom(final AbsoluteDate instant, final TimeScale timeScale) { 1010 final long elapsedDurationA = epoch - instant.epoch; 1011 final double elapsedDurationB = (offset + timeScale.offsetFromTAI(this)) - 1012 (instant.offset + timeScale.offsetFromTAI(instant)); 1013 return elapsedDurationA + elapsedDurationB; 1014 } 1015 1016 /** Compute the offset between two time scales at the current instant. 1017 * <p>The offset is defined as <i>l₁-l₂</i> 1018 * where <i>l₁</i> is the location of the instant in 1019 * the <code>scale1</code> time scale and <i>l₂</i> is the 1020 * location of the instant in the <code>scale2</code> time scale.</p> 1021 * @param scale1 first time scale 1022 * @param scale2 second time scale 1023 * @return offset in seconds between the two time scales at the 1024 * current instant 1025 */ 1026 public double timeScalesOffset(final TimeScale scale1, final TimeScale scale2) { 1027 return scale1.offsetFromTAI(this) - scale2.offsetFromTAI(this); 1028 } 1029 1030 /** Convert the instance to a Java {@link java.util.Date Date}. 1031 * <p>Conversion to the Date class induces a loss of precision because 1032 * the Date class does not provide sub-millisecond information. Java Dates 1033 * are considered to be locations in some times scales.</p> 1034 * @param timeScale time scale to use 1035 * @return a {@link java.util.Date Date} instance representing the location 1036 * of the instant in the time scale 1037 */ 1038 public Date toDate(final TimeScale timeScale) { 1039 final double time = epoch + (offset + timeScale.offsetFromTAI(this)); 1040 return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000)); 1041 } 1042 1043 /** 1044 * Convert the instance to a Java {@link java.time.Instant Instant}. 1045 * Nanosecond precision is preserved during this conversion 1046 * 1047 * @return a {@link java.time.Instant Instant} instance representing the location 1048 * of the instant in the utc time scale 1049 * @since 12.1 1050 */ 1051 @DefaultDataContext 1052 public Instant toInstant() { 1053 return toInstant(TimeScalesFactory.getTimeScales()); 1054 } 1055 1056 /** 1057 * Convert the instance to a Java {@link java.time.Instant Instant}. 1058 * Nanosecond precision is preserved during this conversion 1059 * 1060 * @param timeScales the timescales to use 1061 * @return a {@link java.time.Instant Instant} instance representing the location 1062 * of the instant in the utc time scale 1063 * @since 12.1 1064 */ 1065 public Instant toInstant(final TimeScales timeScales) { 1066 final UTCScale utc = timeScales.getUTC(); 1067 final String stringWithoutUtcOffset = toStringWithoutUtcOffset(utc, 9); 1068 1069 final LocalDateTime localDateTime = LocalDateTime.parse(stringWithoutUtcOffset, DateTimeFormatter.ISO_LOCAL_DATE_TIME); 1070 return localDateTime.toInstant(ZoneOffset.UTC); 1071 } 1072 1073 /** Split the instance into date/time components. 1074 * @param timeScale time scale to use 1075 * @return date/time components 1076 */ 1077 public DateTimeComponents getComponents(final TimeScale timeScale) { 1078 1079 if (Double.isInfinite(offset)) { 1080 // special handling for past and future infinity 1081 if (offset < 0) { 1082 return new DateTimeComponents(DateComponents.MIN_EPOCH, TimeComponents.H00); 1083 } else { 1084 return new DateTimeComponents(DateComponents.MAX_EPOCH, 1085 new TimeComponents(23, 59, 59.999)); 1086 } 1087 } 1088 1089 // Compute offset from 2000-01-01T00:00:00 in specified time scale. 1090 // Use 2Sum for high precision. 1091 final double taiOffset = timeScale.offsetFromTAI(this); 1092 final SumAndResidual sumAndResidual = MathUtils.twoSum(offset, taiOffset); 1093 1094 // split date and time 1095 final long carry = (long) FastMath.floor(sumAndResidual.getSum()); 1096 double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual(); 1097 long offset2000A = epoch + carry + 43200L; 1098 if (offset2000B < 0) { 1099 offset2000A -= 1; 1100 offset2000B += 1; 1101 } 1102 long time = offset2000A % 86400L; 1103 if (time < 0L) { 1104 time += 86400L; 1105 } 1106 final int date = (int) ((offset2000A - time) / 86400L); 1107 1108 // extract calendar elements 1109 final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date); 1110 1111 // extract time element, accounting for leap seconds 1112 final double leap = timeScale.insideLeap(this) ? timeScale.getLeap(this) : 0; 1113 final int minuteDuration = timeScale.minuteDuration(this); 1114 final TimeComponents timeComponents = 1115 TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration); 1116 1117 // build the components 1118 return new DateTimeComponents(dateComponents, timeComponents); 1119 1120 } 1121 1122 /** Split the instance into date/time components for a local time. 1123 * 1124 * <p>This method uses the {@link DataContext#getDefault() default data context}. 1125 * 1126 * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC, 1127 * negative Westward UTC) 1128 * @return date/time components 1129 * @since 7.2 1130 * @see #getComponents(int, TimeScale) 1131 */ 1132 @DefaultDataContext 1133 public DateTimeComponents getComponents(final int minutesFromUTC) { 1134 return getComponents(minutesFromUTC, 1135 DataContext.getDefault().getTimeScales().getUTC()); 1136 } 1137 1138 /** 1139 * Split the instance into date/time components for a local time. 1140 * 1141 * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC, 1142 * negative Westward UTC) 1143 * @param utc time scale used to compute date and time components. 1144 * @return date/time components 1145 * @since 10.1 1146 */ 1147 public DateTimeComponents getComponents(final int minutesFromUTC, 1148 final TimeScale utc) { 1149 1150 final DateTimeComponents utcComponents = getComponents(utc); 1151 1152 // shift the date according to UTC offset, but WITHOUT touching the seconds, 1153 // as they may exceed 60.0 during a leap seconds introduction, 1154 // and we want to preserve these special cases 1155 final double seconds = utcComponents.getTime().getSecond(); 1156 1157 int minute = utcComponents.getTime().getMinute() + minutesFromUTC; 1158 final int hourShift; 1159 if (minute < 0) { 1160 hourShift = (minute - 59) / 60; 1161 } else if (minute > 59) { 1162 hourShift = minute / 60; 1163 } else { 1164 hourShift = 0; 1165 } 1166 minute -= 60 * hourShift; 1167 1168 int hour = utcComponents.getTime().getHour() + hourShift; 1169 final int dayShift; 1170 if (hour < 0) { 1171 dayShift = (hour - 23) / 24; 1172 } else if (hour > 23) { 1173 dayShift = hour / 24; 1174 } else { 1175 dayShift = 0; 1176 } 1177 hour -= 24 * dayShift; 1178 1179 return new DateTimeComponents(new DateComponents(utcComponents.getDate(), dayShift), 1180 new TimeComponents(hour, minute, seconds, minutesFromUTC)); 1181 1182 } 1183 1184 /** Split the instance into date/time components for a time zone. 1185 * 1186 * <p>This method uses the {@link DataContext#getDefault() default data context}. 1187 * 1188 * @param timeZone time zone 1189 * @return date/time components 1190 * @since 7.2 1191 * @see #getComponents(TimeZone, TimeScale) 1192 */ 1193 @DefaultDataContext 1194 public DateTimeComponents getComponents(final TimeZone timeZone) { 1195 return getComponents(timeZone, DataContext.getDefault().getTimeScales().getUTC()); 1196 } 1197 1198 /** 1199 * Split the instance into date/time components for a time zone. 1200 * 1201 * @param timeZone time zone 1202 * @param utc time scale used to computed date and time components. 1203 * @return date/time components 1204 * @since 10.1 1205 */ 1206 public DateTimeComponents getComponents(final TimeZone timeZone, 1207 final TimeScale utc) { 1208 final AbsoluteDate javaEpoch = new AbsoluteDate(DateComponents.JAVA_EPOCH, utc); 1209 final long milliseconds = FastMath.round(1000 * offsetFrom(javaEpoch, utc)); 1210 return getComponents(timeZone.getOffset(milliseconds) / 60000, utc); 1211 } 1212 1213 /** Compare the instance with another date. 1214 * @param date other date to compare the instance to 1215 * @return a negative integer, zero, or a positive integer as this date 1216 * is before, simultaneous, or after the specified date. 1217 */ 1218 public int compareTo(final AbsoluteDate date) { 1219 final double duration = durationFrom(date); 1220 if (!Double.isNaN(duration)) { 1221 return Double.compare(duration, 0.0); 1222 } 1223 // both dates are infinity or one is NaN or both are NaN 1224 return Double.compare(offset, date.offset); 1225 } 1226 1227 /** {@inheritDoc} */ 1228 public AbsoluteDate getDate() { 1229 return this; 1230 } 1231 1232 /** Check if the instance represents the same time as another instance. 1233 * @param date other date 1234 * @return true if the instance and the other date refer to the same instant 1235 */ 1236 public boolean equals(final Object date) { 1237 1238 if (date == this) { 1239 // first fast check 1240 return true; 1241 } 1242 1243 if (date instanceof AbsoluteDate) { 1244 1245 // Improve robustness against positive/negative infinity dates 1246 if ( this.offset == Double.NEGATIVE_INFINITY && ((AbsoluteDate) date).offset == Double.NEGATIVE_INFINITY || 1247 this.offset == Double.POSITIVE_INFINITY && ((AbsoluteDate) date).offset == Double.POSITIVE_INFINITY ) { 1248 return true; 1249 } else { 1250 return durationFrom((AbsoluteDate) date) == 0; 1251 } 1252 } 1253 1254 return false; 1255 } 1256 1257 /** Check if the instance represents the same time as another. 1258 * @param other the instant to compare this date to 1259 * @return true if the instance and the argument refer to the same instant 1260 * @see #isCloseTo(TimeStamped, double) 1261 * @since 10.1 1262 */ 1263 public boolean isEqualTo(final TimeStamped other) { 1264 return this.equals(other.getDate()); 1265 } 1266 1267 /** Check if the instance time is close to another. 1268 * @param other the instant to compare this date to 1269 * @param tolerance the separation, in seconds, under which the two instants will be considered close to each other 1270 * @return true if the duration between the instance and the argument is strictly below the tolerance 1271 * @see #isEqualTo(TimeStamped) 1272 * @since 10.1 1273 */ 1274 public boolean isCloseTo(final TimeStamped other, final double tolerance) { 1275 return FastMath.abs(this.durationFrom(other.getDate())) < tolerance; 1276 } 1277 1278 /** Check if the instance represents a time that is strictly before another. 1279 * @param other the instant to compare this date to 1280 * @return true if the instance is strictly before the argument when ordering chronologically 1281 * @see #isBeforeOrEqualTo(TimeStamped) 1282 * @since 10.1 1283 */ 1284 public boolean isBefore(final TimeStamped other) { 1285 return this.compareTo(other.getDate()) < 0; 1286 } 1287 1288 /** Check if the instance represents a time that is strictly after another. 1289 * @param other the instant to compare this date to 1290 * @return true if the instance is strictly after the argument when ordering chronologically 1291 * @see #isAfterOrEqualTo(TimeStamped) 1292 * @since 10.1 1293 */ 1294 public boolean isAfter(final TimeStamped other) { 1295 return this.compareTo(other.getDate()) > 0; 1296 } 1297 1298 /** Check if the instance represents a time that is before or equal to another. 1299 * @param other the instant to compare this date to 1300 * @return true if the instance is before (or equal to) the argument when ordering chronologically 1301 * @see #isBefore(TimeStamped) 1302 * @since 10.1 1303 */ 1304 public boolean isBeforeOrEqualTo(final TimeStamped other) { 1305 return this.isEqualTo(other) || this.isBefore(other); 1306 } 1307 1308 /** Check if the instance represents a time that is after or equal to another. 1309 * @param other the instant to compare this date to 1310 * @return true if the instance is after (or equal to) the argument when ordering chronologically 1311 * @see #isAfterOrEqualTo(TimeStamped) 1312 * @since 10.1 1313 */ 1314 public boolean isAfterOrEqualTo(final TimeStamped other) { 1315 return this.isEqualTo(other) || this.isAfter(other); 1316 } 1317 1318 /** Check if the instance represents a time that is strictly between two others representing 1319 * the boundaries of a time span. The two boundaries can be provided in any order: in other words, 1320 * whether <code>boundary</code> represents a time that is before or after <code>otherBoundary</code> will 1321 * not change the result of this method. 1322 * @param boundary one end of the time span 1323 * @param otherBoundary the other end of the time span 1324 * @return true if the instance is strictly between the two arguments when ordering chronologically 1325 * @see #isBetweenOrEqualTo(TimeStamped, TimeStamped) 1326 * @since 10.1 1327 */ 1328 public boolean isBetween(final TimeStamped boundary, final TimeStamped otherBoundary) { 1329 final TimeStamped beginning; 1330 final TimeStamped end; 1331 if (boundary.getDate().isBefore(otherBoundary)) { 1332 beginning = boundary; 1333 end = otherBoundary; 1334 } else { 1335 beginning = otherBoundary; 1336 end = boundary; 1337 } 1338 return this.isAfter(beginning) && this.isBefore(end); 1339 } 1340 1341 /** Check if the instance represents a time that is between two others representing 1342 * the boundaries of a time span, or equal to one of them. The two boundaries can be provided in any order: 1343 * in other words, whether <code>boundary</code> represents a time that is before or after 1344 * <code>otherBoundary</code> will not change the result of this method. 1345 * @param boundary one end of the time span 1346 * @param otherBoundary the other end of the time span 1347 * @return true if the instance is between the two arguments (or equal to at least one of them) 1348 * when ordering chronologically 1349 * @see #isBetween(TimeStamped, TimeStamped) 1350 * @since 10.1 1351 */ 1352 public boolean isBetweenOrEqualTo(final TimeStamped boundary, final TimeStamped otherBoundary) { 1353 return this.isEqualTo(boundary) || this.isEqualTo(otherBoundary) || this.isBetween(boundary, otherBoundary); 1354 } 1355 1356 /** Get a hashcode for this date. 1357 * @return hashcode 1358 */ 1359 public int hashCode() { 1360 final long l = Double.doubleToLongBits(durationFrom(ARBITRARY_EPOCH)); 1361 return (int) (l ^ (l >>> 32)); 1362 } 1363 1364 /** 1365 * Get a String representation of the instant location with up to 16 digits of 1366 * precision for the seconds value. 1367 * 1368 * <p> Since this method is used in exception messages and error handling every 1369 * effort is made to return some representation of the instant. If UTC is available 1370 * from the default data context then it is used to format the string in UTC. If not 1371 * then TAI is used. Finally if the prior attempts fail this method falls back to 1372 * converting this class's internal representation to a string. 1373 * 1374 * <p>This method uses the {@link DataContext#getDefault() default data context}. 1375 * 1376 * @return a string representation of the instance, in ISO-8601 format if UTC is 1377 * available from the default data context. 1378 * @see #toString(TimeScale) 1379 * @see #toStringRfc3339(TimeScale) 1380 * @see DateTimeComponents#toString(int, int) 1381 */ 1382 @DefaultDataContext 1383 public String toString() { 1384 // CHECKSTYLE: stop IllegalCatch check 1385 try { 1386 // try to use UTC first at that is likely most familiar to the user. 1387 return toString(DataContext.getDefault().getTimeScales().getUTC()) + "Z"; 1388 } catch (RuntimeException e1) { 1389 // catch OrekitException, OrekitIllegalStateException, etc. 1390 try { 1391 // UTC failed, try to use TAI 1392 return toString(new TAIScale()) + " TAI"; 1393 } catch (RuntimeException e2) { 1394 // catch OrekitException, OrekitIllegalStateException, etc. 1395 // Likely failed to convert to ymdhms. 1396 // Give user some indication of what time it is. 1397 try { 1398 return "(" + this.epoch + " + " + this.offset + ") seconds past epoch"; 1399 } catch (RuntimeException e3) { 1400 // give up and throw an exception 1401 e2.addSuppressed(e3); 1402 e1.addSuppressed(e2); 1403 throw e1; 1404 } 1405 } 1406 } 1407 // CHECKSTYLE: resume IllegalCatch check 1408 } 1409 1410 /** 1411 * Get a String representation of the instant location in ISO-8601 format without the 1412 * UTC offset and with up to 16 digits of precision for the seconds value. 1413 * 1414 * @param timeScale time scale to use 1415 * @return a string representation of the instance. 1416 * @see #toStringRfc3339(TimeScale) 1417 * @see DateTimeComponents#toString(int, int) 1418 */ 1419 public String toString(final TimeScale timeScale) { 1420 return getComponents(timeScale).toStringWithoutUtcOffset(); 1421 } 1422 1423 /** Get a String representation of the instant location for a local time. 1424 * 1425 * <p>This method uses the {@link DataContext#getDefault() default data context}. 1426 * 1427 * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC, 1428 * negative Westward UTC). 1429 * @return string representation of the instance, 1430 * in ISO-8601 format with milliseconds accuracy 1431 * @since 7.2 1432 * @see #toString(int, TimeScale) 1433 */ 1434 @DefaultDataContext 1435 public String toString(final int minutesFromUTC) { 1436 return toString(minutesFromUTC, 1437 DataContext.getDefault().getTimeScales().getUTC()); 1438 } 1439 1440 /** 1441 * Get a String representation of the instant location for a local time. 1442 * 1443 * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC, 1444 * negative Westward UTC). 1445 * @param utc time scale used to compute date and time components. 1446 * @return string representation of the instance, in ISO-8601 format with milliseconds 1447 * accuracy 1448 * @since 10.1 1449 * @see #getComponents(int, TimeScale) 1450 * @see DateTimeComponents#toString(int, int) 1451 */ 1452 public String toString(final int minutesFromUTC, final TimeScale utc) { 1453 final int minuteDuration = utc.minuteDuration(this); 1454 return getComponents(minutesFromUTC, utc).toString(minuteDuration); 1455 } 1456 1457 /** Get a String representation of the instant location for a time zone. 1458 * 1459 * <p>This method uses the {@link DataContext#getDefault() default data context}. 1460 * 1461 * @param timeZone time zone 1462 * @return string representation of the instance, 1463 * in ISO-8601 format with milliseconds accuracy 1464 * @since 7.2 1465 * @see #toString(TimeZone, TimeScale) 1466 */ 1467 @DefaultDataContext 1468 public String toString(final TimeZone timeZone) { 1469 return toString(timeZone, DataContext.getDefault().getTimeScales().getUTC()); 1470 } 1471 1472 /** 1473 * Get a String representation of the instant location for a time zone. 1474 * 1475 * @param timeZone time zone 1476 * @param utc time scale used to compute date and time components. 1477 * @return string representation of the instance, in ISO-8601 format with milliseconds 1478 * accuracy 1479 * @since 10.1 1480 * @see #getComponents(TimeZone, TimeScale) 1481 * @see DateTimeComponents#toString(int, int) 1482 */ 1483 public String toString(final TimeZone timeZone, final TimeScale utc) { 1484 final int minuteDuration = utc.minuteDuration(this); 1485 return getComponents(timeZone, utc).toString(minuteDuration); 1486 } 1487 1488 /** 1489 * Represent the given date as a string according to the format in RFC 3339. RFC3339 1490 * is a restricted subset of ISO 8601 with a well defined grammar. Enough digits are 1491 * included in the seconds value to avoid rounding up to the next minute. 1492 * 1493 * <p>This method is different than {@link AbsoluteDate#toString(TimeScale)} in that 1494 * it includes a {@code "Z"} at the end to indicate the time zone and enough precision 1495 * to represent the point in time without rounding up to the next minute. 1496 * 1497 * <p>RFC3339 is unable to represent BC years, years of 10000 or more, time zone 1498 * offsets of 100 hours or more, or NaN. In these cases the value returned from this 1499 * method will not be valid RFC3339 format. 1500 * 1501 * @param utc time scale. 1502 * @return RFC 3339 format string. 1503 * @see <a href="https://tools.ietf.org/html/rfc3339#page-8">RFC 3339</a> 1504 * @see DateTimeComponents#toStringRfc3339() 1505 * @see #toString(TimeScale) 1506 * @see #getComponents(TimeScale) 1507 */ 1508 public String toStringRfc3339(final TimeScale utc) { 1509 return this.getComponents(utc).toStringRfc3339(); 1510 } 1511 1512 /** 1513 * Return a string representation of this date-time, rounded to the given precision. 1514 * 1515 * <p>The format used is ISO8601 without the UTC offset.</p> 1516 * 1517 * <p>Calling {@code toStringWithoutUtcOffset(DataContext.getDefault().getTimeScales().getUTC(), 1518 * 3)} will emulate the behavior of {@link #toString()} in Orekit 10 and earlier. Note 1519 * this method is more accurate as it correctly handles rounding during leap seconds. 1520 * 1521 * @param timeScale to use to compute components. 1522 * @param fractionDigits the number of digits to include after the decimal point in 1523 * the string representation of the seconds. The date and time 1524 * is first rounded as necessary. {@code fractionDigits} must be 1525 * greater than or equal to {@code 0}. 1526 * @return string representation of this date, time, and UTC offset 1527 * @see #toString(TimeScale) 1528 * @see #toStringRfc3339(TimeScale) 1529 * @see DateTimeComponents#toString(int, int) 1530 * @see DateTimeComponents#toStringWithoutUtcOffset(int, int) 1531 * @since 11.1 1532 */ 1533 public String toStringWithoutUtcOffset(final TimeScale timeScale, 1534 final int fractionDigits) { 1535 return this.getComponents(timeScale) 1536 .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits); 1537 } 1538 1539 }