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