* Copyright 2010-2022 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@gmail.com
* Niels Sascha Reedijk, niels.reedijk@gmail.com
*/
#include <HttpTime.h>
#include <list>
#include <new>
#include <cstdio>
using namespace BPrivate::Network;
static const std::list<std::pair<BHttpTimeFormat, const char*>> kDateFormats = {
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S GMT"},
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S"},
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"},
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S"},
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S"},
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S GMT"},
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S UTC"},
{BHttpTimeFormat::AscTime, "%a %b %e %H:%M:%S %Y"},
};
BHttpTime::InvalidInput::InvalidInput(const char* origin, BString input)
:
BError(origin),
input(std::move(input))
{
}
const char*
BHttpTime::InvalidInput::Message() const noexcept
{
if (input.IsEmpty())
return "A HTTP timestamp cannot be empty";
else
return "The HTTP timestamp string does not match the expected format";
}
BString
BHttpTime::InvalidInput::DebugMessage() const
{
BString output = BError::DebugMessage();
if (!input.IsEmpty())
output << ":\t " << input << "\n";
return output;
}
BHttpTime::BHttpTime() noexcept
:
fDate(BDateTime::CurrentDateTime(B_GMT_TIME)),
fDateFormat(BHttpTimeFormat::RFC1123)
{
}
BHttpTime::BHttpTime(BDateTime date)
:
fDate(date),
fDateFormat(BHttpTimeFormat::RFC1123)
{
if (!fDate.IsValid())
throw InvalidInput(__PRETTY_FUNCTION__, "Invalid BDateTime object");
}
BHttpTime::BHttpTime(const BString& dateString)
:
fDate(0),
fDateFormat(BHttpTimeFormat::RFC1123)
{
_Parse(dateString);
}
void
BHttpTime::SetTo(const BString& string)
{
_Parse(string);
}
void
BHttpTime::SetTo(BDateTime date)
{
if (!date.IsValid())
throw InvalidInput(__PRETTY_FUNCTION__, "Invalid BDateTime object");
fDate = date;
fDateFormat = BHttpTimeFormat::RFC1123;
}
BDateTime
BHttpTime::DateTime() const noexcept
{
return fDate;
}
BHttpTimeFormat
BHttpTime::DateTimeFormat() const noexcept
{
return fDateFormat;
}
BString
BHttpTime::ToString(BHttpTimeFormat outputFormat) const
{
BString expirationFinal;
struct tm expirationTm = {};
expirationTm.tm_sec = fDate.Time().Second();
expirationTm.tm_min = fDate.Time().Minute();
expirationTm.tm_hour = fDate.Time().Hour();
expirationTm.tm_mday = fDate.Date().Day();
expirationTm.tm_mon = fDate.Date().Month() - 1;
expirationTm.tm_year = fDate.Date().Year() - 1900;
expirationTm.tm_wday = fDate.Date().DayOfWeek() % 7;
expirationTm.tm_yday = 0;
expirationTm.tm_isdst = 0;
for (auto& [format, formatString]: kDateFormats) {
if (format != outputFormat)
continue;
static const uint16 kTimetToStringMaxLength = 128;
char expirationString[kTimetToStringMaxLength + 1];
size_t strLength;
strLength
= strftime(expirationString, kTimetToStringMaxLength, formatString, &expirationTm);
expirationFinal.SetTo(expirationString, strLength);
break;
}
return expirationFinal;
}
void
BHttpTime::_Parse(const BString& dateString)
{
if (dateString.Length() < 4)
throw InvalidInput(__PRETTY_FUNCTION__, dateString);
struct tm expireTime = {};
bool found = false;
for (auto& [format, formatString]: kDateFormats) {
const char* result = strptime(dateString.String(), formatString, &expireTime);
if (result == dateString.String() + dateString.Length()) {
fDateFormat = format;
found = true;
break;
}
}
if (!found)
throw InvalidInput(__PRETTY_FUNCTION__, dateString);
BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec);
BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1, expireTime.tm_mday);
fDate = BDateTime(date, time);
}
BDateTime
BPrivate::Network::parse_http_time(const BString& string)
{
BHttpTime httpTime(string);
return httpTime.DateTime();
}
BString
BPrivate::Network::format_http_time(BDateTime timestamp, BHttpTimeFormat outputFormat)
{
BHttpTime httpTime(timestamp);
return httpTime.ToString(outputFormat);
}