diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java index 1b3690f720..455891f644 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/PackagePropertiesPart.java @@ -23,6 +23,8 @@ import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidOperationException; @@ -49,9 +51,22 @@ public final class PackagePropertiesPart extends PackagePart implements public final static String NAMESPACE_DCTERMS_URI = "http://purl.org/dc/terms/"; private final static String DEFAULT_DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; - private final static String ALTERNATIVE_DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SS'Z'"; - - + + private final static String[] DATE_FORMATS = new String[]{ + DEFAULT_DATEFORMAT, + "yyyy-MM-dd'T'HH:mm:ss.SS'Z'", + }; + + //Had to add this and TIME_ZONE_PAT to handle tz with colons. + //When we move to Java 7, we should be able to add another + //date format to DATE_FORMATS that uses XXX and get rid of this + //and TIME_ZONE_PAT + private final String[] TZ_DATE_FORMATS = new String[]{ + "yyyy-MM-dd'T'HH:mm:ssz", + "yyyy-MM-dd'T'HH:mm:ss.SSSz" + }; + + private final Pattern TIME_ZONE_PAT = Pattern.compile("([-+]\\d\\d):?(\\d\\d)"); /** * Constructor. * @@ -562,20 +577,43 @@ public final class PackagePropertiesPart extends PackagePart implements if (dateStr == null || dateStr.equals("")) { return new Nullable(); } + + Matcher m = TIME_ZONE_PAT.matcher(dateStr); + if (m.find()) { + String dateTzStr = dateStr.substring(0, m.start())+ + m.group(1)+m.group(2); + for (String fStr : TZ_DATE_FORMATS) { + SimpleDateFormat df = new SimpleDateFormat(fStr, Locale.ROOT); + df.setTimeZone(LocaleUtil.TIMEZONE_UTC); + Date d = new SimpleDateFormat(fStr).parse(dateTzStr, new ParsePosition(0)); + if (d != null) { + return new Nullable(d); + } + } + } String dateTzStr = dateStr.endsWith("Z") ? dateStr : (dateStr + "Z"); - SimpleDateFormat df = new SimpleDateFormat(DEFAULT_DATEFORMAT, Locale.ROOT); - df.setTimeZone(LocaleUtil.TIMEZONE_UTC); - Date d = df.parse(dateTzStr, new ParsePosition(0)); - if (d == null) { - df = new SimpleDateFormat(ALTERNATIVE_DATEFORMAT, Locale.ROOT); - df.setTimeZone(LocaleUtil.TIMEZONE_UTC); - d = df.parse(dateTzStr, new ParsePosition(0)); + for (String fStr : DATE_FORMATS) { + SimpleDateFormat df = new SimpleDateFormat(fStr, Locale.ROOT); + df.setTimeZone(LocaleUtil.TIMEZONE_UTC); + Date d = df.parse(dateTzStr, new ParsePosition(0)); + if (d != null) { + return new Nullable(d); + } } - if (d == null) { - throw new InvalidFormatException("Date " + dateTzStr + " not well formated, " - + "expected format " + DEFAULT_DATEFORMAT + " or " + ALTERNATIVE_DATEFORMAT); + //if you're here, no pattern matched, throw exception + StringBuilder sb = new StringBuilder(); + int i = 0; + for (String fStr : TZ_DATE_FORMATS) { + if (i++ > 0) { + sb.append(", "); + } + sb.append(fStr); } - return new Nullable(d); + for (String fStr : DATE_FORMATS) { + sb.append(", ").append(fStr); + } + throw new InvalidFormatException("Date " + dateStr + " not well formatted, " + + "expected format in: "+sb.toString()); } /** diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java index bbc53d6b97..612371506d 100644 --- a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java +++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackageCoreProperties.java @@ -70,11 +70,36 @@ public final class TestPackageCoreProperties { df.setTimeZone(LocaleUtil.TIMEZONE_UTC); Date dateToInsert = df.parse("2007-05-12T08:00:00Z", new ParsePosition(0)); + SimpleDateFormat msdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT); + msdf.setTimeZone(LocaleUtil.TIMEZONE_UTC); + PackageProperties props = p.getPackageProperties(); - props.setCategoryProperty("MyCategory"); + + //test various date formats + props.setCreatedProperty("2007-05-12T08:00:00Z"); + assertEquals(dateToInsert, props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T08:00:00"); //no Z, assume Z + assertEquals(dateToInsert, props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T08:00:00.123Z");//millis + assertEquals(msdf.parse("2007-05-12T08:00:00.123Z"), props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T10:00:00+0200"); + assertEquals(dateToInsert, props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T10:00:00+02:00");//colon in tz + assertEquals(dateToInsert, props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T06:00:00-0200"); + assertEquals(dateToInsert, props.getCreatedProperty().getValue()); + + props.setCreatedProperty("2007-05-12T10:00:00.123+0200"); + assertEquals(msdf.parse("2007-05-12T08:00:00.123Z"), props.getCreatedProperty().getValue()); + + props.setCategoryProperty("MyCategory"); props.setContentStatusProperty("MyContentStatus"); props.setContentTypeProperty("MyContentType"); - props.setCreatedProperty(new Nullable(dateToInsert)); props.setCreatorProperty("MyCreator"); props.setDescriptionProperty("MyDescription"); props.setIdentifierProperty("MyIdentifier");