View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2006, 2013
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Endre Bak, ebak@de.ibm.com
12   * 
13   * Flag       Date        Prog         Description
14   * -------------------------------------------------------------------------------
15   * 1565892    2006-10-06  ebak         Make SBLIM client JSR48 compliant
16   * 1678807    2007-03-12  ebak         Minor CIMDateTime suggestions
17   * 1931621    2008-04-02  blaschke-oss CIMDateTimeAbsolute(Calendar) does not respect DST
18   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
19   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
20   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
21   * 2750520    2009-04-10  blaschke-oss Code cleanup from empty statement et al
22   * 2795671    2009-05-22  raman_arora  Add Type to Comparable <T>
23   * 2806362    2009-06-14  blaschke-oss Missing new CIMDateTimeAbsolute.getUTCOffset() method
24   * 2935258    2010-01-22  blaschke-oss Sync up javax.cim.* javadoc with JSR48 1.0.0
25   * 2944826    2010-02-08  blaschke-oss getUTCOffset() incorrect if not significant field
26   * 2973300    2010-03-19  blaschke-oss TCK: CIMDateTimeXXX.compareTo() does not handle null
27   * 3022501    2010-06-30  blaschke-oss Possible integer overflow in getTotalUSec
28   * 3400209    2011-08-31  blaschke-oss Highlighted Static Analysis (PMD) issues
29   * 3565581    2012-09-07  blaschke-oss TCK: remove unnecessary overriding methods
30   *    2674    2013-09-26  blaschke-oss Null pointer exception in CIMDateTime(String)
31   *    2716    2013-12-11  blaschke-oss Sync up javax.* javadoc with JSR48 1.0.0 Final V
32   */
33  
34  package org.metricshub.wbem.javax.cim;
35  
36  /*-
37   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
38   * WBEM Java Client
39   * ჻჻჻჻჻჻
40   * Copyright 2023 - 2025 MetricsHub
41   * ჻჻჻჻჻჻
42   * Licensed under the Apache License, Version 2.0 (the "License");
43   * you may not use this file except in compliance with the License.
44   * You may obtain a copy of the License at
45   *
46   *      http://www.apache.org/licenses/LICENSE-2.0
47   *
48   * Unless required by applicable law or agreed to in writing, software
49   * distributed under the License is distributed on an "AS IS" BASIS,
50   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
51   * See the License for the specific language governing permissions and
52   * limitations under the License.
53   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
54   */
55  
56  import java.util.Calendar;
57  import org.metricshub.wbem.sblim.cimclient.internal.cim.DTStringReader;
58  import org.metricshub.wbem.sblim.cimclient.internal.cim.DTStringWriter;
59  
60  //Sync'd against JSR48 1.0.0 javadoc (version 1.7.0_03) on Tue Dec 10 07:02:50 EST 2013
61  /**
62   * This class represents the datetime data type when used as a time value as
63   * specified in the CIM Infrastructure specification. It is in the format
64   * yyyyMMddHHmmss.SSSSSSsutc where:
65   * <ul>
66   * <li>yyyy - is a 4 digit year</li>
67   * <li>MM - is the month</li>
68   * <li>dd - is the day of the month</li>
69   * <li>HH - is the hour (24 hour clock)</li>
70   * <li>mm - is the minute</li>
71   * <li>ss - is the second</li>
72   * <li>SSSSSS - is the number of microseconds</li>
73   * <li>s - is "+" or "-", indicating the sign of the UTC (Universal Coordinated
74   * Time; for all intents and purposes the same as Greenwich Mean Time)</li>
75   * <li>utc - is the offset from UTC in minutes (using the sign indicated by s).</li>
76   * </ul>
77   * For example Monday, May 25, 1998, at 1:30 PM EST would be represented as:
78   * 19980525133015.000000-300. Values must be zero-padded so that the entire
79   * string is always the same 25-character length. Fields which are not
80   * significant must be replaced with asterisk characters.
81   */
82  public class CIMDateTimeAbsolute extends CIMDateTime {
83  	private static final long serialVersionUID = 7556792806296945178l;
84  
85  	private int iYear, iMonth, iDay, iHour, iMin, iSec, iUSec, iUtc;
86  
87  	private boolean iUnsignificantUtc;
88  
89  	private String iStr;
90  
91  	/**
92  	 * Create a <code>CIMDateTimeAbsolute</code> object using the current
93  	 * Time/Date of the system.
94  	 */
95  	public CIMDateTimeAbsolute() {
96  		set(Calendar.getInstance());
97  	}
98  
99  	/**
100 	 * Create a <code>CIMDateTimeAbsolute</code> object using a
101 	 * <code>Calendar</code> object.
102 	 *
103 	 * @param pCalendar
104 	 *            A <code>Calendar</code> object used to initialize this object.
105 	 * @throws IllegalArgumentException
106 	 *             If <code>Calendar</code> object is <code>null</code>.
107 	 */
108 	public CIMDateTimeAbsolute(Calendar pCalendar) throws IllegalArgumentException {
109 		if (pCalendar == null) throw new IllegalArgumentException("Null Calendar is not allowed!");
110 		if (pCalendar.get(Calendar.YEAR) > 9999) throw new IllegalArgumentException(
111 			"The year field cannot be greater than 9999!"
112 		);
113 		set(pCalendar);
114 	}
115 
116 	/**
117 	 * Creates a <code>CIMDateTimeAbsolute</code> object using a string.
118 	 *
119 	 * @param pDateTime
120 	 *            A string in the format of yyyyMMddHHmmss.SSSSSSsutc.
121 	 * @throws IllegalArgumentException
122 	 *             If string is not in the correct format or <code>null</code>.
123 	 */
124 	public CIMDateTimeAbsolute(String pDateTime) throws IllegalArgumentException {
125 		if (pDateTime == null) throw new IllegalArgumentException("Null DateTime is not allowed!");
126 		DTStringReader reader = new DTStringReader(pDateTime);
127 		this.iYear = reader.readAndCheck(4, "year", 0, 9999, true);
128 		this.iMonth = reader.readAndCheck(2, "month", 1, 12, true);
129 		this.iDay = reader.readAndCheck(2, "day", 1, 31, true);
130 		this.iHour = reader.readAndCheck(2, "hour", 0, 23, true);
131 		this.iMin = reader.readAndCheck(2, "minute", 0, 59, true);
132 		this.iSec = reader.readAndCheck(2, "second", 0, 59, true);
133 		reader.read('.');
134 		this.iUSec = reader.readAndCheck(6, "microSeconds", 0, 999999, true);
135 		char sign = reader.read();
136 		if (sign != '+' && sign != '-') {
137 			String msg = "Illegal character '" + sign + "' at position " + reader.getPos() + "! '+' or '-' is expected.";
138 			throw new IllegalArgumentException(msg);
139 		}
140 		this.iUtc = reader.read(3, "utc", true);
141 		if (reader.isUnsignificant()) {
142 			this.iUnsignificantUtc = true;
143 		} else if (sign == '-') {
144 			this.iUtc = -this.iUtc;
145 		}
146 		if (reader.read() != 0) throw new IllegalArgumentException("Extra character at the end of " + pDateTime + " !");
147 		this.iStr = pDateTime;
148 	}
149 
150 	/**
151 	 * Compares the <code>CIMDateTimeAbsolute</code> object with this one. If
152 	 * either date has "Not Significant" fields then we can only compare the
153 	 * significant fields.
154 	 *
155 	 * @param pDateTime
156 	 *            The <code>CIMDateTimeAbsolute</code> to be compared with this
157 	 *            one.
158 	 * @return -1, zero, or 1 as this date is less than, equal to, or greater
159 	 *         than the specified date.
160 	 * @throws IllegalArgumentException
161 	 *             If the object passed in is not an instance of
162 	 *             <code>CIMDataTimeAbsolute</code>.
163 	 */
164 	public int compareTo(CIMDateTime pDateTime) throws IllegalArgumentException {
165 		if (!(pDateTime instanceof CIMDateTimeAbsolute)) {
166 			String msg =
167 				"pDateTime must be a CIMDateTimeAbsolute instance while it is a " +
168 				(pDateTime == null ? "null!" : pDateTime.getClass().getName() + " instance!");
169 			throw new IllegalArgumentException(msg);
170 		}
171 
172 		CIMDateTimeAbsolute that = (CIMDateTimeAbsolute) pDateTime;
173 		/*
174 		 * Comparison: 1. building Calendars from both dates. If a field is not
175 		 * significant it and it's corresponding pair is cleared.
176 		 */
177 		int mask = getMask() & that.getMask();
178 		long thisMicros = getTotalUSec(mask);
179 		long thatMicros = that.getTotalUSec(mask);
180 		long val = thisMicros - thatMicros;
181 		if (val == 0) return 0;
182 		return val < 0 ? -1 : 1;
183 	}
184 
185 	/**
186 	 * Gets the internal string representation of the date/time object.
187 	 *
188 	 * @return The internal representation of the date/time object.
189 	 */
190 	@Override
191 	public String getDateTimeString() {
192 		if (this.iStr != null) return this.iStr;
193 		// yyyyMMddHHmmss.uuuuuuSutc
194 		DTStringWriter dTWriter = new DTStringWriter();
195 		dTWriter.write(4, this.iYear);
196 		dTWriter.write(2, this.iMonth);
197 		dTWriter.write(2, this.iDay);
198 		dTWriter.write(2, this.iHour);
199 		dTWriter.write(2, this.iMin);
200 		dTWriter.write(2, this.iSec);
201 		dTWriter.write('.');
202 		dTWriter.write(6, this.iUSec);
203 		if (this.iUnsignificantUtc) dTWriter.write("+***"); else dTWriter.writeSigned(3, this.iUtc);
204 		return this.iStr = dTWriter.toString();
205 	}
206 
207 	/**
208 	 * Returns day value of this date.
209 	 *
210 	 * @return If day field "not significant" this returns -1, otherwise returns
211 	 *         day of this date.
212 	 */
213 	public int getDay() {
214 		return this.iDay;
215 	}
216 
217 	/**
218 	 * Returns hour value of this date.
219 	 *
220 	 * @return If hour field "not significant" this returns -1, otherwise
221 	 *         returns hour of this date.
222 	 */
223 	public int getHour() {
224 		return this.iHour;
225 	}
226 
227 	/**
228 	 * Returns microsecond value of this date.
229 	 *
230 	 * @return If microsecond field "not significant" this returns -1, otherwise
231 	 *         returns microseconds of this date.
232 	 */
233 	public int getMicrosecond() {
234 		return this.iUSec;
235 	}
236 
237 	/**
238 	 * Returns minute value of this date.
239 	 *
240 	 * @return If minute field "not significant" this returns -1, otherwise
241 	 *         returns minute of this date.
242 	 */
243 	public int getMinute() {
244 		return this.iMin;
245 	}
246 
247 	/**
248 	 * Returns month value of this date.
249 	 *
250 	 * @return If month field "not significant" this returns -1, otherwise
251 	 *         returns the month of this date.
252 	 */
253 	public int getMonth() {
254 		return this.iMonth;
255 	}
256 
257 	/**
258 	 * Returns second value of this date.
259 	 *
260 	 * @return If second field "not significant" this returns -1, otherwise
261 	 *         returns second of this date.
262 	 */
263 	public int getSecond() {
264 		return this.iSec;
265 	}
266 
267 	/**
268 	 * Returns UTC offset value of this date.
269 	 *
270 	 * @return UTC offset of this date.
271 	 */
272 	public int getUTCOffset() {
273 		return this.iUnsignificantUtc ? -1 : this.iUtc;
274 	}
275 
276 	/**
277 	 * Returns year value of this Date.
278 	 *
279 	 * @return If year field "not significant" this returns -1, otherwise
280 	 *         returns the year of this date.
281 	 */
282 	public int getYear() {
283 		return this.iYear;
284 	}
285 
286 	/**
287 	 * Returns the hash code for this object.
288 	 *
289 	 * @return A hash code value for this object.
290 	 * @see java.lang.Object#hashCode()
291 	 */
292 	@Override
293 	public int hashCode() {
294 		return getDateTimeString().hashCode();
295 	}
296 
297 	/**
298 	 * Returns a <code>String</code> representation of the
299 	 * <code>CIMDateTimeAbsolute</code>. This method is intended to be used only
300 	 * for debugging purposes, and the format of the returned string may vary
301 	 * between implementations. The returned string may be empty but may not be
302 	 * <code>null</code>.
303 	 *
304 	 * @return String representation of this datetime.
305 	 */
306 	@Override
307 	public String toString() {
308 		return getDateTimeString();
309 	}
310 
311 	private static final int YEAR = 1, MONTH = 2, DAY = 4, HOUR = 8, MIN = 16, SEC = 32, USEC = 64, UTC = 128;
312 
313 	/**
314 	 * Get mask of this <code>CIMDateTimeAbsolute</code> where bit is set if the
315 	 * corresponding field is significant or clear if it is not significant.
316 	 *
317 	 * @return Mask of significant fields in datetime object.
318 	 */
319 	private int getMask() {
320 		int mask = 0;
321 		if (this.iYear != -1) mask |= YEAR;
322 		if (this.iMonth != -1) mask |= MONTH;
323 		if (this.iDay != -1) mask |= DAY;
324 		if (this.iHour != -1) mask |= HOUR;
325 		if (this.iMin != -1) mask |= MIN;
326 		if (this.iSec != -1) mask |= SEC;
327 		if (this.iUSec != -1) mask |= USEC;
328 		if (!this.iUnsignificantUtc) mask |= UTC;
329 		return mask;
330 	}
331 
332 	/**
333 	 * Get value of a field based on mask.
334 	 *
335 	 * @param pMask
336 	 *            Mask of significant fields.
337 	 * @param pField
338 	 *            Field.
339 	 * @param pValue
340 	 *            Value of field.
341 	 * @param pInitValue
342 	 *            Initial value of field.
343 	 * @return Value of field if field significant, otherwise initial value.
344 	 */
345 	private int mask(int pMask, int pField, int pValue, int pInitValue) {
346 		return ((pMask & pField) > 0) ? pValue : pInitValue;
347 	}
348 
349 	/**
350 	 * Get total microseconds of <code>CIMDateTimeAbsolute</code> object where
351 	 * only significant fields are used in calculation.
352 	 *
353 	 * @param pMask
354 	 *            Mask of significant fields.
355 	 * @return Total microseconds of significant fields in datetime.
356 	 */
357 	private long getTotalUSec(int pMask) {
358 		Calendar cal = Calendar.getInstance();
359 		cal.set(Calendar.YEAR, mask(pMask, YEAR, this.iYear, 0));
360 		cal.set(Calendar.MONTH, mask(pMask, MONTH, this.iMonth - 1, 0));
361 		cal.set(Calendar.DAY_OF_MONTH, mask(pMask, DAY, this.iDay, 1));
362 		cal.set(Calendar.HOUR_OF_DAY, mask(pMask, HOUR, this.iHour, 0));
363 		cal.set(Calendar.MINUTE, mask(pMask, MIN, this.iMin, 0));
364 		cal.set(Calendar.SECOND, mask(pMask, SEC, this.iSec, 0));
365 		int millis = this.iUSec / 1000, micros = this.iUSec % 1000;
366 		cal.set(Calendar.MILLISECOND, mask(pMask, USEC, millis, 0));
367 		long totalMicros = cal.getTimeInMillis() * 1000;
368 		if ((pMask & USEC) > 0) totalMicros += micros;
369 		/*
370 		 * UTC is added to the calculated micros
371 		 */
372 		if ((pMask & UTC) > 0) totalMicros += this.iUtc * 60000000L;
373 		return totalMicros;
374 	}
375 
376 	/**
377 	 * Initializes this <code>CIMDateTimeAbsolute</code> object from the
378 	 * <code>Calendar</code> passed in.
379 	 *
380 	 * @param pCal
381 	 *            Calendar object.
382 	 */
383 	private void set(Calendar pCal) {
384 		this.iYear = pCal.get(Calendar.YEAR);
385 		this.iMonth = pCal.get(Calendar.MONTH) + 1;
386 		this.iDay = pCal.get(Calendar.DAY_OF_MONTH);
387 		this.iHour = pCal.get(Calendar.HOUR_OF_DAY);
388 		this.iMin = pCal.get(Calendar.MINUTE);
389 		this.iSec = pCal.get(Calendar.SECOND);
390 		this.iUSec = pCal.get(Calendar.MILLISECOND) * 1000;
391 		if (pCal.getTimeZone().inDaylightTime(pCal.getTime())) {
392 			this.iUtc = (pCal.get(Calendar.ZONE_OFFSET) + pCal.get(Calendar.DST_OFFSET)) / 60000;
393 		} else {
394 			this.iUtc = pCal.get(Calendar.ZONE_OFFSET) / 60000;
395 		}
396 	}
397 }