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 *
010 *
011 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
012 * 2001.  All Rights Reserved.
013 *
014 * Contributor(s): ______________________________________.
015 *
016 * Alternatively, the contents of this file may be used under the terms of the
017 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are
018 * applicable instead of those above.  If you wish to allow use of your version of this
019 * file only under the terms of the GPL and not to allow others to use your version
020 * of this file under the MPL, indicate your decision by deleting  the provisions above
021 * and replace  them with the notice and other provisions required by the GPL License.
022 * If you do not delete the provisions above, a recipient may use your version of
023 * this file under either the MPL or the GPL.
024 *
025 */
026
027package ca.uhn.hl7v2.model.primitive;
028
029import ca.uhn.hl7v2.model.DataTypeException;
030import ca.uhn.hl7v2.model.DataTypeUtil;
031
032import java.io.Serializable;
033import java.util.Calendar;
034import java.util.Date;
035import java.util.GregorianCalendar;
036
037/**
038 * <p>
039 * This class contains functionality used by the TS class
040 * in the version 2.3.0, 2.3.1, and 2.4 packages
041 * </p>
042 * 
043 * <p>
044 * Note: The class description below has been excerpted from the Hl7 2.4 documentation. Sectional
045 * references made below also refer to the same documentation.
046 * </p>
047 *
048 * <p>
049 * Format: YYYY[MM[DD[HHMM[SS[.S[S[S[S]]]]]]]][+/-ZZZZ]^<degree of precision>
050 * </p>
051 * 
052 * <p>
053 * Contains the exact time of an event, including the date and time. The date portion of a time stamp follows the rules of a
054 * date field and the time portion follows the rules of a time field. The time zone (+/-ZZZZ) is represented as +/-HHMM
055 * offset from UTC (formerly Greenwich Mean Time (GMT)), where +0000 or -0000 both represent UTC (without offset).
056 * The specific data representations used in the HL7 encoding rules are compatible with ISO 8824-1987(E).
057 * In prior versions of HL7, an optional second component indicates the degree of precision of the time stamp (Y = year, L
058 * = month, D = day, H = hour, M = minute, S = second). This optional second component is retained only for purposes of
059 * backward compatibility.
060 * </p>
061 * 
062 * <p>
063 * By site-specific agreement, YYYYMMDD[HHMM[SS[.S[S[S[S]]]]]][+/-ZZZZ]^<degree of precision> may be used
064 * where backward compatibility must be maintained.
065 * In the current and future versions of HL7, the precision is indicated by limiting the number of digits used, unless the
066 * optional second component is present. Thus, YYYY is used to specify a precision of "year," YYYYMM specifies a
067 * precision of "month," YYYYMMDD specifies a precision of "day," YYYYMMDDHH is used to specify a precision of
068 * "hour," YYYYMMDDHHMM is used to specify a precision of "minute," YYYYMMDDHHMMSS is used to specify a
069 * precision of seconds, and YYYYMMDDHHMMSS.SSSS is used to specify a precision of ten thousandths of a second.
070 * In each of these cases, the time zone is an optional component. Note that if the time zone is not included, the timezone
071 * defaults to that of the local time zone of the sender. Also note that a TS valued field with the HHMM part set to "0000"
072 * represents midnight of the night extending from the previous day to the day given by the YYYYMMDD part (see example
073 * below). Maximum length of the time stamp is 26. 
074 * </p>
075 * <p>
076 * Examples: <br/>
077 * |19760704010159-0500|<br/>
078 * 1:01:59 on July 4, 1976 in the Eastern Standard Time zone (USA).<br/>
079 * |19760704010159-0400|<br/>
080 * 1:01:59 on July 4, 1976 in the Eastern Daylight Saving Time zone (USA).<br/>
081 * |198807050000|<br/>
082 * Midnight of the night extending from July 4 to July 5, 1988 in the local time zone of the sender.<br/>
083 * |19880705|<br/>
084 * Same as prior example, but precision extends only to the day. Could be used for a birthdate, if the time of birth is
085 * unknown.<br/>
086 * |19981004010159+0100|<br/>
087 * 1:01:59 on October 4, 1998 in Amsterdam, NL. (Time zone=+0100).<br/>
088 * </p>
089 * <p>
090 * The HL7 Standard strongly recommends that all systems routinely send the time zone offset but does not require it. All
091 * HL7 systems are required to accept the time zone offset, but its implementation is application specific. For many
092 * applications the time of interest is the local time of the sender. For example, an application in the Eastern Standard Time
093 * zone receiving notification of an admission that takes place at 11:00 PM in San Francisco on December 11 would prefer
094 * to treat the admission as having occurred on December 11 rather than advancing the date to December 12.
095 * </p>
096 * <p>
097 * Note: The time zone [+/-ZZZZ], when used, is restricted to legally-defined time zones and is represented in HHMM
098 * format.
099 * </p>
100 * <p>
101 * One exception to this rule would be a clinical system that processed patient data collected in a clinic and a nearby hospital
102 * that happens to be in a different time zone. Such applications may choose to convert the data to a common
103 * representation. Similar concerns apply to the transitions to and from daylight saving time. HL7 supports such requirements
104 * by requiring that the time zone information be present when the information is sent. It does not, however, specify which of
105 * the treatments discussed here will be applied by the receiving system.
106 * </p>
107 * @author Neal Acharya
108 */
109
110@SuppressWarnings("serial")
111public class CommonTS implements Serializable {
112
113    private CommonDT dt;
114    private CommonTM tm;
115
116    /** Creates new ValidTS
117     * zero argument constructor.
118     * Creates an uninitailized TS datatype
119     */
120    public CommonTS() {
121    } //zero arg constructor
122
123    /**
124     * Constructs a TS object with the given value.
125     * The stored value will be in the following
126     * format YYYY[MM[DD[HHMM[SS[.S[S[S[S]]]]]]]][+/-ZZZZ]
127     */
128    public CommonTS(String val) throws DataTypeException {
129        this.setValue(val);
130    } //end constructor
131
132    /**
133     * Returns the day as an integer.
134     */
135    public int getDay() {
136        int day = 0;
137        if (dt != null) {
138            day = dt.getDay();
139        } //end if
140        return day;
141    } //end method
142
143    /**
144     * Returns the fractional second value as a float.
145     */
146    public float getFractSecond() {
147        float fractionOfSec = 0;
148        if (tm != null) {
149            fractionOfSec = tm.getFractSecond();
150        } //end if
151        return fractionOfSec;
152    } //end method
153
154    /**
155     * Returns the GMT offset value as an integer.
156     */
157    public int getGMTOffset() {
158        int offSet = 0;
159        if (tm != null) {
160                offSet = tm.getGMTOffset();
161        } //end if
162        return offSet;
163    } //end method
164
165    /**
166     * Returns the hour as an integer.
167     */
168    public int getHour() {
169        int hour = 0;
170        if (tm != null) {
171            hour = tm.getHour();
172        } //end if
173        return hour;
174    } //end method
175
176    /**
177     * Returns the minute as an integer.
178     */
179    public int getMinute() {
180        int minute = 0;
181        if (tm != null) {
182            minute = tm.getMinute();
183        } //end if
184        return minute;
185    } //end method
186
187    /**
188     * Returns the month as an integer.
189     */
190    public int getMonth() {
191        int month = 0;
192        if (dt != null) {
193            month = dt.getMonth();
194        } //end if
195        return month;
196    } //end method
197
198    /**
199     * Returns the second as an integer.
200     */
201    public int getSecond() {
202        int seconds = 0;
203        if (tm != null) {
204            seconds = tm.getSecond();
205        } //end if
206        return seconds;
207    } //end method
208
209    /**
210     * Returns the HL7 TS string value.
211     */
212    public String getValue() {
213        String value = null;
214        if (dt != null) {
215            value = dt.getValue();
216        } //end if
217        if (tm != null && value != null && !value.equals("")) {
218            if (tm.getValue() != null && !tm.getValue().equals("")) {
219                //here we know we have a delete value or separate date and the time values supplied
220                if (tm.getValue().equals("\"\"") && dt.getValue().equals("\"\"")) {
221                    //set value to the delete value ("")
222                    value = "\"\"";
223                }
224                else{
225                    //set value to date concatonated with time value
226                    value = value + tm.getValue();
227                }                
228            } //end if
229            if (tm.getValue() == null || tm.getValue().equals("")) {
230                //here we know we both have the date and just the time offset value
231                //change the offset value from an integer to a signed string
232                int offset = tm.getGMTOffset();
233                String offsetStr = "";
234                if (offset != CommonTM.GMT_OFFSET_NOT_SET_VALUE) {
235                    offsetStr = DataTypeUtil.preAppendZeroes(Math.abs(offset), 4);
236                    if (tm.getGMTOffset() >= 0) {
237                        offsetStr = "+" + offsetStr;
238                    } //end if
239                    else {
240                        offsetStr = "-" + offsetStr;
241                    } //end else
242                }
243                value = value + offsetStr;
244            } //end if
245        } //end if
246        return value;
247    } //end method
248    
249    /**
250     * Return the value as a calendar object. If the value is null (e.g. no value has
251     * been set), returns null
252     * 
253     * @since 1.1 
254     */
255    public Calendar getValueAsCalendar() {
256        if (getValue() == null) {
257                return null;
258        }
259        
260        Calendar retVal = tm.getValueAsCalendar();
261
262        retVal.set(Calendar.YEAR, getYear());
263        retVal.set(Calendar.MONTH, getMonth() - 1);
264        retVal.set(Calendar.DATE, getDay());
265        
266        return retVal;
267    }
268
269    /**
270     * Return the value as a date objectIf the value is null (e.g. no value has
271     * been set), returns null
272     * 
273     * @since 1.1 
274     */
275    public Date getValueAsDate() {
276        if (getValue() == null) {
277                return null;
278        }
279
280        return getValueAsCalendar().getTime();
281    }
282    
283    /**
284     * Returns the year as an integer.
285     */
286    public int getYear() {
287        int year = 0;
288        if (dt != null) {
289            year = dt.getYear();
290        } //end if
291        return year;
292    } //end method
293
294    
295    /**
296     * This method takes in integer values for the year, month, day, hour
297     * and minute and performs validations, it then sets the value in the object
298     * formatted as an HL7 Time Stamp value with year&month&day&hour&minute precision (YYYYMMDDHHMM).
299     */
300    public void setDateMinutePrecision(int yr, int mnth, int dy, int hr, int min) throws DataTypeException {
301        try {
302            //set the value of the date object to the input date value
303            this.setDatePrecision(yr, mnth, dy);
304            //create new time object is there isn't one
305            if (tm == null) {
306                tm = new CommonTM();
307            }
308            //set the value of the time object to the minute precision with the input values
309            tm.setHourMinutePrecision(hr, min);
310        } //end try
311
312        catch (DataTypeException e) {
313            throw e;
314        } //end catch
315
316        catch (Exception e) {
317            throw new DataTypeException(e);
318        } //end catch
319    } //end method
320    
321    
322    /**
323     * This method takes in integer values for the year and month and day
324     * and performs validations, it then sets the value in the object
325     * formatted as an HL7 Time Stamp value with year&month&day precision (YYYYMMDD).
326     *
327     */
328    public void setDatePrecision(int yr, int mnth, int dy) throws DataTypeException {
329        try {
330            //create date object if there isn't one
331            if (dt == null) {
332                dt = new CommonDT();
333            }
334            //set the value of the date object to the input date value
335            dt.setYearMonthDayPrecision(yr, mnth, dy);
336            //clear the time value object
337            tm = null;
338        } //end try
339
340        catch (DataTypeException e) {
341            throw e;
342        } //end catch
343
344        catch (Exception e) {
345            throw new DataTypeException(e);
346        } //end catch
347    } //end method
348
349    /**
350     * This method takes in integer values for the year, month, day, hour, minute, seconds,
351     * and fractional seconds (going to the tenthousandths precision).
352     * The method performs validations and then sets the value in the object formatted as an
353     * HL7 time value with a precision that starts from the year and goes down to the tenthousandths
354     * of a second (YYYYMMDDHHMMSS.SSSS).
355     * The Gmt Offset will not be effected.
356     * Note: all of the precisions from tenths down to
357     * tenthousandths of a second are optional. If the precision goes below tenthousandths
358     * of a second then the second value will be rounded to the nearest tenthousandths of a second.
359     */
360    public void setDateSecondPrecision(int yr, int mnth, int dy, int hr, int min, float sec) throws DataTypeException {
361        try {
362            //set the value of the date object to the input date value
363            this.setDatePrecision(yr, mnth, dy);
364            //create new time object is there isn't one
365            if (tm == null) {
366                tm = new CommonTM();
367            }
368            //set the value of the time object to the second precision with the input values
369            tm.setHourMinSecondPrecision(hr, min, sec);
370        } //end try
371
372        catch (DataTypeException e) {
373            throw e;
374        } //end catch
375
376        catch (Exception e) {
377            throw new DataTypeException(e);
378        } //end catch
379    } //end method
380
381    /**
382     * This method takes in the four digit (signed) GMT offset and sets the offset
383     * field
384     */
385    public void setOffset(int signedOffset) throws DataTypeException {
386        try {
387            //create new time object is there isn't one
388            if (tm == null) {
389                tm = new CommonTM();
390            }
391            //set the offset value of the time object to the input value
392            tm.setOffset(signedOffset);
393        }
394
395        catch (DataTypeException e) {
396            throw e;
397        } //end catch
398
399        catch (Exception e) {
400            throw new DataTypeException(e);
401        } //end catch
402    } //end method
403
404    /**
405     * Convenience setter which sets the value using a {@link Calendar} object.
406     * Passing in <code>null</code> clears any existing value.
407     * 
408     * Note: Sets fields using precision up to the millisecond, including timezone offset
409     * 
410     * @param theCalendar The calendar object from which to retrieve values 
411     * @since 1.1 
412     */
413    public void setValue(Calendar theCalendar) throws DataTypeException {
414                if (theCalendar == null) {
415                        setValue((String)null);
416                        return;
417                }
418
419                int yr = theCalendar.get(Calendar.YEAR);
420        int mnth = theCalendar.get(Calendar.MONTH) + 1;
421        int dy = theCalendar.get(Calendar.DATE);
422        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
423        int min = theCalendar.get(Calendar.MINUTE);
424        float sec = theCalendar.get(Calendar.SECOND) + (theCalendar.get(Calendar.MILLISECOND) / 1000.0F);
425        setDateSecondPrecision(yr, mnth, dy, hr, min, sec);
426        
427        // 3410095: care for integer overflow and timezones not at the full hour, e.g. India
428        int timeZoneOffset = theCalendar.get(Calendar.ZONE_OFFSET) + theCalendar.get(Calendar.DST_OFFSET);
429        int hourOffset= timeZoneOffset / (1000 * 60 * 60);   
430        int minuteOffset = (timeZoneOffset / (1000 * 60)) % 60;
431        int zoneOffset = hourOffset * 100 + minuteOffset;
432        setOffset(zoneOffset);
433    }
434
435    /**
436     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
437     * 
438     * Note: Sets fields using precision up to the millisecond, and sets the timezone offset to
439     * the current system offset
440     * 
441     * @param theDate The calendar object from which to retrieve values 
442     * @since 1.1 
443     */
444        public void setValue(Date theDate) throws DataTypeException {
445                if (theDate == null) {
446                        setValue((String)null);
447                        return;
448                }
449
450                GregorianCalendar cal = new GregorianCalendar();
451                cal.setTime(theDate);
452                setValue(cal);
453        }
454
455    /**
456     * This method takes in a string HL7 Time Stamp value and performs validations.
457     * The stored value will be in the following
458     * format YYYY[MM[DD[HHMM[SS[.S[S[S[S]]]]]]]][+/-ZZZZ].
459     * Note: Trailing zeros supplied in the time value (HHMM[SS[.S[S[S[S]]]]]])
460     * and GMT offset ([+/-ZZZZ]) will be preserved.
461     * Note: If the GMT offset is not supplied then the local
462     * time zone (using standard time zone format which is not modified for daylight savings)
463     * will be stored as a default. Passing in <code>null</code> clears any existing value.
464     */
465    public void setValue(String val) throws DataTypeException {
466        if (val != null && !val.equals("") && !val.equals("\"\"")) {
467            try {
468                //check the length of the input value, ensure that it is no less than
469                //8 characters in length
470                if (val.length() < 4) {
471                    String msg = "The length of the TS datatype value must be at least 4 characters in length.";
472                    DataTypeException e = new DataTypeException(msg);
473                    throw e;
474                }
475
476                //check the length of the input value, ensure that it is not greater
477                //than 24 characters in length
478                if (val.length() > 24) {
479                    String msg = "The length of the TS datatype value must not be more than 24 characters in length.";
480                    DataTypeException e = new DataTypeException(msg);
481                    throw e;
482                }
483
484                //at this point we know that we have a value that should conform to the DT
485                //datatype and possibly a value that should conform to the TM datatype
486                String dateVal = null;
487                String timeVal = null;
488                String timeValLessOffset = null;
489                int sp = val.indexOf("+");
490                int sm = val.indexOf("-");
491                int indexOfSign = -1;
492                boolean offsetExists = false;
493                boolean timeValIsOffsetOnly = false;
494                if ((sp != -1) || (sm != -1)) {
495                    offsetExists = true;
496                }
497                if (sp != -1)
498                    indexOfSign = sp;
499                if (sm != -1)
500                    indexOfSign = sm;
501
502                if (!offsetExists) {
503                    if (val.length() <= 8) {
504                        dateVal = val;
505                    }
506                    else {
507                        //here we know that a time value is present
508                        dateVal = val.substring(0, 8);
509                        timeVal = val.substring(8);
510                        timeValLessOffset = timeVal;
511                    }
512                } //offset not exist
513
514                if (offsetExists) {
515                    if (indexOfSign > 8) {
516                        dateVal = val.substring(0, 8);
517                        timeVal = val.substring(8);
518                        timeValLessOffset = val.substring(8, indexOfSign);
519                    }
520                    else {
521                        //we know that the time val is simply the offset
522                        dateVal = val.substring(0, indexOfSign);
523                        timeVal = val.substring(indexOfSign);
524                        timeValIsOffsetOnly = true;
525                    }
526                } //offset exists
527
528                //create date object
529                dt = new CommonDT();
530                //set the value of the date object to the input date value
531                dt.setValue(dateVal);
532                //if the offset does not exist and a timevalue does not exist then
533                //we must provide a default offset = to the local time zone
534                if (timeVal == null && !offsetExists) {
535//                    int defaultOffset = DataTypeUtil.getLocalGMTOffset();
536                    tm = new CommonTM();
537                    //tm.setOffset(defaultOffset);
538                    tm.setValue("");
539                } //end if
540
541                //if we have a time value then make a new time object and set it to the
542                //input time value (as long as the time val has time + offset or just time only)
543                if (timeVal != null && !timeValIsOffsetOnly) {
544                    // must make sure that the time component contains both hours 
545                    // at the very least -- must be at least 2 chars in length.
546                        // Note: this changed as of v2.5, before hours AND minutes were required.
547                    if (timeValLessOffset.length() < 2) {
548                        String msg =
549                            "The length of the time component for the TM datatype"
550                                + " value does not conform to the allowable format"
551                                + " YYYY[MM[DD[HH[MM[SS[.S[S[S[S]]]]]]]]][+/-ZZZZ].";
552                        DataTypeException e = new DataTypeException(msg);
553                        throw e;
554                    } //end if
555                    tm = new CommonTM();
556                    tm.setValue(timeVal);
557                } //end if
558
559                //if we have a time value and it only has the offset then make a new
560                //time object and set the offset value to the input value
561                if (timeVal != null && timeValIsOffsetOnly) {
562                    //we know that the time value is just the offset so we
563                    //must check to see if it is the right length before setting the
564                    //offset field in the tm object
565                    if (timeVal.length() != 5) {
566                        String msg =
567                            "The length of the GMT offset for the TM datatype value does"
568                                + " not conform to the allowable format [+/-ZZZZ]";
569                        DataTypeException e = new DataTypeException(msg);
570                        throw e;
571                    } //end if 
572                    tm = new CommonTM();
573                    //first extract the + sign from the offset value string if it exists
574                    if (timeVal.indexOf("+") == 0) {
575                        timeVal = timeVal.substring(1);
576                    } //end if
577                    int signedOffset = Integer.parseInt(timeVal);
578                    tm.setOffset(signedOffset);
579                } //end if
580            } //end try
581
582            catch (DataTypeException e) {
583                throw e;
584            } //end catch
585
586            catch (Exception e) {
587                throw new DataTypeException(e);
588            } //end catch
589        } //end if
590        else {
591            //set the private value field to null or empty space.
592            if (val == null) {
593                dt = null;
594                tm = null;
595            } //end if
596            if (val != null && val.equals("")) {
597                dt = new CommonDT();
598                dt.setValue("");
599                tm = new CommonTM();
600                tm.setValue("");
601            } //end if
602            if (val != null && val.equals("\"\"")) {
603                dt = new CommonDT();
604                dt.setValue("\"\"");
605                tm = new CommonTM();
606                tm.setValue("\"\"");
607            } //end if
608        } //end else    
609
610    } // end method
611
612    /**
613     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
614     * 
615     * Note: Sets fields using precision up to the minute
616     * 
617     * @param theCalendar The calendar object from which to retrieve values 
618     * @since 1.1 
619     */
620    public void setValueToMinute(Calendar theCalendar) throws DataTypeException {
621                if (theCalendar == null) {
622                        setValue((String)null);
623                        return;
624                }
625
626        int yr = theCalendar.get(Calendar.YEAR);
627        int mnth = theCalendar.get(Calendar.MONTH) + 1;
628        int dy = theCalendar.get(Calendar.DATE);
629        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
630        int min = theCalendar.get(Calendar.MINUTE);
631        setDateMinutePrecision(yr, mnth, dy, hr, min);
632        
633    }
634
635    /**
636     * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
637     * 
638     * Note: Sets fields using precision up to the minute
639     * 
640     * @param theDate The date object from which to retrieve values
641     * @since 1.1 
642     */
643    public void setValueToMinute(Date theDate) throws DataTypeException {
644                if (theDate == null) {
645                        setValue((String)null);
646                        return;
647                }
648
649                Calendar calendar = Calendar.getInstance();
650        calendar.setTime(theDate);
651        setValueToMinute(calendar);
652    }
653
654    /**
655     * Convenience setter which sets the value using a {@link Calendar} object. Passing in <code>null</code> clears any existing value.
656     * 
657     * Note: Sets fields using precision up to the second
658     * 
659     * @param theCalendar The calendar object from which to retrieve values 
660     * @since 1.1 
661     */
662    public void setValueToSecond(Calendar theCalendar) throws DataTypeException {
663                if (theCalendar == null) {
664                        setValue((String)null);
665                        return;
666                }
667
668        int yr = theCalendar.get(Calendar.YEAR);
669        int mnth = theCalendar.get(Calendar.MONTH) + 1;
670        int dy = theCalendar.get(Calendar.DATE);
671        int hr = theCalendar.get(Calendar.HOUR_OF_DAY);
672        int min = theCalendar.get(Calendar.MINUTE);
673        int sec = theCalendar.get(Calendar.SECOND);
674        setDateSecondPrecision(yr, mnth, dy, hr, min, sec);
675    }
676
677    /**
678     * Convenience setter which sets the value using a {@link Date} object. Passing in <code>null</code> clears any existing value.
679     * 
680     * Note: Sets fields using precision up to the second
681     * 
682     * @param theDate The date object from which to retrieve values
683     * @since 1.1 
684     */
685    public void setValueToSecond(Date theDate) throws DataTypeException {
686                if (theDate == null) {
687                        setValue((String)null);
688                        return;
689                }
690
691                Calendar calendar = Calendar.getInstance();
692        calendar.setTime(theDate);
693        setValueToSecond(calendar);
694    }
695
696    /**
697     * Returns a string value representing the input Gregorian Calendar object in
698     * an Hl7 TimeStamp Format.
699     */
700    public static String toHl7TSFormat(GregorianCalendar cal) throws DataTypeException {
701        String val = "";
702        try {
703            //set the input cal object so that it can report errors
704            //on it's value
705            cal.setLenient(false);
706            int calYear = cal.get(GregorianCalendar.YEAR);
707            int calMonth = cal.get(GregorianCalendar.MONTH) + 1;
708            int calDay = cal.get(GregorianCalendar.DAY_OF_MONTH);
709            int calHour = cal.get(GregorianCalendar.HOUR_OF_DAY);
710            int calMin = cal.get(GregorianCalendar.MINUTE);
711            int calSec = cal.get(GregorianCalendar.SECOND);
712            int calMilli = cal.get(GregorianCalendar.MILLISECOND);
713            //the inputs seconds and milli seconds should be combined into a float type
714            float fractSec = calMilli / 1000F;
715            float calSecFloat = calSec + fractSec;
716            int calOffset = cal.get(GregorianCalendar.ZONE_OFFSET) + cal.get(GregorianCalendar.DST_OFFSET);
717            //Note the input's Offset value is in milliseconds, we must convert it to
718            //a 4 digit integer in the HL7 Offset format.
719            int offSetSignInt;
720            if (calOffset < 0) {
721                offSetSignInt = -1;
722            }
723            else {
724                offSetSignInt = 1;
725            }
726            //get the absolute value of the gmtOffSet
727            int absGmtOffSet = Math.abs(calOffset);
728            int gmtOffSetHours = absGmtOffSet / (3600 * 1000);
729            int gmtOffSetMin = (absGmtOffSet / 60000) % (60);
730            //reset calOffset
731            calOffset = ((gmtOffSetHours * 100) + gmtOffSetMin) * offSetSignInt;
732            //Create an object of the TS class and populate it with the above values
733            //then return the HL7 string value from the object
734            CommonTS ts = new CommonTS();
735            ts.setDateSecondPrecision(calYear, calMonth, calDay, calHour, calMin, calSecFloat);
736            ts.setOffset(calOffset);
737            val = ts.getValue();
738        } // end try
739
740        catch (DataTypeException e) {
741            throw e;
742        } //end catch
743
744        catch (Exception e) {
745            throw new DataTypeException(e);
746        } //end catch
747        return val;
748    } //end method
749
750    
751    public static void main(String[] args) throws DataTypeException {
752        
753        CommonTS ts = new CommonTS();
754        ts.setValue("1984");
755        
756        System.out.println(ts.getValue());
757        
758    }
759    
760    
761} //end class