2

While working on a PHP script that builds EPUB 3.0.1 content from external data, I encountered a situation where EPUBCheck complains about a valid ISO8601 date-time string in two places in the container file. The string comes from the following PHP code.

$isoDate=date(DateTime::ISO8601);

The same date string appears in both places of the container. One yields a warning and the other an error. I am positive that the string is valid per the ISO8601 standard quoted in the warning message. So why does this happen?

Below is the top portion of my container.opf file and the errors I got from the online validator at http://validator.idpf.org. The validator used EPUBCheck version 4.1.1.

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://www.idpf.org/2007/opf" xmlns:opf="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="uuid_id">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:opf="http://www.idpf.org/2007/opf">
    <dc:identifier id="uuid_id" opf:scheme="uuid">508c6b12-b888-4d7b-9c58-c25d8fba7846</dc:identifier>
    <dc:title>Franklin's Palindromedary</dc:title>
    <dc:language>en</dc:language>
    <dc:creator>Ray N. Franklin</dc:creator>
    <dc:date>2020-12-09T14:06:40-0700</dc:date>
    <meta property="dcterms:modified">2020-12-09T14:06:40-0700</meta>
</metadata>

The first offending line is #8, declaring the dc:date value. Here's the warning.

WARNING:  content.opf, line 8, position 45:  Date value '2020-12-09T14:06:40-0700' does not follow recommended syntax as per http://www.w3.org/TR/NOTE-datetime:Missing minute field.

The next line declares the required meta property dcterms:modified using the same date string. It garners an error.

ERROR:  content.opf, line 9, position 36:  Error while parsing file: dcterms:modified illegal syntax (expecting: 'CCYY-MM-DDThh:mm:ssZ')

Please note that I am posting this question so I can answer it, because I did eventually find a solution. I want to give the full answer here because everywhere else I found only pieces of the puzzle.

1

This happens because at least two parts of EPUBCheck dislike the ISO8601 timezone format in different ways. In the dc:date context, EPUBCheck is OK with "2020-12-09T14:06:40-07:00" but not with "2020-12-09T14:06:40-0700". The only difference is a colon in the timezone offset of 0700 hours. I learned about this oddity from a PHP manual page comment. The author pointed out that the format method of the DateTime class yields different results for the format specifiers 'c' and ISO8601. The former includes the colon in the offset and the latter does not.

When I applied that correction to my code, the dc:date warning went away because EPUBCheck accepted "2020-12-09T14:06:40-07:00" as a valid date-time. However, the error persisted.

Reading the W3 specs for date-time and the IDPF specs for dcterms:modified, I eventually figured it out. Where I went wrong was in reading the error message. It said it was expecting a date-time of the form "CCYY-MM-DDThh:mm:ssZ". I already knew that the "T" in the middle was a literal character to separate the date from the time. My mistake was in thinking that the "Z" at the end was a symbolic character like all the others. I figured it was a placeholder for the timezone offset.

I was wrong. The "Z" is another literal character that means the time is in UTC, or Coordinated Universal Time, or Greenwich Mean Time, or Zulu Time or whatever other moniker you give to the time zone surrounding zero longitude.

If you read the W3 standard carefully you'll find a slightly ambiguous statement about the meaning of the letter "Z" at the end of the date-time string. Then look up the IDPF standard and search the page for both dc:time and dcterms:modified. All the examples show a literal "Z" in the date-time strings. Nowhere does this standard say it will accept local times with a timezone offset at the end.

Thus, we have two takeaways. 1) Always terminate a date-time value with "Z" in EPUB XML, and 2) Always use UTC time for any date-time values. While EPUBCheck will accept a local time for the dc:time item, that is not a guarantee that an e-reader will interpret the offset correctly.

In the end, I changed the PHP code to use the following. The resulting date-time string validates in EPUBCheck and it looks like all the examples in the IDPF standard. It also generates UTC time.

$isoDate=gmdate('Y-m-d\TH:i:s').'Z';

DO NOT move the "Z" inside the gmdate() format specifier. That will really munge up the string and probably give you more errors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.