Files
tessesframework/src/Date/Date.cpp

943 lines
28 KiB
C++

#include "TessesFramework/Date/Date.hpp"
#include "TessesFramework/Http/HttpUtils.hpp"
#include "../HowardHinnant_date/date.h"
#if defined(__FreeBSD__)
#include <sys/time.h>
#endif
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace std::chrono;
using namespace date;
namespace Tesses::Framework::Date
{
int GetTimeZone()
{
#if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__)
return (int)(-_timezone);
#elif defined(__FreeBSD__)
struct timeval tv;
struct timezone tz;
gettimeofday(&tv,&tz);
return (tz.tz_minuteswest/60);
#else
return (int)(-timezone);
#endif
}
bool TimeZoneSupportDST()
{
#if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__)
return _daylight == 1;
#elif defined(__FreeBSD__)
struct timeval tv;
struct timezone tz;
gettimeofday(&tv,&tz);
return tz.tz_dsttime!=0;
#else
return daylight == 1;
#endif
}
DateTime::DateTime()
{
}
static int64_t div_floor(int64_t a, int64_t b)
{
int64_t v = a / b;
if(a < 0 && (a % b) != 0) v--;
return v;
}
void DateTime::FromEpochNoConvert(int64_t gmt)
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(gmt,86400));
//date::days<int64_t> sys_days_since_epoch = date::days<int64_t>(gmt);
// Convert sys_days to year_month_day
date::year_month_day ymd = date::year_month_day(epoch);
int64_t secs = gmt % 86400;
if(secs < 0) secs += 86400;
second = secs % 60;
secs /= 60;
minute = secs % 60;
secs /= 60;
hour = secs % 24;
day = (int)(unsigned)ymd.day();
month = (int)(unsigned)ymd.month();
year = (int)ymd.year();
}
DateTime::DateTime(int64_t epoch)
{
this->isLocal=false;
this->FromEpochNoConvert(epoch);
}
void DateTime::Set(int64_t epoch)
{
this->isLocal=false;
this->FromEpochNoConvert(epoch);
}
void DateTime::Set(int year, int month, int day, int hour, int minute, int seconds, bool isLocal)
{
this->year = year;
this->month = month;
this->day = day;
this->hour = hour;
this->minute = minute;
this->second = seconds;
this->isLocal = isLocal;
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal)
{
this->year = year;
this->month = month;
this->day = day;
this->hour = hour;
this->minute = minute;
this->second = seconds;
this->isLocal = isLocal;
}
int DateTime::Year() const
{
return this->year;
}
int DateTime::Month() const
{
return this->month;
}
int DateTime::Day() const
{
return this->day;
}
int DateTime::Hour() const
{
return this->hour;
}
int DateTime::Minute() const
{
return this->minute;
}
int DateTime::Second() const
{
return this->second;
}
bool DateTime::IsLocal() const
{
return this->isLocal;
}
int DateTime::DayOfWeek() const
{
date::year_month_day ymd(date::year(year),date::month((uint32_t)month),date::day((uint32_t)day));
date::sys_days d = ymd;
date::year_month_weekday ymw(d);
return ymw.weekday().c_encoding() % 7;
}
void DateTime::SetToLocal()
{
if(this->isLocal) return;
auto local = this->ToEpochNoConvert();
local += GetTimeZone();
if(TimeZoneSupportDST())
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (unsigned)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (unsigned)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (unsigned)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local += 3600;
}
this->isLocal=true;
this->FromEpochNoConvert(local);
}
DateTime DateTime::ToLocal() const
{
DateTime dt = *this;
dt.SetToLocal();
return dt;
}
void DateTime::SetToUTC()
{
if(!this->isLocal) return;
auto local = this->ToEpochNoConvert();
local -= GetTimeZone();
if(TimeZoneSupportDST())
{
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (unsigned)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (unsigned)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (unsigned)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local -= 3600;
}
}
this->isLocal=false;
this->FromEpochNoConvert(local);
}
DateTime DateTime::ToUTC() const
{
DateTime dt = *this;
dt.SetToUTC();
return dt;
}
int64_t DateTime::ToEpoch() const
{
if(this->isLocal)
{
DateTime dt = this->ToUTC();
return dt.ToEpochNoConvert();
}
return this->ToEpochNoConvert();
}
int64_t DateTime::ToEpochNoConvert() const
{
date::year y = (date::year)year;
date::month m = (date::month)month;
date::day d = (date::day)day;
date::year_month_day ymd(y,m,d);
std::chrono::duration hr = hours(this->hour) + minutes(this->minute) + seconds(this->second);
sys_days sd = ymd;
auto res = sd - date::sys_days{date::January/1/1970};
return (res.count() * 86400) + hr.count(); //bad
}
DateTime DateTime::NowUTC()
{
DateTime theTime((int64_t)time(NULL));
return theTime;
}
void DateTime::SetToNowUTC()
{
this->Set((int64_t)time(NULL));
}
void DateTime::SetYear(int y)
{
this->year = y;
}
void DateTime::SetMonth(int m)
{
this->month = m;
}
void DateTime::SetDay(int d)
{
this->day = d;
}
void DateTime::SetHour(int h)
{
this->hour = h;
}
void DateTime::SetMinute(int m)
{
this->minute = m;
}
void DateTime::SetSecond(int s)
{
this->second = s;
}
void DateTime::SetLocal(bool local)
{
this->isLocal=local;
}
void DateTime::SetToNow()
{
int64_t local = (int64_t)time(NULL);
local += GetTimeZone();
if(TimeZoneSupportDST())
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (unsigned)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (unsigned)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (unsigned)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local += 3600;
}
this->isLocal=true;
this->FromEpochNoConvert(local);
}
DateTime DateTime::Now()
{
DateTime dt;
dt.SetToNow();
return dt;
}
const char* weekday_short[] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
};
const char* months_short[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
const char* weekday_long[] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
const char* months_long[] = {
"January",
"Febuary",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
bool DateTime::TryParseHttpDate(std::string txt, DateTime& dt)
{
//Mon, 24 Jul 2018 11:00:00 GMT
auto split = Http::HttpUtils::SplitString(txt,", ",2);
if(split.size() != 2) return false;
bool validDay = false;
for(size_t i = 0; i < 7; i++)
{
std::string_view d = weekday_short[i];
if(d == split[0]) validDay=true;
}
if(!validDay) return false;
//DAY MON YEAR HH:MM:SS GMT
split = Http::HttpUtils::SplitString(split[1]," ",5);
if(split.size() != 5) return false;
if(split[4] != "GMT") return false;
int day=0;
int mon=0;
int year=0;
int hour=0;
int minute=0;
int second=0;
try {
day = std::stoi(split[0]);
if(day < 1 && day > 31) return false;
} catch(std::exception& ex) {
return false;
}
for(int i = 0; i < 12; mon++)
{
std::string_view d = months_short[i];
if(d == split[1])
{
mon = i + 1;
break;
}
}
if(mon == 0) return false;
try {
year = std::stoi(split[2]);
} catch(std::exception& ex) {
return false;
}
split = Http::HttpUtils::SplitString(split[3],":",3);
if(split.size() != 3) return false;
try {
hour = std::stoi(split[0]);
minute = std::stoi(split[1]);
second = std::stoi(split[2]);
} catch(std::exception& ex) {
return false;
}
dt.day = day;
dt.month = mon;
dt.year = year;
dt.hour = hour;
dt.minute = minute;
dt.second = second;
return true;
}
std::string DateTime::ToHttpDate() const
{
auto utc=this->ToUTC();
std::string weekday=weekday_short[utc.DayOfWeek()];
std::string month = months_short[utc.month-1];
std::stringstream strm(std::ios_base::out);
strm << weekday << ", " << std::setfill('0') << std::setw(2) << utc.day;
strm << " " << month << " " << std::setfill('0') << std::setw(4) << utc.year;
strm << " " << std::setfill('0') << std::setw(2) << utc.hour;
strm << ":" << std::setfill('0') << std::setw(2) << utc.minute;
strm << ":" << std::setfill('0') << std::setw(2) << utc.second;
strm << " GMT";
return strm.str();
}
std::string DateTime::ToString() const
{
return ToString("%Y/%m/%d %H:%M:%S");
}
std::string DateTime::ToString(std::string fmt) const
{
auto weekday = this->DayOfWeek();
std::string text = "";
for(size_t i = 0; i < fmt.size(); i++)
{
if(fmt[i]=='%')
{
i++;
if(i < fmt.size())
switch(fmt[i])
{
case 'a':
text.append(weekday_short[weekday]);
break;
case 'A':
text.append(weekday_long[weekday]);
break;
case 'S':
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
break;
case 'm':
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
break;
case 'd':
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
break;
case 'e':
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,' '));
break;
case 'M':
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
break;
case 'H':
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
break;
case 'F':
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
text.push_back('-');
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
text.push_back('-');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
break;
case 'D':
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
text.push_back('/');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
text.push_back('/');
text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0'));
break;
case 'y':
text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0'));
break;
case 'r':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.append(hour >= 12 ? " PM" : " AM");
}
break;
case 'R':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
}
break;
case 'T':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
}
break;
case 'u':
{
int dow = weekday + 6;
dow %= 7;
text.append(std::to_string(dow+1));
}
break;
case 'w':
{
text.append(std::to_string(weekday));
}
break;
case 'c':
{
text.append(weekday_short[weekday]);
text.push_back(' ');
text.append(months_short[month]);
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
}
break;
case 'C':
text.append(Http::HttpUtils::LeftPad(std::to_string(year / 100),2,'0'));
break;
case 'Y':
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
break;
case 'p':
text.append(hour >= 12 ? "PM" : "AM");
break;
case 'I':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
}
break;
case 'h':
case 'b':
text.append(months_short[month-1]);
break;
case 'B':
text.append(months_long[month-1]);
break;
case '%':
text.push_back('%');
break;
case 'n':
text.push_back('\n');
break;
case 't':
text.push_back('\t');
break;
}
}
else
{
text.push_back(fmt[i]);
}
}
return text;
}
TimeSpan::TimeSpan()
{
this->totalSeconds = 0;
}
TimeSpan::TimeSpan(int64_t totalSeconds)
{
this->totalSeconds = totalSeconds;
}
TimeSpan::TimeSpan(int hours, int minutes, int seconds) : TimeSpan(0,hours,minutes,seconds)
{
}
TimeSpan::TimeSpan(int days,int hours, int minutes, int seconds)
{
this->totalSeconds = (int64_t)days * 86400;
this->totalSeconds += (int64_t)hours * 3600;
this->totalSeconds += (int64_t)minutes * 60;
this->totalSeconds += (int64_t)seconds;
}
void TimeSpan::Set(int days, int hours, int minutes, int seconds)
{
this->totalSeconds = (int64_t)days * 86400;
this->totalSeconds += (int64_t)hours * 3600;
this->totalSeconds += (int64_t)minutes * 60;
this->totalSeconds += (int64_t)seconds;
}
void TimeSpan::Set(int hours, int minutes, int seconds)
{
this->totalSeconds = (int64_t)hours * 3600;
this->totalSeconds += (int64_t)minutes * 60;
this->totalSeconds += (int64_t)seconds;
}
void TimeSpan::SetDays(int d)
{
Set(d,this->Hours(),this->Minutes(),this->Seconds());
}
void TimeSpan::SetHours(int h)
{
Set(this->Days(),h,this->Minutes(), this->Seconds());
}
void TimeSpan::SetMinutes(int m)
{
Set(this->Days(),this->Hours(),m,this->Seconds());
}
void TimeSpan::SetSeconds(int s)
{
Set(this->Days(),this->Hours(),this->Minutes(),s);
}
int TimeSpan::Days() const
{
return (int)(this->totalSeconds / 86400);
}
int TimeSpan::Hours() const
{
return (int)((this->totalSeconds / 3600) % 24);
}
int TimeSpan::Minutes() const
{
return (int)((this->totalSeconds / 60) % 60);
}
int TimeSpan::Seconds() const
{
return (int)(this->totalSeconds % 60);
}
int64_t TimeSpan::TotalSeconds() const
{
return this->totalSeconds;
}
int64_t TimeSpan::TotalMinutes() const
{
return this->totalSeconds / 60;
}
int64_t TimeSpan::TotalHours() const
{
return this->totalSeconds / 3600;
}
void TimeSpan::AddSeconds(int64_t seconds)
{
this->totalSeconds += seconds;
}
void TimeSpan::AddMinutes(int64_t minutes)
{
this->totalSeconds += minutes * 60;
}
void TimeSpan::AddHours(int64_t hours)
{
this->totalSeconds += hours * 3600;
}
void TimeSpan::AddDays(int64_t days)
{
this->totalSeconds += days * 86400;
}
void TimeSpan::SetTotalSeconds(int64_t totalSeconds)
{
this->totalSeconds = totalSeconds;
}
void TimeSpan::SetTotalMinutes(int64_t totalMinutes)
{
this->totalSeconds = totalMinutes * 60;
}
void TimeSpan::SetTotalHours(int64_t totalHours)
{
this->totalSeconds = totalHours * 3600;
}
std::string TimeSpan::ToString(bool slim) const
{
std::string str={};
if(this->totalSeconds < 0)
str += "-";
if(slim && this->totalSeconds > -36000 && this->totalSeconds < 36000)
{
//0:00
//00:00
//0:00:00
if(this->totalSeconds <= -3600 || this->totalSeconds >= 3600)
{
//hours must force multi digit minutes
str += std::to_string(this->Hours());
str += ":";
str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,'0');
}
else
{
str += std::to_string(this->Minutes());
}
str += ":";
str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,'0');
}
else
{
//00:00:00
//0.00:00:00
if(this->totalSeconds <= -86400 || this->totalSeconds >= 86400)
{
str += std::to_string(this->Days());
str += ".";
}
str += Http::HttpUtils::LeftPad(std::to_string(this->Hours()),2,'0');
str += ":";
str += Http::HttpUtils::LeftPad(std::to_string(this->Minutes()),2,'0');
str += ":";
str += Http::HttpUtils::LeftPad(std::to_string(this->Seconds()),2,'0');
}
return str;
}
bool TimeSpan::TryParse(std::string text, TimeSpan& span)
{
if(text.empty()) return false;
bool negative = text[0] == '-';
int64_t totalSeconds = 0;
try{
std::string colonPart = text.substr(negative ? 1 : 0);
auto res = Http::HttpUtils::SplitString(colonPart,":");
if(res.size() < 2 || res.size() > 3) return false;
std::string hour = res[0];
size_t index=hour.find('.');
if(index != std::string::npos)
{
totalSeconds += std::stoll(hour.substr(0,index)) * 86400;
hour = hour.substr(index+1);
}
if(res.size() == 2)
{
//mm:ss
totalSeconds += std::stoll(hour) * 60;
totalSeconds += std::stoll(res[1]);
}
else if(res.size() == 3)
{
totalSeconds += std::stoll(hour) * 3600;
totalSeconds += std::stoll(res[1]) * 60;
totalSeconds += std::stoll(res[2]);
}
else return false;
}catch(...) {return false;}
if(negative) totalSeconds = -totalSeconds;
span.SetTotalSeconds(totalSeconds);
return true;
}
}