224 lines
7.7 KiB
C++
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 {};
|
|
}
|
|
}
|
|
} |