1   /* Copyright 2002-2020 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  
21  import org.hipparchus.util.FastMath;
22  import org.orekit.utils.Constants;
23  
24  /** Holder for date and time components.
25   * <p>This class is a simple holder with no processing methods.</p>
26   * <p>Instance of this class are guaranteed to be immutable.</p>
27   * @see AbsoluteDate
28   * @see DateComponents
29   * @see TimeComponents
30   * @author Luc Maisonobe
31   */
32  public class DateTimeComponents implements Serializable, Comparable<DateTimeComponents> {
33  
34      /**
35       * The Julian Epoch.
36       *
37       * @see TimeScales#getJulianEpoch()
38       */
39      public static final DateTimeComponents JULIAN_EPOCH =
40              new DateTimeComponents(DateComponents.JULIAN_EPOCH, TimeComponents.H12);
41  
42      /** Serializable UID. */
43      private static final long serialVersionUID = 5061129505488924484L;
44  
45      /** Date component. */
46      private final DateComponents date;
47  
48      /** Time component. */
49      private final TimeComponents time;
50  
51      /** Build a new instance from its components.
52       * @param date date component
53       * @param time time component
54       */
55      public DateTimeComponents(final DateComponents date, final TimeComponents time) {
56          this.date = date;
57          this.time = time;
58      }
59  
60      /** Build an instance from raw level components.
61       * @param year year number (may be 0 or negative for BC years)
62       * @param month month number from 1 to 12
63       * @param day day number from 1 to 31
64       * @param hour hour number from 0 to 23
65       * @param minute minute number from 0 to 59
66       * @param second second number from 0.0 to 60.0 (excluded)
67       * @exception IllegalArgumentException if inconsistent arguments
68       * are given (parameters out of range, february 29 for non-leap years,
69       * dates during the gregorian leap in 1582 ...)
70       */
71      public DateTimeComponents(final int year, final int month, final int day,
72                                final int hour, final int minute, final double second)
73          throws IllegalArgumentException {
74          this.date = new DateComponents(year, month, day);
75          this.time = new TimeComponents(hour, minute, second);
76      }
77  
78      /** Build an instance from raw level components.
79       * @param year year number (may be 0 or negative for BC years)
80       * @param month month enumerate
81       * @param day day number from 1 to 31
82       * @param hour hour number from 0 to 23
83       * @param minute minute number from 0 to 59
84       * @param second second number from 0.0 to 60.0 (excluded)
85       * @exception IllegalArgumentException if inconsistent arguments
86       * are given (parameters out of range, february 29 for non-leap years,
87       * dates during the gregorian leap in 1582 ...)
88       */
89      public DateTimeComponents(final int year, final Month month, final int day,
90                                final int hour, final int minute, final double second)
91          throws IllegalArgumentException {
92          this.date = new DateComponents(year, month, day);
93          this.time = new TimeComponents(hour, minute, second);
94      }
95  
96      /** Build an instance from raw level components.
97       * <p>The hour is set to 00:00:00.000.</p>
98       * @param year year number (may be 0 or negative for BC years)
99       * @param month month number from 1 to 12
100      * @param day day number from 1 to 31
101      * @exception IllegalArgumentException if inconsistent arguments
102      * are given (parameters out of range, february 29 for non-leap years,
103      * dates during the gregorian leap in 1582 ...)
104      */
105     public DateTimeComponents(final int year, final int month, final int day)
106         throws IllegalArgumentException {
107         this.date = new DateComponents(year, month, day);
108         this.time = TimeComponents.H00;
109     }
110 
111     /** Build an instance from raw level components.
112      * <p>The hour is set to 00:00:00.000.</p>
113      * @param year year number (may be 0 or negative for BC years)
114      * @param month month enumerate
115      * @param day day number from 1 to 31
116      * @exception IllegalArgumentException if inconsistent arguments
117      * are given (parameters out of range, february 29 for non-leap years,
118      * dates during the gregorian leap in 1582 ...)
119      */
120     public DateTimeComponents(final int year, final Month month, final int day)
121         throws IllegalArgumentException {
122         this.date = new DateComponents(year, month, day);
123         this.time = TimeComponents.H00;
124     }
125 
126     /** Build an instance from a seconds offset with respect to another one.
127      * @param reference reference date/time
128      * @param offset offset from the reference in seconds
129      * @see #offsetFrom(DateTimeComponents)
130      */
131     public DateTimeComponentstml#DateTimeComponents">DateTimeComponents(final DateTimeComponents reference,
132                               final double offset) {
133 
134         // extract linear data from reference date/time
135         int    day     = reference.getDate().getJ2000Day();
136         double seconds = reference.getTime().getSecondsInLocalDay();
137 
138         // apply offset
139         seconds += offset;
140 
141         // fix range
142         final int dayShift = (int) FastMath.floor(seconds / Constants.JULIAN_DAY);
143         seconds -= Constants.JULIAN_DAY * dayShift;
144         day     += dayShift;
145         final TimeComponentsComponents">TimeComponents tmpTime = new TimeComponents(seconds);
146 
147         // set up components
148         this.date = new DateComponents(day);
149         this.time = new TimeComponents(tmpTime.getHour(), tmpTime.getMinute(), tmpTime.getSecond(),
150                                        reference.getTime().getMinutesFromUTC());
151 
152     }
153 
154     /** Parse a string in ISO-8601 format to build a date/time.
155      * <p>The supported formats are all date formats supported by {@link DateComponents#parseDate(String)}
156      * and all time formats supported by {@link TimeComponents#parseTime(String)} separated
157      * by the standard time separator 'T', or date components only (in which case a 00:00:00 hour is
158      * implied). Typical examples are 2000-01-01T12:00:00Z or 1976W186T210000.
159      * </p>
160      * @param string string to parse
161      * @return a parsed date/time
162      * @exception IllegalArgumentException if string cannot be parsed
163      */
164     public static DateTimeComponents parseDateTime(final String string) {
165 
166         // is there a time ?
167         final int tIndex = string.indexOf('T');
168         if (tIndex > 0) {
169             return new DateTimeComponents(DateComponents.parseDate(string.substring(0, tIndex)),
170                                           TimeComponents.parseTime(string.substring(tIndex + 1)));
171         }
172 
173         return new DateTimeComponents(DateComponents.parseDate(string), TimeComponents.H00);
174 
175     }
176 
177     /** Compute the seconds offset between two instances.
178      * @param dateTime dateTime to subtract from the instance
179      * @return offset in seconds between the two instants
180      * (positive if the instance is posterior to the argument)
181      * @see #DateTimeComponents(DateTimeComponents, double)
182      */
183     public double offsetFrom(final DateTimeComponents dateTime) {
184         final int dateOffset = date.getJ2000Day() - dateTime.date.getJ2000Day();
185         final double timeOffset = time.getSecondsInUTCDay() - dateTime.time.getSecondsInUTCDay();
186         return Constants.JULIAN_DAY * dateOffset + timeOffset;
187     }
188 
189     /** Get the date component.
190      * @return date component
191      */
192     public DateComponents getDate() {
193         return date;
194     }
195 
196     /** Get the time component.
197      * @return time component
198      */
199     public TimeComponents getTime() {
200         return time;
201     }
202 
203     /** {@inheritDoc} */
204     public int compareTo(final DateTimeComponents other) {
205         final int dateComparison = date.compareTo(other.date);
206         if (dateComparison < 0) {
207             return -1;
208         } else if (dateComparison > 0) {
209             return 1;
210         }
211         return time.compareTo(other.time);
212     }
213 
214     /** {@inheritDoc} */
215     public boolean equals(final Object other) {
216         try {
217             final DateTimeComponents/time/DateTimeComponents.html#DateTimeComponents">DateTimeComponents otherDateTime = (DateTimeComponents) other;
218             return (otherDateTime != null) &&
219                    date.equals(otherDateTime.date) && time.equals(otherDateTime.time);
220         } catch (ClassCastException cce) {
221             return false;
222         }
223     }
224 
225     /** {@inheritDoc} */
226     public int hashCode() {
227         return (date.hashCode() << 16) ^ time.hashCode();
228     }
229 
230     /** Return a string representation of this pair.
231      * <p>The format used is ISO8601.</p>
232      * @return string representation of this pair
233      */
234     public String toString() {
235         return toString(60);
236     }
237 
238     /** Return a string representation of this pair.
239      * <p>The format used is ISO8601.</p>
240      * @param minuteDuration 60 or 61 depending on the date being
241      * close to a leap second introduction
242      * @return string representation of this pair
243      */
244     public String toString(final int minuteDuration) {
245         double second = time.getSecond();
246         final double wrap = minuteDuration - 0.0005;
247         if (second >= wrap) {
248             // we should wrap around next millisecond
249             int minute = time.getMinute();
250             int hour   = time.getHour();
251             int j2000  = date.getJ2000Day();
252             second = 0;
253             ++minute;
254             if (minute > 59) {
255                 minute = 0;
256                 ++hour;
257                 if (hour > 23) {
258                     hour = 0;
259                     ++j2000;
260                 }
261             }
262             return new DateComponents(j2000).toString() + 'T' + new TimeComponents(hour, minute, second).toString();
263         }
264         return date.toString() + 'T' + time.toString();
265     }
266 
267 }
268