2021-08-22 20:23:18 +08:00
|
|
|
|
// Copyright (c) 2014-2021 Thomas Fussell
|
2016-07-03 23:22:53 +08:00
|
|
|
|
//
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
//
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
//
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
2021-08-22 20:23:18 +08:00
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
2016-07-03 23:22:53 +08:00
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
// THE SOFTWARE
|
|
|
|
|
//
|
|
|
|
|
// @license: http://www.opensource.org/licenses/mit-license.php
|
|
|
|
|
// @author: see AUTHORS file
|
|
|
|
|
|
2016-07-21 07:47:47 +08:00
|
|
|
|
#include <algorithm>
|
2017-09-13 23:03:18 +08:00
|
|
|
|
#include <cctype>
|
2016-07-03 23:41:59 +08:00
|
|
|
|
#include <cmath>
|
2022-03-19 06:46:27 +08:00
|
|
|
|
#include <limits>
|
2016-07-03 23:41:59 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
#include <xlnt/utils/exceptions.hpp>
|
2020-04-25 10:00:58 +08:00
|
|
|
|
#include <xlnt/utils/numeric.hpp>
|
2016-12-14 15:58:15 +08:00
|
|
|
|
#include <detail/default_case.hpp>
|
2017-04-21 02:03:03 +08:00
|
|
|
|
#include <detail/number_format/number_formatter.hpp>
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
namespace {
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
const std::unordered_map<int, std::string> known_locales()
|
|
|
|
|
{
|
2020-11-20 20:24:47 +08:00
|
|
|
|
static const std::unordered_map<int, std::string> all = std::unordered_map<int, std::string>(
|
2019-09-06 03:37:55 +08:00
|
|
|
|
{
|
|
|
|
|
{0x1, "Arabic"},
|
|
|
|
|
{0x2, "Bulgarian"},
|
|
|
|
|
{0x3, "Catalan"},
|
|
|
|
|
{0x4, "Chinese (Simplified)"},
|
|
|
|
|
{0x4, "Chinese (Simplified) Legacy"},
|
|
|
|
|
{0x5, "Czech"},
|
|
|
|
|
{0x6, "Danish"},
|
|
|
|
|
{0x7, "German"},
|
|
|
|
|
{0x8, "Greek"},
|
|
|
|
|
{0x9, "English"},
|
|
|
|
|
{0xA, "Spanish"},
|
|
|
|
|
{0xB, "Finnish"},
|
|
|
|
|
{0xC, "French"},
|
|
|
|
|
{0xD, "Hebrew"},
|
|
|
|
|
{0xE, "Hungarian"},
|
|
|
|
|
{0xF, "Icelandic"},
|
|
|
|
|
{0x10, "Italian"},
|
|
|
|
|
{0x11, "Japanese"},
|
|
|
|
|
{0x12, "Korean"},
|
|
|
|
|
{0x13, "Dutch"},
|
|
|
|
|
{0x14, "Norwegian"},
|
|
|
|
|
{0x15, "Polish"},
|
|
|
|
|
{0x16, "Portuguese"},
|
|
|
|
|
{0x17, "Romansh"},
|
|
|
|
|
{0x18, "Romanian"},
|
|
|
|
|
{0x19, "Russian"},
|
|
|
|
|
{0x1A, "Croatian"},
|
|
|
|
|
{0x1B, "Slovak"},
|
|
|
|
|
{0x1C, "Albanian"},
|
|
|
|
|
{0x1D, "Swedish"},
|
|
|
|
|
{0x1E, "Thai"},
|
|
|
|
|
{0x1F, "Turkish"},
|
|
|
|
|
{0x20, "Urdu"},
|
|
|
|
|
{0x21, "Indonesian"},
|
|
|
|
|
{0x22, "Ukrainian"},
|
|
|
|
|
{0x23, "Belarusian"},
|
|
|
|
|
{0x24, "Slovenian"},
|
|
|
|
|
{0x25, "Estonian"},
|
|
|
|
|
{0x26, "Latvian"},
|
|
|
|
|
{0x27, "Lithuanian"},
|
|
|
|
|
{0x28, "Tajik"},
|
|
|
|
|
{0x29, "Persian"},
|
|
|
|
|
{0x2A, "Vietnamese"},
|
|
|
|
|
{0x2B, "Armenian"},
|
|
|
|
|
{0x2C, "Azerbaijani"},
|
|
|
|
|
{0x2D, "Basque"},
|
|
|
|
|
{0x2E, "Upper Sorbian"},
|
|
|
|
|
{0x2F, "Macedonian (FYROM)"},
|
|
|
|
|
{0x30, "Southern Sotho"},
|
|
|
|
|
{0x31, "Tsonga"},
|
|
|
|
|
{0x32, "Setswana"},
|
|
|
|
|
{0x33, "Venda"},
|
|
|
|
|
{0x34, "isiXhosa"},
|
|
|
|
|
{0x35, "isiZulu"},
|
|
|
|
|
{0x36, "Afrikaans"},
|
|
|
|
|
{0x37, "Georgian"},
|
|
|
|
|
{0x38, "Faroese"},
|
|
|
|
|
{0x39, "Hindi"},
|
|
|
|
|
{0x3A, "Maltese"},
|
|
|
|
|
{0x3B, "Sami (Northern)"},
|
|
|
|
|
{0x3C, "Irish"},
|
|
|
|
|
{0x3D, "Yiddish"},
|
|
|
|
|
{0x3E, "Malay"},
|
|
|
|
|
{0x3F, "Kazakh"},
|
|
|
|
|
{0x40, "Kyrgyz"},
|
|
|
|
|
{0x41, "Kiswahili"},
|
|
|
|
|
{0x42, "Turkmen"},
|
|
|
|
|
{0x43, "Uzbek"},
|
|
|
|
|
{0x44, "Tatar"},
|
|
|
|
|
{0x45, "Bangla"},
|
|
|
|
|
{0x46, "Punjabi"},
|
|
|
|
|
{0x47, "Gujarati"},
|
|
|
|
|
{0x48, "Odia"},
|
|
|
|
|
{0x49, "Tamil"},
|
|
|
|
|
{0x4A, "Telugu"},
|
|
|
|
|
{0x4B, "Kannada"},
|
|
|
|
|
{0x4C, "Malayalam"},
|
|
|
|
|
{0x4D, "Assamese"},
|
|
|
|
|
{0x4E, "Marathi"},
|
|
|
|
|
{0x4F, "Sanskrit"},
|
|
|
|
|
{0x50, "Mongolian"},
|
|
|
|
|
{0x51, "Tibetan"},
|
|
|
|
|
{0x52, "Welsh"},
|
|
|
|
|
{0x53, "Khmer"},
|
|
|
|
|
{0x54, "Lao"},
|
|
|
|
|
{0x55, "Burmese"},
|
|
|
|
|
{0x56, "Galician"},
|
|
|
|
|
{0x57, "Konkani"},
|
|
|
|
|
{0x58, "Manipuri"},
|
|
|
|
|
{0x59, "Sindhi"},
|
|
|
|
|
{0x5A, "Syriac"},
|
|
|
|
|
{0x5B, "Sinhala"},
|
|
|
|
|
{0x5C, "Cherokee"},
|
|
|
|
|
{0x5D, "Inuktitut"},
|
|
|
|
|
{0x5E, "Amharic"},
|
|
|
|
|
{0x5F, "Tamazight"},
|
|
|
|
|
{0x60, "Kashmiri"},
|
|
|
|
|
{0x61, "Nepali"},
|
|
|
|
|
{0x62, "Frisian"},
|
|
|
|
|
{0x63, "Pashto"},
|
|
|
|
|
{0x64, "Filipino"},
|
|
|
|
|
{0x65, "Divehi"},
|
|
|
|
|
{0x66, "Edo"},
|
|
|
|
|
{0x67, "Fulah"},
|
|
|
|
|
{0x68, "Hausa"},
|
|
|
|
|
{0x69, "Ibibio"},
|
|
|
|
|
{0x6A, "Yoruba"},
|
|
|
|
|
{0x6B, "Quechua"},
|
|
|
|
|
{0x6C, "Sesotho sa Leboa"},
|
|
|
|
|
{0x6D, "Bashkir"},
|
|
|
|
|
{0x6E, "Luxembourgish"},
|
|
|
|
|
{0x6F, "Greenlandic"},
|
|
|
|
|
{0x70, "Igbo"},
|
|
|
|
|
{0x71, "Kanuri"},
|
|
|
|
|
{0x72, "Oromo"},
|
|
|
|
|
{0x73, "Tigrinya"},
|
|
|
|
|
{0x74, "Guarani"},
|
|
|
|
|
{0x75, "Hawaiian"},
|
|
|
|
|
{0x76, "Latin"},
|
|
|
|
|
{0x77, "Somali"},
|
|
|
|
|
{0x78, "Yi"},
|
|
|
|
|
{0x79, "Papiamento"},
|
|
|
|
|
{0x7A, "Mapudungun"},
|
|
|
|
|
{0x7C, "Mohawk"},
|
|
|
|
|
{0x7E, "Breton"},
|
|
|
|
|
{0x7F, "Invariant Language (Invariant Country)"},
|
|
|
|
|
{0x80, "Uyghur"},
|
|
|
|
|
{0x81, "Maori"},
|
|
|
|
|
{0x82, "Occitan"},
|
|
|
|
|
{0x83, "Corsican"},
|
|
|
|
|
{0x84, "Alsatian"},
|
|
|
|
|
{0x85, "Sakha"},
|
|
|
|
|
{0x86, "K’iche’"},
|
|
|
|
|
{0x87, "Kinyarwanda"},
|
|
|
|
|
{0x88, "Wolof"},
|
|
|
|
|
{0x8C, "Dari"},
|
|
|
|
|
{0x91, "Scottish Gaelic"},
|
|
|
|
|
{0x92, "Central Kurdish"},
|
|
|
|
|
{0x401, "Arabic (Saudi Arabia)"},
|
|
|
|
|
{0x402, "Bulgarian (Bulgaria)"},
|
|
|
|
|
{0x403, "Catalan (Catalan)"},
|
|
|
|
|
{0x404, "Chinese (Traditional, Taiwan)"},
|
|
|
|
|
{0x405, "Czech (Czech Republic)"},
|
|
|
|
|
{0x406, "Danish (Denmark)"},
|
|
|
|
|
{0x407, "German (Germany)"},
|
|
|
|
|
{0x408, "Greek (Greece)"},
|
|
|
|
|
{0x409, "English (United States)"},
|
|
|
|
|
{0x40B, "Finnish (Finland)"},
|
|
|
|
|
{0x40C, "French (France)"},
|
|
|
|
|
{0x40D, "Hebrew (Israel)"},
|
|
|
|
|
{0x40E, "Hungarian (Hungary)"},
|
|
|
|
|
{0x40F, "Icelandic (Iceland)"},
|
|
|
|
|
{0x410, "Italian (Italy)"},
|
|
|
|
|
{0x411, "Japanese (Japan)"},
|
|
|
|
|
{0x412, "Korean (Korea)"},
|
|
|
|
|
{0x413, "Dutch (Netherlands)"},
|
|
|
|
|
{0x414, "Norwegian, Bokmål (Norway)"},
|
|
|
|
|
{0x415, "Polish (Poland)"},
|
|
|
|
|
{0x416, "Portuguese (Brazil)"},
|
|
|
|
|
{0x417, "Romansh (Switzerland)"},
|
|
|
|
|
{0x418, "Romanian (Romania)"},
|
|
|
|
|
{0x419, "Russian (Russia)"},
|
|
|
|
|
{0x41A, "Croatian (Croatia)"},
|
|
|
|
|
{0x41B, "Slovak (Slovakia)"},
|
|
|
|
|
{0x41C, "Albanian (Albania)"},
|
|
|
|
|
{0x41D, "Swedish (Sweden)"},
|
|
|
|
|
{0x41E, "Thai (Thailand)"},
|
|
|
|
|
{0x41F, "Turkish (Turkey)"},
|
|
|
|
|
{0x420, "Urdu (Islamic Republic of Pakistan)"},
|
|
|
|
|
{0x421, "Indonesian (Indonesia)"},
|
|
|
|
|
{0x422, "Ukrainian (Ukraine)"},
|
|
|
|
|
{0x423, "Belarusian (Belarus)"},
|
|
|
|
|
{0x424, "Slovenian (Slovenia)"},
|
|
|
|
|
{0x425, "Estonian (Estonia)"},
|
|
|
|
|
{0x426, "Latvian (Latvia)"},
|
|
|
|
|
{0x427, "Lithuanian (Lithuania)"},
|
|
|
|
|
{0x428, "Tajik (Cyrillic, Tajikistan)"},
|
|
|
|
|
{0x429, "Persian (Iran)"},
|
|
|
|
|
{0x42A, "Vietnamese (Vietnam)"},
|
|
|
|
|
{0x42B, "Armenian (Armenia)"},
|
|
|
|
|
{0x42C, "Azerbaijani (Latin, Azerbaijan)"},
|
|
|
|
|
{0x42D, "Basque (Basque)"},
|
|
|
|
|
{0x42E, "Upper Sorbian (Germany)"},
|
|
|
|
|
{0x42F, "Macedonian (Former Yugoslav Republic of Macedonia)"},
|
|
|
|
|
{0x430, "Southern Sotho (South Africa)"},
|
|
|
|
|
{0x431, "Tsonga (South Africa)"},
|
|
|
|
|
{0x432, "Setswana (South Africa)"},
|
|
|
|
|
{0x433, "Venda (South Africa)"},
|
|
|
|
|
{0x434, "isiXhosa (South Africa)"},
|
|
|
|
|
{0x435, "isiZulu (South Africa)"},
|
|
|
|
|
{0x436, "Afrikaans (South Africa)"},
|
|
|
|
|
{0x437, "Georgian (Georgia)"},
|
|
|
|
|
{0x438, "Faroese (Faroe Islands)"},
|
|
|
|
|
{0x439, "Hindi (India)"},
|
|
|
|
|
{0x43A, "Maltese (Malta)"},
|
|
|
|
|
{0x43B, "Sami, Northern (Norway)"},
|
|
|
|
|
{0x43D, "Yiddish (World)"},
|
|
|
|
|
{0x43E, "Malay (Malaysia)"},
|
|
|
|
|
{0x43F, "Kazakh (Kazakhstan)"},
|
|
|
|
|
{0x440, "Kyrgyz (Kyrgyzstan)"},
|
|
|
|
|
{0x441, "Kiswahili (Kenya)"},
|
|
|
|
|
{0x442, "Turkmen (Turkmenistan)"},
|
|
|
|
|
{0x443, "Uzbek (Latin, Uzbekistan)"},
|
|
|
|
|
{0x444, "Tatar (Russia)"},
|
|
|
|
|
{0x445, "Bangla (India)"},
|
|
|
|
|
{0x446, "Punjabi (India)"},
|
|
|
|
|
{0x447, "Gujarati (India)"},
|
|
|
|
|
{0x448, "Odia (India)"},
|
|
|
|
|
{0x449, "Tamil (India)"},
|
|
|
|
|
{0x44A, "Telugu (India)"},
|
|
|
|
|
{0x44B, "Kannada (India)"},
|
|
|
|
|
{0x44C, "Malayalam (India)"},
|
|
|
|
|
{0x44D, "Assamese (India)"},
|
|
|
|
|
{0x44E, "Marathi (India)"},
|
|
|
|
|
{0x44F, "Sanskrit (India)"},
|
|
|
|
|
{0x450, "Mongolian (Cyrillic, Mongolia)"},
|
|
|
|
|
{0x451, "Tibetan (PRC)"},
|
|
|
|
|
{0x452, "Welsh (United Kingdom)"},
|
|
|
|
|
{0x453, "Khmer (Cambodia)"},
|
|
|
|
|
{0x454, "Lao (Lao P.D.R.)"},
|
|
|
|
|
{0x455, "Burmese (Myanmar)"},
|
|
|
|
|
{0x456, "Galician (Galician)"},
|
|
|
|
|
{0x457, "Konkani (India)"},
|
|
|
|
|
{0x458, "Manipuri (India)"},
|
|
|
|
|
{0x459, "Sindhi (Devanagari, India)"},
|
|
|
|
|
{0x45A, "Syriac (Syria)"},
|
|
|
|
|
{0x45B, "Sinhala (Sri Lanka)"},
|
|
|
|
|
{0x45C, "Cherokee (Cherokee)"},
|
|
|
|
|
{0x45D, "Inuktitut (Syllabics, Canada)"},
|
|
|
|
|
{0x45E, "Amharic (Ethiopia)"},
|
|
|
|
|
{0x45F, "Central Atlas Tamazight (Arabic, Morocco)"},
|
|
|
|
|
{0x460, "Kashmiri (Perso-Arabic)"},
|
|
|
|
|
{0x461, "Nepali (Nepal)"},
|
|
|
|
|
{0x462, "Frisian (Netherlands)"},
|
|
|
|
|
{0x463, "Pashto (Afghanistan)"},
|
|
|
|
|
{0x464, "Filipino (Philippines)"},
|
|
|
|
|
{0x465, "Divehi (Maldives)"},
|
|
|
|
|
{0x466, "Edo (Nigeria)"},
|
|
|
|
|
{0x467, "Fulah (Nigeria)"},
|
|
|
|
|
{0x468, "Hausa (Latin, Nigeria)"},
|
|
|
|
|
{0x469, "Ibibio (Nigeria)"},
|
|
|
|
|
{0x46A, "Yoruba (Nigeria)"},
|
|
|
|
|
{0x46B, "Quechua (Bolivia)"},
|
|
|
|
|
{0x46C, "Sesotho sa Leboa (South Africa)"},
|
|
|
|
|
{0x46D, "Bashkir (Russia)"},
|
|
|
|
|
{0x46E, "Luxembourgish (Luxembourg)"},
|
|
|
|
|
{0x46F, "Greenlandic (Greenland)"},
|
|
|
|
|
{0x470, "Igbo (Nigeria)"},
|
|
|
|
|
{0x471, "Kanuri (Nigeria)"},
|
|
|
|
|
{0x472, "Oromo (Ethiopia)"},
|
|
|
|
|
{0x473, "Tigrinya (Ethiopia)"},
|
|
|
|
|
{0x474, "Guarani (Paraguay)"},
|
|
|
|
|
{0x475, "Hawaiian (United States)"},
|
|
|
|
|
{0x476, "Latin (World)"},
|
|
|
|
|
{0x477, "Somali (Somalia)"},
|
|
|
|
|
{0x478, "Yi (PRC)"},
|
|
|
|
|
{0x479, "Papiamento (Caribbean)"},
|
|
|
|
|
{0x47A, "Mapudungun (Chile)"},
|
|
|
|
|
{0x47C, "Mohawk (Mohawk)"},
|
|
|
|
|
{0x47E, "Breton (France)"},
|
|
|
|
|
{0x480, "Uyghur (PRC)"},
|
|
|
|
|
{0x481, "Maori (New Zealand)"},
|
|
|
|
|
{0x482, "Occitan (France)"},
|
|
|
|
|
{0x483, "Corsican (France)"},
|
|
|
|
|
{0x484, "Alsatian (France)"},
|
|
|
|
|
{0x485, "Sakha (Russia)"},
|
|
|
|
|
{0x486, "K’iche’ (Guatemala)"},
|
|
|
|
|
{0x487, "Kinyarwanda (Rwanda)"},
|
|
|
|
|
{0x488, "Wolof (Senegal)"},
|
|
|
|
|
{0x48C, "Dari (Afghanistan)"},
|
|
|
|
|
{0x491, "Scottish Gaelic (United Kingdom)"},
|
|
|
|
|
{0x492, "Central Kurdish (Iraq)"},
|
|
|
|
|
{0x801, "Arabic (Iraq)"},
|
|
|
|
|
{0x803, "Valencian (Spain)"},
|
|
|
|
|
{0x804, "Chinese (Simplified, PRC)"},
|
|
|
|
|
{0x807, "German (Switzerland)"},
|
|
|
|
|
{0x809, "English (United Kingdom)"},
|
|
|
|
|
{0x80A, "Spanish (Mexico)"},
|
|
|
|
|
{0x80C, "French (Belgium)"},
|
|
|
|
|
{0x810, "Italian (Switzerland)"},
|
|
|
|
|
{0x813, "Dutch (Belgium)"},
|
|
|
|
|
{0x814, "Norwegian, Nynorsk (Norway)"},
|
|
|
|
|
{0x816, "Portuguese (Portugal)"},
|
|
|
|
|
{0x818, "Romanian (Moldova)"},
|
|
|
|
|
{0x819, "Russian (Moldova)"},
|
|
|
|
|
{0x81D, "Swedish (Finland)"},
|
|
|
|
|
{0x820, "Urdu (India)"},
|
|
|
|
|
{0x82C, "Azerbaijani (Cyrillic, Azerbaijan)"},
|
|
|
|
|
{0x82E, "Lower Sorbian (Germany)"},
|
|
|
|
|
{0x832, "Setswana (Botswana)"},
|
|
|
|
|
{0x83B, "Sami, Northern (Sweden)"},
|
|
|
|
|
{0x83C, "Irish (Ireland)"},
|
|
|
|
|
{0x83E, "Malay (Brunei Darussalam)"},
|
|
|
|
|
{0x843, "Uzbek (Cyrillic, Uzbekistan)"},
|
|
|
|
|
{0x845, "Bangla (Bangladesh)"},
|
|
|
|
|
{0x846, "Punjabi (Islamic Republic of Pakistan)"},
|
|
|
|
|
{0x849, "Tamil (Sri Lanka)"},
|
|
|
|
|
{0x850, "Mongolian (Traditional Mongolian, PRC)"},
|
|
|
|
|
{0x859, "Sindhi (Islamic Republic of Pakistan)"},
|
|
|
|
|
{0x85D, "Inuktitut (Latin, Canada)"},
|
|
|
|
|
{0x85F, "Tamazight (Latin, Algeria)"},
|
|
|
|
|
{0x860, "Kashmiri (Devanagari, India)"},
|
|
|
|
|
{0x861, "Nepali (India)"},
|
|
|
|
|
{0x867, "Fulah (Latin, Senegal)"},
|
|
|
|
|
{0x86B, "Quechua (Ecuador)"},
|
|
|
|
|
{0x873, "Tigrinya (Eritrea)"},
|
|
|
|
|
{0xC01, "Arabic (Egypt)"},
|
|
|
|
|
{0xC04, "Chinese (Traditional, Hong Kong S.A.R.)"},
|
|
|
|
|
{0xC07, "German (Austria)"},
|
|
|
|
|
{0xC09, "English (Australia)"},
|
|
|
|
|
{0xC0A, "Spanish (Spain)"},
|
|
|
|
|
{0xC0C, "French (Canada)"},
|
|
|
|
|
{0xC3B, "Sami, Northern (Finland)"},
|
|
|
|
|
{0xC50, "Mongolian (Traditional Mongolian, Mongolia)"},
|
|
|
|
|
{0xC51, "Dzongkha (Bhutan)"},
|
|
|
|
|
{0xC6B, "Quechua (Peru)"},
|
|
|
|
|
{0x1001, "Arabic (Libya)"},
|
|
|
|
|
{0x1004, "Chinese (Simplified, Singapore)"},
|
|
|
|
|
{0x1007, "German (Luxembourg)"},
|
|
|
|
|
{0x1009, "English (Canada)"},
|
|
|
|
|
{0x100A, "Spanish (Guatemala)"},
|
|
|
|
|
{0x100C, "French (Switzerland)"},
|
|
|
|
|
{0x101A, "Croatian (Latin, Bosnia and Herzegovina)"},
|
|
|
|
|
{0x103B, "Sami, Lule (Norway)"},
|
|
|
|
|
{0x105F, "Central Atlas Tamazight (Tifinagh, Morocco)"},
|
|
|
|
|
{0x1401, "Arabic (Algeria)"},
|
|
|
|
|
{0x1404, "Chinese (Traditional, Macao S.A.R.)"},
|
|
|
|
|
{0x1407, "German (Liechtenstein)"},
|
|
|
|
|
{0x1409, "English (New Zealand)"},
|
|
|
|
|
{0x140A, "Spanish (Costa Rica)"},
|
|
|
|
|
{0x140C, "French (Luxembourg)"},
|
|
|
|
|
{0x141A, "Bosnian (Latin, Bosnia and Herzegovina)"},
|
|
|
|
|
{0x143B, "Sami, Lule (Sweden)"},
|
|
|
|
|
{0x1801, "Arabic (Morocco)"},
|
|
|
|
|
{0x1809, "English (Ireland)"},
|
|
|
|
|
{0x180A, "Spanish (Panama)"},
|
|
|
|
|
{0x180C, "French (Monaco)"},
|
|
|
|
|
{0x181A, "Serbian (Latin, Bosnia and Herzegovina)"},
|
|
|
|
|
{0x183B, "Sami, Southern (Norway)"},
|
|
|
|
|
{0x1C01, "Arabic (Tunisia)"},
|
|
|
|
|
{0x1C09, "English (South Africa)"},
|
|
|
|
|
{0x1C0A, "Spanish (Dominican Republic)"},
|
|
|
|
|
{0x1C0C, "French (Caribbean)"},
|
|
|
|
|
{0x1C1A, "Serbian (Cyrillic, Bosnia and Herzegovina)"},
|
|
|
|
|
{0x1C3B, "Sami, Southern (Sweden)"},
|
|
|
|
|
{0x2001, "Arabic (Oman)"},
|
|
|
|
|
{0x2009, "English (Jamaica)"},
|
|
|
|
|
{0x200A, "Spanish (Venezuela)"},
|
|
|
|
|
{0x200C, "French (Reunion)"},
|
|
|
|
|
{0x201A, "Bosnian (Cyrillic, Bosnia and Herzegovina)"},
|
|
|
|
|
{0x203B, "Sami, Skolt (Finland)"},
|
|
|
|
|
{0x2401, "Arabic (Yemen)"},
|
|
|
|
|
{0x2409, "English (Caribbean)"},
|
|
|
|
|
{0x240A, "Spanish (Colombia)"},
|
|
|
|
|
{0x240C, "French (Congo DRC)"},
|
|
|
|
|
{0x241A, "Serbian (Latin, Serbia)"},
|
|
|
|
|
{0x243B, "Sami, Inari (Finland)"},
|
|
|
|
|
{0x2801, "Arabic (Syria)"},
|
|
|
|
|
{0x2809, "English (Belize)"},
|
|
|
|
|
{0x280A, "Spanish (Peru)"},
|
|
|
|
|
{0x280C, "French (Senegal)"},
|
|
|
|
|
{0x281A, "Serbian (Cyrillic, Serbia)"},
|
|
|
|
|
{0x2C01, "Arabic (Jordan)"},
|
|
|
|
|
{0x2C09, "English (Trinidad and Tobago)"},
|
|
|
|
|
{0x2C0A, "Spanish (Argentina)"},
|
|
|
|
|
{0x2C0C, "French (Cameroon)"},
|
|
|
|
|
{0x2C1A, "Serbian (Latin, Montenegro)"},
|
|
|
|
|
{0x3001, "Arabic (Lebanon)"},
|
|
|
|
|
{0x3009, "English (Zimbabwe)"},
|
|
|
|
|
{0x300A, "Spanish (Ecuador)"},
|
|
|
|
|
{0x300C, "French (Côte d’Ivoire)"},
|
|
|
|
|
{0x301A, "Serbian (Cyrillic, Montenegro)"},
|
|
|
|
|
{0x3401, "Arabic (Kuwait)"},
|
|
|
|
|
{0x3409, "English (Philippines)"},
|
|
|
|
|
{0x340A, "Spanish (Chile)"},
|
|
|
|
|
{0x340C, "French (Mali)"},
|
|
|
|
|
{0x3801, "Arabic (U.A.E.)"},
|
|
|
|
|
{0x3809, "English (Indonesia)"},
|
|
|
|
|
{0x380A, "Spanish (Uruguay)"},
|
|
|
|
|
{0x380C, "French (Morocco)"},
|
|
|
|
|
{0x3C01, "Arabic (Bahrain)"},
|
|
|
|
|
{0x3C09, "English (Hong Kong SAR)"},
|
|
|
|
|
{0x3C0A, "Spanish (Paraguay)"},
|
|
|
|
|
{0x3C0C, "French (Haiti)"},
|
|
|
|
|
{0x4001, "Arabic (Qatar)"},
|
|
|
|
|
{0x4009, "English (India)"},
|
|
|
|
|
{0x400A, "Spanish (Bolivia)"},
|
|
|
|
|
{0x4409, "English (Malaysia)"},
|
|
|
|
|
{0x440A, "Spanish (El Salvador)"},
|
|
|
|
|
{0x4809, "English (Singapore)"},
|
|
|
|
|
{0x480A, "Spanish (Honduras)"},
|
|
|
|
|
{0x4C0A, "Spanish (Nicaragua)"},
|
|
|
|
|
{0x500A, "Spanish (Puerto Rico)"},
|
|
|
|
|
{0x540A, "Spanish (United States)"},
|
|
|
|
|
{0x580A, "Spanish (Latin America)"},
|
|
|
|
|
{0x5C0A, "Spanish (Cuba)"},
|
|
|
|
|
{0x641A, "Bosnian (Cyrillic)"},
|
|
|
|
|
{0x681A, "Bosnian (Latin)"},
|
|
|
|
|
{0x6C1A, "Serbian (Cyrillic)"},
|
|
|
|
|
{0x701A, "Serbian (Latin)"},
|
|
|
|
|
{0x703B, "Sami (Inari)"},
|
|
|
|
|
{0x742C, "Azerbaijani (Cyrillic)"},
|
|
|
|
|
{0x743B, "Sami (Skolt)"},
|
|
|
|
|
{0x7804, "Chinese"},
|
|
|
|
|
{0x7814, "Norwegian (Nynorsk)"},
|
|
|
|
|
{0x781A, "Bosnian"},
|
|
|
|
|
{0x782C, "Azerbaijani (Latin)"},
|
|
|
|
|
{0x783B, "Sami (Southern)"},
|
|
|
|
|
{0x7843, "Uzbek (Cyrillic)"},
|
|
|
|
|
{0x7850, "Mongolian (Cyrillic)"},
|
|
|
|
|
{0x785D, "Inuktitut (Syllabics)"},
|
|
|
|
|
{0x785F, "Tamazight (Tifinagh)"},
|
|
|
|
|
{0x7C04, "Chinese (Traditional)"},
|
|
|
|
|
{0x7C04, "Chinese (Traditional) Legacy"},
|
|
|
|
|
{0x7C14, "Norwegian (Bokmål)"},
|
|
|
|
|
{0x7C1A, "Serbian"},
|
|
|
|
|
{0x7C28, "Tajik (Cyrillic)"},
|
|
|
|
|
{0x7C2E, "Lower Sorbian"},
|
|
|
|
|
{0x7C3B, "Sami (Lule)"},
|
|
|
|
|
{0x7C43, "Uzbek (Latin)"},
|
|
|
|
|
{0x7C46, "Punjabi (Arabic)"},
|
|
|
|
|
{0x7C50, "Mongolian (Traditional Mongolian)"},
|
|
|
|
|
{0x7C59, "Sindhi (Arabic)"},
|
|
|
|
|
{0x7C5C, "Cherokee (Cherokee)"},
|
|
|
|
|
{0x7C5D, "Inuktitut (Latin)"},
|
|
|
|
|
{0x7C5F, "Tamazight (Latin)"},
|
|
|
|
|
{0x7C67, "Fulah (Latin)"},
|
|
|
|
|
{0x7C68, "Hausa (Latin)"},
|
|
|
|
|
{0x7C86, "K’iche’"},
|
|
|
|
|
{0x7C92, "Central Kurdish (Arabic)"},
|
2021-12-02 02:31:14 +08:00
|
|
|
|
{0xF400, "System Default for Time"},
|
|
|
|
|
{0xF800, "System Default for Long Date"},
|
2019-09-06 03:37:55 +08:00
|
|
|
|
});
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2020-11-20 20:24:47 +08:00
|
|
|
|
return all;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
|
[[noreturn]] void unhandled_case_error()
|
2016-11-01 08:48:43 +08:00
|
|
|
|
{
|
|
|
|
|
throw xlnt::exception("unhandled");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void unhandled_case(bool error)
|
|
|
|
|
{
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
|
unhandled_case_error();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
namespace xlnt {
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
2017-09-09 09:05:34 +08:00
|
|
|
|
bool format_condition::satisfied_by(double number) const
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case condition_type::greater_or_equal:
|
|
|
|
|
return number >= value;
|
|
|
|
|
case condition_type::greater_than:
|
|
|
|
|
return number > value;
|
|
|
|
|
case condition_type::less_or_equal:
|
|
|
|
|
return number <= value;
|
|
|
|
|
case condition_type::less_than:
|
|
|
|
|
return number < value;
|
|
|
|
|
case condition_type::not_equal:
|
2017-09-09 10:59:19 +08:00
|
|
|
|
return std::fabs(number - value) != 0.0;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
case condition_type::equal:
|
2017-09-09 10:59:19 +08:00
|
|
|
|
return std::fabs(number - value) == 0.0;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-14 15:58:15 +08:00
|
|
|
|
|
|
|
|
|
default_case(false);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
number_format_parser::number_format_parser(const std::string &format_string)
|
|
|
|
|
{
|
|
|
|
|
reset(format_string);
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-02 21:37:50 +08:00
|
|
|
|
const std::vector<format_code> &number_format_parser::result() const
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
return codes_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void number_format_parser::reset(const std::string &format_string)
|
|
|
|
|
{
|
|
|
|
|
format_string_ = format_string;
|
|
|
|
|
position_ = 0;
|
|
|
|
|
codes_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void number_format_parser::parse()
|
|
|
|
|
{
|
|
|
|
|
auto token = parse_next_token();
|
|
|
|
|
format_code section;
|
|
|
|
|
template_part part;
|
|
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
switch (token.type)
|
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::end_section: {
|
|
|
|
|
codes_.push_back(section);
|
|
|
|
|
section = format_code();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::color: {
|
|
|
|
|
if (section.has_color || section.has_condition || section.has_locale || !section.parts.empty())
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
throw xlnt::exception("color should be the first part of a format");
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.has_color = true;
|
|
|
|
|
section.color = color_from_string(token.string);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::locale: {
|
|
|
|
|
if (section.has_locale)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
throw xlnt::exception("multiple locales");
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.has_locale = true;
|
|
|
|
|
auto parsed_locale = locale_from_string(token.string);
|
|
|
|
|
section.locale = parsed_locale.first;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (!parsed_locale.second.empty())
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::text;
|
|
|
|
|
part.string = parsed_locale.second;
|
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case number_format_token::token_type::condition: {
|
|
|
|
|
if (section.has_condition)
|
|
|
|
|
{
|
|
|
|
|
throw xlnt::exception("multiple conditions");
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.has_condition = true;
|
|
|
|
|
std::string value;
|
|
|
|
|
|
|
|
|
|
if (token.string.front() == '<')
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (token.string[1] == '=')
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.condition.type = format_condition::condition_type::less_or_equal;
|
|
|
|
|
value = token.string.substr(2);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else if (token.string[1] == '>')
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.condition.type = format_condition::condition_type::not_equal;
|
|
|
|
|
value = token.string.substr(2);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.condition.type = format_condition::condition_type::less_than;
|
|
|
|
|
value = token.string.substr(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (token.string.front() == '>')
|
|
|
|
|
{
|
|
|
|
|
if (token.string[1] == '=')
|
|
|
|
|
{
|
|
|
|
|
section.condition.type = format_condition::condition_type::greater_or_equal;
|
|
|
|
|
value = token.string.substr(2);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.condition.type = format_condition::condition_type::greater_than;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
value = token.string.substr(1);
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else if (token.string.front() == '=')
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.condition.type = format_condition::condition_type::equal;
|
|
|
|
|
value = token.string.substr(1);
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2020-04-25 10:00:58 +08:00
|
|
|
|
detail::number_serialiser ser;
|
|
|
|
|
section.condition.value = ser.deserialise(value);
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::text: {
|
|
|
|
|
part.type = template_part::template_type::text;
|
|
|
|
|
part.string = token.string;
|
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::fill: {
|
|
|
|
|
part.type = template_part::template_type::fill;
|
|
|
|
|
part.string = token.string;
|
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::space: {
|
|
|
|
|
part.type = template_part::template_type::space;
|
|
|
|
|
part.string = token.string;
|
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case number_format_token::token_type::number: {
|
|
|
|
|
part.type = template_part::template_type::general;
|
|
|
|
|
part.placeholders = parse_placeholders(token.string);
|
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case number_format_token::token_type::datetime: {
|
|
|
|
|
section.is_datetime = true;
|
|
|
|
|
|
|
|
|
|
switch (token.string.front())
|
|
|
|
|
{
|
|
|
|
|
case '[':
|
|
|
|
|
section.is_timedelta = true;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (token.string == "[h]" || token.string == "[hh]")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::elapsed_hours;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "[m]" || token.string == "[mm]")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::elapsed_minutes;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "[s]" || token.string == "[ss]")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::elapsed_seconds;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 'm':
|
|
|
|
|
if (token.string == "m")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::month_number;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "mm")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::month_number_leading_zero;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "mmm")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::month_abbreviation;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "mmmm")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::month_name;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "mmmmm")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::month_letter;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 'd':
|
|
|
|
|
if (token.string == "d")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::day_number;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "dd")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::day_number_leading_zero;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "ddd")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::day_abbreviation;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "dddd")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::day_name;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 'y':
|
|
|
|
|
if (token.string == "yy")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::year_short;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "yyyy")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::year_long;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 'h':
|
|
|
|
|
if (token.string == "h")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::hour;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string == "hh")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::hour_leading_zero;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 's':
|
|
|
|
|
if (token.string == "s")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::second;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
|
|
|
|
else if (token.string == "ss")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::second_leading_zero;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case 'A':
|
|
|
|
|
section.twelve_hour = true;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (token.string == "AM/PM")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::am_pm;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
}
|
|
|
|
|
else if (token.string == "A/P")
|
|
|
|
|
{
|
|
|
|
|
part.type = template_part::template_type::a_p;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
default:
|
|
|
|
|
unhandled_case(true);
|
2016-11-01 08:48:43 +08:00
|
|
|
|
break;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
section.parts.push_back(part);
|
|
|
|
|
part = template_part();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case number_format_token::token_type::end: {
|
|
|
|
|
codes_.push_back(section);
|
|
|
|
|
finalize();
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token = parse_next_token();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void number_format_parser::finalize()
|
|
|
|
|
{
|
|
|
|
|
for (auto &code : codes_)
|
|
|
|
|
{
|
|
|
|
|
bool fix = false;
|
|
|
|
|
bool leading_zero = false;
|
|
|
|
|
std::size_t minutes_index = 0;
|
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
bool integer_part = false;
|
|
|
|
|
bool fractional_part = false;
|
|
|
|
|
std::size_t integer_part_index = 0;
|
|
|
|
|
|
|
|
|
|
bool percentage = false;
|
|
|
|
|
|
|
|
|
|
bool exponent = false;
|
|
|
|
|
std::size_t exponent_index = 0;
|
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
bool fraction = false;
|
|
|
|
|
std::size_t fraction_denominator_index = 0;
|
|
|
|
|
std::size_t fraction_numerator_index = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
bool seconds = false;
|
|
|
|
|
bool fractional_seconds = false;
|
|
|
|
|
std::size_t seconds_index = 0;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
for (std::size_t i = 0; i < code.parts.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
const auto &part = code.parts[i];
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
|
|
if (i > 0 && i + 1 < code.parts.size() && part.type == template_part::template_type::text
|
2016-07-09 07:39:28 +08:00
|
|
|
|
&& part.string == "/"
|
|
|
|
|
&& code.parts[i - 1].placeholders.type == format_placeholders::placeholders_type::integer_part
|
|
|
|
|
&& code.parts[i + 1].placeholders.type == format_placeholders::placeholders_type::integer_part)
|
|
|
|
|
{
|
|
|
|
|
fraction = true;
|
|
|
|
|
fraction_numerator_index = i - 1;
|
|
|
|
|
fraction_denominator_index = i + 1;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (part.placeholders.type == format_placeholders::placeholders_type::integer_part)
|
|
|
|
|
{
|
|
|
|
|
integer_part = true;
|
|
|
|
|
integer_part_index = i;
|
|
|
|
|
}
|
|
|
|
|
else if (part.placeholders.type == format_placeholders::placeholders_type::fractional_part)
|
|
|
|
|
{
|
|
|
|
|
fractional_part = true;
|
|
|
|
|
}
|
|
|
|
|
else if (part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_plus
|
|
|
|
|
|| part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_minus)
|
|
|
|
|
{
|
|
|
|
|
exponent = true;
|
|
|
|
|
exponent_index = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (part.placeholders.percentage)
|
|
|
|
|
{
|
|
|
|
|
percentage = true;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
if (part.type == template_part::template_type::second
|
|
|
|
|
|| part.type == template_part::template_type::second_leading_zero)
|
|
|
|
|
{
|
|
|
|
|
seconds = true;
|
|
|
|
|
seconds_index = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (seconds && part.placeholders.type == format_placeholders::placeholders_type::fractional_part)
|
|
|
|
|
{
|
|
|
|
|
fractional_seconds = true;
|
|
|
|
|
}
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
|
// TODO this block needs improvement
|
2016-07-03 23:22:53 +08:00
|
|
|
|
if (part.type == template_part::template_type::month_number
|
|
|
|
|
|| part.type == template_part::template_type::month_number_leading_zero)
|
|
|
|
|
{
|
2016-07-05 00:09:51 +08:00
|
|
|
|
if (code.parts.size() > 1 && i < code.parts.size() - 2)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
const auto &next = code.parts[i + 1];
|
|
|
|
|
const auto &after_next = code.parts[i + 2];
|
2016-07-05 00:09:51 +08:00
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if ((next.type == template_part::template_type::second
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|| next.type == template_part::template_type::second_leading_zero)
|
|
|
|
|
|| (next.type == template_part::template_type::text && next.string == ":"
|
2019-12-27 01:03:12 +08:00
|
|
|
|
&& (after_next.type == template_part::template_type::second
|
|
|
|
|
|| after_next.type == template_part::template_type::second_leading_zero)))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
fix = true;
|
|
|
|
|
leading_zero = part.type == template_part::template_type::month_number_leading_zero;
|
|
|
|
|
minutes_index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if (!fix && i > 1)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
const auto &previous = code.parts[i - 1];
|
|
|
|
|
const auto &before_previous = code.parts[i - 2];
|
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
|
if (previous.type == template_part::template_type::text && previous.string == ":"
|
2016-07-30 23:58:36 +08:00
|
|
|
|
&& (before_previous.type == template_part::template_type::hour_leading_zero
|
2019-12-27 01:03:12 +08:00
|
|
|
|
|| before_previous.type == template_part::template_type::hour))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
fix = true;
|
|
|
|
|
leading_zero = part.type == template_part::template_type::month_number_leading_zero;
|
|
|
|
|
minutes_index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
if (fix)
|
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
code.parts[minutes_index].type =
|
|
|
|
|
leading_zero ? template_part::template_type::minute_leading_zero : template_part::template_type::minute;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
|
|
|
|
if (integer_part && !fractional_part)
|
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
code.parts[integer_part_index].placeholders.type = format_placeholders::placeholders_type::integer_only;
|
2016-07-06 09:27:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (integer_part && fractional_part && percentage)
|
|
|
|
|
{
|
|
|
|
|
code.parts[integer_part_index].placeholders.percentage = true;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (exponent)
|
|
|
|
|
{
|
|
|
|
|
const auto &next = code.parts[exponent_index + 1];
|
|
|
|
|
auto temp = code.parts[exponent_index].placeholders.type;
|
|
|
|
|
code.parts[exponent_index].placeholders = next.placeholders;
|
|
|
|
|
code.parts[exponent_index].placeholders.type = temp;
|
2016-11-27 22:44:41 +08:00
|
|
|
|
code.parts.erase(code.parts.begin() + static_cast<std::ptrdiff_t>(exponent_index + 1));
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < code.parts.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
code.parts[i].placeholders.scientific = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
if (fraction)
|
|
|
|
|
{
|
|
|
|
|
code.parts[fraction_numerator_index].placeholders.type =
|
|
|
|
|
format_placeholders::placeholders_type::fraction_numerator;
|
|
|
|
|
code.parts[fraction_denominator_index].placeholders.type =
|
|
|
|
|
format_placeholders::placeholders_type::fraction_denominator;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
for (std::size_t i = 0; i < code.parts.size(); ++i)
|
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
if (code.parts[i].placeholders.type == format_placeholders::placeholders_type::integer_part)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
code.parts[i].placeholders.type = format_placeholders::placeholders_type::fraction_integer;
|
2016-07-09 07:39:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
if (fractional_seconds)
|
|
|
|
|
{
|
|
|
|
|
if (code.parts[seconds_index].type == template_part::template_type::second)
|
|
|
|
|
{
|
|
|
|
|
code.parts[seconds_index].type = template_part::template_type::second_fractional;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
code.parts[seconds_index].type = template_part::template_type::second_leading_zero_fractional;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
validate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
number_format_token number_format_parser::parse_next_token()
|
|
|
|
|
{
|
|
|
|
|
number_format_token token;
|
2019-12-27 01:03:12 +08:00
|
|
|
|
|
|
|
|
|
auto to_lower = [](char c) {
|
2017-09-13 22:47:18 +08:00
|
|
|
|
return static_cast<char>(std::tolower(static_cast<std::uint8_t>(c)));
|
|
|
|
|
};
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
if (format_string_.size() <= position_)
|
|
|
|
|
{
|
|
|
|
|
token.type = number_format_token::token_type::end;
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto current_char = format_string_[position_++];
|
|
|
|
|
|
|
|
|
|
switch (current_char)
|
|
|
|
|
{
|
|
|
|
|
case '[':
|
2016-07-14 07:20:31 +08:00
|
|
|
|
if (position_ == format_string_.size())
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("missing ]");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if (format_string_[position_] == ']')
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("empty []");
|
2016-07-21 07:04:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-14 07:20:31 +08:00
|
|
|
|
do
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-14 07:20:31 +08:00
|
|
|
|
token.string.push_back(format_string_[position_++]);
|
2016-12-24 23:04:57 +08:00
|
|
|
|
} while (position_ < format_string_.size() && format_string_[position_] != ']');
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if (token.string[0] == '<' || token.string[0] == '>' || token.string[0] == '=')
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
token.type = number_format_token::token_type::condition;
|
|
|
|
|
}
|
|
|
|
|
else if (token.string[0] == '$')
|
|
|
|
|
{
|
|
|
|
|
token.type = number_format_token::token_type::locale;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
else if (token.string.size() <= 2
|
|
|
|
|
&& ((token.string == "h" || token.string == "hh") || (token.string == "m" || token.string == "mm")
|
2019-12-27 01:03:12 +08:00
|
|
|
|
|| (token.string == "s" || token.string == "ss")))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
token.type = number_format_token::token_type::datetime;
|
|
|
|
|
token.string = "[" + token.string + "]";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
token.type = number_format_token::token_type::color;
|
|
|
|
|
color_from_string(token.string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++position_;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(format_string_[position_++]);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'G':
|
|
|
|
|
if (format_string_.substr(position_ - 1, 7) != "General")
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("expected General");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token.type = number_format_token::token_type::number;
|
|
|
|
|
token.string = "General";
|
|
|
|
|
position_ += 6;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '_':
|
|
|
|
|
token.type = number_format_token::token_type::space;
|
|
|
|
|
token.string.push_back(format_string_[position_++]);
|
|
|
|
|
|
|
|
|
|
break;
|
2016-07-12 06:10:57 +08:00
|
|
|
|
|
|
|
|
|
case '*':
|
|
|
|
|
token.type = number_format_token::token_type::fill;
|
|
|
|
|
token.string.push_back(format_string_[position_++]);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '0':
|
|
|
|
|
case '#':
|
|
|
|
|
case '?':
|
|
|
|
|
case '.':
|
|
|
|
|
token.type = number_format_token::token_type::number;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
token.string.push_back(current_char);
|
2016-07-06 09:27:35 +08:00
|
|
|
|
current_char = format_string_[position_++];
|
2016-12-24 23:04:57 +08:00
|
|
|
|
} while (current_char == '0' || current_char == '#' || current_char == '?' || current_char == ',');
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
--position_;
|
|
|
|
|
|
|
|
|
|
if (current_char == '%')
|
|
|
|
|
{
|
|
|
|
|
token.string.push_back('%');
|
|
|
|
|
++position_;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
break;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'y':
|
2017-09-13 22:47:18 +08:00
|
|
|
|
case 'Y':
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'm':
|
2017-09-13 22:47:18 +08:00
|
|
|
|
case 'M':
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'd':
|
2017-09-13 22:47:18 +08:00
|
|
|
|
case 'D':
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'h':
|
2017-09-13 22:47:18 +08:00
|
|
|
|
case 'H':
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 's':
|
2017-09-13 22:47:18 +08:00
|
|
|
|
case 'S':
|
2016-07-03 23:22:53 +08:00
|
|
|
|
token.type = number_format_token::token_type::datetime;
|
2017-09-13 22:47:18 +08:00
|
|
|
|
token.string.push_back(to_lower(current_char));
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
while (format_string_[position_] == current_char)
|
|
|
|
|
{
|
2017-09-13 22:47:18 +08:00
|
|
|
|
token.string.push_back(to_lower(current_char));
|
2016-07-03 23:22:53 +08:00
|
|
|
|
++position_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'A':
|
|
|
|
|
token.type = number_format_token::token_type::datetime;
|
|
|
|
|
|
|
|
|
|
if (format_string_.substr(position_ - 1, 5) == "AM/PM")
|
|
|
|
|
{
|
|
|
|
|
position_ += 4;
|
|
|
|
|
token.string = "AM/PM";
|
|
|
|
|
}
|
|
|
|
|
else if (format_string_.substr(position_ - 1, 3) == "A/P")
|
|
|
|
|
{
|
|
|
|
|
position_ += 2;
|
|
|
|
|
token.string = "A/P";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("expected AM/PM or A/P");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case '"': {
|
2016-07-03 23:22:53 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
auto start = position_;
|
|
|
|
|
auto end = format_string_.find('"', position_);
|
|
|
|
|
|
|
|
|
|
while (end != std::string::npos && format_string_[end - 1] == '\\')
|
|
|
|
|
{
|
2016-07-12 06:10:57 +08:00
|
|
|
|
token.string.append(format_string_.substr(start, end - start - 1));
|
|
|
|
|
token.string.push_back('"');
|
|
|
|
|
position_ = end + 1;
|
|
|
|
|
start = position_;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
end = format_string_.find('"', position_);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 06:10:57 +08:00
|
|
|
|
if (end != start)
|
|
|
|
|
{
|
|
|
|
|
token.string.append(format_string_.substr(start, end - start));
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
position_ = end + 1;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
|
token.type = number_format_token::token_type::end_section;
|
|
|
|
|
break;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '(':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case ')':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '-':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '+':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case ':':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case ' ':
|
2016-11-01 08:48:43 +08:00
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '/':
|
|
|
|
|
token.type = number_format_token::token_type::text;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
|
|
|
|
|
break;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case '@':
|
|
|
|
|
token.type = number_format_token::token_type::number;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
break;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
case 'E':
|
|
|
|
|
token.type = number_format_token::token_type::number;
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
current_char = format_string_[position_++];
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (current_char == '+' || current_char == '-')
|
|
|
|
|
{
|
|
|
|
|
token.string.push_back(current_char);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
default:
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("unexpected character");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void number_format_parser::validate()
|
|
|
|
|
{
|
|
|
|
|
if (codes_.size() > 4)
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("too many format codes");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (codes_.size() > 2)
|
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
if (codes_[0].has_condition && codes_[1].has_condition && codes_[2].has_condition)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("format should have a maximum of two codes with conditions");
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:34:44 +08:00
|
|
|
|
format_placeholders number_format_parser::parse_placeholders(const std::string &placeholders_string)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
format_placeholders p;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
if (placeholders_string == "General")
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
p.type = format_placeholders::placeholders_type::general;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
else if (placeholders_string == "@")
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
p.type = format_placeholders::placeholders_type::text;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
else if (placeholders_string.front() == '.')
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
p.type = format_placeholders::placeholders_type::fractional_part;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-07-06 09:27:35 +08:00
|
|
|
|
else if (placeholders_string.front() == 'E')
|
|
|
|
|
{
|
2016-12-24 23:04:57 +08:00
|
|
|
|
p.type = placeholders_string[1] == '+' ? format_placeholders::placeholders_type::scientific_exponent_plus
|
|
|
|
|
: format_placeholders::placeholders_type::scientific_exponent_minus;
|
2016-07-06 09:27:35 +08:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
p.type = format_placeholders::placeholders_type::integer_part;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (placeholders_string.back() == '%')
|
|
|
|
|
{
|
|
|
|
|
p.percentage = true;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
std::vector<std::size_t> comma_indices;
|
|
|
|
|
|
|
|
|
|
for (std::size_t i = 0; i < placeholders_string.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
auto c = placeholders_string[i];
|
|
|
|
|
|
|
|
|
|
if (c == '0')
|
|
|
|
|
{
|
|
|
|
|
++p.num_zeros;
|
|
|
|
|
}
|
|
|
|
|
else if (c == '#')
|
|
|
|
|
{
|
|
|
|
|
++p.num_optionals;
|
|
|
|
|
}
|
|
|
|
|
else if (c == '?')
|
|
|
|
|
{
|
|
|
|
|
++p.num_spaces;
|
|
|
|
|
}
|
|
|
|
|
else if (c == ',')
|
|
|
|
|
{
|
|
|
|
|
comma_indices.push_back(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
if (!comma_indices.empty())
|
|
|
|
|
{
|
2016-07-12 06:10:57 +08:00
|
|
|
|
std::size_t i = placeholders_string.size() - 1;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-12 06:10:57 +08:00
|
|
|
|
while (!comma_indices.empty() && i == comma_indices.back())
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
++p.thousands_scale;
|
|
|
|
|
--i;
|
|
|
|
|
comma_indices.pop_back();
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
p.use_comma_separator = !comma_indices.empty();
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:34:44 +08:00
|
|
|
|
format_color number_format_parser::color_from_string(const std::string &color)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
switch (color[0])
|
|
|
|
|
{
|
|
|
|
|
case 'C':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Cyan") || (color == "CYAN"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
return format_color::cyan;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2021-08-17 05:46:39 +08:00
|
|
|
|
else if ((color.substr(0, 5) == "Color") || (color.substr(0, 5) == "COLOR"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
auto color_number = std::stoull(color.substr(5));
|
|
|
|
|
|
|
|
|
|
if (color_number >= 1 && color_number <= 56)
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
return static_cast<format_color>(color_number);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-01 08:48:43 +08:00
|
|
|
|
|
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'B':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Black") || (color == "BLACK"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
return format_color::black;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2021-08-17 05:46:39 +08:00
|
|
|
|
else if ((color == "Blue") || (color == "BLUE"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
return format_color::blue;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'G':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Green") || (color == "GREEN"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-21 07:04:44 +08:00
|
|
|
|
return format_color::green;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'W':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "White") || (color == "WHITE"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
return format_color::white;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'M':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Magenta") || (color == "MAGENTA"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-21 07:04:44 +08:00
|
|
|
|
return format_color::magenta;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'Y':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Yellow") || (color == "YELLOW"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-21 07:04:44 +08:00
|
|
|
|
return format_color::yellow;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
case 'R':
|
2021-08-17 05:46:39 +08:00
|
|
|
|
if ((color == "Red") || (color == "RED"))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-21 07:04:44 +08:00
|
|
|
|
return format_color::red;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
|
|
|
|
break;
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
default:
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case(true);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-11-01 08:48:43 +08:00
|
|
|
|
unhandled_case_error();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:34:44 +08:00
|
|
|
|
std::pair<format_locale, std::string> number_format_parser::locale_from_string(const std::string &locale_string)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
auto hyphen_index = locale_string.find('-');
|
|
|
|
|
|
|
|
|
|
if (locale_string.empty() || locale_string.front() != '$' || hyphen_index == std::string::npos)
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("bad locale: " + locale_string);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:34:44 +08:00
|
|
|
|
std::pair<format_locale, std::string> result;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
if (hyphen_index > 1)
|
|
|
|
|
{
|
|
|
|
|
result.second = locale_string.substr(1, hyphen_index - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto country_code_string = locale_string.substr(hyphen_index + 1);
|
|
|
|
|
|
|
|
|
|
if (country_code_string.empty())
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("bad locale: " + locale_string);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
for (auto c : country_code_string)
|
|
|
|
|
{
|
|
|
|
|
if (!((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9')))
|
|
|
|
|
{
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("bad locale: " + locale_string);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto country_code = std::stoi(country_code_string, nullptr, 16);
|
2019-09-07 17:32:49 +08:00
|
|
|
|
country_code &= 0xFFFF;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
for (const auto &known_locale : known_locales())
|
|
|
|
|
{
|
|
|
|
|
if (known_locale.first == country_code)
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
result.first = static_cast<format_locale>(country_code);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-13 20:48:22 +08:00
|
|
|
|
throw xlnt::exception("unknown country code: " + country_code_string);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
number_formatter::number_formatter(const std::string &format_string, xlnt::calendar calendar)
|
2016-12-24 23:04:57 +08:00
|
|
|
|
: parser_(format_string), calendar_(calendar)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
parser_.parse();
|
2016-12-02 21:37:50 +08:00
|
|
|
|
format_ = parser_.result();
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-09 09:05:34 +08:00
|
|
|
|
std::string number_formatter::format_number(double number)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-30 23:58:36 +08:00
|
|
|
|
if (format_[0].has_condition)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
if (format_[0].condition.satisfied_by(number))
|
|
|
|
|
{
|
|
|
|
|
return format_number(format_[0], number);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (format_.size() == 1)
|
|
|
|
|
{
|
|
|
|
|
return std::string(11, '#');
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
|
|
|
|
if (!format_[1].has_condition || format_[1].condition.satisfied_by(number))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
return format_number(format_[1], number);
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
if (format_.size() == 2)
|
|
|
|
|
{
|
|
|
|
|
return std::string(11, '#');
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return format_number(format_[2], number);
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
// no conditions, format based on sign:
|
|
|
|
|
|
|
|
|
|
// 1 section, use for all
|
|
|
|
|
if (format_.size() == 1)
|
|
|
|
|
{
|
|
|
|
|
return format_number(format_[0], number);
|
|
|
|
|
}
|
|
|
|
|
// 2 sections, first for positive and zero, second for negative
|
|
|
|
|
else if (format_.size() == 2)
|
|
|
|
|
{
|
|
|
|
|
if (number >= 0)
|
|
|
|
|
{
|
|
|
|
|
return format_number(format_[0], number);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-07-03 23:41:59 +08:00
|
|
|
|
return format_number(format_[1], std::fabs(number));
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 3+ sections, first for positive, second for negative, third for zero
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (number > 0)
|
|
|
|
|
{
|
|
|
|
|
return format_number(format_[0], number);
|
|
|
|
|
}
|
|
|
|
|
else if (number < 0)
|
|
|
|
|
{
|
2016-07-03 23:41:59 +08:00
|
|
|
|
return format_number(format_[1], std::fabs(number));
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return format_number(format_[2], number);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string number_formatter::format_text(const std::string &text)
|
|
|
|
|
{
|
|
|
|
|
if (format_.size() < 4)
|
|
|
|
|
{
|
2016-07-09 07:39:28 +08:00
|
|
|
|
format_code temp;
|
|
|
|
|
template_part temp_part;
|
|
|
|
|
temp_part.type = template_part::template_type::general;
|
|
|
|
|
temp_part.placeholders.type = format_placeholders::placeholders_type::general;
|
|
|
|
|
temp.parts.push_back(temp_part);
|
|
|
|
|
return format_text(temp, text);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return format_text(format_[3], text);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-09 09:05:34 +08:00
|
|
|
|
std::string number_formatter::fill_placeholders(const format_placeholders &p, double number)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-30 23:58:36 +08:00
|
|
|
|
std::string result;
|
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if (p.type == format_placeholders::placeholders_type::general
|
|
|
|
|
|| p.type == format_placeholders::placeholders_type::text)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2020-04-26 10:53:15 +08:00
|
|
|
|
auto s = serialiser_.serialise_short(number);
|
|
|
|
|
while (s.size() > 1 && s.back() == '0')
|
|
|
|
|
{
|
|
|
|
|
s.pop_back();
|
|
|
|
|
}
|
|
|
|
|
if (s.back() == '.')
|
|
|
|
|
{
|
|
|
|
|
s.pop_back();
|
|
|
|
|
}
|
|
|
|
|
return s;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (p.percentage)
|
|
|
|
|
{
|
|
|
|
|
number *= 100;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 06:10:57 +08:00
|
|
|
|
if (p.thousands_scale > 0)
|
|
|
|
|
{
|
2017-09-10 21:36:04 +08:00
|
|
|
|
number /= std::pow(1000.0, p.thousands_scale);
|
2016-07-12 06:10:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
auto integer_part = static_cast<int>(number);
|
|
|
|
|
|
2016-07-30 23:58:36 +08:00
|
|
|
|
if (p.type == format_placeholders::placeholders_type::integer_only
|
|
|
|
|
|| p.type == format_placeholders::placeholders_type::integer_part
|
|
|
|
|
|| p.type == format_placeholders::placeholders_type::fraction_integer)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-30 23:58:36 +08:00
|
|
|
|
result = std::to_string(integer_part);
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
while (result.size() < p.num_zeros)
|
|
|
|
|
{
|
|
|
|
|
result = "0" + result;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
while (result.size() < p.num_zeros + p.num_spaces)
|
|
|
|
|
{
|
|
|
|
|
result = " " + result;
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
if (p.use_comma_separator)
|
|
|
|
|
{
|
|
|
|
|
std::vector<char> digits(result.rbegin(), result.rend());
|
|
|
|
|
std::string temp;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
for (std::size_t i = 0; i < digits.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
temp.push_back(digits[i]);
|
|
|
|
|
|
|
|
|
|
if (i % 3 == 2)
|
|
|
|
|
{
|
|
|
|
|
temp.push_back(',');
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
result = std::string(temp.rbegin(), temp.rend());
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (p.percentage && p.type == format_placeholders::placeholders_type::integer_only)
|
|
|
|
|
{
|
|
|
|
|
result.push_back('%');
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-07-30 23:58:36 +08:00
|
|
|
|
else if (p.type == format_placeholders::placeholders_type::fractional_part)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
auto fractional_part = number - integer_part;
|
2017-09-09 09:05:34 +08:00
|
|
|
|
result = std::fabs(fractional_part) < std::numeric_limits<double>::min()
|
2016-12-24 23:04:57 +08:00
|
|
|
|
? std::string(".")
|
2020-04-25 10:00:58 +08:00
|
|
|
|
: serialiser_.serialise_short(fractional_part).substr(1);
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
2016-07-30 23:58:36 +08:00
|
|
|
|
while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + p.num_spaces + 1))
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
result.pop_back();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 10:00:58 +08:00
|
|
|
|
|
|
|
|
|
if (result.size() < p.num_zeros + 1)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2020-04-25 10:00:58 +08:00
|
|
|
|
result.resize(p.num_zeros + 1, '0');
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-25 10:00:58 +08:00
|
|
|
|
if (result.size() < p.num_zeros + p.num_optionals + p.num_spaces + 1)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2020-04-25 10:00:58 +08:00
|
|
|
|
result.resize(p.num_zeros + p.num_optionals + p.num_spaces + 1, ' ');
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (p.percentage)
|
|
|
|
|
{
|
|
|
|
|
result.push_back('%');
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2016-07-30 23:58:36 +08:00
|
|
|
|
return result;
|
2016-07-21 07:04:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string number_formatter::fill_scientific_placeholders(const format_placeholders &integer_part,
|
2017-09-09 09:05:34 +08:00
|
|
|
|
const format_placeholders &fractional_part, const format_placeholders &exponent_part, double number)
|
2016-07-21 07:04:44 +08:00
|
|
|
|
{
|
2016-07-21 07:47:47 +08:00
|
|
|
|
std::size_t logarithm = 0;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2017-09-09 10:59:19 +08:00
|
|
|
|
if (number != 0.0)
|
2016-07-06 09:27:35 +08:00
|
|
|
|
{
|
2016-11-10 08:52:18 +08:00
|
|
|
|
logarithm = static_cast<std::size_t>(std::log10(number));
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
if (integer_part.num_zeros + integer_part.num_optionals > 1)
|
2016-07-06 09:27:35 +08:00
|
|
|
|
{
|
2016-07-21 07:04:44 +08:00
|
|
|
|
logarithm = integer_part.num_zeros + integer_part.num_optionals;
|
2016-07-06 09:27:35 +08:00
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
}
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
2017-09-10 21:36:04 +08:00
|
|
|
|
number /= std::pow(10.0, logarithm);
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
auto integer = static_cast<int>(number);
|
|
|
|
|
auto fraction = number - integer;
|
2016-07-06 09:27:35 +08:00
|
|
|
|
|
2016-07-21 07:04:44 +08:00
|
|
|
|
std::string integer_string = std::to_string(integer);
|
|
|
|
|
|
2017-09-09 10:59:19 +08:00
|
|
|
|
if (number == 0.0)
|
2016-07-21 07:04:44 +08:00
|
|
|
|
{
|
|
|
|
|
integer_string = std::string(integer_part.num_zeros + integer_part.num_optionals, '0');
|
2016-07-06 09:27:35 +08:00
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2020-04-25 10:00:58 +08:00
|
|
|
|
std::string fractional_string = serialiser_.serialise_short(fraction).substr(1, fractional_part.num_zeros + fractional_part.num_optionals + 1);
|
2016-07-21 07:04:44 +08:00
|
|
|
|
std::string exponent_string = std::to_string(logarithm);
|
|
|
|
|
|
|
|
|
|
while (exponent_string.size() < fractional_part.num_zeros)
|
|
|
|
|
{
|
|
|
|
|
exponent_string.insert(0, "0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exponent_part.type == format_placeholders::placeholders_type::scientific_exponent_plus)
|
|
|
|
|
{
|
|
|
|
|
exponent_string.insert(0, "E+");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
exponent_string.insert(0, "E");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return integer_string + fractional_string + exponent_string;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-24 23:04:57 +08:00
|
|
|
|
std::string number_formatter::fill_fraction_placeholders(const format_placeholders & /*numerator*/,
|
2017-09-09 09:05:34 +08:00
|
|
|
|
const format_placeholders &denominator, double number, bool /*improper*/)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
|
|
|
|
auto fractional_part = number - static_cast<int>(number);
|
|
|
|
|
auto original_fractional_part = fractional_part;
|
|
|
|
|
fractional_part *= 10;
|
|
|
|
|
|
2017-09-09 10:59:19 +08:00
|
|
|
|
while (std::abs(fractional_part - static_cast<int>(fractional_part)) > 0.000001
|
|
|
|
|
&& std::abs(fractional_part - static_cast<int>(fractional_part)) < 0.999999)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
|
|
|
|
fractional_part *= 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fractional_part = static_cast<int>(fractional_part);
|
|
|
|
|
auto denominator_digits = denominator.num_zeros + denominator.num_optionals + denominator.num_spaces;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
// auto denominator_digits = static_cast<std::size_t>(std::ceil(std::log10(fractional_part)));
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
|
|
|
|
auto lower = static_cast<int>(std::pow(10, denominator_digits - 1));
|
|
|
|
|
auto upper = static_cast<int>(std::pow(10, denominator_digits));
|
|
|
|
|
auto best_denominator = lower;
|
2017-09-09 10:59:19 +08:00
|
|
|
|
auto best_difference = 1000.0;
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
|
|
|
|
for (int i = lower; i < upper; ++i)
|
|
|
|
|
{
|
|
|
|
|
auto numerator_full = original_fractional_part * i;
|
|
|
|
|
auto numerator_rounded = static_cast<int>(std::round(numerator_full));
|
2017-09-09 09:05:34 +08:00
|
|
|
|
auto difference = std::fabs(original_fractional_part - (numerator_rounded / static_cast<double>(i)));
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
if (difference < best_difference)
|
|
|
|
|
{
|
|
|
|
|
best_difference = difference;
|
|
|
|
|
best_denominator = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto numerator_rounded = static_cast<int>(std::round(original_fractional_part * best_denominator));
|
|
|
|
|
return std::to_string(numerator_rounded) + "/" + std::to_string(best_denominator);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-09 09:05:34 +08:00
|
|
|
|
std::string number_formatter::format_number(const format_code &format, double number)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2020-11-20 20:24:47 +08:00
|
|
|
|
static const std::vector<std::string> month_names = std::vector<std::string>{"January", "February", "March",
|
2016-12-24 23:04:57 +08:00
|
|
|
|
"April", "May", "June", "July", "August", "September", "October", "November", "December"};
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2020-11-20 20:24:47 +08:00
|
|
|
|
static const std::vector<std::string> day_names =
|
|
|
|
|
std::vector<std::string>{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
std::string result;
|
|
|
|
|
|
|
|
|
|
if (number < 0)
|
|
|
|
|
{
|
|
|
|
|
result.push_back('-');
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
|
|
|
|
if (format.is_datetime)
|
|
|
|
|
{
|
|
|
|
|
return std::string(11, '#');
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:41:59 +08:00
|
|
|
|
number = std::fabs(number);
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
xlnt::datetime dt(0, 1, 0);
|
|
|
|
|
std::size_t hour = 0;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
if (format.is_datetime)
|
|
|
|
|
{
|
2017-09-09 10:59:19 +08:00
|
|
|
|
if (number != 0.0)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
|
|
|
|
dt = xlnt::datetime::from_number(number, calendar_);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 08:52:18 +08:00
|
|
|
|
hour = static_cast<std::size_t>(dt.hour);
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
|
|
|
|
if (format.twelve_hour)
|
|
|
|
|
{
|
|
|
|
|
hour %= 12;
|
|
|
|
|
|
|
|
|
|
if (hour == 0)
|
|
|
|
|
{
|
|
|
|
|
hour = 12;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
bool improper_fraction = true;
|
2016-07-12 06:10:57 +08:00
|
|
|
|
std::size_t fill_index = 0;
|
|
|
|
|
bool fill = false;
|
|
|
|
|
std::string fill_character;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2016-07-09 07:39:28 +08:00
|
|
|
|
for (std::size_t i = 0; i < format.parts.size(); ++i)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2016-07-09 07:39:28 +08:00
|
|
|
|
const auto &part = format.parts[i];
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
switch (part.type)
|
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::space: {
|
|
|
|
|
result.push_back(' ');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::text: {
|
|
|
|
|
result.append(part.string);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::fill: {
|
|
|
|
|
fill = true;
|
|
|
|
|
fill_index = result.size();
|
|
|
|
|
fill_character = part.string;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::general: {
|
|
|
|
|
if (part.placeholders.type == format_placeholders::placeholders_type::fractional_part
|
|
|
|
|
&& (format.is_datetime || format.is_timedelta))
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
auto digits = std::min(
|
|
|
|
|
static_cast<std::size_t>(6), part.placeholders.num_zeros + part.placeholders.num_optionals);
|
|
|
|
|
auto denominator = static_cast<int>(std::pow(10.0, digits));
|
|
|
|
|
auto fractional_seconds = dt.microsecond / 1.0E6 * denominator;
|
|
|
|
|
fractional_seconds = std::round(fractional_seconds) / denominator;
|
|
|
|
|
result.append(fill_placeholders(part.placeholders, fractional_seconds));
|
2017-03-21 07:22:46 +08:00
|
|
|
|
break;
|
2016-07-09 07:39:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (part.placeholders.type == format_placeholders::placeholders_type::fraction_integer)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
improper_fraction = false;
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (part.placeholders.type == format_placeholders::placeholders_type::fraction_numerator)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
i += 2;
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
if (number == 0.0)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.pop_back();
|
|
|
|
|
break;
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(fill_fraction_placeholders(
|
|
|
|
|
part.placeholders, format.parts[i].placeholders, number, improper_fraction));
|
2016-07-21 07:04:44 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else if (part.placeholders.scientific
|
|
|
|
|
&& part.placeholders.type == format_placeholders::placeholders_type::integer_part)
|
2016-07-09 07:39:28 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
auto integer_part = part.placeholders;
|
|
|
|
|
++i;
|
|
|
|
|
auto fractional_part = format.parts[i++].placeholders;
|
|
|
|
|
auto exponent_part = format.parts[i++].placeholders;
|
|
|
|
|
result.append(fill_scientific_placeholders(integer_part, fractional_part, exponent_part, number));
|
2016-07-09 07:39:28 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(fill_placeholders(part.placeholders, number));
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::day_number: {
|
|
|
|
|
result.append(std::to_string(dt.day));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::day_number_leading_zero: {
|
|
|
|
|
if (dt.day < 10)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.day));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::month_abbreviation: {
|
2020-11-20 20:24:47 +08:00
|
|
|
|
result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1).substr(0, 3));
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::month_name: {
|
2020-11-20 20:24:47 +08:00
|
|
|
|
result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1));
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::month_number: {
|
|
|
|
|
result.append(std::to_string(dt.month));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::month_number_leading_zero: {
|
|
|
|
|
if (dt.month < 10)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.month));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::year_short: {
|
|
|
|
|
if (dt.year % 1000 < 10)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.year % 1000));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::year_long: {
|
|
|
|
|
result.append(std::to_string(dt.year));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::hour: {
|
|
|
|
|
result.append(std::to_string(hour));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::hour_leading_zero: {
|
|
|
|
|
if (hour < 10)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(hour));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::minute: {
|
|
|
|
|
result.append(std::to_string(dt.minute));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::minute_leading_zero: {
|
|
|
|
|
if (dt.minute < 10)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.minute));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::second: {
|
|
|
|
|
result.append(std::to_string(dt.second + (dt.microsecond > 500000 ? 1 : 0)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-03-21 07:22:46 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::second_fractional: {
|
|
|
|
|
result.append(std::to_string(dt.second));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-09 07:39:28 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::second_leading_zero: {
|
|
|
|
|
if ((dt.second + (dt.microsecond > 500000 ? 1 : 0)) < 10)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.second + (dt.microsecond > 500000 ? 1 : 0)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::second_leading_zero_fractional: {
|
|
|
|
|
if (dt.second < 10)
|
2016-07-21 07:04:44 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.push_back('0');
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append(std::to_string(dt.second));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::am_pm: {
|
|
|
|
|
if (dt.hour < 12)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append("AM");
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append("PM");
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-21 07:04:44 +08:00
|
|
|
|
|
2019-12-27 01:03:12 +08:00
|
|
|
|
case template_part::template_type::a_p: {
|
|
|
|
|
if (dt.hour < 12)
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append("A");
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
else
|
2017-03-21 07:22:46 +08:00
|
|
|
|
{
|
2019-12-27 01:03:12 +08:00
|
|
|
|
result.append("P");
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
2019-12-27 01:03:12 +08:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::elapsed_hours: {
|
|
|
|
|
result.append(std::to_string(24 * static_cast<int>(number) + dt.hour));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::elapsed_minutes: {
|
|
|
|
|
result.append(std::to_string(24 * 60 * static_cast<int>(number)
|
|
|
|
|
+ (60 * dt.hour) + dt.minute));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::elapsed_seconds: {
|
|
|
|
|
result.append(std::to_string(24 * 60 * 60 * static_cast<int>(number)
|
|
|
|
|
+ (60 * 60 * dt.hour) + (60 * dt.minute) + dt.second));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::month_letter: {
|
2020-11-20 20:24:47 +08:00
|
|
|
|
result.append(month_names.at(static_cast<std::size_t>(dt.month) - 1).substr(0, 1));
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::day_abbreviation: {
|
2020-11-20 20:24:47 +08:00
|
|
|
|
result.append(day_names.at(static_cast<std::size_t>(dt.weekday())).substr(0, 3));
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case template_part::template_type::day_name: {
|
2020-11-20 20:24:47 +08:00
|
|
|
|
result.append(day_names.at(static_cast<std::size_t>(dt.weekday())));
|
2019-12-27 01:03:12 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-12 06:10:57 +08:00
|
|
|
|
const std::size_t width = 11;
|
2016-12-24 23:04:57 +08:00
|
|
|
|
|
2016-07-12 06:10:57 +08:00
|
|
|
|
if (fill && result.size() < width)
|
|
|
|
|
{
|
|
|
|
|
auto remaining = width - result.size();
|
|
|
|
|
|
|
|
|
|
std::string fill_string(remaining, fill_character.front());
|
2016-07-21 07:04:44 +08:00
|
|
|
|
// TODO: A UTF-8 character could be multiple bytes
|
2016-07-12 06:10:57 +08:00
|
|
|
|
|
|
|
|
|
result = result.substr(0, fill_index) + fill_string + result.substr(fill_index);
|
|
|
|
|
}
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string number_formatter::format_text(const format_code &format, const std::string &text)
|
|
|
|
|
{
|
|
|
|
|
std::string result;
|
2016-07-06 09:27:35 +08:00
|
|
|
|
bool any_text_part = false;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
|
|
|
|
|
for (const auto &part : format.parts)
|
|
|
|
|
{
|
2017-03-21 07:22:46 +08:00
|
|
|
|
if (part.type == template_part::template_type::text)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
result.append(part.string);
|
2016-07-06 09:27:35 +08:00
|
|
|
|
any_text_part = true;
|
2017-03-21 07:22:46 +08:00
|
|
|
|
}
|
|
|
|
|
else if (part.type == template_part::template_type::general)
|
|
|
|
|
{
|
2016-07-03 23:34:44 +08:00
|
|
|
|
if (part.placeholders.type == format_placeholders::placeholders_type::general
|
|
|
|
|
|| part.placeholders.type == format_placeholders::placeholders_type::text)
|
2016-07-03 23:22:53 +08:00
|
|
|
|
{
|
|
|
|
|
result.append(text);
|
2016-07-06 09:27:35 +08:00
|
|
|
|
any_text_part = true;
|
2016-07-03 23:22:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 09:27:35 +08:00
|
|
|
|
if (!format.parts.empty() && !any_text_part)
|
|
|
|
|
{
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-03 23:22:53 +08:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
} // namespace xlnt
|