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