001/**
002 * The contents of this file are subject to the Mozilla Public License Version 1.1
003 * (the "License"); you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
005 * Software distributed under the License is distributed on an "AS IS" basis,
006 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
007 * specific language governing rights and limitations under the License.
008 *
009 * The Original Code is "CommmonTM.java".  Description:
010 * "Note: The class description below has been excerpted from the Hl7 2.4 documentation"
011 *
012 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 * 2001.  All Rights Reserved.
014 *
015 * Contributor(s): ______________________________________.
016 *
017 * Alternatively, the contents of this file may be used under the terms of the
018 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
019 * applicable instead of those above.  If you wish to allow use of your version of this
020 * file only under the terms of the GPL and not to allow others to use your version
021 * of this file under the MPL, indicate your decision by deleting  the provisions above
022 * and replace  them with the notice and other provisions required by the GPL License.
023 * If you do not delete the provisions above, a recipient may use your version of
024 * this file under either the MPL or the GPL.
025 *
026 */
027
028package ca.uhn.hl7v2.model.primitive;
029
030import java.util.Calendar;
031import java.util.Date;
032import java.util.GregorianCalendar;
033import java.util.TimeZone;
034import java.io.Serializable;
035
036import ca.uhn.hl7v2.model.DataTypeException;
037import ca.uhn.hl7v2.model.DataTypeUtil;
038
039/**
040 * This class contains functionality used by the TM class
041 * in the version 2.3.0, 2.3.1, and 2.4 packages
042 *
043 * Note: The class description below has been excerpted from the Hl7 2.4 documentation. Sectional
044 * references made below also refer to the same documentation.
045 *
046 * Format: HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]
047 * In prior versions of HL7, this data type was always specified to be in the
048 * format HHMM[SS[.SSSS]][+/-ZZZZ] using a 24 hour clock notation. In the
049 * current and future versions, the precision of a time may be expressed by
050 * limiting the number of digits used with the format specification as shown
051 * above. By site-specific agreement, HHMM[SS[.SSSS]][+/-ZZZZ] may be used where
052 * backward compatibility must be maintained.
053 * Thus, HH is used to specify a precision of "hour," HHMM is used to specify a
054 * precision of "minute," HHMMSS is used to specify a precision of seconds, and
055 * HHMMSS.SSSS is used to specify a precision of ten-thousandths of a second.
056 * In each of these cases, the time zone is an optional component. The fractional
057 * seconds could be sent by a transmitter who requires greater precision than whole
058 * seconds. Fractional representations of minutes, hours or other higher-order units
059 * of time are not permitted.
060 * Note: The time zone [+/-ZZZZ], when used, is restricted to legally-defined time zones
061 * and is represented in HHMM format.
062 * The time zone of the sender may be sent optionally as an offset from the coordinated
063 * universal time (previously known as Greenwich Mean Time). Where the time zone
064 * is not present in a particular TM field but is included as part of the date/time
065 * field in the MSH segment, the MSH value will be used as the default time zone.
066 * Otherwise, the time is understood to refer to the local time of the sender.
067 * Midnight is represented as 0000.
068 * Examples:|235959+1100| 1 second before midnight in a time zone eleven hours
069 * ahead of Universal Coordinated Time (i.e., east of Greenwich).
070 * |0800| Eight AM, local time of the sender.
071 * |093544.2312| 44.2312 seconds after Nine thirty-five AM, local time of sender.
072 * |13| 1pm (with a precision of hours), local time of sender.
073 * @author Neal Acharya
074 */
075
076@SuppressWarnings("serial")
077public class CommonTM implements Serializable {
078    
079        /**
080     * Value returned by {@link #getGMTOffset()} if no offset is set
081     */
082    public static final int GMT_OFFSET_NOT_SET_VALUE = -99;
083
084    private String value;
085    private int hour;
086    private int minute;
087    private int second;
088    private float fractionOfSec;
089    private int offSet;
090    private boolean omitOffsetFg = false;
091
092    /**
093     * Constructs a TM datatype with fields initialzed to zero and the value set to
094     * null.
095     */
096    public CommonTM() {
097        //initialize all DT fields
098        value = null;
099        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                    throw new DataTypeException(msg);
161                } //end if
162
163                if (d != -1) {
164                    //here we know that decimal exists
165                    //thus length of the time value can be between 8 and 11 characters
166                    if ((timeVal.length() < 8) || (timeVal.length() > 11)) {
167                        String msg =
168                            "The length of the TM datatype value does not conform to an allowable"
169                                + " format. Format should conform to HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]";
170                        throw new DataTypeException(msg);
171                    } //end if
172                } //end if
173
174                if (d == -1) {
175                    //here we know that the decimal does not exist
176                    //thus length of the time value can be 2 or 4 or 6 characters
177                    if ((timeVal.length() != 2) && (timeVal.length() != 4) && (timeVal.length() != 6)) {
178                        String msg =
179                            "The length of the TM datatype value does not conform to an allowable"
180                                + " format. Format should conform to HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]";
181                        throw new DataTypeException(msg);
182                    } //end if
183                } //end if
184
185                //We will now try to validate the timeVal portion of the TM datatype value
186                if (timeVal.length() >= 2) {
187                    //extract the hour data from the input value.  If the first 2 characters
188                    //are not numeric then a number format exception will be generated
189                    int hrInt = Integer.parseInt(timeVal.substring(0, 2));
190                    //check to see if the hour value is valid
191                    if ((hrInt < 0) || (hrInt > 23)) {
192                        String msg = "The hour value of the TM datatype must be >=0 and <=23";
193                        throw new DataTypeException(msg);
194                    } //end if
195                    hour = hrInt;
196                } //end if
197
198                if (timeVal.length() >= 4) {
199                    //extract the minute data from the input value
200                    //If these characters are not numeric then a number
201                    //format exception will be generated
202                    int minInt = Integer.parseInt(timeVal.substring(2, 4));
203                    //check to see if the minute value is valid
204                    if ((minInt < 0) || (minInt > 59)) {
205                        String msg = "The minute value of the TM datatype must be >=0 and <=59";
206                        throw new DataTypeException(msg);
207                    } //end if
208                    minute = minInt;
209                } //end if
210
211                if (timeVal.length() >= 6) {
212                    //extract the seconds data from the input value
213                    //If these characters are not numeric then a number
214                    //format exception will be generated
215                    int secInt = Integer.parseInt(timeVal.substring(4, 6));
216                    //check to see if the seconds value is valid
217                    if ((secInt < 0) || (secInt > 59)) {
218                        String msg = "The seconds value of the TM datatype must be >=0 and <=59";
219                        throw new DataTypeException(msg);
220                    } //end if
221                    second = secInt;
222                } //end if
223
224                if (timeVal.length() >= 8) {
225                    //extract the fractional second value from the input value
226                    //If these characters are not numeric then a number
227                    //format exception will be generated
228                    float fract = Float.parseFloat(timeVal.substring(6));
229                    //check to see if the fractional second value is valid
230                    if ((fract < 0) || (fract >= 1)) {
231                        String msg = "The fractional second value of the TM datatype must be >= 0 and < 1";
232                        throw new DataTypeException(msg);
233                    } //end if
234                    fractionOfSec = fract;
235                } //end if
236
237                //We will now try to validate the tempOffset portion of the TM datatype value
238                if (offsetExists) {
239                    //in case the offset are a series of zeros we should not omit displaying
240                    //it in the return value from the getValue() method
241                    omitOffsetFg = false;
242                    //remove the sign from the temp offset
243                    String tempOffsetNoS = tempOffset.substring(1);
244                    //extract the hour data from the offset value.  If the first 2 characters
245                    //are not numeric then a number format exception will be generated
246                    int offsetInt = Integer.parseInt(tempOffsetNoS.substring(0, 2));
247                    //check to see if the hour value is valid
248                    if ((offsetInt < 0) || (offsetInt > 23)) {
249                        String msg = "The GMT offset hour value of the TM datatype must be >=0 and <=23";
250                        throw new DataTypeException(msg);
251                    } //end if
252                    //extract the minute data from the offset value.  If these characters
253                    //are not numeric then a number format exception will be generated
254                    offsetInt = Integer.parseInt(tempOffsetNoS.substring(2, 4));
255                    //check to see if the minute value is valid
256                    if ((offsetInt < 0) || (offsetInt > 59)) {
257                        String msg = "The GMT offset minute value of the TM datatype must be >=0 and <=59";
258                        throw new DataTypeException(msg);
259                    } //end if
260                    //validation done, update the offSet field
261                    offSet = Integer.parseInt(tempOffsetNoS);
262                    //add the sign back to the offset if it is negative
263                    if (sm != -1) {
264                        offSet = -1 * offSet;
265                    } //end if
266                } //end if
267
268                //If the GMT offset has not been supplied then set the offset to the
269                //local timezone
270                //[Bryan: changing this to omit time zone because erroneous if parser in different zone than sender]
271                if (!offsetExists) {
272                    omitOffsetFg = true;
273                    // set the offSet field to the current time and local time zone
274                    //offSet = DataTypeUtil.getLocalGMTOffset();
275                } //end if
276
277                //validations are now done store the time value into the private value field
278                value = timeVal;
279            } //end try
280
281            catch (DataTypeException e) {
282                throw e;
283            } //end catch
284
285            catch (Exception e) {
286                throw new DataTypeException(e);
287            } //end catch
288        } //end if
289        else {
290            //set the private value field to null or empty space.
291            value = val;
292        } //end else
293    } //end method
294
295    /**
296     * This method takes in an integer value for the hour and performs validations,
297     * it then sets the value field formatted as an HL7 time
298     * value with hour precision (HH).
299     */
300    public void setHourPrecision(int hr) throws DataTypeException {
301        try {
302            //validate input value
303            if ((hr < 0) || (hr > 23)) {
304                String msg = "The hour value of the TM datatype must be >=0 and <=23";
305                throw new DataTypeException(msg);
306            } //end if
307            hour = hr;
308            minute = 0;
309            second = 0;
310            fractionOfSec = 0;
311            offSet = 0;
312            //Here the offset is not defined, we should omit showing it in the
313            //return value from the getValue() method
314            omitOffsetFg = true;
315            value = DataTypeUtil.preAppendZeroes(hr, 2);
316        } //end try
317
318        catch (DataTypeException e) {
319            throw e;
320        } //end catch
321
322        catch (Exception e) {
323            throw new DataTypeException(e.getMessage());
324        } //end catch
325
326    } //end method
327
328    /**
329     * This method takes in integer values for the hour and minute and performs validations,
330     * it then sets the value field formatted as an HL7 time value
331     * with hour&minute precision (HHMM).
332     */
333    public void setHourMinutePrecision(int hr, int min) throws DataTypeException {
334        try {
335            this.setHourPrecision(hr);
336            //validate input minute value
337            if ((min < 0) || (min > 59)) {
338                String msg = "The minute value of the TM datatype must be >=0 and <=59";
339                throw new DataTypeException(msg);
340            } //end if
341            minute = min;
342            second = 0;
343            fractionOfSec = 0;
344            offSet = 0;
345            //Here the offset is not defined, we should omit showing it in the
346            //return value from the getValue() method
347            omitOffsetFg = true;
348            value = value + DataTypeUtil.preAppendZeroes(min, 2);
349        } //end try
350
351        catch (DataTypeException e) {
352            throw e;
353        } //end catch
354
355        catch (Exception e) {
356            throw new DataTypeException(e.getMessage());
357        } //end catch
358    } //end method
359
360    /**
361     * This method takes in integer values for the hour, minute, seconds, and fractional seconds
362     * (going to the tenthousandths precision).
363     * The method performs validations and then sets the value field formatted as an
364     * HL7 time value with a precision that starts from the hour and goes down to the tenthousandths
365     * of a second (HHMMSS.SSSS).
366     * Note: all of the precisions from tenths down to tenthousandths of a
367     * second are optional. If the precision goes below tenthousandths of a second then the second
368     * value will be rounded to the nearest tenthousandths of a second.
369     */
370    public void setHourMinSecondPrecision(int hr, int min, float sec) throws DataTypeException {
371        try {
372            this.setHourMinutePrecision(hr, min);
373            //multiply the seconds input value by 10000 and round the result
374            //then divide the number by tenthousand and store it back.
375            //This will round the fractional seconds to the nearest tenthousandths
376            int secMultRound = Math.round(10000F * sec);
377            sec = secMultRound / 10000F;
378            //Now store the second and fractional component
379            second = (int) Math.floor(sec);
380                        //validate input seconds value
381                        if ((second < 0) || (second >= 60)) {
382                                String msg = "The (rounded) second value of the TM datatype must be >=0 and <60";
383                throw new DataTypeException(msg);
384                        } //end if
385            int fractionOfSecInt = secMultRound - (second * 10000);
386            fractionOfSec = fractionOfSecInt / 10000F;
387            String fractString = "";
388            //Now convert the fractionOfSec field to a string without the leading zero
389            if (fractionOfSec != 0.0F) {
390                fractString = (Float.toString(fractionOfSec)).substring(1);
391            } //end if
392            //Now update the value field
393            offSet = 0;
394            //Here the offset is not defined, we should omit showing it in the
395            //return value from the getValue() method
396            omitOffsetFg = true;
397            value = value + DataTypeUtil.preAppendZeroes(second, 2) + fractString;
398        } //end try
399
400        catch (DataTypeException e) {
401            throw e;
402        } //end catch
403
404        catch (Exception e) {
405            throw new DataTypeException(e);
406        } //end catch
407    } //end method
408
409    /**
410     * This method takes in the four digit (signed) GMT offset and sets the offset
411     * field
412     */
413    public void setOffset(int signedOffset) throws DataTypeException {
414        try {
415            //When this function is called an offset is being created/updated
416            //we should not omit displaying it in the return value from
417            //the getValue() method
418            omitOffsetFg = false;
419            String offsetStr = Integer.toString(signedOffset);
420            if ((signedOffset >= 0 && offsetStr.length() > 4) || (signedOffset < 0 && offsetStr.length() > 5)) {
421                //The length of the GMT offset must be no greater than 5 characters (including the sign)
422                String msg =
423                    "The length of the GMT offset for the TM datatype value does"
424                        + " not conform to the allowable format [+/-ZZZZ]. Value: " + signedOffset;
425                throw new DataTypeException(msg);
426            } //end if
427            //obtain the absolute value of the input
428            int absOffset = Math.abs(signedOffset);
429            //extract the hour data from the offset value.
430            //first preappend zeros so we have a 4 char offset value (without sign)
431            offsetStr = DataTypeUtil.preAppendZeroes(absOffset, 4);
432            int hrOffsetInt = Integer.parseInt(offsetStr.substring(0, 2));
433            //check to see if the hour value is valid
434            if ((hrOffsetInt < 0) || (hrOffsetInt > 23)) {
435                String msg = "The GMT offset hour value of the TM datatype must be >=0 and <=23";
436                throw new DataTypeException(msg);
437            } //end if
438            //extract the minute data from the offset value.
439            int minOffsetInt = Integer.parseInt(offsetStr.substring(2, 4));
440            //check to see if the minute value is valid
441            if ((minOffsetInt < 0) || (minOffsetInt > 59)) {
442                String msg = "The GMT offset minute value of the TM datatype must be >=0 and <=59";
443                throw new DataTypeException(msg);
444            } //end if
445            //The input value is valid, now store it in the offset field
446            offSet = signedOffset;
447        } //end try
448
449        catch (DataTypeException e) {
450            throw e;
451        } //end catch
452
453        catch (Exception e) {
454            throw new DataTypeException(e);
455        } //end catch
456    } //end method
457
458    /**
459     * Returns the HL7 TM string value.
460     */
461    public String getValue() {
462        //combine the value field with the offSet field and return it
463        String returnVal = null;
464        if (value != null && !value.equals("")) {
465            if (!omitOffsetFg && !value.equals("\"\"")) {
466                int absOffset = Math.abs(offSet);
467                String sign;
468                if (offSet >= 0) {
469                    sign = "+";
470                } //end if
471                else {
472                    sign = "-";
473                } //end else
474                returnVal = value + sign + DataTypeUtil.preAppendZeroes(absOffset, 4);
475            }
476            else {
477                returnVal = value;
478            } //end else
479        } //end if
480        return returnVal;
481    } //end method
482
483    /**
484     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
485     * 
486     * Note: Sets fields using precision up to the minute
487     * 
488     * @param theCalendar The calendar object from which to retrieve values 
489     * @since 1.1 
490     */
491    public void setValueToMinute(Calendar theCalendar) throws DataTypeException {
492                if (theCalendar == null) {
493                        setValue((String)null);
494                        return;
495                }
496
497        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
498        int min = theCalendar.get(Calendar.MINUTE);
499        setHourMinutePrecision(hr, min);
500    }
501
502    /**
503     * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
504     * 
505     * Note: Sets fields using precision up to the minute
506     * Note: Date is timezone-agnostic, representing always GMT time
507     * 
508     * @param theDate The date object from which to retrieve values
509     * @since 1.1 
510     */
511    public void setValueToMinute(Date theDate) throws DataTypeException {
512                if (theDate == null) {
513                        setValue((String)null);
514                        return;
515                }
516
517                Calendar calendar = Calendar.getInstance();
518        calendar.setTime(theDate);
519        setValueToMinute(calendar);
520    }
521    
522    /**
523     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
524     * 
525     * Note: Sets fields using precision up to the second
526     * 
527     * @param theCalendar The calendar object from which to retrieve values 
528     * @since 1.1 
529     */
530    public void setValueToSecond(Calendar theCalendar) throws DataTypeException {
531                if (theCalendar == null) {
532                        setValue((String)null);
533                        return;
534                }
535
536        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
537        int min = theCalendar.get(Calendar.MINUTE);
538        int sec = theCalendar.get(Calendar.SECOND);
539        
540        setHourMinSecondPrecision(hr, min, sec);
541    }
542
543    /**
544     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
545     * 
546     * Note: Sets fields using precision up to the millisecond, including timezone offset
547     * 
548     * @param theCalendar The calendar object from which to retrieve values 
549     * @since 1.1 
550     */
551    public void setValue(Calendar theCalendar) throws DataTypeException {
552                if (theCalendar == null) {
553                        setValue((String)null);
554                        return;
555                }
556
557        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
558        int min = theCalendar.get(Calendar.MINUTE);
559        float sec = theCalendar.get(Calendar.SECOND) + (theCalendar.get(Calendar.MILLISECOND) / 1000.0F);
560        setHourMinSecondPrecision(hr, min, sec);
561        
562        // 3410095: care for integer overflow and timezones not at the full hour, e.g. India
563        int hourOffset= theCalendar.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);   
564        int minuteOffset = (theCalendar.get(Calendar.ZONE_OFFSET) / (1000 * 60)) % 60;
565        int zoneOffset = hourOffset * 100 + minuteOffset;
566        setOffset(zoneOffset);
567    }
568   
569    /**
570     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
571     * 
572     * Note: Sets fields using precision up to the millisecond, and sets the timezone offset to
573     * the current system offset
574     * Note: Date is timezone-agnostic, representing always GMT time
575     * 
576     * @param theDate The calendar object from which to retrieve values 
577     * @since 1.1 
578     */
579        public void setValue(Date theDate) throws DataTypeException {
580                if (theDate == null) {
581                        setValue((String)null);
582                        return;
583                }
584
585                GregorianCalendar cal = new GregorianCalendar();
586                cal.setTime(theDate);
587                setValue(cal);
588        }
589    
590    /**
591     * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
592     * 
593     * Note: Sets fields using precision up to the second
594     * Note: Date is timezone-agnostic, representing always GMT time
595     * 
596     * @param theDate The date object from which to retrieve values
597     * @since 1.1 
598     */
599    public void setValueToSecond(Date theDate) throws DataTypeException {
600                if (theDate == null) {
601                        setValue((String)null);
602                        return;
603                }
604
605        Calendar calendar = Calendar.getInstance();
606        calendar.setTime(theDate);
607        setValueToSecond(calendar);
608    }
609    
610    /**
611     * <p>Return the value as a calendar object.</p> 
612     * 
613     * <b>Note that only the time component of the return value is set to
614     * the value from this object. Returned value will have today's date</b> 
615     * @since 1.1 
616     */
617    public Calendar getValueAsCalendar() {
618        int gmtOff = getGMTOffset();
619        Calendar retVal;
620        if (gmtOff != GMT_OFFSET_NOT_SET_VALUE && !omitOffsetFg) {
621            int hrOffset = gmtOff / 100;
622            int minOffset = Math.abs(gmtOff % 100);
623            String timeZone = String.format("GMT%+d:%02d", hrOffset, minOffset);
624            retVal = new GregorianCalendar(TimeZone.getTimeZone(timeZone));
625        } else {
626            retVal = Calendar.getInstance();
627        }
628
629        retVal.set(Calendar.HOUR_OF_DAY, getHour());
630        retVal.set(Calendar.MINUTE, getMinute());
631        retVal.set(Calendar.SECOND, getSecond());
632        float fractSecond = getFractSecond();
633        retVal.set(Calendar.MILLISECOND, (int) Math.round(fractSecond * 1000.0));
634
635        return retVal;
636    }
637
638    
639    /**
640     * <p>Return the value as a date object</p>
641     * 
642     * <b>Note that only the time component of the return value is set to
643     * the value from this object. Returned value will have today's date</b> 
644     * Note: Date is timezone-agnostic, representing always GMT time
645     * @since 1.1 
646     */
647    public Date getValueAsDate() {
648        return getValueAsCalendar().getTime();
649    }    
650    
651    /**
652     * Returns the hour as an integer.
653     */
654    public int getHour() {
655        return hour;
656    } //end method
657
658    /**
659     * Returns the minute as an integer.
660     */
661    public int getMinute() {
662        return minute;
663    } //end method
664
665    /**
666     * Returns the second as an integer.
667     */
668    public int getSecond() {
669        return second;
670    } //end method
671
672    /**
673     * Returns the fractional second value as a float.
674     */
675    public float getFractSecond() {
676        return fractionOfSec;
677    } //end method
678
679    /**
680     * Returns the GMT offset value as an integer, {@link #GMT_OFFSET_NOT_SET_VALUE} if not set.  
681     */
682    public int getGMTOffset() {
683        return offSet;
684    } //end method
685    
686    /**
687     * Returns a string value representing the input Gregorian Calendar object in
688     * an Hl7 Time Format.
689     */
690    public static String toHl7TMFormat(GregorianCalendar cal) throws DataTypeException {
691        String val;
692        try {
693            //set the input cal object so that it can report errors
694            //on it's value
695            cal.setLenient(false);
696            int calHour = cal.get(GregorianCalendar.HOUR_OF_DAY);
697            int calMin = cal.get(GregorianCalendar.MINUTE);
698            int calSec = cal.get(GregorianCalendar.SECOND);
699            int calMilli = cal.get(GregorianCalendar.MILLISECOND);
700            //the inputs seconds and milli seconds should be combined into a float type
701            float fractSec = calMilli / 1000F;
702            float calSecFloat = calSec + fractSec;
703            int calOffset = cal.get(GregorianCalendar.ZONE_OFFSET) + cal.get(GregorianCalendar.DST_OFFSET); 
704            //Note the input's Offset value is in milliseconds, we must convert it to
705            //a 4 digit integer in the HL7 Offset format.
706            int offSetSignInt;
707            if (calOffset < 0) {
708                offSetSignInt = -1;
709            }
710            else {
711                offSetSignInt = 1;
712            }
713            //get the absolute value of the gmtOffSet
714            int absGmtOffSet = Math.abs(calOffset);
715            int gmtOffSetHours = absGmtOffSet / (3600 * 1000);
716            int gmtOffSetMin = (absGmtOffSet / 60000) % (60);
717            //reset calOffset
718            calOffset = ((gmtOffSetHours * 100) + gmtOffSetMin) * offSetSignInt;
719            //Create an object of the TS class and populate it with the above values
720            //then return the HL7 string value from the object
721            CommonTM tm = new CommonTM();
722            tm.setHourMinSecondPrecision(calHour, calMin, calSecFloat);
723            tm.setOffset(calOffset);
724            val = tm.getValue();
725        } // end try
726
727        catch (DataTypeException e) {
728            throw e;
729        } //end catch
730
731        catch (Exception e) {
732            throw new DataTypeException(e);
733        } //end catch
734        return val;
735    } //end method
736
737} //end class