AuroraRuntime/Source/Locale/LocaleStrings.cpp

224 lines
7.7 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: LocaleStrings.cpp
Date: 2022-1-23
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "LocaleStrings.hpp"
namespace Aurora::Locale
{
static const AuString kDaySuffix = "d"; //for.now
static const AuString kMSSuffix = "ms"; //for.now
static const AuString kSecondSuffix = "s"; //for.now
static const AuString kDelm = "."; //for.now
static const AuString kDelmIncorrect = ","; // angry foreigner noises
static const AuString kNewLineDelm = AuBuild::kIsNtDerived ? "\r\n" : "\n";
AUKN_SYM const AuString &NumbericLocaleGetDecimal()
{
return kDelm;
}
AUKN_SYM const AuString &TimeLocaleGetDayChar()
{
return kDaySuffix;
}
AUKN_SYM const AuString &TimeLocaleGetSChar()
{
return kSecondSuffix;
}
AUKN_SYM const AuString &TimeLocaleGetMSChar()
{
return kMSSuffix;
}
AUKN_SYM const AuString &NewLine()
{
return kNewLineDelm;
}
static AuString &_TextPrepadZeroIfOne(AuString &in)
{
if (in.size() == 1) in.insert(in.begin(), '0');
return in;
}
static AuString _TextPrepadZeroIfOne(const AuString &in)
{
AuString ret = in;
if (ret.size() == 1) ret.insert(ret.begin(), '0');
return ret;
}
static AuString _TextPrepadZeroMS(const AuString &in)
{
AuString ret = in;
if (ret.size() == 1) ret.insert(0, "000", 2);
else if (ret.size() == 2) ret.insert(0, "000", 1);
while (ret.size() > 1 && ret[ret.size() - 1] == '0')
ret.pop_back();
return ret;
}
static AuString _TextPrepadZeroNS(const AuString &in)
{
AuString ret = in;
switch (ret.size())
{
case 1: ret.insert(0, "000000", 5); break;
case 2: ret.insert(0, "000000", 4); break;
case 3: ret.insert(0, "000000", 3); break;
case 4: ret.insert(0, "000000", 2); break;
case 5: ret.insert(0, "000000", 1); break;
}
while (ret.size() > 1 && ret[ret.size() - 1] == '0')
ret.pop_back();
return ret;
}
AUKN_SYM AuString ConvertMSToTimescale(AuUInt64 ms)
{
const auto msDiv1000 = ms / 1000; // seconds
const auto msDiv1000Mod60 = msDiv1000 % 60; // remaining seconds relative to next whole minute
const auto msDiv1000Div60 = msDiv1000 / 60; // total minutes
try
{
// This is completely arbitrary
// I feel as though this would do juststice to large and small timescales; with sane formatting, without being too autistic on resolution, and without returning excessively long (^ and localized) strings
// We probably don't need to keep the MS around for more than a minute
// We can use the lengthy MS padding to pad out seconds more into the ballpark of HH:MM:SS
// We can measure months, perhaps years, using mere days. We, and most normies, can comprehend 30/60/90/360/720 without much problem
//
// ! = pad
//
// S!2.MS!3s (01.500s)
// M!2.S!2 (02:29)
// H!2:M!2:S!2 (01:02:29)
// D!0d H!2:M!2:S!2 (9d 01:02:29)
if (ms < 1000)
{
return AuToString(ms) + TimeLocaleGetMSChar();
}
else if (ms < (1000 * 60))
{
auto s = msDiv1000;
auto remMs = ms % 1000;
return AuToString(s) + NumbericLocaleGetDecimal() + _TextPrepadZeroMS(AuToString(remMs)) + TimeLocaleGetSChar();
}
else if (ms < (1000 * 60 * 60))
{
auto m = msDiv1000Div60;
auto remS = msDiv1000Mod60;
return _TextPrepadZeroIfOne(AuToString(m)) + ":" + _TextPrepadZeroIfOne(AuToString(remS));
}
else if (ms < (1000 * 60 * 60 * 24))
{
auto h = msDiv1000Div60 / 60;
auto remM = msDiv1000Div60;
auto remS = msDiv1000Mod60;
return _TextPrepadZeroIfOne(AuToString(h)) + ":" + _TextPrepadZeroIfOne(AuToString(remM)) + ":" + _TextPrepadZeroIfOne(AuToString(remS));
}
else
{
auto d = (msDiv1000Div60 / 60 / 24);
auto h = (msDiv1000Div60 / 60) - (d * 24);
auto remM = msDiv1000Div60;
auto remS = msDiv1000Mod60;
return AuToString(d) + TimeLocaleGetDayChar() + " " + _TextPrepadZeroIfOne(AuToString(h)) + ":" + _TextPrepadZeroIfOne(AuToString(remM)) + ":" + _TextPrepadZeroIfOne(AuToString(remS));
}
}
catch (...)
{
SysPushErrorGeneric("ConvertNSToTimescale failed -> returning empty string");
return {};
}
}
AUKN_SYM AuString ConvertNSToTimescale(AuUInt64 ns)
{
try
{
if (ns < AuMSToNS<AuUInt64>(1000))
{
const auto ms = ns / 1000000;
const auto remNs = ns % 1000000;
return AuToString(ms) + NumbericLocaleGetDecimal() + _TextPrepadZeroNS(AuToString(remNs)) + TimeLocaleGetMSChar();
}
else
{
return ConvertMSToTimescale(AuNSToMS<AuUInt64>(ns));
}
}
catch (...)
{
SysPushErrorGeneric("ConvertNSToTimescale failed -> returning empty string");
return {};
}
}
AUKN_SYM AuString TimeDateToString(const Time::tm &time)
{
try
{
bool simple = time.tm_mday == 0 && time.tm_mon == 0 && time.tm_year == 0;
if (simple)
{
return fmt::format("{:02}-{:02}-{:02}", time.tm_hour, time.tm_min, time.tm_sec);
}
else
{
// Hard-code ISO-8601 locale because, the Americans locale doesn't make any sense whatsoever, and east asia has the right idea
// EU users and burgers seethe... we normalize one sane standard to disambiguate this shit across locale boundaries
return fmt::format("{:04}-{:02}-{:02} {:02}-{:02}-{:02}", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
}
}
catch (...)
{
SysPushErrorGeneric("TimeDateToString failed -> returning empty string");
return {};
}
}
AUKN_SYM AuString TimeDateToFileNameISO8601(const Time::tm &time, AuTime::ETimezoneShift shift)
{
try
{
AuString tz {};
auto tnorm = AuTime::NormalizeCivilTimezone(time, shift);
return fmt::format("{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z",
tnorm.tm_year + 1900, tnorm.tm_mon + 1, tnorm.tm_mday,
tnorm.tm_hour, tnorm.tm_min, tnorm.tm_sec);
}
catch (...)
{
SysPushErrorGeneric("TimeDateToFileNameISO8601 failed -> returning empty string");
return {};
}
}
AUKN_SYM AuString TimeDateToISO8601(const Time::tm &time, AuTime::ETimezoneShift shift)
{
try
{
AuString tz {};
auto tnorm = AuTime::NormalizeCivilTimezone(time, shift);
return fmt::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
tnorm.tm_year + 1900, tnorm.tm_mon + 1, tnorm.tm_mday,
tnorm.tm_hour, tnorm.tm_min, tnorm.tm_sec);
}
catch (...)
{
SysPushErrorGeneric("TimeDateToISO8601 failed -> returning empty string");
return {};
}
}
}