/** * The contents of this file are subject to the OpenMRS Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.hl7; import java.io.File; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.api.APIException; import org.openmrs.api.context.Context; import org.openmrs.util.OpenmrsConstants; import org.openmrs.util.OpenmrsUtil; import ca.uhn.hl7v2.HL7Exception; /** * HL7-related utilities * * @version 1.0 */ public class HL7Util { private static Log log = LogFactory.getLog(HL7Util.class); // Date and time format parsers private final static DateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss.SSSZ"); private final static DateFormat TIME_FORMAT = new SimpleDateFormat("HHmmss.SSSZ"); public final static String LOCAL_TIMEZONE_OFFSET = new SimpleDateFormat("Z").format(new Date()); /** * Converts an HL7 timestamp into a java.util.Date object. HL7 timestamps can be created with * varying levels of precision — e.g., just the year or just the year and month, etc. * Since java.util.Date cannot store a partial value, we fill in defaults like January, 01 at * midnight within the current timezone. * * @param s HL7 timestamp to be parsed * @return Date object * @throws HL7Exception * @should fail on 78 * @should handle 1978 * @should fail on 19784 * @should handle 197804 * @should fail on 197841 * @should handle 19780411 * @should fail on 197804116 * @should handle 1978041106 * @should fail on 19780411065 * @should handle 197804110615 * @should fail on 1978041106153 * @should handle 19780411061538 * @should handle 19780411061538.1 * @should handle 19780411061538.12 * @should handle 19780411061538.123 * @should handle 19780411061538.1234 * @should fail on 197804110615-5 * @should handle 197804110615-05 * @should handle 197804110615-0200 * @should not flub dst with 20091225123000 */ public static Date parseHL7Timestamp(String s) throws HL7Exception { // HL7 dates must at least contain year and cannot exceed 24 bytes if (s == null || s.length() < 4 || s.length() > 24) throw new HL7Exception("Invalid date '" + s + "'"); StringBuffer dateString = new StringBuffer(); dateString.append(s.substring(0, 4)); // year if (s.length() >= 6) dateString.append(s.substring(4, 6)); // month else dateString.append("01"); if (s.length() >= 8) { dateString.append(s.substring(6, 8)); //day } else dateString.append("01"); // Parse timezone (optional in HL7 format) String timeZoneOffset; try { Date parsedDay = new SimpleDateFormat("yyyyMMdd").parse(s.substring(0, 8)); timeZoneOffset = getTimeZoneOffset(s, parsedDay); } catch (ParseException e) { throw new HL7Exception("Error parsing date: '" + s.substring(0, 8) + "' for time zone offset'" + s + "'", e); } s = s.replace(timeZoneOffset, ""); // remove the timezone from the string if (s.length() >= 10) dateString.append(s.substring(8, 10)); // hour else dateString.append("00"); if (s.length() >= 12) dateString.append(s.substring(10, 12)); // minute else dateString.append("00"); if (s.length() >= 14) dateString.append(s.substring(12, 14)); // seconds else dateString.append("00"); if (s.length() >= 15 && s.charAt(14) != '.') // decimal point throw new HL7Exception("Invalid date format '" + s + "'"); else dateString.append("."); if (s.length() >= 16) dateString.append(s.substring(15, 16)); // tenths else dateString.append("0"); if (s.length() >= 17) dateString.append(s.substring(16, 17)); // hundredths else dateString.append("0"); if (s.length() >= 18) dateString.append(s.subSequence(17, 18)); // milliseconds else dateString.append("0"); dateString.append(timeZoneOffset); Date date; try { date = TIMESTAMP_FORMAT.parse(dateString.toString()); } catch (ParseException e) { throw new HL7Exception("Error parsing date '" + s + "'"); } return date; } /** * Gets the timezone string for this given fullString. If fullString contains a + or - sign, the * strings after those are considered to be the timezone. <br/> * <br/> * If the fullString does not contain a timezone, the timezone is determined from the server's * timezone on the "givenDate". (givenDate is needed to account for daylight savings time.) * * @param fullString the hl7 string being parsed * @param givenDate the date that should be used if no timezone exists on the fullString * @return a string like +0500 or -0500 for the timezone * @should return timezone string if exists in given string * @should return timezone for givenDate and not the current date */ protected static String getTimeZoneOffset(String fullString, Date givenDate) { // Parse timezone (optional in HL7 format) String timeZoneOffset; int tzPlus = fullString.indexOf('+'); int tzMinus = fullString.indexOf('-'); boolean timeZoneFlag = (tzPlus > 0 || tzMinus > 0); if (timeZoneFlag) { int tzIndex; if (tzPlus > 0) tzIndex = tzPlus; else tzIndex = tzMinus; timeZoneOffset = fullString.substring(tzIndex); if (timeZoneOffset.length() != 5) log.error("Invalid timestamp because its too short: " + timeZoneOffset); } else { //set default timezone offset from the current day Calendar cal = Calendar.getInstance(); cal.setTime(givenDate); timeZoneOffset = new SimpleDateFormat("Z").format(cal.getTime()); } return timeZoneOffset; } /** * Convenience method for parsing HL7 dates (treated just like a timestamp with only year, * month, and day specified) * * @see org.openmrs.hl7.HL7Util#parseHL7Timestamp(String) * @throws HL7Exception */ public static Date parseHL7Date(String s) throws HL7Exception { return parseHL7Timestamp(s); } /** * Converts an HL7 time into a java.util.Date object. Since the java.util.Date object cannot * store just the time, the date will remain at the epoch (e.g., January 1, 1970). Time more * precise than microseconds is ignored. * * @param s HL7 time to be converted * @return Date object set to time specified by HL7 * @throws HL7Exception * @should fail on 197804110615 * @should handle 0615 * @should handle 061538 * @should handle 061538.1 * @should handle 061538.12 * @should handle 061538.123 * @should handle 061538.1234 * @should handle 061538-0300 */ public static Date parseHL7Time(String s) throws HL7Exception { String timeZoneOffset = getTimeZoneOffset(s, new Date()); s = s.replace(timeZoneOffset, ""); // remove the timezone from the string StringBuffer timeString = new StringBuffer(); if (s.length() < 2 || s.length() > 16) throw new HL7Exception("Invalid time format '" + s + "'"); timeString.append(s.substring(0, 2)); // hour if (s.length() >= 4) timeString.append(s.substring(2, 4)); // minute else timeString.append("00"); if (s.length() >= 6) timeString.append(s.substring(4, 6)); // seconds else timeString.append("00"); if (s.length() >= 7 && s.charAt(6) != '.') // decimal point throw new HL7Exception("Invalid time format '" + s + "'"); else timeString.append("."); if (s.length() >= 8) timeString.append(s.substring(7, 8)); // tenths else timeString.append("0"); if (s.length() >= 9) timeString.append(s.substring(8, 9)); // hundredths else timeString.append("0"); if (s.length() >= 10) timeString.append(s.subSequence(9, 10)); // milliseconds else timeString.append("0"); // Parse timezone (optional in HL7 format) timeString.append(timeZoneOffset); Date date; try { date = (Date) TIME_FORMAT.parse(timeString.toString()); } catch (ParseException e) { throw new HL7Exception("Invalid time format: '" + s + "' [" + timeString + "]", e); } return date; } /** * Gets the destination directory for hl7 archives. * * @return The destination directory for the hl7 in archive */ public static File getHl7ArchivesDirectory() throws APIException { return OpenmrsUtil.getDirectoryInApplicationDataDirectory(Context.getAdministrationService().getGlobalProperty( OpenmrsConstants.GLOBAL_PROPERTY_HL7_ARCHIVE_DIRECTORY)); } }