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().getSecondsInLocalDay(); 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 final TimeComponents tmpTime = new TimeComponents(seconds); 138 139 // set up components 140 this.date = new DateComponents(day); 141 this.time = new TimeComponents(tmpTime.getHour(), tmpTime.getMinute(), tmpTime.getSecond(), 142 reference.getTime().getMinutesFromUTC()); 143 144 } 145 146 /** Parse a string in ISO-8601 format to build a date/time. 147 * <p>The supported formats are all date formats supported by {@link DateComponents#parseDate(String)} 148 * and all time formats supported by {@link TimeComponents#parseTime(String)} separated 149 * by the standard time separator 'T', or date components only (in which case a 00:00:00 hour is 150 * implied). Typical examples are 2000-01-01T12:00:00Z or 1976W186T210000. 151 * </p> 152 * @param string string to parse 153 * @return a parsed date/time 154 * @exception IllegalArgumentException if string cannot be parsed 155 */ 156 public static DateTimeComponents parseDateTime(final String string) { 157 158 // is there a time ? 159 final int tIndex = string.indexOf('T'); 160 if (tIndex > 0) { 161 return new DateTimeComponents(DateComponents.parseDate(string.substring(0, tIndex)), 162 TimeComponents.parseTime(string.substring(tIndex + 1))); 163 } 164 165 return new DateTimeComponents(DateComponents.parseDate(string), TimeComponents.H00); 166 167 } 168 169 /** Compute the seconds offset between two instances. 170 * @param dateTime dateTime to subtract from the instance 171 * @return offset in seconds between the two instants 172 * (positive if the instance is posterior to the argument) 173 * @see #DateTimeComponents(DateTimeComponents, double) 174 */ 175 public double offsetFrom(final DateTimeComponents dateTime) { 176 final int dateOffset = date.getJ2000Day() - dateTime.date.getJ2000Day(); 177 final double timeOffset = time.getSecondsInUTCDay() - dateTime.time.getSecondsInUTCDay(); 178 return Constants.JULIAN_DAY * dateOffset + timeOffset; 179 } 180 181 /** Get the date component. 182 * @return date component 183 */ 184 public DateComponents getDate() { 185 return date; 186 } 187 188 /** Get the time component. 189 * @return time component 190 */ 191 public TimeComponents getTime() { 192 return time; 193 } 194 195 /** {@inheritDoc} */ 196 public int compareTo(final DateTimeComponents other) { 197 final int dateComparison = date.compareTo(other.date); 198 if (dateComparison < 0) { 199 return -1; 200 } else if (dateComparison > 0) { 201 return 1; 202 } 203 return time.compareTo(other.time); 204 } 205 206 /** {@inheritDoc} */ 207 public boolean equals(final Object other) { 208 try { 209 final DateTimeComponents otherDateTime = (DateTimeComponents) other; 210 return (otherDateTime != null) && 211 date.equals(otherDateTime.date) && time.equals(otherDateTime.time); 212 } catch (ClassCastException cce) { 213 return false; 214 } 215 } 216 217 /** {@inheritDoc} */ 218 public int hashCode() { 219 return (date.hashCode() << 16) ^ time.hashCode(); 220 } 221 222 /** Return a string representation of this pair. 223 * <p>The format used is ISO8601.</p> 224 * @return string representation of this pair 225 */ 226 public String toString() { 227 return toString(false); 228 } 229 230 /** Return a string representation of this pair. 231 * <p>The format used is ISO8601.</p> 232 * @param inLeap if true the date is a UTC date during a leap second introduction 233 * @return string representation of this pair 234 */ 235 public String toString(final boolean inLeap) { 236 double second = time.getSecond(); 237 final double wrap = inLeap ? 60.9995 : 59.9995; 238 if (second >= wrap) { 239 // we should wrap around next millisecond 240 int minute = time.getMinute(); 241 int hour = time.getHour(); 242 int j2000 = date.getJ2000Day(); 243 second = 0; 244 ++minute; 245 if (minute > 59) { 246 minute = 0; 247 ++hour; 248 if (hour > 23) { 249 hour = 0; 250 ++j2000; 251 } 252 } 253 return new DateComponents(j2000).toString() + 'T' + new TimeComponents(hour, minute, second).toString(); 254 } 255 return date.toString() + 'T' + time.toString(); 256 } 257 258 } 259