|
| 1 | +#include "core/datetime_names.h" |
| 2 | + |
| 3 | +#include <cctype> |
| 4 | + |
| 5 | +namespace vxcore { |
| 6 | + |
| 7 | +// NOTE: every non-ASCII entry below is written as explicit UTF-8 byte escapes. |
| 8 | +// The production `vxcore` target is NOT compiled with MSVC's /utf-8 flag (that |
| 9 | +// flag is applied directory-wide only under libs/vxcore/tests), so a raw CJK |
| 10 | +// literal here would be re-encoded using the active ANSI code page in the DLL |
| 11 | +// while the direct-compiled test copy compiled correctly. The escapes remove |
| 12 | +// the source-encoding dependence entirely. Do NOT "clean them up" into raw |
| 13 | +// literals, and keep the comments in this file ASCII-only. |
| 14 | +namespace { |
| 15 | + |
| 16 | +// --------------------------------------------------------------------------- |
| 17 | +// English |
| 18 | +// --------------------------------------------------------------------------- |
| 19 | +const char *const kEnShortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 20 | + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
| 21 | +const char *const kEnLongMonths[12] = {"January", "February", "March", "April", |
| 22 | + "May", "June", "July", "August", |
| 23 | + "September", "October", "November", "December"}; |
| 24 | +const char *const kEnShortDays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
| 25 | +const char *const kEnLongDays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", |
| 26 | + "Thursday", "Friday", "Saturday"}; |
| 27 | + |
| 28 | +// --------------------------------------------------------------------------- |
| 29 | +// Shared CJK building blocks (UTF-8 byte escapes). |
| 30 | +// --------------------------------------------------------------------------- |
| 31 | +#define VX_YUE "\xE6\x9C\x88" // U+6708 month |
| 32 | +#define VX_ZHOU "\xE5\x91\xA8" // U+5468 week (zh abbreviated day prefix) |
| 33 | +#define VX_XING "\xE6\x98\x9F" // U+661F |
| 34 | +#define VX_QI "\xE6\x9C\x9F" // U+671F |
| 35 | +#define VX_YAO "\xE6\x9B\x9C" // U+66DC (ja day-of-week marker) |
| 36 | + |
| 37 | +#define VX_RI "\xE6\x97\xA5" // U+65E5 sun/day |
| 38 | +#define VX_YUE_MOON "\xE6\x9C\x88" // U+6708 moon (same glyph as month) |
| 39 | +#define VX_HUO "\xE7\x81\xAB" // U+706B fire |
| 40 | +#define VX_SHUI "\xE6\xB0\xB4" // U+6C34 water |
| 41 | +#define VX_MU "\xE6\x9C\xA8" // U+6728 wood |
| 42 | +#define VX_JIN "\xE9\x87\x91" // U+91D1 metal |
| 43 | +#define VX_TU "\xE5\x9C\x9F" // U+571F earth |
| 44 | + |
| 45 | +#define VX_YI "\xE4\xB8\x80" // U+4E00 one |
| 46 | +#define VX_ER "\xE4\xBA\x8C" // U+4E8C two |
| 47 | +#define VX_SAN "\xE4\xB8\x89" // U+4E09 three |
| 48 | +#define VX_SI "\xE5\x9B\x9B" // U+56DB four |
| 49 | +#define VX_WU "\xE4\xBA\x94" // U+4E94 five |
| 50 | +#define VX_LIU "\xE5\x85\xAD" // U+516D six |
| 51 | +#define VX_QId "\xE4\xB8\x83" // U+4E03 seven |
| 52 | +#define VX_BA "\xE5\x85\xAB" // U+516B eight |
| 53 | +#define VX_JIU "\xE4\xB9\x9D" // U+4E5D nine |
| 54 | +#define VX_SHI "\xE5\x8D\x81" // U+5341 ten |
| 55 | + |
| 56 | +// --------------------------------------------------------------------------- |
| 57 | +// Simplified Chinese (zh_CN) |
| 58 | +// --------------------------------------------------------------------------- |
| 59 | +const char *const kZhShortMonths[12] = { |
| 60 | + "1" VX_YUE, "2" VX_YUE, "3" VX_YUE, "4" VX_YUE, "5" VX_YUE, "6" VX_YUE, |
| 61 | + "7" VX_YUE, "8" VX_YUE, "9" VX_YUE, "10" VX_YUE, "11" VX_YUE, "12" VX_YUE}; |
| 62 | +const char *const kZhLongMonths[12] = { |
| 63 | + VX_YI VX_YUE, VX_ER VX_YUE, VX_SAN VX_YUE, VX_SI VX_YUE, |
| 64 | + VX_WU VX_YUE, VX_LIU VX_YUE, VX_QId VX_YUE, VX_BA VX_YUE, |
| 65 | + VX_JIU VX_YUE, VX_SHI VX_YUE, VX_SHI VX_YI VX_YUE, VX_SHI VX_ER VX_YUE}; |
| 66 | +const char *const kZhShortDays[7] = {VX_ZHOU VX_RI, VX_ZHOU VX_YI, VX_ZHOU VX_ER, |
| 67 | + VX_ZHOU VX_SAN, VX_ZHOU VX_SI, VX_ZHOU VX_WU, |
| 68 | + VX_ZHOU VX_LIU}; |
| 69 | +const char *const kZhLongDays[7] = { |
| 70 | + VX_XING VX_QI VX_RI, VX_XING VX_QI VX_YI, VX_XING VX_QI VX_ER, VX_XING VX_QI VX_SAN, |
| 71 | + VX_XING VX_QI VX_SI, VX_XING VX_QI VX_WU, VX_XING VX_QI VX_LIU}; |
| 72 | + |
| 73 | +// --------------------------------------------------------------------------- |
| 74 | +// Japanese (ja) |
| 75 | +// --------------------------------------------------------------------------- |
| 76 | +const char *const kJaShortMonths[12] = { |
| 77 | + "1" VX_YUE, "2" VX_YUE, "3" VX_YUE, "4" VX_YUE, "5" VX_YUE, "6" VX_YUE, |
| 78 | + "7" VX_YUE, "8" VX_YUE, "9" VX_YUE, "10" VX_YUE, "11" VX_YUE, "12" VX_YUE}; |
| 79 | +const char *const *const kJaLongMonths = kJaShortMonths; |
| 80 | +const char *const kJaShortDays[7] = {VX_RI, VX_YUE_MOON, VX_HUO, VX_SHUI, |
| 81 | + VX_MU, VX_JIN, VX_TU}; |
| 82 | +const char *const kJaLongDays[7] = { |
| 83 | + VX_RI VX_YAO VX_RI, VX_YUE_MOON VX_YAO VX_RI, VX_HUO VX_YAO VX_RI, VX_SHUI VX_YAO VX_RI, |
| 84 | + VX_MU VX_YAO VX_RI, VX_JIN VX_YAO VX_RI, VX_TU VX_YAO VX_RI}; |
| 85 | + |
| 86 | +std::string NormalizeLocale(const std::string &locale) { |
| 87 | + std::string s; |
| 88 | + s.reserve(locale.size()); |
| 89 | + for (char c : locale) { |
| 90 | + if (c == '-') { |
| 91 | + s.push_back('_'); |
| 92 | + } else { |
| 93 | + s.push_back(static_cast<char>(std::tolower(static_cast<unsigned char>(c)))); |
| 94 | + } |
| 95 | + } |
| 96 | + return s; |
| 97 | +} |
| 98 | + |
| 99 | +} // namespace |
| 100 | + |
| 101 | +std::string CanonicalizeLocale(const std::string &locale) { |
| 102 | + const std::string s = NormalizeLocale(locale); |
| 103 | + if (s.empty()) { |
| 104 | + return "en"; |
| 105 | + } |
| 106 | + |
| 107 | + // Exact match on the canonical names first. |
| 108 | + if (s == "en") return "en"; |
| 109 | + if (s == "zh_cn") return "zh_CN"; |
| 110 | + if (s == "ja") return "ja"; |
| 111 | + |
| 112 | + // Language subtag match. |
| 113 | + const std::string lang = s.substr(0, s.find('_')); |
| 114 | + if (lang == "zh") return "zh_CN"; |
| 115 | + if (lang == "ja") return "ja"; |
| 116 | + if (lang == "en") return "en"; |
| 117 | + |
| 118 | + return "en"; |
| 119 | +} |
| 120 | + |
| 121 | +const DateTimeNames &DateTimeNames::ForLocale(const std::string &locale) { |
| 122 | + static const DateTimeNames kEn("en", kEnShortMonths, kEnLongMonths, kEnShortDays, kEnLongDays); |
| 123 | + static const DateTimeNames kZh("zh_CN", kZhShortMonths, kZhLongMonths, kZhShortDays, |
| 124 | + kZhLongDays); |
| 125 | + static const DateTimeNames kJa("ja", kJaShortMonths, kJaLongMonths, kJaShortDays, kJaLongDays); |
| 126 | + |
| 127 | + const std::string canonical = CanonicalizeLocale(locale); |
| 128 | + if (canonical == "zh_CN") return kZh; |
| 129 | + if (canonical == "ja") return kJa; |
| 130 | + return kEn; |
| 131 | +} |
| 132 | + |
| 133 | +const char *DateTimeNames::ShortMonth(int tm_mon) const { |
| 134 | + if (tm_mon < 0 || tm_mon > 11) tm_mon = 0; |
| 135 | + return short_months_[tm_mon]; |
| 136 | +} |
| 137 | + |
| 138 | +const char *DateTimeNames::LongMonth(int tm_mon) const { |
| 139 | + if (tm_mon < 0 || tm_mon > 11) tm_mon = 0; |
| 140 | + return long_months_[tm_mon]; |
| 141 | +} |
| 142 | + |
| 143 | +const char *DateTimeNames::ShortDay(int tm_wday) const { |
| 144 | + if (tm_wday < 0 || tm_wday > 6) tm_wday = 0; |
| 145 | + return short_days_[tm_wday]; |
| 146 | +} |
| 147 | + |
| 148 | +const char *DateTimeNames::LongDay(int tm_wday) const { |
| 149 | + if (tm_wday < 0 || tm_wday > 6) tm_wday = 0; |
| 150 | + return long_days_[tm_wday]; |
| 151 | +} |
| 152 | + |
| 153 | +const char *DateTimeNames::CanonicalName() const { return canonical_name_; } |
| 154 | + |
| 155 | +} // namespace vxcore |
0 commit comments