fix precision on time to number, fix type guessing, fix long long on osx

pull/14/head
Thomas Fussell 2015-10-06 12:31:49 -04:00
parent 4001a47ec0
commit 77d6bbb41b
10 changed files with 191 additions and 252 deletions

View File

@ -123,11 +123,11 @@ public:
void set_value(std::uint32_t i);
void set_value(std::uint64_t i);
#ifdef _WIN32
void set_value(unsigned long i);
void set_value(unsigned long i);
#endif
#ifdef __linux__
void set_value(long long i);
void set_value(unsigned long long i);
void set_value(long long i);
void set_value(unsigned long long i);
#endif
void set_value(float f);
void set_value(double d);

View File

@ -1,98 +0,0 @@
// Copyright (c) 2014 Thomas Fussell
// Copyright (c) 2010-2014 openpyxl
//
// 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
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// 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
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include "../styles/style.hpp"
#include "../common/types.hpp"
namespace xlnt {
struct date;
struct datetime;
struct time;
class cell_value
{
public:
enum class type
{
null,
numeric,
string,
formula,
boolean,
error
};
std::string to_string() const;
type get_data_type() const;
void set_null();
cell_value &operator=(const cell_value &rhs);
cell_value &operator=(bool value);
cell_value &operator=(int value);
cell_value &operator=(double value);
cell_value &operator=(long int value);
cell_value &operator=(long long value);
cell_value &operator=(long double value);
cell_value &operator=(const std::string &value);
cell_value &operator=(const char *value);
cell_value &operator=(const date &value);
cell_value &operator=(const time &value);
cell_value &operator=(const datetime &value);
bool operator==(const cell_value &comparand) const;
bool operator==(std::nullptr_t) const;
bool operator==(bool comparand) const;
bool operator==(int comparand) const;
bool operator==(double comparand) const;
bool operator==(const std::string &comparand) const;
bool operator==(const char *comparand) const;
bool operator==(const date &comparand) const;
bool operator==(const time &comparand) const;
bool operator==(const datetime &comparand) const;
friend bool operator==(std::nullptr_t, const cell_value &cell);
friend bool operator==(bool comparand, const cell_value &cell);
friend bool operator==(int comparand, const cell_value &cell);
friend bool operator==(double comparand, const cell_value &cell);
friend bool operator==(const std::string &comparand, const cell_value &cell);
friend bool operator==(const char *comparand, const cell_value &cell);
friend bool operator==(const date &comparand, const cell_value &cell);
friend bool operator==(const time &comparand, const cell_value &cell);
friend bool operator==(const datetime &comparand, const cell_value &cell);
};
inline std::ostream &operator<<(std::ostream &stream, const xlnt::cell_value &value)
{
return stream << value.to_string();
}
} // namespace xlnt

View File

@ -81,6 +81,10 @@ public:
bool operator==(std::uint64_t comparand) const;
#ifdef _WIN32
bool operator==(unsigned long comparand) const;
#endif
#ifdef __linux__
bool operator==(long long comparand) const;
bool operator==(unsigned long long comparand) const;
#endif
bool operator==(float comparand) const;
bool operator==(double comparand) const;
@ -103,6 +107,10 @@ public:
friend bool operator==(std::uint64_t comparand, const value &v);
#ifdef _WIN32
friend bool operator==(unsigned long comparand, const value &v);
#endif
#ifdef __linux__
friend bool operator==(long long comparand, const value &v);
friend bool operator==(unsigned long long comparand, const value &v);
#endif
friend bool operator==(float comparand, const value &v);
friend bool operator==(double comparand, const value &v);

View File

@ -61,7 +61,7 @@ struct time
}
explicit time(const std::string &time_string);
double to_number() const;
long double to_number() const;
bool operator==(const time &comparand) const;
int hour;
@ -80,7 +80,7 @@ struct datetime
{
}
double to_number(calendar base_date) const;
long double to_number(calendar base_date) const;
bool operator==(const datetime &comparand) const;
int year;

View File

@ -94,8 +94,9 @@ public:
format get_format_code() const { return format_code_; }
void set_format_code(format format_code) { format_code_ = format_code; }
void set_format_code(const std::string &format_code) { custom_format_code_ = format_code; }
void set_format_code_string(const std::string &format_code) { custom_format_code_ = format_code; }
std::string get_format_code_string();
private:
std::string custom_format_code_ = "";
format format_code_ = format::general;

View File

@ -274,16 +274,26 @@ void cell::set_value(unsigned long long i)
}
#endif
void cell::set_value(float f)
{
d_->value_ = value(f);
}
void cell::set_value(double d)
{
d_->value_ = value(d);
}
void cell::set_value(long double d)
{
d_->value_ = value(d);
}
void cell::set_value(const date &d)
{
d_->is_date_ = true;
auto date_format_code = xlnt::number_format::lookup_format(14);
auto number_format = xlnt::number_format(date_format_code);
auto code = xlnt::number_format::format::date_yyyymmdd2;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
auto base_date = get_parent().get_parent().get_properties().excel_base_date;
set_value(d.to_number(base_date));
@ -292,8 +302,8 @@ void cell::set_value(const date &d)
void cell::set_value(const datetime &d)
{
d_->is_date_ = true;
auto date_format_code = xlnt::number_format::lookup_format(22);
auto number_format = xlnt::number_format(date_format_code);
auto code = xlnt::number_format::format::date_datetime;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
auto base_date = get_parent().get_parent().get_properties().excel_base_date;
set_value(d.to_number(base_date));
@ -302,6 +312,9 @@ void cell::set_value(const datetime &d)
void cell::set_value(const time &t)
{
d_->is_date_ = true;
auto code = xlnt::number_format::format::date_time6;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
set_value(t.to_number());
}

View File

@ -121,16 +121,14 @@ time::time(const std::string &time_string) : hour(0), minute(0), second(0), micr
}
}
double time::to_number() const
long double time::to_number() const
{
double number = microsecond;
number /= 1000000;
number += second;
number /= 60;
number += minute;
number /= 60;
number += hour;
number /= 24;
std::size_t microseconds = microsecond;
microseconds += second * 1e6;
microseconds += minute * 1e6 * 60;
microseconds += hour * 1e6 * 60 * 60;
auto number = microseconds / (24.0L * 60 * 60 * 1e6L);
number = std::floor(number * 1e11L + 0.5L) / 1e11L;
return number;
}
@ -159,7 +157,7 @@ int date::to_number(calendar base_date) const
return days_since_1900;
}
double datetime::to_number(calendar base_date) const
long double datetime::to_number(calendar base_date) const
{
return date(year, month, day).to_number(base_date)
+ time(hour, minute, second, microsecond).to_number();

View File

@ -3,49 +3,6 @@
#include <xlnt/styles/number_format.hpp>
namespace xlnt {
const std::unordered_map<number_format::format, std::string, number_format::format_hash> &number_format::format_strings()
{
static const std::unordered_map<number_format::format, std::string, number_format::format_hash> strings =
{
{format::general, "General"},
{format::text, "@"},
{format::number, "0"},
{format::number_00, "0.00"},
{format::number_comma_separated1, "#,##0.00"},
{format::number_comma_separated2, "#,##0.00_-"},
{format::percentage, "0%"},
{format::percentage_00, "0.00%"},
{format::date_yyyymmdd2, "yyyy-mm-dd"},
{format::date_yyyymmdd, "yy-mm-dd"},
{format::date_ddmmyyyy, "dd/mm/yy"},
{format::date_dmyslash, "d/m/y"},
{format::date_dmyminus, "d-m-y"},
{format::date_dmminus, "d-m"},
{format::date_myminus, "m-y"},
{format::date_xlsx14, "mm-dd-yy"},
{format::date_xlsx15, "d-mmm-yy"},
{format::date_xlsx16, "d-mmm"},
{format::date_xlsx17, "mmm-yy"},
{format::date_xlsx22, "m/d/yy h:mm"},
{format::date_datetime, "d/m/y h:mm"},
{format::date_time1, "h:mm AM/PM"},
{format::date_time2, "h:mm:ss AM/PM"},
{format::date_time3, "h:mm"},
{format::date_time4, "h:mm:ss"},
{format::date_time5, "mm:ss"},
{format::date_time6, "h:mm:ss"},
{format::date_time7, "i:s.S"},
{format::date_time8, "h:mm:ss@"},
{format::date_timedelta, "[hh]:mm:ss"},
{format::date_yyyymmddslash, "yy/mm/dd@"},
{format::currency_usd_simple, "\"$\"#,##0.00_-"},
{format::currency_usd, "$#,##0_-"},
{format::currency_eur_simple, "[$EUR ]#,##0.00_-"}
};
return strings;
}
const std::unordered_map<int, std::string> &number_format::builtin_formats()
{
@ -105,51 +62,58 @@ const std::unordered_map<int, std::string> &number_format::builtin_formats()
return formats;
}
const std::unordered_map<std::string, int> &number_format::reversed_builtin_formats()
const std::unordered_map<number_format::format, std::string, number_format::format_hash> &number_format::format_strings()
{
static const std::unordered_map<std::string, int> formats =
static const std::unordered_map<number_format::format, std::string, number_format::format_hash> strings =
{
{"General", 0},
{"0", 1},
{"0.00", 2},
{"#,##0", 3},
{"#,##0.00", 4},
{"\"$\"#,##0_);(\"$\"#,##0)", 5},
{"\"$\"#,##0_);[Red](\"$\"#,##0)", 6},
{"\"$\"#,##0.00_);(\"$\"#,##0.00)", 7},
{"\"$\"#,##0.00_);[Red](\"$\"#,##0.00)", 8},
{"0%", 9},
{"0.00%", 10},
{"0.00E+00", 11},
{"# ?/?", 12},
{"# \?\?/??", 13}, //escape trigraph
{"mm-dd-yy", 14},
{"d-mmm-yy", 15},
{"d-mmm", 16},
{"mmm-yy", 17},
{"h:mm AM/PM", 18},
{"h:mm:ss AM/PM", 19},
{"h:mm", 20},
{"h:mm:ss", 21},
{"m/d/yy h:mm", 22},
{"#,##0_);(#,##0)", 37},
{"#,##0_);[Red](#,##0)", 38},
{"#,##0.00_);(#,##0.00)", 39},
{"#,##0.00_);[Red](#,##0.00)", 40},
{"_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)", 41},
{"_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)", 42},
{"_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)", 43},
{"_(\"$\"* #,##0.00_)_(\"$\"* \\(#,##0.00\\)_(\"$\"* \"-\"??_)_(@_)", 44},
{"mm:ss", 45},
{"[h]:mm:ss", 46},
{"mmss.0", 47},
{"##0.0E+0", 48},
{"@", 49}
{format::general, builtin_formats().at(0)},
{format::text, builtin_formats().at(49)},
{format::number, builtin_formats().at(1)},
{format::number_00, builtin_formats().at(2)},
{format::number_comma_separated1, builtin_formats().at(4)},
{format::number_comma_separated2, "#,##0.00_-"},
{format::percentage, builtin_formats().at(9)},
{format::percentage_00, builtin_formats().at(10)},
{format::date_yyyymmdd2, "yyyy-mm-dd"},
{format::date_yyyymmdd, "yy-mm-dd"},
{format::date_ddmmyyyy, "dd/mm/yy"},
{format::date_dmyslash, "d/m/y"},
{format::date_dmyminus, "d-m-y"},
{format::date_dmminus, "d-m"},
{format::date_myminus, "m-y"},
{format::date_xlsx14, builtin_formats().at(14)},
{format::date_xlsx15, builtin_formats().at(15)},
{format::date_xlsx16, builtin_formats().at(16)},
{format::date_xlsx17, builtin_formats().at(17)},
{format::date_xlsx22, builtin_formats().at(22)},
{format::date_datetime, "yyyy-mm-dd h:mm:ss"},
{format::date_time1, builtin_formats().at(18)},
{format::date_time2, builtin_formats().at(19)},
{format::date_time3, builtin_formats().at(20)},
{format::date_time4, builtin_formats().at(21)},
{format::date_time5, builtin_formats().at(45)},
{format::date_time6, builtin_formats().at(21)},
{format::date_time7, "i:s.S"},
{format::date_time8, "h:mm:ss@"},
{format::date_timedelta, "[hh]:mm:ss"},
{format::date_yyyymmddslash, "yy/mm/dd@"},
{format::currency_usd_simple, "\"$\"#,##0.00_-"},
{format::currency_usd, "$#,##0_-"},
{format::currency_eur_simple, "[$EUR ]#,##0.00_-"}
};
return strings;
}
const std::unordered_map<std::string, int> &number_format::reversed_builtin_formats()
{
static std::unordered_map<std::string, int> formats;
for(auto format_pair : builtin_formats())
{
formats[format_pair.second] = format_pair.first;
}
return formats;
}
@ -171,4 +135,14 @@ number_format::format number_format::lookup_format(int code)
return match->first;
}
std::string number_format::get_format_code_string()
{
if(format_code_ == format::unknown)
{
return custom_format_code_;
}
return format_strings().at(format_code_);
}
} // namespace xlnt

View File

@ -100,6 +100,18 @@ value::value(const std::string &s) : type_(type::string), string_value_(s)
{
}
value::value(const date &d) : type_(type::numeric), numeric_value_(d.to_number(xlnt::calendar::windows_1900))
{
}
value::value(const datetime &d) : type_(type::numeric), numeric_value_(d.to_number(xlnt::calendar::windows_1900))
{
}
value::value(const time &t) : type_(type::numeric), numeric_value_(t.to_number())
{
}
value &value::operator=(value other)
{
swap(*this, other);
@ -129,7 +141,26 @@ double value::as() const
{
case type::boolean:
case type::numeric:
return (double)numeric_value_;
return static_cast<double>(numeric_value_);
case type::string:
return std::stod(string_value_);
case type::error:
throw std::runtime_error("invalid");
case type::null:
return 0;
}
return 0;
}
template<>
long double value::as() const
{
switch(type_)
{
case type::boolean:
case type::numeric:
return numeric_value_;
case type::string:
return std::stod(string_value_);
case type::error:

View File

@ -11,8 +11,10 @@ class test_cell : public CxxTest::TestSuite
public:
void test_infer_numeric()
{
wb.set_guess_types(true);
xlnt::worksheet ws = wb.create_sheet();
xlnt::workbook wb_guess_types;
wb_guess_types.set_guess_types(true);
auto ws = wb_guess_types.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value("4.2");
@ -52,6 +54,21 @@ public:
TS_ASSERT(cell.get_value() == xlnt::time(0, 30, 33, 865633));
}
void test_ctor()
{
auto ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
TS_ASSERT(cell.get_value().get_type() == xlnt::value::type::null);
TS_ASSERT(cell.get_column() == "A");
TS_ASSERT(cell.get_row() == 1);
TS_ASSERT(cell.get_reference() == "A1");
TS_ASSERT(cell.get_value() == xlnt::value::null());
//TS_ASSERT(cell.get_xf_index() == 0);
TS_ASSERT(!cell.has_comment());
}
void test_coordinates()
{
xlnt::cell_reference coord("ZF46");
@ -214,70 +231,59 @@ public:
}
}
void test_insert_float()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value(3.14);
TS_ASSERT(cell.get_value().is(xlnt::value::type::numeric));
}
void test_insert_percentage()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value("3.14%");
TS_ASSERT_DELTA(0.0314, cell.get_value().as<double>(), 1e-7);
}
void test_insert_datetime()
{
xlnt::datetime value(2010, 7, 13, 6, 37, 41);
auto internal = 40372.27616898148L;
auto number_format = "yyyy-mm-dd h:mm:ss";
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value(xlnt::date::today());
TS_ASSERT(cell.get_value().is(xlnt::value::type::numeric));
cell.set_value(value);
TS_ASSERT(cell.get_value().get_type() == xlnt::value::type::numeric);
TS_ASSERT(cell.get_value().as<long double>() == internal);
TS_ASSERT(cell.is_date());
TS_ASSERT(cell.get_style().get_number_format().get_format_code_string() == number_format);
}
void test_insert_date()
{
xlnt::date value(2010, 7, 13);
auto internal = 40372;
auto number_format = "yyyy-mm-dd";
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value(xlnt::datetime::now());
TS_ASSERT(cell.get_value().is(xlnt::value::type::numeric));
cell.set_value(value);
TS_ASSERT(cell.get_value().get_type() == xlnt::value::type::numeric);
TS_ASSERT(cell.get_value().as<long double>() == internal);
TS_ASSERT(cell.is_date());
TS_ASSERT(cell.get_style().get_number_format().get_format_code_string() == number_format);
}
void test_internal_date()
void test_insert_time()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
xlnt::datetime dt(2010, 7, 13, 6, 37, 41);
cell.set_value(dt);
TS_ASSERT_EQUALS(40372.27616898148, cell.get_value().as<double>());
}
xlnt::time value(1, 3);
auto internal = 0.04375L;
auto number_format = "h:mm:ss";
void test_datetime_interpretation()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
xlnt::datetime dt(2010, 7, 13, 6, 37, 41);
cell.set_value(dt);
TS_ASSERT_EQUALS(cell.get_value(), dt);
TS_ASSERT_DELTA(cell.get_value().as<double>(), 40372.27616898148, 1e-7);
}
cell.set_value(value);
void test_date_interpretation()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::cell cell(ws, "A1");
xlnt::date dt(2010, 7, 13);
cell.set_value(dt);
TS_ASSERT_EQUALS(cell.get_value(), dt);
TS_ASSERT_EQUALS(cell.get_value().as<int>(), 40372);
TS_ASSERT(cell.get_value().get_type() == xlnt::value::type::numeric);
TS_ASSERT(cell.get_value().as<long double>() == internal);
TS_ASSERT(cell.is_date());
TS_ASSERT(cell.get_style().get_number_format().get_format_code_string() == number_format);
}
void test_number_format_style()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::workbook wb_guess_types;
wb_guess_types.set_guess_types(true);
xlnt::worksheet ws = wb_guess_types.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value("12.6%");
TS_ASSERT_EQUALS(xlnt::number_format::format::percentage, cell.get_style().get_number_format().get_format_code());
@ -285,7 +291,10 @@ public:
void test_data_type_check()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::workbook wb_guess_types;
wb_guess_types.set_guess_types(true);
xlnt::worksheet ws = wb_guess_types.create_sheet();
xlnt::cell cell(ws, "A1");
TS_ASSERT(cell.get_value().is(xlnt::value::type::null));
@ -311,7 +320,10 @@ public:
void test_time()
{
xlnt::worksheet ws = wb.create_sheet();
xlnt::workbook wb_guess_types;
wb_guess_types.set_guess_types(true);
xlnt::worksheet ws = wb_guess_types.create_sheet();
xlnt::cell cell(ws, "A1");
cell.set_value("03:40:16");
@ -382,7 +394,7 @@ public:
xlnt::cell cell(ws, "A1");
cell.set_value(-13.5);
cell.get_style().get_number_format().set_format_code("0.00_);[Red]\\(0.00\\)");
cell.get_style().get_number_format().set_format_code_string("0.00_);[Red]\\(0.00\\)");
TS_ASSERT(!cell.is_date());
}