package com.tyndalehouse.step.dataloader.beans; import java.text.ParseException; import java.util.Calendar; import java.util.GregorianCalendar; import org.apache.commons.lang.StringUtils; import com.tyndalehouse.step.dataloader.common.DateParsingException; /** * This class is the way dates are represented in the databased * and they should be parsed back into this object on their way out! * * The date field indicates when the event (or start of the event) took * place, the precision type indicates whether how much of the date * can be trusted... * * This means we can store dates such as (01/03/1900, MONTH), meaning * March 1900 (and not 1st March 1900). * @author CJBurrell */ public class PartialDate { /** * The date to be represented (whether fully accurate or not) */ private final Calendar c; /** * The precision specifier which tells us just quite * how accurate the date is (year, month, day) * @see com.tyndalehouse.step.dataloader.beans.PrecisionType */ private final PrecisionType precision; /** * Public constructor to give us a partial date. * @param c date partial reprentation of a date * @param precision precision indicating how much of the * date can be trusted day/month/year or month/year or just year */ public PartialDate(final Calendar c, final PrecisionType precision) { this.c = c; this.precision = precision; } /** * Date is specified in yy-mm-dd or yyyy-mm-dd and gets parsed in to a date. * the mm and dd are optional which is what determines the precision of the date. * @param date date to be parsed as a string * @param delimiter delimiter in the date, say a - or a / * @return a PartialDate * @throws ParseException an error during the parsing of the date */ public static PartialDate parseDate(final String date, char delimiter) throws DateParsingException { String[] parts; Calendar c = Calendar.getInstance(); PrecisionType p; int yearSign = 1; // -1 for negative //if passed in empty, return null and be done with empty strings! if(StringUtils.isEmpty(date)) { return new PartialDate(null, PrecisionType.NONE); } //the date might start with a dash, so best not to split and do it character by character? if(date.charAt(0) == '-') { yearSign = -1; //check we have more than one character left after substring: if(date.length() < 2) { throw new DateParsingException("The date " + date + " was not long enough."); } //split the remainder of the date into parts parts = date.substring(1).trim().split("" + delimiter); // ignore first character } else { parts = date.trim().split(""+delimiter); } try { //length of field determines how much of the date has been specified switch(parts.length) { case 0: throw new DateParsingException("The date " + date + " could not be parsed."); case 1: //only the year is specified, so use 1st of Jan Year c.set(Integer.parseInt(parts[0]), 1, 1); p = PrecisionType.YEAR; if(yearSign == -1) { c.set(Calendar.ERA, GregorianCalendar.BC); } c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); break; case 2: c.set(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), 1); p = PrecisionType.MONTH; break; case 3: c.set(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); p = PrecisionType.DAY; break; default: throw new DateParsingException("Too many parts to the date: " + date); } } catch(NumberFormatException nfe) { throw new DateParsingException("Could not parse date into year, month or day."); } c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); if(yearSign == -1) { c.set(Calendar.ERA, GregorianCalendar.BC); } return new PartialDate(c, p); } public Calendar getDate() { return c; } public PrecisionType getPrecision() { return precision; } }