Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Time improvements #27

Merged
merged 15 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions include/xlnt/utils/date.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct XLNT_API date
{
/// <summary>
/// Return the current date according to the system time.
/// If the current date could not be determined, the date will be in an empty state (is_null will return true).
/// </summary>
static date today();

Expand All @@ -49,10 +50,15 @@ struct XLNT_API date
static date from_number(int days_since_base_year, calendar base_date);

/// <summary>
/// Constructs a data from a given year, month, and day.
/// Constructs a date from a given year, month, and day.
/// </summary>
date(int year_, int month_, int day_);

/// <summary>
/// Constructs an empty date (a call to is_null will return true).
/// </summary>
date() = default;
m7913d marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Return the number of days between this date and base_date.
/// </summary>
Expand All @@ -61,6 +67,7 @@ struct XLNT_API date
/// <summary>
/// Calculates and returns the day of the week that this date represents in the range
/// 0 to 6 where 0 represents Sunday.
/// Returns -1 if the weekday could not be determined.
m7913d marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
int weekday() const;

Expand All @@ -77,17 +84,22 @@ struct XLNT_API date
/// <summary>
/// The year
/// </summary>
int year;
int year = 0;

/// <summary>
/// The month
/// </summary>
int month;
int month = 0;

/// <summary>
/// The day
/// </summary>
int day;
int day = 0;

/// <summary>
/// Whether the date is in an empty state.
/// </summary>
bool is_null = true;
};

} // namespace xlnt
8 changes: 8 additions & 0 deletions include/xlnt/utils/datetime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ struct XLNT_API datetime
{
/// <summary>
/// Returns the current date and time according to the system time.
/// If the current date could not be determined, the date will be in an empty state (is_null will return true).
/// </summary>
static datetime now();

/// <summary>
/// Returns the current date and time according to the system time.
/// This is equivalent to datetime::now().
/// If the current date could not be determined, the date will be in an empty state (is_null will return true).
/// </summary>
static datetime today();

Expand Down Expand Up @@ -94,6 +96,7 @@ struct XLNT_API datetime
/// <summary>
/// Calculates and returns the day of the week that this date represents in the range
/// 0 to 6 where 0 represents Sunday.
/// Returns -1 if the weekday could not be determined.
/// </summary>
int weekday() const;

Expand Down Expand Up @@ -131,6 +134,11 @@ struct XLNT_API datetime
/// The microsecond
/// </summary>
int microsecond;

/// <summary>
/// Whether the date is in an empty state.
/// </summary>
bool is_null = true;
m7913d marked this conversation as resolved.
Show resolved Hide resolved
};

} // namespace xlnt
14 changes: 12 additions & 2 deletions source/detail/number_format/number_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2036,12 +2036,22 @@ std::string number_formatter::format_number(const format_code &format, double nu
}

case template_part::template_type::day_abbreviation: {
result.append(day_names.at(static_cast<std::size_t>(dt.weekday())).substr(0, 3));
int weekday = dt.weekday();

if (weekday != -1)
{
result.append(day_names.at(static_cast<std::size_t>(weekday)).substr(0, 3));
}
break;
}

case template_part::template_type::day_name: {
result.append(day_names.at(static_cast<std::size_t>(dt.weekday())));
int weekday = dt.weekday();

if (weekday != -1)
{
result.append(day_names.at(static_cast<std::size_t>(weekday)));
}
break;
}
}
Expand Down
95 changes: 95 additions & 0 deletions source/detail/time_helpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2024 xlnt-community
//
// 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, ARISING 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 <ctime>
#include <xlnt/utils/optional.hpp>

namespace xlnt {
namespace detail {

/// Converts given time since epoch (a time_t value) into calendar time, expressed in local time, in the struct tm format.
/// If the conversion failed, an empty optional will be returned.
inline optional<std::tm> localtime_safe(std::time_t raw_time)
{
optional<std::tm> returned_value;

#ifdef _MSC_VER
std::tm result{};
errno_t err = localtime_s(&result, &raw_time);
if (err != 0)
{
returned_value = result;
}
#elif _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
std::tm result{};
if (localtime_r(&raw_time, &result))
{
returned_value = result;
}
#else
std::tm *tm = std::localtime(&raw_time);

if (tm != nullptr)
{
returned_value = *tm;
}
#endif

return returned_value;
}

/// Converts given time since epoch (a time_t value) into calendar time, expressed in Coordinated Universal Time (UTC) in the struct tm format.
/// If the conversion failed, an empty optional will be returned.
inline optional<std::tm> gmtime_safe(std::time_t raw_time)
{
optional<std::tm> returned_value;

#ifdef _MSC_VER
std::tm result{};
errno_t err = gmtime_s(&result, &raw_time);
if (err != 0)
{
returned_value = result;
}
#elif _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE
std::tm result{};
if (gmtime_r(&raw_time, &result))
{
returned_value = result;
}
#else
std::tm *tm = std::gmtime(&raw_time);

if (tm != nullptr)
{
returned_value = *tm;
}
#endif

return returned_value;
}

} // namespace detail
} // namespace xlnt
2 changes: 2 additions & 0 deletions source/detail/unicode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file

#pragma once

#include <string>

namespace xlnt {
Expand Down
71 changes: 44 additions & 27 deletions source/utils/date.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,20 @@
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#include <cmath>

#include <ctime>

#include <xlnt/utils/date.hpp>
#include <detail/time_helpers.hpp>

namespace {

std::tm safe_localtime(std::time_t raw_time)
{
#ifdef _MSC_VER
std::tm result;
localtime_s(&result, &raw_time);

return result;
#else
return *localtime(&raw_time);
#endif
}

} // namespace
/// Invalid weekday for checking whether std::mktime was successful - see below.
/// Must be outside of the range [0, 6].
constexpr int INVALID_WDAY = -1;
m7913d marked this conversation as resolved.
Show resolved Hide resolved

namespace xlnt {

date::date(int year_, int month_, int day_)
: year(year_), month(month_), day(day_)
: year(year_), month(month_), day(day_), is_null(false)
{
}

Expand Down Expand Up @@ -86,7 +75,7 @@ date date::from_number(int days_since_base_year, calendar base_date)

bool date::operator==(const date &comparand) const
{
return year == comparand.year && month == comparand.month && day == comparand.day;
return year == comparand.year && month == comparand.month && day == comparand.day && is_null == comparand.is_null;
}

bool date::operator!=(const date &comparand) const
Expand Down Expand Up @@ -120,19 +109,47 @@ int date::to_number(calendar base_date) const

date date::today()
{
std::tm now = safe_localtime(std::time(nullptr));
return date(1900 + now.tm_year, now.tm_mon + 1, now.tm_mday);
optional<std::tm> now = detail::localtime_safe(std::time(nullptr));

if (now.is_set())
{
return date(1900 + now.get().tm_year, now.get().tm_mon + 1, now.get().tm_mday);
}
else
{
return date();
}
}

int date::weekday() const
{
std::tm tm = std::tm();
tm.tm_mday = day;
tm.tm_mon = month - 1;
tm.tm_year = year - 1900;
std::time_t time = std::mktime(&tm);

return safe_localtime(time).tm_wday;
if (!is_null)
{
std::tm tm = std::tm();
tm.tm_wday = INVALID_WDAY;
tm.tm_mday = day;
tm.tm_mon = month - 1;
tm.tm_year = year - 1900;

// Important: if the conversion made by std::mktime is successful, the time object is modified. All fields of time are updated
// to fit their proper ranges. time->tm_wday and time->tm_yday are recalculated using information available in other fields.
// IMPORTANT: the return value -1 could either be an error or mean 1 second before 1970-1-1. However, an application wishing to check
// for error situations should set tm_wday to a value less than 0 or greater than 6 before calling mktime(). On return, if tm_wday has not changed an error has occurred.
/*std::time_t time =*/std::mktime(&tm);

if (tm.tm_wday != INVALID_WDAY)
{
return tm.tm_wday;
}
else
{
return -1;
}
}
else
{
return -1;
}
}

} // namespace xlnt
6 changes: 4 additions & 2 deletions source/utils/datetime.cpp
m7913d marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ bool datetime::operator==(const datetime &comparand) const
&& hour == comparand.hour
&& minute == comparand.minute
&& second == comparand.second
&& microsecond == comparand.microsecond;
&& microsecond == comparand.microsecond
&& is_null == comparand.is_null;
}

double datetime::to_number(calendar base_date) const
Expand Down Expand Up @@ -97,7 +98,8 @@ datetime::datetime(const date &d, const time &t)
hour(t.hour),
minute(t.minute),
second(t.second),
microsecond(t.microsecond)
microsecond(t.microsecond),
is_null(d.is_null)
{
}

Expand Down
29 changes: 11 additions & 18 deletions source/utils/time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,7 @@
#include <ctime>

#include <xlnt/utils/time.hpp>

namespace {

std::tm safe_localtime(std::time_t raw_time)
{
#ifdef _MSC_VER
std::tm result;
localtime_s(&result, &raw_time);

return result;
#else
return *localtime(&raw_time);
#endif
}

} // namespace
#include <detail/time_helpers.hpp>

namespace xlnt {

Expand Down Expand Up @@ -126,8 +111,16 @@ double time::to_number() const

time time::now()
{
std::tm now = safe_localtime(std::time(nullptr));
return time(now.tm_hour, now.tm_min, now.tm_sec);
optional<std::tm> now = detail::localtime_safe(std::time(nullptr));

if (now.is_set())
{
return time(now.get().tm_hour, now.get().tm_min, now.get().tm_sec);
}
else
{
return time();
}
}

} // namespace xlnt
Loading