• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xlnt-community / xlnt / 2fb57988-1891-456a-9f50-ba8202ac9608

27 Jan 2026 07:00AM UTC coverage: 83.928% (+1.1%) from 82.793%
2fb57988-1891-456a-9f50-ba8202ac9608

Pull #87

circleci

doomlaur
Slightly clarified character encodings in README.md
Pull Request #87: Improve documentation when exceptions are thrown (fixes #81)

15263 of 19954 branches covered (76.49%)

722 of 901 new or added lines in 37 files covered. (80.13%)

15 existing lines in 5 files now uncovered.

12491 of 14883 relevant lines covered (83.93%)

12216.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.27
./source/styles/number_format.cpp
1
// Copyright (c) 2014-2022 Thomas Fussell
2
// Copyright (c) 2010-2015 openpyxl
3
// Copyright (c) 2024-2025 xlnt-community
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
// THE SOFTWARE
22
//
23
// @license: http://www.opensource.org/licenses/mit-license.php
24
// @author: see AUTHORS file
25

26
#include <unordered_map>
27
#include <vector>
28

29
#include <xlnt/styles/number_format.hpp>
30
#include <xlnt/utils/datetime.hpp>
31
#include <xlnt/utils/exceptions.hpp>
32
#include <detail/number_format/number_formatter.hpp>
33

34
namespace {
35

36
const std::unordered_map<std::size_t, xlnt::number_format> &builtin_formats()
909✔
37
{
38
    static std::unordered_map<std::size_t, xlnt::number_format> formats;
909!
39

40
    if (formats.size() == 0)
909✔
41
    {
42
        const std::unordered_map<std::size_t, std::string> format_strings{
43
            {0, "General"},
×
44
            {1, "0"},
×
45
            {2, "0.00"},
×
46
            {3, "#,##0"},
×
47
            {4, "#,##0.00"},
×
48
            {9, "0%"},
×
49
            {10, "0.00%"},
×
50
            {11, "0.00E+00"},
×
51
            {12, "# ?/?"},
×
52
            {13, "# \?\?/??"}, // escape trigraph
×
53
            {14, "mm-dd-yy"},
×
54
            {15, "d-mmm-yy"},
×
55
            {16, "d-mmm"},
×
56
            {17, "mmm-yy"},
×
57
            {18, "h:mm AM/PM"},
×
58
            {19, "h:mm:ss AM/PM"},
×
59
            {20, "h:mm"},
×
60
            {21, "h:mm:ss"},
×
61
            {22, "m/d/yy h:mm"},
×
62
            {37, "#,##0 ;(#,##0)"},
×
63
            {38, "#,##0 ;[Red](#,##0)"},
×
64
            {39, "#,##0.00;(#,##0.00)"},
×
65
            {40, "#,##0.00;[Red](#,##0.00)"},
×
66

67
            // 41-44 aren't in the ECMA-376 5th edition, but Libre Office uses them
68
            {41, "_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)"},
×
69
            {42, "_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)"},
×
70
            {43, "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)"},
×
71
            {44, "_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)"},
×
72

73
            {45, "mm:ss"},
×
74
            {46, "[h]:mm:ss"},
×
75
            {47, "mmss.0"},
×
76
            {48, "##0.0E+0"},
×
NEW
77
            {49, "@"},
×
78

79
            // Note: general builtin formats go up to ID 49, but some languages have formats up to ID 81 (see OOXML section 18.8.30).
80
            // Note: custom formats have IDs 164 and higher.
81
        };
34!
82

83
        for (const auto &format_string_pair : format_strings)
33✔
84
        {
85
            formats[format_string_pair.first] =
32✔
86
                xlnt::number_format(format_string_pair.second, format_string_pair.first);
64✔
87
        }
88
    }
1✔
89

90
    return formats;
909✔
91
}
1!
92

93
} // namespace
94

95
namespace xlnt {
96

97
const number_format number_format::general()
681✔
98
{
99
    return builtin_formats().at(0);
681✔
100
}
101

102
const number_format number_format::text()
1✔
103
{
104
    return builtin_formats().at(49);
1✔
105
}
106

107
const number_format number_format::number()
1✔
108
{
109
    return builtin_formats().at(1);
1✔
110
}
111

112
const number_format number_format::number_00()
1✔
113
{
114
    return builtin_formats().at(2);
1✔
115
}
116

117
const number_format number_format::number_comma_separated1()
1✔
118
{
119
    return builtin_formats().at(4);
1✔
120
}
121

122
const number_format number_format::percentage()
6✔
123
{
124
    return builtin_formats().at(9);
6✔
125
}
126

127
const number_format number_format::percentage_00()
1✔
128
{
129
    return builtin_formats().at(10);
1✔
130
}
131

132
const number_format number_format::date_yyyymmdd2()
3✔
133
{
134
    static const number_format format = number_format("yyyy-mm-dd");
5!
135
    return format;
3✔
136
}
137

138
const number_format number_format::date_yymmdd()
1✔
139
{
140
    static const number_format format = number_format("yy-mm-dd");
3!
141
    return format;
1✔
142
}
143

144
const number_format number_format::date_ddmmyyyy()
6✔
145
{
146
    static const number_format format = number_format("dd/mm/yy");
8!
147
    return format;
6✔
148
}
149

150
const number_format number_format::date_dmyslash()
1✔
151
{
152
    static const number_format format = number_format("d/m/yy");
3!
153
    return format;
1✔
154
}
155

156
const number_format number_format::date_dmyminus()
1✔
157
{
158
    static const number_format format = number_format("d-m-yy");
3!
159
    return format;
1✔
160
}
161

162
const number_format number_format::date_dmminus()
3✔
163
{
164
    static const number_format format = number_format("d-m");
5!
165
    return format;
3✔
166
}
167

168
const number_format number_format::date_myminus()
1✔
169
{
170
    static const number_format format = number_format("m-yy");
3!
171
    return format;
1✔
172
}
173

174
const number_format number_format::date_xlsx14()
1✔
175
{
176
    return builtin_formats().at(14);
1✔
177
}
178

179
const number_format number_format::date_xlsx15()
1✔
180
{
181
    return builtin_formats().at(15);
1✔
182
}
183

184
const number_format number_format::date_xlsx16()
1✔
185
{
186
    return builtin_formats().at(16);
1✔
187
}
188

189
const number_format number_format::date_xlsx17()
1✔
190
{
191
    return builtin_formats().at(17);
1✔
192
}
193

194
const number_format number_format::date_xlsx22()
1✔
195
{
196
    return builtin_formats().at(22);
1✔
197
}
198

199
const number_format number_format::date_datetime()
7✔
200
{
201
    static const number_format format = number_format("yyyy-mm-dd h:mm:ss");
9!
202
    return format;
7✔
203
}
204

205
const number_format number_format::date_time1()
1✔
206
{
207
    return builtin_formats().at(18);
1✔
208
}
209

210
const number_format number_format::date_time2()
5✔
211
{
212
    return builtin_formats().at(19);
5✔
213
}
214

215
const number_format number_format::date_time3()
1✔
216
{
217
    return builtin_formats().at(20);
1✔
218
}
219

220
const number_format number_format::date_time4()
2✔
221
{
222
    return builtin_formats().at(21);
2✔
223
}
224

225
const number_format number_format::date_time5()
1✔
226
{
227
    return builtin_formats().at(45);
1✔
228
}
229

230
const number_format number_format::date_time6()
5✔
231
{
232
    return builtin_formats().at(21);
5✔
233
}
234

235
number_format::number_format()
155✔
236
    : number_format("General", 0)
155✔
237
{
238
}
155✔
239

240
number_format::number_format(std::size_t id)
2✔
241
    : number_format(from_builtin_id(id))
2✔
242
{
243
}
2✔
244

245
number_format::number_format(const std::string &format_string)
22✔
246
    : format_string_(format_string)
22✔
247
{
248
}
22✔
249

250
number_format::number_format(const std::string &format, std::size_t id)
189✔
251
{
252
    format_string(format, id);
189✔
253
}
189✔
254

255
bool number_format::is_builtin_format(std::size_t builtin_id)
23✔
256
{
257
    return builtin_formats().find(builtin_id) != builtin_formats().end();
23✔
258
}
259

260
const number_format &number_format::from_builtin_id(std::size_t builtin_id)
19✔
261
{
262
    auto format = builtin_formats().find(builtin_id);
19✔
263

264
    if (format == builtin_formats().end())
19✔
265
    {
266
        throw invalid_parameter("invalid builtin ID " + std::to_string(builtin_id) + " for number format");
1✔
267
    }
268

269
    return format->second;
36✔
270
}
271

272
std::string number_format::format_string() const
3,591✔
273
{
274
    return format_string_;
3,591✔
275
}
276

277
void number_format::format_string(const std::string &format_string)
113✔
278
{
279
    format_string_ = format_string;
113✔
280

281
    for (const auto &pair : builtin_formats())
3,677✔
282
    {
283
        if (pair.second.format_string() == format_string)
3,578✔
284
        {
285
            id_ = pair.first;
14✔
286
            break;
14✔
287
        }
288
    }
289
}
113✔
290

291
void number_format::format_string(const std::string &format_string, std::size_t id)
189✔
292
{
293
    format_string_ = format_string;
189✔
294
    id_ = id;
189✔
295
}
189✔
296

297
bool number_format::has_id() const
2,320✔
298
{
299
    return id_.is_set();
2,320✔
300
}
301

302
void number_format::id(std::size_t id)
49✔
303
{
304
    id_ = id;
49✔
305
}
49✔
306

307
std::size_t number_format::id() const
1,592✔
308
{
309
    if (!has_id())
1,592✔
310
    {
311
        throw invalid_attribute("the number format has no ID");
6✔
312
    }
313

314
    return id_.get();
1,590✔
315
}
316

317
bool number_format::is_date_format() const
4✔
318
{
319
    detail::number_format_parser p(format_string_);
4✔
320
    p.parse();
4✔
321
    auto parsed = p.result();
4✔
322

323
    bool any_datetime = false;
4✔
324
    bool any_timedelta = false;
4✔
325

326
    for (const auto &section : parsed)
8✔
327
    {
328
        if (section.is_datetime)
4!
329
        {
330
            any_datetime = true;
4✔
331
        }
332

333
        if (section.is_timedelta)
4✔
334
        {
335
            any_timedelta = true;
1✔
336
        }
337
    }
338

339
    return any_datetime && !any_timedelta;
8!
340
}
4✔
341

342
std::string number_format::format(const std::string &text) const
76✔
343
{
344
    return detail::number_formatter(format_string_, calendar::windows_1900).format_text(text);
76✔
345
}
346

347
std::string number_format::format(double number, calendar base_date) const
218✔
348
{
349
    return detail::number_formatter(format_string_, base_date).format_number(number);
218✔
350
}
351

352
bool number_format::operator==(const number_format &other) const
47✔
353
{
354
    return format_string_ == other.format_string_;
47✔
355
}
356

357
bool number_format::operator!=(const number_format &other) const
×
358
{
359
    return !(*this == other);
×
360
}
361

362
} // namespace xlnt
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc