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 }