#include "pdf/pdf_utils/dates.h"
#include <stdint.h>
#include <optional>
#include <string_view>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
namespace chrome_pdf {
namespace {
class DateDeserializer final {
public:
explicit DateDeserializer(std::string_view parsing)
: deserializing_(parsing) {}
~DateDeserializer() = default;
std::optional<int> PopDigits(size_t num_digits) {
if (stopped_)
return std::nullopt;
uint32_t value;
if (deserializing_.size() < num_digits ||
!base::IsAsciiDigit(deserializing_.front()) ||
!base::StringToUint(deserializing_.substr(0, num_digits), &value)) {
stopped_ = true;
return std::nullopt;
}
deserializing_ = deserializing_.substr(num_digits);
return value;
}
std::optional<char> TryPopNonDigit() {
if (stopped_ || deserializing_.empty())
return std::nullopt;
const char front = deserializing_.front();
if (base::IsAsciiDigit(front))
return std::nullopt;
deserializing_ = deserializing_.substr(1);
return front;
}
void unstop() { stopped_ = false; }
private:
std::string_view deserializing_;
bool stopped_ = false;
};
base::TimeDelta ParseOffset(DateDeserializer& deserializer) {
base::TimeDelta offset;
const std::optional<char> sign = deserializer.TryPopNonDigit();
if (!sign.has_value() || (sign.value() != '+' && sign.value() != '-'))
return offset;
offset += base::Hours(deserializer.PopDigits(2).value_or(0));
const std::optional<char> apostrophe = deserializer.TryPopNonDigit();
if (apostrophe.has_value() && apostrophe.value() != '\'')
return sign.value() == '+' ? offset : -offset;
offset += base::Minutes(deserializer.PopDigits(2).value_or(0));
return sign.value() == '+' ? offset : -offset;
}
}
base::Time ParsePdfDate(std::string_view date) {
if (date.substr(0, 2) == "D:")
date = date.substr(2);
DateDeserializer deserializer(date);
const std::optional<int> deserialized_year = deserializer.PopDigits(4);
if (!deserialized_year.has_value())
return base::Time();
base::Time::Exploded exploded = {
.year = deserialized_year.value(),
.month = deserializer.PopDigits(2).value_or(1),
.day_of_month = deserializer.PopDigits(2).value_or(1),
.hour = deserializer.PopDigits(2).value_or(0),
.minute = deserializer.PopDigits(2).value_or(0),
.second = deserializer.PopDigits(2).value_or(0)};
base::Time parsed;
if (!base::Time::FromUTCExploded(exploded, &parsed))
return base::Time();
deserializer.unstop();
return parsed - ParseOffset(deserializer);
}
}