/******************************************************************************* | |
* Crown Copyright (c) 2006, 2007, Copyright (c) 2006, 2007 Jiva Medical. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Jiva Medical - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.uomo.util.impl; | |
import java.util.Calendar; | |
import java.util.Date; | |
import java.util.GregorianCalendar; | |
import java.util.TimeZone; | |
import org.apache.commons.lang.StringUtils; | |
import org.eclipse.osgi.util.NLS; | |
import org.eclipse.uomo.core.UOMoException; | |
import org.eclipse.uomo.util.internal.Messages; | |
/** | |
* ISO 8601 Dates | |
* | |
* HL7 uses a date format which is conforms to the constrained ISO 8601 that is | |
* defined in ISO 8824 (ASN.1) under clause 32 (generalized time). | |
* | |
* Because of the ubiquity of HL7 interfaces, and the general utility of this | |
* time format representation, it's encountered fairly often in healthcare | |
* | |
* This Date class represents the notion of a date time that may have years, | |
* months, days, hours, minutes, seconds, milliseconds and timezones specified. | |
* | |
* There is three ways to interact with this date - read and write the component | |
* fields directly - read and write the string form using parse() and render() - | |
* get and set the time as a java Date object using getAsDate and setAsDate | |
* | |
* It's possible to set the component fields to non-sensical values. Use | |
* validate() to check whether the values are okay. | |
* | |
* Note about parsing: | |
* | |
* There is several entry points to the parser. The simplest takes only the | |
* actual string to parse. If the string is a proper value according to the | |
* specifications referenced above, it will be read in. | |
* | |
* In addition, there is another entry point that allows the caller to specify a | |
* mask that describes what parts of the date are required, and what is allowed, | |
* Consult the ISO_DATE_VALIDATION_FULL constant for an example of how to use | |
* the mask. In addition, the caller can specify what to do with the timezone | |
* | |
* Finally, there is a static routine that takes the same parameters and returns | |
* a date or throws an exception | |
* | |
*/ | |
public class Iso8601Date { | |
// public constants FIXME change to enum values | |
/** | |
* timezone information will be ignored | |
*/ | |
public static final int IGNORE = 0; | |
/** | |
* timezone is required | |
*/ | |
public static final int REQUIRED = 1; | |
/** | |
* timezone is optional | |
*/ | |
public static final int OPTIONAL = 2; | |
/** | |
* timezone is prohibited | |
*/ | |
public static final int PROHIBITED = 3; | |
/** | |
* parsing: timezone information will be cleared | |
*/ | |
public static final int CLEAR = 4; | |
/** | |
* timezone information will be set to local value | |
*/ | |
public static final int SETLOCAL = 5; | |
/** | |
* use timezone information if specified | |
*/ | |
public static final int IF_PRESENT = 6; | |
/** | |
* parsing mask for a fully optional date other than the year, which is | |
* required | |
*/ | |
public static final String ISO_DATE_VALIDATION_FULL = Messages.Iso8601Date_ISO_DATE_VALIDATION_FULL; | |
/** | |
* just the date, with optional month and day | |
*/ | |
public static final String ISO_DATE_VALIDATION_DATE = Messages.Iso8601Date_ISO_DATE_VALIDATION_DATE; | |
/** | |
* default format for writing | |
*/ | |
public static final String DEF_OUTPUT_FORMAT = Messages.Iso8601Date_DEF_OUTPUT_FORMAT; | |
// private fields | |
private int year; | |
private int month; | |
private int day; | |
private int hour; | |
private int minute; | |
private int second; | |
private int milli; | |
private int tzHour; | |
private int tzMinute; | |
private Boolean tzNegative = null; // null means no timezone specified | |
// administration | |
public Iso8601Date() { | |
super(); | |
reset(true); | |
} | |
private void reset(boolean timezoneToo) { | |
year = -1; | |
month = -1; | |
day = -1; | |
hour = -1; | |
minute = -1; | |
second = -1; | |
milli = -1; | |
if (timezoneToo) { | |
tzHour = 0; | |
tzMinute = 0; | |
tzNegative = null; | |
} | |
} | |
/** | |
* | |
* @return a string describing any errors with the component field values | |
*/ | |
public String validate(boolean checkYearReasonable) { | |
StringBuffer s = new StringBuffer(); | |
// TODO use Units where possible | |
boolean content = false; | |
content = checkField(s, milli, content, 0, 999, | |
Messages.Iso8601Date_MILIS); | |
content = checkField(s, second, content, 0, 59, | |
Messages.Iso8601Date_SECONDS); | |
content = checkField(s, minute, content, 0, 59, | |
Messages.Iso8601Date_MINUTES); | |
content = checkField(s, hour, content, 0, 23, | |
Messages.Iso8601Date_HOURS); | |
content = checkField(s, day, content, 1, 31, Messages.Iso8601Date_DAYS); | |
content = checkField(s, month, content, 1, 12, | |
Messages.Iso8601Date_MONTHS); | |
if (checkYearReasonable) | |
content = checkField(s, year, content, 1000, 2500, | |
Messages.Iso8601Date_YEARS); | |
else | |
content = checkField(s, year, content, Integer.MIN_VALUE, | |
Integer.MAX_VALUE, "years"); //$NON-NLS-1$ | |
if (!content) | |
s.append(Messages.Iso8601Date_NO_VALUES_SET); | |
if (year != -1 && month != -1 && day != -1 | |
&& day > daysForMonth(month, year)) | |
s.append(Messages.Iso8601Date_THE_DAY | |
+ Integer.toString(day) | |
+ " is not valid for the month " + Integer.toString(month) + " in the year " + Integer.toString(year) + "\r\n"); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ | |
if (tzNegative != null) { | |
content = checkField(s, tzMinute, content, 0, 59, | |
Messages.Iso8601Date_TZ_MINUTES); | |
content = checkField(s, tzHour, content, 0, 13, | |
Messages.Iso8601Date_TZ_HOURS); | |
} | |
if (s.length() > 0) | |
return s.toString(); | |
else | |
return null; | |
}; | |
private boolean checkField(StringBuffer s, int value, boolean required, | |
int min, int max, String desc) { | |
if (value == -1) { | |
if (required) | |
s.append(Messages.Iso8601Date_VALUE_MISSING + desc | |
+ " is missing and required in this context"); //$NON-NLS-2$ | |
return required; | |
} else { | |
if (value < min || value > max) | |
s.append(Messages.Iso8601Date_VALUE_INVALID | |
+ desc | |
+ " is invalid (is " + Integer.toString(value) + " should be " + Integer.toString(min) + " - " + Integer.toString(max) + ")"); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ | |
return true; | |
} | |
} | |
// component field access | |
/** | |
* @return the year. -1 means not set | |
*/ | |
public int getYear() { | |
return year; | |
} | |
/** | |
* @param year | |
* the year to set. -1 means not set | |
*/ | |
public void setYear(int year) { | |
this.year = year; | |
} | |
/** | |
* @return the month. -1 means not set | |
*/ | |
public int getMonth() { | |
return month; | |
} | |
/** | |
* @param month | |
* the month to set. -1 means not set | |
*/ | |
public void setMonth(int month) { | |
this.month = month; | |
} | |
/** | |
* @return the day. -1 means not set | |
*/ | |
public int getDay() { | |
return day; | |
} | |
/** | |
* @param day | |
* the day to set. -1 means not set | |
*/ | |
public void setDay(int day) { | |
this.day = day; | |
} | |
/** | |
* @return the hour. -1 means not set | |
*/ | |
public int getHour() { | |
return hour; | |
} | |
/** | |
* @param hour | |
* the hour to set. -1 means not set | |
*/ | |
public void setHour(int hour) { | |
this.hour = hour; | |
} | |
/** | |
* @return the minute. -1 means not set | |
*/ | |
public int getMinute() { | |
return minute; | |
} | |
/** | |
* @param minute | |
* the minute to set. -1 means not set | |
*/ | |
public void setMinute(int minute) { | |
this.minute = minute; | |
} | |
/** | |
* @return the second. -1 means not set | |
*/ | |
public int getSecond() { | |
return second; | |
} | |
/** | |
* @param second | |
* the second to set. -1 means not set | |
*/ | |
public void setSecond(int second) { | |
this.second = second; | |
} | |
/** | |
* @return the milliseconds. -1 means not set | |
*/ | |
public int getMilli() { | |
return milli; | |
} | |
/** | |
* @param milli | |
* the milliseconds to set. -1 means not set | |
*/ | |
public void setMilli(int milli) { | |
this.milli = milli; | |
} | |
/** | |
* @return the timezone Hour. use tzNegative to see whether timezone is set | |
*/ | |
public int getTzHour() { | |
return tzHour; | |
} | |
/** | |
* @param tzHour | |
* the timezone Hour to set. use tzNegative to see whether | |
* timezone is set | |
*/ | |
public void setTzHour(int tzHour) { | |
this.tzHour = tzHour; | |
} | |
/** | |
* @return the timezone Minute. use tzNegative to see whether timezone is | |
* set | |
*/ | |
public int getTzMinute() { | |
return tzMinute; | |
} | |
/** | |
* @param tzMinute | |
* the timezone Minute to set. use tzNegative to see whether | |
* timezone is set | |
*/ | |
public void setTzMinute(int tzMinute) { | |
this.tzMinute = tzMinute; | |
} | |
/** | |
* @return the tzNegative - true if the timezone is negative, false it it's | |
* positive, and null if there's no timezone info | |
*/ | |
public Boolean getTzNegative() { | |
return tzNegative; | |
} | |
/** | |
* @param tzNegative | |
* the tzNegative to set - true if the timezone is negative, | |
* false it it's positive, and null if there's no timezone info | |
*/ | |
public void setTzNegative(Boolean tzNegative) { | |
this.tzNegative = tzNegative; | |
} | |
// Utilities | |
public Iso8601Date correctForTZ() { | |
Iso8601Date result = new Iso8601Date(); | |
result.year = year; | |
result.month = month; | |
result.day = day; | |
result.hour = hour; | |
result.minute = minute; | |
result.second = second; | |
result.milli = milli; | |
if (tzNegative != null) { | |
if (tzNegative.booleanValue()) { | |
result.addMin(-(tzHour * 60 + tzMinute)); | |
} else { | |
result.addMin((tzHour * 60 + tzMinute)); | |
} | |
} | |
return result; | |
} | |
// TODO: remove if nolonger used | |
// private void addSec(int i) { | |
// if (i != 0) { | |
// if (second == -1) | |
// addMin(i / 60); | |
// else { | |
// second = second + i; | |
// int min = 0; | |
// while (second < 0) { | |
// second = second + 60; | |
// min = min - 1; | |
// } | |
// while (second >= 60) { | |
// second = second - 60; | |
// min = min + 1; | |
// } | |
// addMin(min); | |
// } | |
// } | |
// } | |
private void addMin(int i) { | |
if (i != 0) { | |
if (minute == -1) | |
addHour(i / 60); | |
else { | |
minute = minute + i; | |
int hr = 0; | |
while (minute < 0) { | |
minute = minute + 60; | |
hr = hr - 1; | |
} | |
while (minute >= 60) { | |
minute = minute - 60; | |
hr = hr + 1; | |
} | |
addHour(hr); | |
} | |
} | |
} | |
private void addHour(int i) { | |
if (i != 0) { | |
if (hour == -1) | |
addDay(i / 24); | |
else { | |
hour = hour + i; | |
int d = 0; | |
while (hour < 0) { | |
hour = hour + 24; | |
d = d - 1; | |
} | |
while (hour >= 24) { | |
hour = hour - 24; | |
d = d + 1; | |
} | |
addDay(d); | |
} | |
} | |
} | |
private void addDay(int i) { | |
if (i != 0 && day != -1) { | |
day = day + i; | |
while (day < 1 || day > daysForMonth(month, year)) { | |
if (day < 1) { | |
day = day + daysForMonth(month, year); | |
month = month - 1; | |
if (month == 0) { | |
month = 12; | |
year = year - 1; | |
} | |
} | |
if (day > daysForMonth(month, year)) { | |
month = month + 1; | |
if (month == 13) { | |
month = 1; | |
year = year + 1; | |
} | |
day = day - daysForMonth(month, year); | |
} | |
} | |
} | |
} | |
// Date access | |
/** | |
* get the date specified as a Date. | |
* | |
* @param timezone | |
* - REQUIRED, IF_PRESENT or IGNORE | |
* @throws OHFException | |
*/ | |
public Date getAsDate(int timezone) throws UOMoException { | |
GregorianCalendar cal = new GregorianCalendar(); | |
cal.clear(); | |
if (year != -1) | |
cal.set(Calendar.YEAR, year); | |
if (month != -1) | |
cal.set(Calendar.MONTH, month - 1); // Calendar month start with 0 | |
if (day != -1) | |
cal.set(Calendar.DAY_OF_MONTH, day); | |
if (hour != -1) | |
cal.set(Calendar.HOUR_OF_DAY, hour); | |
if (minute != -1) | |
cal.set(Calendar.MINUTE, minute); | |
if (second != -1) | |
cal.set(Calendar.SECOND, second); | |
if (milli != -1) | |
cal.set(Calendar.MILLISECOND, milli); | |
if (timezone == REQUIRED || timezone == IF_PRESENT | |
|| timezone == OPTIONAL) { | |
if (timezone == REQUIRED && tzNegative == null) | |
throw new UOMoException(Messages.Iso8601Date_TZ_NOT_DEFINED); | |
if (tzNegative != null) { | |
TimeZone tzMsg = TimeZone.getTimeZone(Messages.Iso8601Date_GMT | |
+ prepTimezone()); | |
cal.setTimeZone(tzMsg); | |
} | |
} | |
return cal.getTime(); | |
} | |
/** | |
* | |
* @param date | |
* the date to set the component fields to | |
* @param timezone | |
* - what to do about timezone (CLEAR, SETLOCAL or IGNORE) | |
*/ | |
public void setAsDate(Date date, int timezone) { | |
GregorianCalendar cal = new GregorianCalendar(); | |
cal.setTime(date); | |
reset(timezone != IGNORE); | |
year = cal.get(Calendar.YEAR); | |
month = cal.get(Calendar.MONTH) + 1; // java Month starts from 0, but | |
// ours starts from 1 | |
day = cal.get(Calendar.DAY_OF_MONTH); | |
hour = cal.get(Calendar.HOUR_OF_DAY); | |
minute = cal.get(Calendar.MINUTE); | |
second = cal.get(Calendar.SECOND); | |
milli = cal.get(Calendar.MILLISECOND); | |
if (timezone == CLEAR) { | |
tzHour = 0; | |
tzMinute = 0; | |
tzNegative = null; | |
} else if (timezone == SETLOCAL) { | |
TimeZone tzLcl = TimeZone.getDefault(); | |
int offset = tzLcl.getOffset(date.getTime()); | |
if (offset < 0) { | |
tzNegative = new Boolean(true); | |
offset = -offset; | |
} else { | |
tzNegative = new Boolean(true); | |
} | |
tzHour = offset / (1000 * 60 * 60); | |
tzMinute = (offset - (tzHour * 1000 * 60 * 60)) / (1000 * 60); | |
} | |
} | |
// String format | |
/** | |
* get the date as a formatted string | |
* | |
* @param format | |
* - the format to use. Must be consistent with the components | |
* provided. Will be cross-checked | |
* @param timezone | |
* - what to do about timezone (REQUIRED, IF_PRESENT, PROHIBITED, | |
* IGNORE) | |
* @return | |
* @throws OHFException | |
* if cross check fails | |
*/ | |
public String render(String format, boolean checkValues, int timezone) | |
throws UOMoException { | |
String v = validate(false); | |
if (v != null) | |
throw new UOMoException(v); | |
StringBuffer str = new StringBuffer(); | |
render(str, | |
checkValues, | |
format.length() >= 4 | |
&& Messages.Iso8601Date_yyyy.equalsIgnoreCase(format | |
.substring(0, 4)), 4, year, format, | |
Messages.Iso8601Date_year, null); | |
render(str, | |
checkValues, | |
format.length() >= 6 | |
&& Messages.Iso8601Date_mm.equalsIgnoreCase(format | |
.substring(4, 6)), 2, month, format, | |
Messages.Iso8601Date_month, null); | |
render(str, | |
checkValues, | |
format.length() >= 8 | |
&& Messages.Iso8601Date_dd.equalsIgnoreCase(format | |
.substring(6, 8)), 2, day, format, | |
Messages.Iso8601Date_day, null); | |
render(str, | |
checkValues, | |
format.length() >= 10 | |
&& Messages.Iso8601Date_hh.equalsIgnoreCase(format | |
.substring(8, 10)), 2, hour, format, | |
Messages.Iso8601Date_hour, null); | |
render(str, | |
checkValues, | |
format.length() >= 12 | |
&& Messages.Iso8601Date_nn.equalsIgnoreCase(format | |
.substring(10, 12)), 2, minute, format, | |
Messages.Iso8601Date_minute, null); | |
render(str, | |
checkValues, | |
format.length() >= 14 | |
&& Messages.Iso8601Date_ss.equalsIgnoreCase(format | |
.substring(12, 14)), 2, second, format, | |
Messages.Iso8601Date_second, null); | |
if (format.length() >= 18 | |
&& Messages.Iso8601Date_sss.equalsIgnoreCase(format.substring( | |
15, 18))) { | |
render(str, checkValues, true, 3, milli, format, | |
Messages.Iso8601Date_milisecond, Messages.Iso8601Date_DOT); | |
} else if (format.length() == 17 | |
&& "ss".equalsIgnoreCase(format.substring(15, 17))) { //$NON-NLS-1$ | |
render(str, checkValues, true, 2, milli / 10, format, | |
"millisecond", Messages.Iso8601Date_DOT); //$NON-NLS-1$ | |
} else if (format.length() == 16 | |
&& Messages.Iso8601Date_s.equalsIgnoreCase(format.substring(15, | |
16))) { | |
render(str, checkValues, true, 1, milli / 100, format, | |
"millisecond", "."); //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
if (timezone == REQUIRED || timezone == IF_PRESENT) { | |
if (timezone == REQUIRED && tzNegative == null) | |
throw new UOMoException( | |
Messages.Iso8601Date_TZ_REQUIRED_BUT_NOT_DEFINED); | |
if (tzNegative != null) { | |
if (tzNegative.booleanValue()) | |
str.append('-'); | |
else | |
str.append('+'); | |
render(str, checkValues, true, 2, tzHour, format, | |
Messages.Iso8601Date_tz_hour, null); | |
render(str, checkValues, true, 2, tzMinute, format, | |
Messages.Iso8601Date_tz_minutes, null); | |
} | |
} | |
if (timezone == PROHIBITED && tzNegative != null) | |
throw new UOMoException( | |
Messages.Iso8601Date_TZ_PROHIBITED_BUT_DEFINED); | |
return str.toString(); | |
} | |
private void render(StringBuffer str, boolean checkValues, boolean render, | |
int len, int value, String format, String desc, String prefix) | |
throws UOMoException { | |
if (render) { | |
if (value != -1) { | |
if (prefix != null) | |
str.append(prefix); | |
str.append(StringUtils.leftPad(Integer.toString(value), len, | |
Messages.Iso8601Date_ZERO)); | |
} else if (checkValues) | |
throw new UOMoException( | |
Messages.Iso8601Date_NO_SUCH_VALUE_PROVIDED | |
+ format | |
+ "' specifies " + desc + " but no such value has been provided"); //$NON-NLS-2$ //$NON-NLS-3$ | |
} | |
} | |
/** | |
* Parse the value. no rules other than the base format rules | |
* | |
* @param value | |
* - the value to parse | |
*/ | |
public void parse(String value) { | |
parse(value, ISO_DATE_VALIDATION_FULL, OPTIONAL); | |
} | |
/** | |
* Parse the value. check that it conforms to the specified format and | |
* timezone rules | |
* | |
* @param value | |
* - the value to parse | |
* @param format | |
* - the format rules to adhere to | |
* @param timezone | |
* - rules for timezone (REQUIRED, OPTIONAL, PROHIBITED, IGNORE) | |
* @return - String description of parsing error, or null if no error | |
* encountered | |
*/ | |
public String parse(String value, String format, int timezone) { | |
reset(timezone != IGNORE); | |
String[] parts = { value }; | |
tzNegative = null; | |
if (value.indexOf('+') > 0) { | |
parts = StringUtils.split(value, "+", 2); //$NON-NLS-1$ | |
tzNegative = new Boolean(false); | |
} else if (value.indexOf('-') > 0) { | |
parts = StringUtils.split(value, "-", 2); //$NON-NLS-1$ | |
tzNegative = new Boolean(true); | |
} | |
String error = checkSections(parts[0], value, format); | |
if (error != null) | |
return error; | |
if (parts.length > 1 && timezone != IGNORE) | |
error = checkSections(parts[1], value, Messages.Iso8601Date_58); | |
if (error != null) | |
return error; | |
if (timezone == REQUIRED) { | |
if (parts.length < 2) | |
return Messages.Iso8601Date_59; | |
} else if (timezone == PROHIBITED && parts.length > 1) | |
return Messages.Iso8601Date_60; | |
return null; | |
} | |
/** | |
* Parse the value to a Date. check that it conforms to the specified format | |
* and timezone rules | |
* | |
* @param value | |
* - the value to parse | |
* @param format | |
* - the format rules to adhere to | |
* @param timezone | |
* - rules for timezone (REQUIRED, OPTIONAL, PROHIBITED, IGNORE) | |
* @return - Date (or throw exception if conversion not successful | |
*/ | |
public static Date parseToDate(String value, String format, int timezone) | |
throws UOMoException { | |
Iso8601Date d = new Iso8601Date(); | |
String err = d.parse(value, format, timezone); | |
if (err != null) | |
throw new UOMoException(Messages.Iso8601Date_61 + value | |
+ Messages.Iso8601Date_62 + err); | |
return d.getAsDate(timezone); | |
} | |
public static String renderFromDate(Date date, String format, int timezone) | |
throws UOMoException { | |
Iso8601Date d = new Iso8601Date(); | |
d.setAsDate(date, timezone == REQUIRED ? SETLOCAL : CLEAR); | |
return d.render(format, true, timezone); | |
} | |
// private parsing routines | |
private boolean inFraction; | |
private String checkSections(String content, String whole, String mask) { | |
String workingMask = StringUtils.strip(mask, Messages.Iso8601Date_63); | |
String[] parts = { "", workingMask }; //$NON-NLS-1$ | |
boolean first = true; | |
inFraction = false; | |
do { | |
parts = StringUtils.splitPreserveAllTokens(parts[1], | |
Messages.Iso8601Date_65, 2); | |
String token = parts[0]; | |
if (token != null) { // support use of [ at first point to make | |
// everything optional | |
String section = content == null | |
|| content.length() < token.length() ? null : content | |
.substring(0, token.length()); // sSection = | |
// copy(sContent, 1, | |
// length(sToken)); | |
if (section == null) { // if sSection = '' then | |
if (!first) { | |
if (content != null | |
&& content.length() < token.length()) | |
return Messages.Iso8601Date_66 + content | |
+ Messages.Iso8601Date_67 + token; | |
else | |
return Messages.Iso8601Date_68 + token | |
+ Messages.Iso8601Date_69 + mask | |
+ Messages.Iso8601Date_70 + whole; | |
} | |
} else if (section.length() < token.length()) { | |
return Messages.Iso8601Date_71 + token | |
+ Messages.Iso8601Date_72 + mask | |
+ Messages.Iso8601Date_73 + whole | |
+ Messages.Iso8601Date_74 + section; | |
} else { | |
String error = checkSection(token, section); | |
if (error != null) | |
return error; | |
else if (section.length() >= content.length()) | |
content = null; | |
else | |
content = content.substring(section.length()); | |
} | |
} | |
first = false; | |
} while (parts.length > 1 && content != null); // until not result or | |
// (sFormat = '') or | |
// (sContent = ''); | |
if (content != null) { | |
return Messages.Iso8601Date_75 + content + Messages.Iso8601Date_76 | |
+ whole + Messages.Iso8601Date_77 + mask; | |
} else | |
return null; | |
} | |
private boolean starts(String source, String test) { | |
return source == null || source.length() < test.length() ? false | |
: source.substring(0, test.length()).equals(test); | |
} | |
private String checkSection(String token, String content) { | |
String error = null; | |
if (starts(token, Messages.Iso8601Date_YYYY)) { | |
error = checkYear(content.substring(0, 4)); | |
if (error != null) | |
return error; | |
token = token.substring(4, token.length()); | |
content = content.substring(4, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_MM)) { | |
error = checkMonth(content.substring(0, 2)); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_DD)) { | |
error = checkDay(content.substring(0, 2)); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_HH)) { | |
error = checkHour(content.substring(0, 2), false); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_TT)) { | |
error = checkHour(content.substring(0, 2), true); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_NN)) { | |
error = checkMinute(content.substring(0, 2), false); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_tt)) { | |
error = checkMinute(content.substring(0, 2), true); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, Messages.Iso8601Date_SS) && !inFraction) { | |
error = checkSecond(content.substring(0, 2)); | |
if (error != null) | |
return error; | |
token = token.substring(2, token.length()); | |
content = content.substring(2, content.length()); | |
} | |
; | |
if (starts(token, ".") && !inFraction) { //$NON-NLS-1$ | |
error = checkDot(content.substring(0, 1)); | |
if (error != null) | |
return error; | |
token = token.substring(1, token.length()); | |
content = content.substring(1, content.length()); | |
} | |
; | |
while (starts(token, Messages.Iso8601Date_U) && inFraction) { | |
error = checkFraction(content.substring(0, 1)); | |
if (error != null) | |
return error; | |
token = token.substring(1, token.length()); | |
content = content.substring(1, content.length()); | |
} | |
; | |
if (!(token == null || token.equals(""))) //$NON-NLS-1$ | |
return Messages.Iso8601Date_DATE_FRAGMENT_NOT_KNOWN + token | |
+ " is not known"; //$NON-NLS-2$ | |
return null; | |
} | |
private String checkYear(String value) { | |
if (value.length() != 4) | |
return "Year Value " + value + " is not 4 digits in length"; //$NON-NLS-1$ //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_YEAR_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else { | |
year = Integer.parseInt(value); | |
if (year <= 0) | |
return Messages.Iso8601Date_YEAR_VALUE_NEGATIVE_NOT_SUPPORTED | |
+ value + ": negative numbers are not supported"; //$NON-NLS-2$ | |
else | |
return null; | |
} | |
} | |
private String checkMonth(String value) { | |
if (value.length() != 2) | |
return Messages.Iso8601Date_MONTH_VALUE_NOT_2_DIGITS + value | |
+ " is not 2 digits in length"; //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_MONTH_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else { | |
month = Integer.parseInt(value); | |
if (month <= 0 || month > 12) | |
return Messages.Iso8601Date_MONTH_VALUE_MUST_BE_1_12 + value | |
+ ": month must be 1 - 12"; //$NON-NLS-2$ | |
else | |
return null; | |
} | |
} | |
private String checkDay(String value) { | |
if (value.length() != 2) | |
return Messages.Iso8601Date_DAY_VALUE_NOT_2_DIGITS + value | |
+ " is not 2 digits in length"; //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_DAY_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else { | |
day = Integer.parseInt(value); | |
if (day <= 0) | |
return Messages.Iso8601Date_DAY_VALUE_MUST_BE_POSITIVE + value | |
+ ": Day must be >= 1"; //$NON-NLS-2$ | |
else if (month == 0) | |
return Messages.Iso8601Date_DAY_VALUE_MONTH_MUST_BE_KNOWN | |
+ value + ": Month must be known"; //$NON-NLS-2$ | |
else if (year == 0) | |
return Messages.Iso8601Date_DAY_VALUE_YEAR_MUST_BE_KNOWN | |
+ value + ": Year must be known"; //$NON-NLS-2$ | |
else if (day > daysForMonth(month, year)) | |
return Messages.Iso8601Date_DAY_VALUE_ILLEGAL_FOR_MONTH | |
+ value | |
+ ": is illegal for the month " + Integer.toString(month) + "-" + Integer.toString(year); //$NON-NLS-2$ //$NON-NLS-3$ | |
else | |
return null; | |
} | |
} | |
private int daysForMonth(int m, int y) { | |
int[] daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
if (m < 1 || m > 12) | |
return 30; | |
else if (m == 2 && new GregorianCalendar().isLeapYear(y)) | |
return 29; | |
else | |
return daysInMonth[m - 1]; | |
} | |
private String checkHour(String value, boolean inTimezone) { | |
if (value.length() != 2) | |
return Messages.Iso8601Date_HOUR_VALUE_NOT_2_DIGITS + value | |
+ " is not 2 digits in length"; //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_HOUR_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else if (inTimezone) { | |
tzHour = Integer.parseInt(value); | |
if (tzHour < 0) | |
return Messages.Iso8601Date_TZ_HOUR_VALUE_MUST_BE_0_OR_POSITIVE | |
+ value + ": Hour must be >= 0"; //$NON-NLS-2$ | |
else if (tzHour > 12) | |
return Messages.Iso8601Date_TZ_HOUR_VALUE_MUST_BE_12_OR_LESS | |
+ value + ": Hour must be <= 12"; //$NON-NLS-2$ | |
} else { | |
hour = Integer.parseInt(value); | |
if (hour < 0) | |
return Messages.Iso8601Date_HOUR_VALUE_MUST_BE_0_OR_POSITIVE | |
+ value + ": Hour must be >= 0"; //$NON-NLS-2$ | |
else if (inTimezone && hour > 12) | |
return Messages.Iso8601Date_HOUR_VALUE_MUST_BE_12_OR_LESS | |
+ value + ": Hour must be <= 12"; //$NON-NLS-2$ | |
else if (hour > 23) | |
return Messages.Iso8601Date_TZ_HOUR_VALUE_MUST_BE_23_OR_LESS | |
+ value + ": Hour must be <= 23"; //$NON-NLS-2$ | |
} | |
return null; | |
} | |
private String checkMinute(String value, boolean inTimezone) { | |
if (value.length() != 2) | |
return Messages.Iso8601Date_MINUTE_VALUE_NOT_2_DIGITS_LONG + value | |
+ " is not 2 digits in length"; //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_MINUTE_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else if (inTimezone) { | |
tzMinute = Integer.parseInt(value); | |
if (tzMinute != 0 && tzMinute != 30) | |
return Messages.Iso8601Date_TZ_MINUTE_VALUE_MUST_BE_0_OR_30 | |
+ value + ": Minute must be 0 or 30"; //$NON-NLS-2$ | |
} else { | |
minute = Integer.parseInt(value); | |
if (minute < 0 || minute > 59) | |
return Messages.Iso8601Date_MINUTE_VALUE_MUST_BE_0_AND_59 | |
+ value + ": Minute must be 0 and 59"; //$NON-NLS-2$ | |
} | |
return null; | |
} | |
private String checkSecond(String value) { | |
if (value.length() != 2) | |
return Messages.Iso8601Date_SECOND_VALUE_NOT_2_DIGITS_LONG + value | |
+ " is not 2 digits in length"; //$NON-NLS-2$ | |
else if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_SECOND_VALUE_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else { | |
second = Integer.parseInt(value); | |
if (second < 0 || second > 59) | |
return NLS.bind( | |
Messages.Iso8601Date_SECOND_VALUE_MUST_BE_0_AND_59, | |
value); | |
else | |
return null; | |
} | |
} | |
private String checkDot(String value) { | |
if (value.equals(".")) { //$NON-NLS-1$ | |
inFraction = true; | |
return null; | |
} else | |
return Messages.Iso8601Date_EXPECTED_DOT; | |
} | |
private String checkFraction(String value) { | |
// TODO - read milliseconds | |
if (!StringUtils.isNumeric(value)) | |
return Messages.Iso8601Date_FRACT_VAL_NOT_NUMERICAL + value | |
+ " is not numerical"; //$NON-NLS-2$ | |
else | |
return null; | |
} | |
private String prepTimezone() { | |
return (tzNegative.booleanValue() ? Messages.Iso8601Date_MINUS | |
: Messages.Iso8601Date_PLUS) | |
+ StringUtils.leftPad(Integer.toString(tzHour), 2, '0') | |
+ StringUtils.leftPad(Integer.toString(tzMinute), 2, '0'); | |
} | |
} |