#include "pdf/pdf_utils/dates.h"
#include <stdint.h>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace chrome_pdf {
namespace {
class DateDeserializer final {
public:
explicit DateDeserializer(base::StringPiece parsing)
: deserializing_(parsing) {}
~DateDeserializer() = default;
absl::optional<int> PopDigits(size_t num_digits) {
if (stopped_)
return absl::nullopt;
uint32_t value;
if (deserializing_.size() < num_digits ||
!base::IsAsciiDigit(deserializing_.front()) ||
!base::StringToUint(deserializing_.substr(0, num_digits), &value)) {
stopped_ = true;
return absl::nullopt;
}
deserializing_ = deserializing_.substr(num_digits);
return value;
}
absl::optional<char> TryPopNonDigit() {
if (stopped_ || deserializing_.empty())
return absl::nullopt;
const char front = deserializing_.front();
if (base::IsAsciiDigit(front))
return absl::nullopt;
deserializing_ = deserializing_.substr(1);
return front;
}
void unstop() { stopped_ = false; }
private:
base::StringPiece deserializing_;
bool stopped_ = false;
};
base::TimeDelta ParseOffset(DateDeserializer& deserializer) {
base::TimeDelta offset;
const absl::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 absl::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(base::StringPiece date) {
if (date.substr(0, 2) == "D:")
date = date.substr(2);
DateDeserializer deserializer(date);
const absl::optional<int> deserialized_year = deserializer.PopDigits(4);
if (!deserialized_year.has_value())
return base::Time();
base::Time::Exploded exploded = {};
exploded.year = deserialized_year.value();
exploded.month = deserializer.PopDigits(2).value_or(1);
exploded.day_of_month = deserializer.PopDigits(2).value_or(1);
exploded.hour = deserializer.PopDigits(2).value_or(0);
exploded.minute = deserializer.PopDigits(2).value_or(0);
exploded.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);
}
}