View Javadoc

1   /**
2    * The contents of this file are subject to the Mozilla Public License Version 1.1
3    * (the "License"); you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
5    * Software distributed under the License is distributed on an "AS IS" basis,
6    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
7    * specific language governing rights and limitations under the License.
8    *
9    * The Original Code is "CommmonTM.java".  Description:
10   * "Note: The class description below has been excerpted from the Hl7 2.4 documentation"
11   *
12   * The Initial Developer of the Original Code is University Health Network. Copyright (C)
13   * 2001.  All Rights Reserved.
14   *
15   * Contributor(s): ______________________________________.
16   *
17   * Alternatively, the contents of this file may be used under the terms of the
18   * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
19   * applicable instead of those above.  If you wish to allow use of your version of this
20   * file only under the terms of the GPL and not to allow others to use your version
21   * of this file under the MPL, indicate your decision by deleting  the provisions above
22   * and replace  them with the notice and other provisions required by the GPL License.
23   * If you do not delete the provisions above, a recipient may use your version of
24   * this file under either the MPL or the GPL.
25   *
26   */
27  
28  package ca.uhn.hl7v2.model.primitive;
29  
30  import java.util.Calendar;
31  import java.util.Date;
32  import java.util.GregorianCalendar;
33  import java.util.TimeZone;
34  import java.io.Serializable;
35  
36  import ca.uhn.hl7v2.model.DataTypeException;
37  import ca.uhn.hl7v2.model.DataTypeUtil;
38  
39  /**
40   * This class contains functionality used by the TM class
41   * in the version 2.3.0, 2.3.1, and 2.4 packages
42   *
43   * Note: The class description below has been excerpted from the Hl7 2.4 documentation. Sectional
44   * references made below also refer to the same documentation.
45   *
46   * Format: HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]
47   * In prior versions of HL7, this data type was always specified to be in the
48   * format HHMM[SS[.SSSS]][+/-ZZZZ] using a 24 hour clock notation. In the
49   * current and future versions, the precision of a time may be expressed by
50   * limiting the number of digits used with the format specification as shown
51   * above. By site-specific agreement, HHMM[SS[.SSSS]][+/-ZZZZ] may be used where
52   * backward compatibility must be maintained.
53   * Thus, HH is used to specify a precision of "hour," HHMM is used to specify a
54   * precision of "minute," HHMMSS is used to specify a precision of seconds, and
55   * HHMMSS.SSSS is used to specify a precision of ten-thousandths of a second.
56   * In each of these cases, the time zone is an optional component. The fractional
57   * seconds could be sent by a transmitter who requires greater precision than whole
58   * seconds. Fractional representations of minutes, hours or other higher-order units
59   * of time are not permitted.
60   * Note: The time zone [+/-ZZZZ], when used, is restricted to legally-defined time zones
61   * and is represented in HHMM format.
62   * The time zone of the sender may be sent optionally as an offset from the coordinated
63   * universal time (previously known as Greenwich Mean Time). Where the time zone
64   * is not present in a particular TM field but is included as part of the date/time
65   * field in the MSH segment, the MSH value will be used as the default time zone.
66   * Otherwise, the time is understood to refer to the local time of the sender.
67   * Midnight is represented as 0000.
68   * Examples:|235959+1100| 1 second before midnight in a time zone eleven hours
69   * ahead of Universal Coordinated Time (i.e., east of Greenwich).
70   * |0800| Eight AM, local time of the sender.
71   * |093544.2312| 44.2312 seconds after Nine thirty-five AM, local time of sender.
72   * |13| 1pm (with a precision of hours), local time of sender.
73   * @author Neal Acharya
74   */
75  
76  @SuppressWarnings("serial")
77  public class CommonTM implements Serializable {
78      
79  	/**
80       * Value returned by {@link #getGMTOffset()} if no offset is set
81       */
82      public static final int GMT_OFFSET_NOT_SET_VALUE = -99;
83  
84      private String value;
85      private int hour;
86      private int minute;
87      private int second;
88      private float fractionOfSec;
89      private int offSet;
90      private boolean omitOffsetFg = false;
91  
92      /**
93       * Constructs a TM datatype with fields initialzed to zero and the value set to
94       * null.
95       */
96      public CommonTM() {
97          //initialize all DT fields
98          value = null;
99          hour = 0;
100         minute = 0;
101         second = 0;
102         fractionOfSec = 0;
103         offSet = GMT_OFFSET_NOT_SET_VALUE;
104     } //end constructor
105 
106     /**
107      * Constructs a TM object with the given value.
108      * The stored value will be in the following
109      * format HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ].
110      */
111     public CommonTM(String val) throws DataTypeException {
112         this.setValue(val);
113     } //end constructor
114 
115     /**
116      * This method takes in a string HL7 Time value and performs validations
117      * then sets the value field.  The stored value will be in the following
118      * format HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ].
119      * Note: Trailing zeros supplied in the time value (HH[MM[SS[.S[S[S[S]]]]]])
120      * and GMT offset ([+/-ZZZZ]) will be preserved.
121      * Note: If the GMT offset is not supplied then the local
122      * time zone (using standard time zone format which is not modified for daylight savings)
123      * will be stored as a default. Passing in <code>null</code> clears any existing value.
124      */
125     public void setValue(String val) throws DataTypeException {
126 
127         if (val != null && !val.equals("") && !val.equals("\"\"")) {
128             //check to see if any of the following characters exist: "." or "+/-"
129             //this will help us determine the acceptable lengths
130 
131             int d = val.indexOf(".");
132             int sp = val.indexOf("+");
133             int sm = val.indexOf("-");
134             int indexOfSign = -1;
135             boolean offsetExists = false;
136             if ((sp != -1) || (sm != -1))
137                 offsetExists = true;
138             if (sp != -1)
139                 indexOfSign = sp;
140             if (sm != -1)
141                 indexOfSign = sm;
142 
143             try {
144                 //If the GMT offset exists then extract it from the input string and store it
145                 //in another variable called tempOffset. Also, store the time value
146                 //(without the offset)in a separate variable called timeVal.
147                 //If there is no GMT offset then simply set timeVal to val.
148                 String timeVal = val;
149                 String tempOffset = null;
150                 if (offsetExists) {
151                     timeVal = val.substring(0, indexOfSign);
152                     tempOffset = val.substring(indexOfSign);
153                 } //end if
154 
155                 if (offsetExists && (tempOffset.length() != 5)) {
156                     //The length of the GMT offset must be 5 characters (including the sign)
157                     String msg =
158                         "The length of the TM datatype value does not conform to an allowable"
159                             + " format. Format should conform to HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]";
160                     DataTypeException e = new DataTypeException(msg);
161                     throw e;
162                 } //end if
163 
164                 if (d != -1) {
165                     //here we know that decimal exists
166                     //thus length of the time value can be between 8 and 11 characters
167                     if ((timeVal.length() < 8) || (timeVal.length() > 11)) {
168                         String msg =
169                             "The length of the TM datatype value does not conform to an allowable"
170                                 + " format. Format should conform to HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]";
171                         DataTypeException e = new DataTypeException(msg);
172                         throw e;
173                     } //end if
174                 } //end if
175 
176                 if (d == -1) {
177                     //here we know that the decimal does not exist
178                     //thus length of the time value can be 2 or 4 or 6 characters
179                     if ((timeVal.length() != 2) && (timeVal.length() != 4) && (timeVal.length() != 6)) {
180                         String msg =
181                             "The length of the TM datatype value does not conform to an allowable"
182                                 + " format. Format should conform to HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]";
183                         DataTypeException e = new DataTypeException(msg);
184                         throw e;
185                     } //end if
186                 } //end if
187 
188                 //We will now try to validate the timeVal portion of the TM datatype value
189                 if (timeVal.length() >= 2) {
190                     //extract the hour data from the input value.  If the first 2 characters
191                     //are not numeric then a number format exception will be generated
192                     int hrInt = Integer.parseInt(timeVal.substring(0, 2));
193                     //check to see if the hour value is valid
194                     if ((hrInt < 0) || (hrInt > 23)) {
195                         String msg = "The hour value of the TM datatype must be >=0 and <=23";
196                         DataTypeException e = new DataTypeException(msg);
197                         throw e;
198                     } //end if
199                     hour = hrInt;
200                 } //end if
201 
202                 if (timeVal.length() >= 4) {
203                     //extract the minute data from the input value
204                     //If these characters are not numeric then a number
205                     //format exception will be generated
206                     int minInt = Integer.parseInt(timeVal.substring(2, 4));
207                     //check to see if the minute value is valid
208                     if ((minInt < 0) || (minInt > 59)) {
209                         String msg = "The minute value of the TM datatype must be >=0 and <=59";
210                         DataTypeException e = new DataTypeException(msg);
211                         throw e;
212                     } //end if
213                     minute = minInt;
214                 } //end if
215 
216                 if (timeVal.length() >= 6) {
217                     //extract the seconds data from the input value
218                     //If these characters are not numeric then a number
219                     //format exception will be generated
220                     int secInt = Integer.parseInt(timeVal.substring(4, 6));
221                     //check to see if the seconds value is valid
222                     if ((secInt < 0) || (secInt > 59)) {
223                         String msg = "The seconds value of the TM datatype must be >=0 and <=59";
224                         DataTypeException e = new DataTypeException(msg);
225                         throw e;
226                     } //end if
227                     second = secInt;
228                 } //end if
229 
230                 if (timeVal.length() >= 8) {
231                     //extract the fractional second value from the input value
232                     //If these characters are not numeric then a number
233                     //format exception will be generated
234                     float fract = Float.parseFloat(timeVal.substring(6));
235                     //check to see if the fractional second value is valid
236                     if ((fract < 0) || (fract >= 1)) {
237                         String msg = "The fractional second value of the TM datatype must be >= 0 and < 1";
238                         DataTypeException e = new DataTypeException(msg);
239                         throw e;
240                     } //end if
241                     fractionOfSec = fract;
242                 } //end if
243 
244                 //We will now try to validate the tempOffset portion of the TM datatype value
245                 if (offsetExists) {
246                     //in case the offset are a series of zeros we should not omit displaying
247                     //it in the return value from the getValue() method
248                     omitOffsetFg = false;
249                     //remove the sign from the temp offset
250                     String tempOffsetNoS = tempOffset.substring(1);
251                     //extract the hour data from the offset value.  If the first 2 characters
252                     //are not numeric then a number format exception will be generated
253                     int offsetInt = Integer.parseInt(tempOffsetNoS.substring(0, 2));
254                     //check to see if the hour value is valid
255                     if ((offsetInt < 0) || (offsetInt > 23)) {
256                         String msg = "The GMT offset hour value of the TM datatype must be >=0 and <=23";
257                         DataTypeException e = new DataTypeException(msg);
258                         throw e;
259                     } //end if
260                     //extract the minute data from the offset value.  If these characters
261                     //are not numeric then a number format exception will be generated
262                     offsetInt = Integer.parseInt(tempOffsetNoS.substring(2, 4));
263                     //check to see if the minute value is valid
264                     if ((offsetInt < 0) || (offsetInt > 59)) {
265                         String msg = "The GMT offset minute value of the TM datatype must be >=0 and <=59";
266                         DataTypeException e = new DataTypeException(msg);
267                         throw e;
268                     } //end if
269                     //validation done, update the offSet field
270                     offSet = Integer.parseInt(tempOffsetNoS);
271                     //add the sign back to the offset if it is negative
272                     if (sm != -1) {
273                         offSet = -1 * offSet;
274                     } //end if
275                 } //end if
276 
277                 //If the GMT offset has not been supplied then set the offset to the
278                 //local timezone
279                 //[Bryan: changing this to omit time zone because erroneous if parser in different zone than sender]
280                 if (!offsetExists) {
281                     omitOffsetFg = true;
282                     // set the offSet field to the current time and local time zone
283                     //offSet = DataTypeUtil.getLocalGMTOffset();
284                 } //end if
285 
286                 //validations are now done store the time value into the private value field
287                 value = timeVal;
288             } //end try
289 
290             catch (DataTypeException e) {
291                 throw e;
292             } //end catch
293 
294             catch (Exception e) {
295                 throw new DataTypeException(e);
296             } //end catch
297         } //end if
298         else {
299             //set the private value field to null or empty space.
300             value = val;
301         } //end else
302     } //end method
303 
304     /**
305      * This method takes in an integer value for the hour and performs validations,
306      * it then sets the value field formatted as an HL7 time
307      * value with hour precision (HH).
308      */
309     public void setHourPrecision(int hr) throws DataTypeException {
310         try {
311             //validate input value
312             if ((hr < 0) || (hr > 23)) {
313                 String msg = "The hour value of the TM datatype must be >=0 and <=23";
314                 DataTypeException e = new DataTypeException(msg);
315                 throw e;
316             } //end if
317             hour = hr;
318             minute = 0;
319             second = 0;
320             fractionOfSec = 0;
321             offSet = 0;
322             //Here the offset is not defined, we should omit showing it in the
323             //return value from the getValue() method
324             omitOffsetFg = true;
325             value = DataTypeUtil.preAppendZeroes(hr, 2);
326         } //end try
327 
328         catch (DataTypeException e) {
329             throw e;
330         } //end catch
331 
332         catch (Exception e) {
333             throw new DataTypeException(e.getMessage());
334         } //end catch
335 
336     } //end method
337 
338     /**
339      * This method takes in integer values for the hour and minute and performs validations,
340      * it then sets the value field formatted as an HL7 time value
341      * with hour&minute precision (HHMM).
342      */
343     public void setHourMinutePrecision(int hr, int min) throws DataTypeException {
344         try {
345             this.setHourPrecision(hr);
346             //validate input minute value
347             if ((min < 0) || (min > 59)) {
348                 String msg = "The minute value of the TM datatype must be >=0 and <=59";
349                 DataTypeException e = new DataTypeException(msg);
350                 throw e;
351             } //end if
352             minute = min;
353             second = 0;
354             fractionOfSec = 0;
355             offSet = 0;
356             //Here the offset is not defined, we should omit showing it in the
357             //return value from the getValue() method
358             omitOffsetFg = true;
359             value = value + DataTypeUtil.preAppendZeroes(min, 2);
360         } //end try
361 
362         catch (DataTypeException e) {
363             throw e;
364         } //end catch
365 
366         catch (Exception e) {
367             throw new DataTypeException(e.getMessage());
368         } //end catch
369     } //end method
370 
371     /**
372      * This method takes in integer values for the hour, minute, seconds, and fractional seconds
373      * (going to the tenthousandths precision).
374      * The method performs validations and then sets the value field formatted as an
375      * HL7 time value with a precision that starts from the hour and goes down to the tenthousandths
376      * of a second (HHMMSS.SSSS).
377      * Note: all of the precisions from tenths down to tenthousandths of a
378      * second are optional. If the precision goes below tenthousandths of a second then the second
379      * value will be rounded to the nearest tenthousandths of a second.
380      */
381     public void setHourMinSecondPrecision(int hr, int min, float sec) throws DataTypeException {
382         try {
383             this.setHourMinutePrecision(hr, min);
384             //multiply the seconds input value by 10000 and round the result
385             //then divide the number by tenthousand and store it back.
386             //This will round the fractional seconds to the nearest tenthousandths
387             int secMultRound = Math.round(10000F * sec);
388             sec = secMultRound / 10000F;
389             //Now store the second and fractional component
390             second = (int) Math.floor(sec);
391 			//validate input seconds value
392 			if ((second < 0) || (second >= 60)) {
393 				String msg = "The (rounded) second value of the TM datatype must be >=0 and <60";
394 				DataTypeException e = new DataTypeException(msg);
395 				throw e;
396 			} //end if
397             int fractionOfSecInt = (int) (secMultRound - (second * 10000));
398             fractionOfSec = fractionOfSecInt / 10000F;
399             String fractString = "";
400             //Now convert the fractionOfSec field to a string without the leading zero
401             if (fractionOfSec != 0.0F) {
402                 fractString = (Float.toString(fractionOfSec)).substring(1);
403             } //end if
404             //Now update the value field
405             offSet = 0;
406             //Here the offset is not defined, we should omit showing it in the
407             //return value from the getValue() method
408             omitOffsetFg = true;
409             value = value + DataTypeUtil.preAppendZeroes(second, 2) + fractString;
410         } //end try
411 
412         catch (DataTypeException e) {
413             throw e;
414         } //end catch
415 
416         catch (Exception e) {
417             throw new DataTypeException(e);
418         } //end catch
419     } //end method
420 
421     /**
422      * This method takes in the four digit (signed) GMT offset and sets the offset
423      * field
424      */
425     public void setOffset(int signedOffset) throws DataTypeException {
426         try {
427             //When this function is called an offset is being created/updated
428             //we should not omit displaying it in the return value from
429             //the getValue() method
430             omitOffsetFg = false;
431             String offsetStr = Integer.toString(signedOffset);
432             if ((signedOffset >= 0 && offsetStr.length() > 4) || (signedOffset < 0 && offsetStr.length() > 5)) {
433                 //The length of the GMT offset must be no greater than 5 characters (including the sign)
434                 String msg =
435                     "The length of the GMT offset for the TM datatype value does"
436                         + " not conform to the allowable format [+/-ZZZZ]. Value: " + signedOffset;
437                 DataTypeException e = new DataTypeException(msg);
438                 throw e;
439             } //end if
440             //obtain the absolute value of the input
441             int absOffset = Math.abs(signedOffset);
442             //extract the hour data from the offset value.
443             //first preappend zeros so we have a 4 char offset value (without sign)
444             offsetStr = DataTypeUtil.preAppendZeroes(absOffset, 4);
445             int hrOffsetInt = Integer.parseInt(offsetStr.substring(0, 2));
446             //check to see if the hour value is valid
447             if ((hrOffsetInt < 0) || (hrOffsetInt > 23)) {
448                 String msg = "The GMT offset hour value of the TM datatype must be >=0 and <=23";
449                 DataTypeException e = new DataTypeException(msg);
450                 throw e;
451             } //end if
452             //extract the minute data from the offset value.
453             int minOffsetInt = Integer.parseInt(offsetStr.substring(2, 4));
454             //check to see if the minute value is valid
455             if ((minOffsetInt < 0) || (minOffsetInt > 59)) {
456                 String msg = "The GMT offset minute value of the TM datatype must be >=0 and <=59";
457                 DataTypeException e = new DataTypeException(msg);
458                 throw e;
459             } //end if
460             //The input value is valid, now store it in the offset field
461             offSet = signedOffset;
462         } //end try
463 
464         catch (DataTypeException e) {
465             throw e;
466         } //end catch
467 
468         catch (Exception e) {
469             throw new DataTypeException(e);
470         } //end catch
471     } //end method
472 
473     /**
474      * Returns the HL7 TM string value.
475      */
476     public String getValue() {
477         //combine the value field with the offSet field and return it
478         String returnVal = null;
479         if (value != null && !value.equals("")) {
480             if (omitOffsetFg == false && !value.equals("\"\"")) {
481                 int absOffset = Math.abs(offSet);
482                 String sign = "";
483                 if (offSet >= 0) {
484                     sign = "+";
485                 } //end if
486                 else {
487                     sign = "-";
488                 } //end else
489                 returnVal = value + sign + DataTypeUtil.preAppendZeroes(absOffset, 4);
490             }
491             else {
492                 returnVal = value;
493             } //end else
494         } //end if
495         return returnVal;
496     } //end method
497 
498     /**
499      * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
500      * 
501      * Note: Sets fields using precision up to the minute
502      * 
503      * @param theCalendar The calendar object from which to retrieve values 
504      * @since 1.1 
505      */
506     public void setValueToMinute(Calendar theCalendar) throws DataTypeException {
507 		if (theCalendar == null) {
508 			setValue((String)null);
509 			return;
510 		}
511 
512         int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
513         int min = theCalendar.get(Calendar.MINUTE);
514         setHourMinutePrecision(hr, min);
515     }
516 
517     /**
518      * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
519      * 
520      * Note: Sets fields using precision up to the minute
521      * Note: Date is timezone-agnostic, representing always GMT time
522      * 
523      * @param theDate The date object from which to retrieve values
524      * @since 1.1 
525      */
526     public void setValueToMinute(Date theDate) throws DataTypeException {
527 		if (theDate == null) {
528 			setValue((String)null);
529 			return;
530 		}
531 
532 		Calendar calendar = Calendar.getInstance();
533         calendar.setTime(theDate);
534         setValueToMinute(calendar);
535     }
536     
537     /**
538      * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
539      * 
540      * Note: Sets fields using precision up to the second
541      * 
542      * @param theCalendar The calendar object from which to retrieve values 
543      * @since 1.1 
544      */
545     public void setValueToSecond(Calendar theCalendar) throws DataTypeException {
546 		if (theCalendar == null) {
547 			setValue((String)null);
548 			return;
549 		}
550 
551         int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
552         int min = theCalendar.get(Calendar.MINUTE);
553         int sec = theCalendar.get(Calendar.SECOND);
554         
555         setHourMinSecondPrecision(hr, min, sec);
556     }
557 
558     /**
559      * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
560      * 
561      * Note: Sets fields using precision up to the millisecond, including timezone offset
562      * 
563      * @param theCalendar The calendar object from which to retrieve values 
564      * @since 1.1 
565      */
566     public void setValue(Calendar theCalendar) throws DataTypeException {
567 		if (theCalendar == null) {
568 			setValue((String)null);
569 			return;
570 		}
571 
572         int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
573         int min = theCalendar.get(Calendar.MINUTE);
574         float sec = theCalendar.get(Calendar.SECOND) + (theCalendar.get(Calendar.MILLISECOND) / 1000.0F);
575         setHourMinSecondPrecision(hr, min, sec);
576         
577         // 3410095: care for integer overflow and timezones not at the full hour, e.g. India
578         int hourOffset= theCalendar.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);   
579         int minuteOffset = (theCalendar.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
580         int zoneOffset = hourOffset * 100 + minuteOffset;
581         setOffset(zoneOffset);
582     }
583    
584     /**
585      * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
586      * 
587      * Note: Sets fields using precision up to the millisecond, and sets the timezone offset to
588      * the current system offset
589      * Note: Date is timezone-agnostic, representing always GMT time
590      * 
591      * @param theDate The calendar object from which to retrieve values 
592      * @since 1.1 
593      */
594 	public void setValue(Date theDate) throws DataTypeException {
595 		if (theDate == null) {
596 			setValue((String)null);
597 			return;
598 		}
599 
600 		GregorianCalendar cal = new GregorianCalendar();
601 		cal.setTime(theDate);
602 		setValue(cal);
603 	}
604     
605     /**
606      * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
607      * 
608      * Note: Sets fields using precision up to the second
609      * Note: Date is timezone-agnostic, representing always GMT time
610      * 
611      * @param theDate The date object from which to retrieve values
612      * @since 1.1 
613      */
614     public void setValueToSecond(Date theDate) throws DataTypeException {
615 		if (theDate == null) {
616 			setValue((String)null);
617 			return;
618 		}
619 
620         Calendar calendar = Calendar.getInstance();
621         calendar.setTime(theDate);
622         setValueToSecond(calendar);
623     }
624     
625     /**
626      * <p>Return the value as a calendar object.</p> 
627      * 
628      * <b>Note that only the time component of the return value is set to
629      * the value from this object. Returned value will have today's date</b> 
630      * @since 1.1 
631      */
632     public Calendar getValueAsCalendar() {
633         Calendar retVal = Calendar.getInstance();
634         retVal.set(Calendar.HOUR_OF_DAY, getHour());
635         retVal.set(Calendar.MINUTE, getMinute());
636         retVal.set(Calendar.SECOND, getSecond());
637         
638         float fractSecond = getFractSecond();
639         retVal.set(Calendar.MILLISECOND, (int) (fractSecond * 1000.0));
640         
641         int gmtOff = getGMTOffset();
642         if (gmtOff != GMT_OFFSET_NOT_SET_VALUE && !omitOffsetFg) {
643             retVal.set(Calendar.ZONE_OFFSET, (gmtOff/100) * (1000 * 60 * 60));
644             
645             /*
646              * The following sets the TimeZone associated with the returned calendar
647              * to use the offset specified in the value if this conflicts with the
648              * value it already contains.
649              * 
650              * This is needed in situations where daylight savings is in effect
651              * during part of the year, and a date is parsed which contains the 
652              * other part of the year (i.e. parsing a DST DateTime when it is not actually
653              * DST according to the system clock).
654              * 
655              * See CommonTSTest#testGetCalendarRespectsDaylightSavings() for an example
656              * which fails if this is removed.
657              */
658             if (retVal.getTimeZone().getRawOffset() != retVal.get(Calendar.ZONE_OFFSET)) {
659 	            int hrOffset = gmtOff / 100;
660 	            int minOffset = gmtOff % 100;
661 	            StringBuilder tzBuilder = new StringBuilder("GMT");
662 	            
663 	            if (hrOffset < 0) {
664 	            	tzBuilder.append('-');
665 	            }
666 	            tzBuilder.append(Math.abs(hrOffset));
667 	            tzBuilder.append(':');
668 	            if (minOffset < 10) {
669 	            	tzBuilder.append('0');
670 	            }
671 	            tzBuilder.append(minOffset);
672 	            
673 	            retVal.setTimeZone(TimeZone.getTimeZone(tzBuilder.toString()));
674             }
675             
676         }
677         
678         return retVal;
679     }
680 
681     
682     /**
683      * <p>Return the value as a date object</p>
684      * 
685      * <b>Note that only the time component of the return value is set to
686      * the value from this object. Returned value will have today's date</b> 
687      * Note: Date is timezone-agnostic, representing always GMT time
688      * @since 1.1 
689      */
690     public Date getValueAsDate() {
691         return getValueAsCalendar().getTime();
692     }    
693     
694     /**
695      * Returns the hour as an integer.
696      */
697     public int getHour() {
698         return hour;
699     } //end method
700 
701     /**
702      * Returns the minute as an integer.
703      */
704     public int getMinute() {
705         return minute;
706     } //end method
707 
708     /**
709      * Returns the second as an integer.
710      */
711     public int getSecond() {
712         return second;
713     } //end method
714 
715     /**
716      * Returns the fractional second value as a float.
717      */
718     public float getFractSecond() {
719         return fractionOfSec;
720     } //end method
721 
722     /**
723      * Returns the GMT offset value as an integer, {@link #GMT_OFFSET_NOT_SET_VALUE} if not set.  
724      */
725     public int getGMTOffset() {
726         return offSet;
727     } //end method
728     
729     /**
730      * Returns a string value representing the input Gregorian Calendar object in
731      * an Hl7 Time Format.
732      */
733     public static String toHl7TMFormat(GregorianCalendar cal) throws DataTypeException {
734         String val = "";
735         try {
736             //set the input cal object so that it can report errors
737             //on it's value
738             cal.setLenient(false);
739             int calHour = cal.get(GregorianCalendar.HOUR_OF_DAY);
740             int calMin = cal.get(GregorianCalendar.MINUTE);
741             int calSec = cal.get(GregorianCalendar.SECOND);
742             int calMilli = cal.get(GregorianCalendar.MILLISECOND);
743             //the inputs seconds and milli seconds should be combined into a float type
744             float fractSec = calMilli / 1000F;
745             float calSecFloat = calSec + fractSec;
746             int calOffset = cal.get(GregorianCalendar.ZONE_OFFSET) + cal.get(GregorianCalendar.DST_OFFSET); 
747             //Note the input's Offset value is in milliseconds, we must convert it to
748             //a 4 digit integer in the HL7 Offset format.
749             int offSetSignInt;
750             if (calOffset < 0) {
751                 offSetSignInt = -1;
752             }
753             else {
754                 offSetSignInt = 1;
755             }
756             //get the absolute value of the gmtOffSet
757             int absGmtOffSet = Math.abs(calOffset);
758             int gmtOffSetHours = absGmtOffSet / (3600 * 1000);
759             int gmtOffSetMin = (absGmtOffSet / 60000) % (60);
760             //reset calOffset
761             calOffset = ((gmtOffSetHours * 100) + gmtOffSetMin) * offSetSignInt;
762             //Create an object of the TS class and populate it with the above values
763             //then return the HL7 string value from the object
764             CommonTM tm = new CommonTM();
765             tm.setHourMinSecondPrecision(calHour, calMin, calSecFloat);
766             tm.setOffset(calOffset);
767             val = tm.getValue();
768         } // end try
769 
770         catch (DataTypeException e) {
771             throw e;
772         } //end catch
773 
774         catch (Exception e) {
775             throw new DataTypeException(e);
776         } //end catch
777         return val;
778     } //end method
779 
780 } //end class