diff --git a/ChangeLog b/ChangeLog index 4a8122fc..ea445e64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-05-30 Jay Berkenbilt + + * Add QUtil::qpdf_time_to_iso8601 and QUtil::pdf_time_to_iso8601 + for converting PDF/qpdf timestamps to ISO-8601 date format. + 2022-05-18 Jay Berkenbilt * Add QUtil::FileCloser to the public API. This is a simple inline diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index 283db861..32aeae1f 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -284,6 +284,10 @@ namespace QUtil QPDF_DLL std::string qpdf_time_to_pdf_time(QPDFTime const&); + // Convert QPDFTime to a second-granularity ISO-8601 timestamp. + QPDF_DLL + std::string qpdf_time_to_iso8601(QPDFTime const&); + // Convert a PDF timestamp string to a QPDFTime. If syntactically // valid, return true and fill in qtm. If not valid, return false, // and do not modify qtm. If qtm is null, just check the validity @@ -291,6 +295,11 @@ namespace QUtil QPDF_DLL bool pdf_time_to_qpdf_time(std::string const&, QPDFTime* qtm = nullptr); + // Convert PDF timestamp to a second-granularity ISO-8601 + // timestamp. If syntactically valid, return true and initialize + // iso8601. Otherwise, return false. + bool pdf_time_to_iso8601(std::string const& pdf_time, std::string& iso8601); + // Return a string containing the byte representation of the UTF-8 // encoding for the unicode value passed in. QPDF_DLL diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 3e68d95e..7a5d3caf 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -984,6 +984,32 @@ QUtil::qpdf_time_to_pdf_time(QPDFTime const& qtm) QUtil::int_to_string(qtm.second, 2) + tz_offset); } +std::string +QUtil::qpdf_time_to_iso8601(QPDFTime const& qtm) +{ + std::string tz_offset; + int t = qtm.tz_delta; + if (t == 0) { + tz_offset = "Z"; + } else { + if (t < 0) { + t = -t; + tz_offset += "+"; + } else { + tz_offset += "-"; + } + tz_offset += QUtil::int_to_string(t / 60, 2) + ":" + + QUtil::int_to_string(t % 60, 2); + } + return ( + QUtil::int_to_string(qtm.year, 4) + "-" + + QUtil::int_to_string(qtm.month, 2) + "-" + + QUtil::int_to_string(qtm.day, 2) + "T" + + QUtil::int_to_string(qtm.hour, 2) + ":" + + QUtil::int_to_string(qtm.minute, 2) + ":" + + QUtil::int_to_string(qtm.second, 2) + tz_offset); +} + bool QUtil::pdf_time_to_qpdf_time(std::string const& str, QPDFTime* qtm) { @@ -1018,6 +1044,17 @@ QUtil::pdf_time_to_qpdf_time(std::string const& str, QPDFTime* qtm) return true; } +bool +QUtil::pdf_time_to_iso8601(std::string const& pdf_time, std::string& iso8601) +{ + QPDFTime qtm; + if (pdf_time_to_qpdf_time(pdf_time, &qtm)) { + iso8601 = qpdf_time_to_iso8601(qtm); + return true; + } + return false; +} + std::string QUtil::toUTF8(unsigned long uval) { diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out index d91acba9..48d22fb9 100644 --- a/libtests/qtest/qutil/qutil.out +++ b/libtests/qtest/qutil/qutil.out @@ -127,7 +127,10 @@ rename over existing delete file ---- timestamp D:20210209144925-05'00' +2021-02-09T14:49:25-05:00 D:20210210011925+05'30' +2021-02-10T01:19:25+05:30 D:20210209191925Z +2021-02-09T19:19:25Z ---- is_long_long done diff --git a/libtests/qutil.cc b/libtests/qutil.cc index ea0aece7..995a7599 100644 --- a/libtests/qutil.cc +++ b/libtests/qutil.cc @@ -656,10 +656,14 @@ timestamp_test() { auto check = [](QUtil::QPDFTime const& t) { std::string pdf = QUtil::qpdf_time_to_pdf_time(t); - std::cout << pdf << std::endl; + std::string iso8601 = QUtil::qpdf_time_to_iso8601(t); + std::cout << pdf << std::endl << iso8601 << std::endl; QUtil::QPDFTime t2; + std::string iso8601_2; assert(QUtil::pdf_time_to_qpdf_time(pdf, &t2)); assert(QUtil::qpdf_time_to_pdf_time(t2) == pdf); + assert(QUtil::pdf_time_to_iso8601(pdf, iso8601_2)); + assert(iso8601 == iso8601_2); }; check(QUtil::QPDFTime(2021, 2, 9, 14, 49, 25, 300)); check(QUtil::QPDFTime(2021, 2, 10, 1, 19, 25, -330));