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