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   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
18   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
19   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
20   * 2750520    2009-04-10  blaschke-oss Code cleanup from empty statement et al
21   * 2763216    2009-04-14  blaschke-oss Code cleanup: visible spelling/grammar errors
22   * 2795671    2009-05-22  raman_arora  Add Type to Comparable <T>
23   * 2935258    2010-01-22  blaschke-oss Sync up javax.cim.* javadoc with JSR48 1.0.0
24   * 2944830    2010-02-08  blaschke-oss Fix spelling of checkGranurality()
25   * 2973300    2010-03-19  blaschke-oss TCK: CIMDateTimeXXX.compareTo() does not handle null
26   * 2989367    2010-04-29  blaschke-oss CIMDateTimeInterval(long) constructor range wrong
27   * 2989424    2010-04-29  blaschke-oss TCK: CIMDateTimeInterval constructor
28   * 2992349    2010-04-29  blaschke-oss CIMDateTimeInterval hr/min/sec max is 23/59/59, not 24/60/60
29   * 2994249    2010-04-30  blaschke-oss CIMDateTimeInterval(long) calculates milliseconds
30   * 3018178    2010-06-18  blaschke-oss CIMDateTimeInterval clean up
31   * 3026302    2010-07-07  blaschke-oss CIMDateTimeInterval uses # constructor instead of valueOf
32   * 3400209    2011-08-31  blaschke-oss Highlighted Static Analysis (PMD) issues
33   * 3565581    2012-09-07  blaschke-oss TCK: remove unnecessary overriding methods
34   *    2674    2013-09-26  blaschke-oss Null pointer exception in CIMDateTime(String)
35   *    2716    2013-12-11  blaschke-oss Sync up javax.* javadoc with JSR48 1.0.0 Final V
36   */
37  
38  package org.metricshub.wbem.javax.cim;
39  
40  /*-
41   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
42   * WBEM Java Client
43   * ჻჻჻჻჻჻
44   * Copyright 2023 - 2025 MetricsHub
45   * ჻჻჻჻჻჻
46   * Licensed under the Apache License, Version 2.0 (the "License");
47   * you may not use this file except in compliance with the License.
48   * You may obtain a copy of the License at
49   *
50   *      http://www.apache.org/licenses/LICENSE-2.0
51   *
52   * Unless required by applicable law or agreed to in writing, software
53   * distributed under the License is distributed on an "AS IS" BASIS,
54   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
55   * See the License for the specific language governing permissions and
56   * limitations under the License.
57   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
58   */
59  
60  import org.metricshub.wbem.sblim.cimclient.internal.cim.DTStringReader;
61  import org.metricshub.wbem.sblim.cimclient.internal.cim.DTStringWriter;
62  
63  //Sync'd against JSR48 1.0.0 javadoc (version 1.7.0_03) on Tue Dec 10 07:02:50 EST 2013
64  /**
65   * This class represents the datetime data type when used as a time value as
66   * specified by the Distributed Management Task Force (<a
67   * href=http://www.dmtf.org>DMTF</a>) CIM Infrastructure Specification (<a
68   * href=http://www.dmtf.org/standards/published_documents/DSP0004V2.3_final.pdf
69   * >DSP004</a>). It is in the format ddddddddHHMMSS.mmmmmm:000 where:
70   * <ul>
71   * <li>dddddddd - is the days in interval</li>
72   * <li>HH - is the hours in interval</li>
73   * <li>MM - is the minutes in interval</li>
74   * <li>SS - is the seconds in interval</li>
75   * <li>mmmmmm - is the microseconds in interval</li>
76   * </ul>
77   * For example, an elapsed time of 1 day, 13 hours, 23 minutes, 12 seconds would
78   * be: 00000001132312.000000:000. A UTC offset of zero is always used for
79   * interval properties.<br>
80   * Fields that are not significant MUST be replaced with asterisk ("*")
81   * characters. Not significant fields are those that are beyond the resolution
82   * of the data source. This is used to indicate the precision of the value and
83   * can only be done for an adjacent set of fields, starting with the least
84   * significant field (mmmmmm), and continuing to more significant fields. The
85   * granularity of using asterisks is always the entire field, except for the
86   * mmmmmm field where the granularity is single digits. The UTC offset field
87   * MUST NOT contain asterisks. <br>
88   * For example, if an interval of 1 day, 13 hours, 23 minutes, 12 seconds, and
89   * 125 milliseconds was measured with a precision of 1 millisecond, the format
90   * would be: 00000001132312.125***:000.
91   *
92   */
93  public class CIMDateTimeInterval extends CIMDateTime {
94  	private static final long serialVersionUID = -7362881414085831668l;
95  
96  	private int iDays, iHours, iMinutes, iSeconds, iUSeconds;
97  
98  	private String iStr;
99  
100 	private boolean iTotalMicrosCalced;
101 
102 	private long iTotalMicros;
103 
104 	private int iUnsignificantUSecDigits;
105 
106 	private static final int MAX_DAY = 99999999, MAX_HOUR = 23, MAX_MIN = 59, MAX_SEC = 59, MAX_MILLISEC =
107 		999, MAX_MICROSEC = 999999;
108 
109 	private static final long MAX_INTERVAL =
110 		((((long) MAX_DAY * 24 * 60 * 60) + ((long) MAX_HOUR * 60 * 60) + ((long) MAX_MIN * 60) + MAX_SEC) * 1000) +
111 		MAX_MILLISEC;
112 
113 	private static final long MILLISEC_ACCURACY_DIV = 1000, SEC_ACCURACY_DIV =
114 		1000 * MILLISEC_ACCURACY_DIV, MIN_ACCURACY_DIV = 60 * SEC_ACCURACY_DIV, HOUR_ACCURACY_DIV =
115 		60 * MIN_ACCURACY_DIV, DAY_ACCURACY_DIV = 24 * HOUR_ACCURACY_DIV, NO_ACCURACY_DIV = 100000000 * DAY_ACCURACY_DIV;
116 
117 	private static final long[] USEC_ACCURACY_DIV_A = {
118 		1,
119 		10,
120 		100,
121 		MILLISEC_ACCURACY_DIV,
122 		10000,
123 		100000,
124 		SEC_ACCURACY_DIV
125 	};
126 
127 	private long iAccuracyDivisor = 1;
128 
129 	/**
130 	 * Constructs a <code>CIMDateTimeInterval</code> using the individual values
131 	 * of the interval (day, hours, minutes, seconds and microseconds). Any
132 	 * property that has a -1 will make that field "not significant" (i.e. that
133 	 * field has asterisks in the datetime string).
134 	 *
135 	 * @param pDays
136 	 *            Number of days in the interval (-1 - 99999999).
137 	 * @param pHours
138 	 *            Number of hours in the interval (-1 - 23).
139 	 * @param pMinutes
140 	 *            Number of minutes in the interval (-1 - 59).
141 	 * @param pSeconds
142 	 *            Number of seconds in the interval (-1 - 59).
143 	 * @param pMicroseconds
144 	 *            Number of microseconds in the interval (-1 - 999999).
145 	 * @throws IllegalArgumentException
146 	 *             If any value is not valid.
147 	 */
148 	public CIMDateTimeInterval(int pDays, int pHours, int pMinutes, int pSeconds, int pMicroseconds)
149 		throws IllegalArgumentException {
150 		boolean notSignificant = checkLimits("pDays", pDays, 0, MAX_DAY, false);
151 		if (notSignificant) this.iAccuracyDivisor = NO_ACCURACY_DIV;
152 		notSignificant = checkLimits("pHours", pHours, 0, MAX_HOUR, notSignificant);
153 		if (notSignificant && this.iAccuracyDivisor == 1) this.iAccuracyDivisor = DAY_ACCURACY_DIV;
154 		notSignificant = checkLimits("pMinutes", pMinutes, 0, MAX_MIN, notSignificant);
155 		if (notSignificant && this.iAccuracyDivisor == 1) this.iAccuracyDivisor = HOUR_ACCURACY_DIV;
156 		notSignificant = checkLimits("pSeconds", pSeconds, 0, MAX_SEC, notSignificant);
157 		if (notSignificant && this.iAccuracyDivisor == 1) this.iAccuracyDivisor = MIN_ACCURACY_DIV;
158 		notSignificant = checkLimits("pMicroseconds", pMicroseconds, 0, MAX_MICROSEC, notSignificant);
159 		if (notSignificant) {
160 			this.iUnsignificantUSecDigits = 6;
161 			if (this.iAccuracyDivisor == 1) this.iAccuracyDivisor = USEC_ACCURACY_DIV_A[this.iUnsignificantUSecDigits];
162 		} else {
163 			this.iUnsignificantUSecDigits = 0;
164 		}
165 
166 		this.iDays = pDays;
167 		this.iHours = pHours;
168 		this.iMinutes = pMinutes;
169 		this.iSeconds = pSeconds;
170 		this.iUSeconds = pMicroseconds;
171 
172 		debug(
173 			"CIMDateTimeInterval(" +
174 			pDays +
175 			"," +
176 			pHours +
177 			"," +
178 			pMinutes +
179 			"," +
180 			pSeconds +
181 			"," +
182 			pMicroseconds +
183 			"): days=" +
184 			this.iDays +
185 			", hours=" +
186 			this.iHours +
187 			", mins=" +
188 			this.iMinutes +
189 			", secs=" +
190 			this.iSeconds +
191 			", usecs=" +
192 			this.iUSeconds +
193 			", acc div=" +
194 			this.iAccuracyDivisor +
195 			", unsig digits=" +
196 			this.iUnsignificantUSecDigits
197 		);
198 	}
199 
200 	/**
201 	 * Constructs a <code>CIMDateTimeInterval</code> using a milliseconds value.
202 	 *
203 	 * @param pMilliseconds
204 	 *            Number of milliseconds in the interval (0 - 8639999999999999).
205 	 * @throws IllegalArgumentException
206 	 */
207 	public CIMDateTimeInterval(long pMilliseconds) throws IllegalArgumentException {
208 		if (pMilliseconds < 0 || pMilliseconds > MAX_INTERVAL) throw new IllegalArgumentException(
209 			"pMilliseconds must be between 0 and " + MAX_INTERVAL + ". " + pMilliseconds + " is illegal!"
210 		);
211 		long totalUSecs = pMilliseconds * 1000;
212 		long totalSecs = pMilliseconds / 1000;
213 		long totalMins = totalSecs / 60;
214 		long totalHours = totalMins / 60;
215 		long totalDays = totalHours / 24;
216 
217 		this.iUSeconds = (int) (totalUSecs % 1000000);
218 		this.iSeconds = (int) (totalSecs % 60);
219 		this.iMinutes = (int) (totalMins % 60);
220 		this.iHours = (int) (totalHours % 24);
221 		this.iDays = (int) totalDays;
222 
223 		this.iUnsignificantUSecDigits = 3;
224 		this.iAccuracyDivisor = USEC_ACCURACY_DIV_A[this.iUnsignificantUSecDigits];
225 
226 		this.iTotalMicrosCalced = true;
227 		this.iTotalMicros = pMilliseconds * 1000;
228 
229 		debug(
230 			"CIMDateTimeInterval(" +
231 			pMilliseconds +
232 			"): days=" +
233 			this.iDays +
234 			", hours=" +
235 			this.iHours +
236 			", mins=" +
237 			this.iMinutes +
238 			", secs=" +
239 			this.iSeconds +
240 			", usecs=" +
241 			this.iUSeconds +
242 			", acc div=" +
243 			this.iAccuracyDivisor +
244 			", unsig digits=" +
245 			this.iUnsignificantUSecDigits
246 		);
247 	}
248 
249 	/**
250 	 * Creates a <code>CIMDateTimeInterval</code> object using a string.
251 	 *
252 	 * @param pIntervalString
253 	 *            A string in the format of ddddddddHHMMSS.mmmmmm:000.
254 	 * @throws IllegalArgumentException
255 	 *             If string is not in the correct format or <code>null</code>.
256 	 */
257 	public CIMDateTimeInterval(String pIntervalString) throws IllegalArgumentException {
258 		if (pIntervalString == null) throw new IllegalArgumentException("Null IntervalString is not allowed!");
259 		if (pIntervalString.length() != LENGTH) throw new IllegalArgumentException(
260 			"Length of " + pIntervalString + " must be " + LENGTH + ", " + pIntervalString.length() + " is not valid!"
261 		);
262 		String intervalStr = setAccuracy(pIntervalString);
263 		DTStringReader dtReader = new DTStringReader(intervalStr);
264 		this.iDays = dtReader.readAndCheck(8, "days", 0, MAX_DAY, true);
265 		this.iHours = dtReader.readAndCheck(2, "hours", 0, MAX_HOUR, true);
266 		this.iMinutes = dtReader.readAndCheck(2, "minutes", 0, MAX_MIN, true);
267 		this.iSeconds = dtReader.readAndCheck(2, "seconds", 0, MAX_SEC, true);
268 		dtReader.read('.');
269 		this.iUSeconds = dtReader.readAndCheck(6, "microseconds", 0, MAX_MICROSEC, false);
270 		if (this.iAccuracyDivisor >= SEC_ACCURACY_DIV) this.iUSeconds = -1;
271 		dtReader.read(':');
272 		int zeros = dtReader.read(3, "zeros", false);
273 		if (zeros != 0) {
274 			String msg = "In " + pIntervalString + " the last 3 characters after ':' must be zeros!";
275 			throw new IllegalArgumentException(msg);
276 		}
277 
278 		debug(
279 			"CIMDateTimeInterval(\"" +
280 			pIntervalString +
281 			"\"): days=" +
282 			this.iDays +
283 			", hours=" +
284 			this.iHours +
285 			", mins=" +
286 			this.iMinutes +
287 			", secs=" +
288 			this.iSeconds +
289 			", usecs=" +
290 			this.iUSeconds +
291 			", acc div=" +
292 			this.iAccuracyDivisor +
293 			", unsig digits=" +
294 			this.iUnsignificantUSecDigits
295 		);
296 	}
297 
298 	/**
299 	 * Compares the <code>CIMDateTimeInterval</code> object with this one. If
300 	 * either interval has "Not Significant" fields then we only compare the
301 	 * significant fields.
302 	 *
303 	 * @param pObj
304 	 *            The <code>CIMDateTimeInterval</code> to be compared with this
305 	 *            one.
306 	 * @return -1, zero, or 1 as this interval is less than, equal to, or
307 	 *         greater than the specified interval.
308 	 * @throws IllegalArgumentException
309 	 *             If the object passed in is not an instance of
310 	 *             <code>CIMDataTimeInterval</code>.
311 	 */
312 	public int compareTo(CIMDateTime pObj) throws IllegalArgumentException {
313 		if (!(pObj instanceof CIMDateTimeInterval)) {
314 			String msg =
315 				"This method requires a CIMDateTimeInterval instance, while it has received a " +
316 				(pObj == null ? "null!" : pObj.getClass().getName() + " instance!");
317 			throw new IllegalArgumentException(msg);
318 		}
319 		CIMDateTimeInterval that = (CIMDateTimeInterval) pObj;
320 		long accuracyDivisor = Math.max(this.iAccuracyDivisor, that.iAccuracyDivisor);
321 		debug(
322 			"this.acDiv=" + this.iAccuracyDivisor + ", that.acDiv=" + that.iAccuracyDivisor + ", acDiv=" + accuracyDivisor
323 		);
324 		Long thisMicros = Long.valueOf(calcMicros(accuracyDivisor));
325 		Long thatMicros = Long.valueOf(that.calcMicros(accuracyDivisor));
326 		debug("thisUs=" + thisMicros + ", thatUs=" + thatMicros);
327 		return thisMicros.compareTo(thatMicros);
328 	}
329 
330 	/**
331 	 * Gets the internal string representation of this object.
332 	 *
333 	 * @return The internal representation of the
334 	 *         <code>CIMDateTimeInterval</code> object.
335 	 */
336 	@Override
337 	public String getDateTimeString() {
338 		if (this.iStr != null) return this.iStr;
339 		// ddddddddHHMMSS.mmmmmm:000
340 		DTStringWriter dtWriter = new DTStringWriter();
341 		dtWriter.write(8, this.iDays);
342 		dtWriter.write(2, this.iHours);
343 		dtWriter.write(2, this.iMinutes);
344 		dtWriter.write(2, this.iSeconds);
345 		dtWriter.write('.');
346 		dtWriter.writeUSec(this.iUSeconds / (int) this.iAccuracyDivisor, this.iUnsignificantUSecDigits);
347 		dtWriter.write(":000");
348 		return dtWriter.toString();
349 	}
350 
351 	/**
352 	 * Returns days value of this interval.
353 	 *
354 	 * @return If days field "not significant" this returns -1, otherwise
355 	 *         returns number of days in the interval.
356 	 */
357 	public int getDays() {
358 		return this.iDays;
359 	}
360 
361 	/**
362 	 * Returns hours value of this interval.
363 	 *
364 	 * @return If hours field "not significant" this returns -1, otherwise
365 	 *         returns number of hours in the interval.
366 	 */
367 	public int getHours() {
368 		return this.iHours;
369 	}
370 
371 	/**
372 	 * Returns microseconds value of this interval.
373 	 *
374 	 * @return If microseconds field "not significant" this returns -1,
375 	 *         otherwise returns number of microseconds in the interval.
376 	 */
377 	public int getMicroseconds() {
378 		return this.iUSeconds;
379 	}
380 
381 	/**
382 	 * Returns minutes value of this interval.
383 	 *
384 	 * @return If minutes field "not significant" this returns -1, otherwise
385 	 *         returns number of minutes in the interval.
386 	 */
387 	public int getMinutes() {
388 		return this.iMinutes;
389 	}
390 
391 	/**
392 	 * Returns seconds value of this interval.
393 	 *
394 	 * @return If seconds field "not significant" this returns -1, otherwise
395 	 *         returns number of seconds in the interval.
396 	 */
397 	public int getSeconds() {
398 		return this.iSeconds;
399 	}
400 
401 	/**
402 	 * Returns the total length of the interval in milliseconds.
403 	 *
404 	 * @return The length of the interval in milliseconds.
405 	 */
406 	public long getTotalMilliseconds() {
407 		return getTotalMicros() / 1000;
408 	}
409 
410 	/**
411 	 * Returns the hash code for this object.
412 	 *
413 	 * @return A hash code value for this object.
414 	 * @see java.lang.Object#hashCode()
415 	 */
416 	@Override
417 	public int hashCode() {
418 		return getDateTimeString().hashCode();
419 	}
420 
421 	/**
422 	 * Returns a <code>String</code> representation of the
423 	 * <code>CIMDateTimeInterval</code>. This method is intended to be used only
424 	 * for debugging purposes, and the format of the returned string may vary
425 	 * between implementations. The returned string may be empty but may not be
426 	 * <code>null</code>.
427 	 *
428 	 * @return String representation of this datetime.
429 	 */
430 	@Override
431 	public String toString() {
432 		return getDateTimeString();
433 	}
434 
435 	// ddddddddHHMMSS.mmmmmm:000
436 
437 	private static final int DAY_START_IDX = 0, HOUR_START_IDX = 8, MIN_START_IDX = 10, SEC_START_IDX = 12, DOT_IDX =
438 		14, USEC_START_IDX = 15, UTC_START_IDX = 21, LENGTH = 25;
439 
440 	/**
441 	 * Checks the granularity of the <code>CIMDateTimeAbsolute</code> precision.
442 	 *
443 	 * @param pIdx
444 	 *            Index of first asterisk in datetime string.
445 	 * @param pFieldName
446 	 *            Name of field.
447 	 * @param pFieldStartIdx
448 	 *            Index of first character in field of datetime string.
449 	 * @param pNextStartIdx
450 	 *            Index of first character in subsequent field of datetime
451 	 *            string.
452 	 * @return <code>true</code> if field is completely unsignificant,
453 	 *         <code>false</code> otherwise.
454 	 * @throws IllegalArgumentException
455 	 *             If field is not completely significant or completely
456 	 *             unsignificant (mix of asterisks and digits).
457 	 */
458 	private boolean checkGranularity(int pIdx, String pFieldName, int pFieldStartIdx, int pNextStartIdx)
459 		throws IllegalArgumentException {
460 		if (pIdx > pFieldStartIdx && pIdx < pNextStartIdx) throw new IllegalArgumentException(
461 			"Partial unsignificant digits are not allowed for field " + pFieldName + " in " + this.iStr + "!"
462 		);
463 		return pIdx == pFieldStartIdx;
464 	}
465 
466 	/**
467 	 * Find index of first asterisk within string.
468 	 *
469 	 * @param pIvStr
470 	 *            Time interval string.
471 	 * @return Index of first asterisk or -1 if asterisk does not exist.
472 	 */
473 	private static int findStar(String pIvStr) {
474 		for (int i = 0; i < UTC_START_IDX; i++) if (pIvStr.charAt(i) == '*') return i;
475 		return -1;
476 	}
477 
478 	/**
479 	 * Verify that all characters in the string after the starting index are
480 	 * asterisks.
481 	 *
482 	 * @param pIvStr
483 	 *            Time interval string.
484 	 * @param pStartIdx
485 	 *            Starting index.
486 	 */
487 	private static void checkStars(String pIvStr, int pStartIdx) {
488 		for (int i = pStartIdx; i < UTC_START_IDX; i++) {
489 			char ch = pIvStr.charAt(i);
490 			if (i != DOT_IDX && ch != '*') throw new IllegalArgumentException(
491 				"In " +
492 				pIvStr +
493 				" every digit character after the first '*' character must " +
494 				"be '*', '" +
495 				ch +
496 				"' is invalid!"
497 			);
498 		}
499 	}
500 
501 	/**
502 	 * Determines the accuracy (precision) of the interval string and replaces
503 	 * all asterisks in microsecond field with zeros.
504 	 *
505 	 * @param pIntervalStr
506 	 *            Time interval string.
507 	 * @return Corrected time interval string with <code>iAccuracyDivisor</code>
508 	 *         set.
509 	 */
510 	private String setAccuracy(String pIntervalStr) throws IllegalArgumentException {
511 		this.iStr = pIntervalStr;
512 		/*
513 		 * String transformation. Checking the correct location of '*'
514 		 * characters. '*' characters in usec field are replaced into '0'
515 		 * characters
516 		 */
517 		int startIdx = findStar(this.iStr);
518 		if (startIdx < 0) return this.iStr;
519 		// all remaining digit fields must contain '*' characters except for utc
520 		checkStars(this.iStr, startIdx + 1);
521 		if (checkGranularity(startIdx, "Day", DAY_START_IDX, HOUR_START_IDX)) {
522 			this.iAccuracyDivisor = NO_ACCURACY_DIV;
523 		} else if (checkGranularity(startIdx, "Hour", HOUR_START_IDX, MIN_START_IDX)) {
524 			this.iAccuracyDivisor = DAY_ACCURACY_DIV;
525 		} else if (checkGranularity(startIdx, "Minute", MIN_START_IDX, SEC_START_IDX)) {
526 			this.iAccuracyDivisor = HOUR_ACCURACY_DIV;
527 		} else if (checkGranularity(startIdx, "Second", SEC_START_IDX, DOT_IDX)) {
528 			this.iAccuracyDivisor = MIN_ACCURACY_DIV;
529 		} else {
530 			// dealing with usec granularity
531 			this.iUnsignificantUSecDigits = UTC_START_IDX - startIdx;
532 			this.iAccuracyDivisor = USEC_ACCURACY_DIV_A[this.iUnsignificantUSecDigits];
533 		}
534 		// all '*' in usec field is replaced into '0'
535 		char[] buf = this.iStr.toCharArray();
536 		if (startIdx < USEC_START_IDX) startIdx = USEC_START_IDX;
537 		for (int i = startIdx; i < UTC_START_IDX; i++) {
538 			if (i == DOT_IDX) continue;
539 			if (this.iStr.charAt(i) != '*') throw new IllegalArgumentException(
540 				"All remaining digits must be marked as unsignificant in " + this.iStr + " !"
541 			);
542 			buf[i] = '0';
543 		}
544 		return new String(buf);
545 	}
546 
547 	/**
548 	 * Get total microseconds of <code>CIMDateTimeInterval</code> object.
549 	 *
550 	 * @return Total microseconds of time interval.
551 	 */
552 	private long getTotalMicros() {
553 		if (this.iTotalMicrosCalced) return this.iTotalMicros;
554 		this.iTotalMicrosCalced = true;
555 		// iTotalMicros = iUSeconds + 1000000 * (iSeconds + 60 * (iMinutes + 60
556 		// * (iHours + 24 * iDays)));
557 		this.iTotalMicros = this.iUSeconds > 0 ? this.iUSeconds : 0;
558 		if (this.iSeconds > 0) this.iTotalMicros += SEC_ACCURACY_DIV * this.iSeconds;
559 		if (this.iMinutes > 0) this.iTotalMicros += MIN_ACCURACY_DIV * this.iMinutes;
560 		if (this.iHours > 0) this.iTotalMicros += HOUR_ACCURACY_DIV * this.iHours;
561 		if (this.iDays > 0) this.iTotalMicros += DAY_ACCURACY_DIV * this.iDays;
562 		debug(
563 			"days=" +
564 			this.iDays +
565 			" ,hours=" +
566 			this.iHours +
567 			" ,mins=" +
568 			this.iMinutes +
569 			", secs=" +
570 			this.iSeconds +
571 			", usecs=" +
572 			this.iUSeconds +
573 			", totalMicros=" +
574 			this.iTotalMicros
575 		);
576 		return this.iTotalMicros;
577 	}
578 
579 	/**
580 	 * Calculates microseconds of <code>CIMDateTimeInterval</code> object taking
581 	 * accuracy (precision) into consideration.
582 	 *
583 	 * @param pAccuracyDivisor
584 	 *            Accuracy divisor of time interval.
585 	 * @return Total microseconds of time interval rounded down to precision.
586 	 */
587 	private long calcMicros(long pAccuracyDivisor) {
588 		long remainder = getTotalMicros() % pAccuracyDivisor;
589 		return getTotalMicros() - remainder;
590 	}
591 
592 	/**
593 	 * Checks validity of time interval field, making sure value is between
594 	 * minimum and maximum values and is not significant if following an
595 	 * unsignifcant field.
596 	 *
597 	 * @param pName
598 	 *            Name of field.
599 	 * @param pValue
600 	 *            Value of field.
601 	 * @param pLow
602 	 *            Minimum value of field.
603 	 * @param pHigh
604 	 *            Maximum value of field.
605 	 * @param pNotSignificant
606 	 *            <code>true</code>if previous field not significant.
607 	 * @return <code>true</code> if field is not significant, <code>false</code>
608 	 *         otherwise.
609 	 * @throws IllegalArgumentException
610 	 */
611 	private static boolean checkLimits(String pName, int pValue, int pLow, int pHigh, boolean pNotSignificant)
612 		throws IllegalArgumentException {
613 		if (pValue == -1) return true;
614 		if (pNotSignificant) throw new IllegalArgumentException(
615 			"Not significant fields must be followed by not significant fields!"
616 		);
617 		if (pValue < pLow || pValue > pHigh) throw new IllegalArgumentException(
618 			pName + " must be between " + pLow + " and " + pHigh + ". " + pValue + " is invalid!"
619 		);
620 		return false;
621 	}
622 
623 	/**
624 	 * Prints debug message.
625 	 *
626 	 * @param pMsg
627 	 *            Debug message.
628 	 */
629 	private static void debug(String pMsg) {
630 		// System.out.println(pMsg);
631 	}
632 }