/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: LocaleStrings.cpp Date: 2022-1-23 Author: Reece ***/ #include #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(1000)) { const auto ms = ns / 1000000; const auto remNs = ns % 1000000; return AuToString(ms) + NumbericLocaleGetDecimal() + _TextPrepadZeroNS(AuToString(remNs)) + TimeLocaleGetMSChar(); } else { return ConvertMSToTimescale(AuNSToMS(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 {}; } } }