14 Commits

Author SHA1 Message Date
6e921e1b1d Add tests for %I and %p format options 2021-04-16 21:26:33 -04:00
39b4edf279 Add flags in README to compile using cmake (#661) 2021-04-16 21:06:08 -04:00
2ef74cb41a If %I is used without %p, mark the ambiguity with failbit 2021-04-16 15:15:45 -04:00
ac6ca2a095 Add is_clock_v 2021-04-15 11:41:20 -04:00
ae017078c9 Fix Posix::time_zone for southern hemisphere
* The get_info functions were not prepared for the case where the start
  and end times of a rule are chronologically reversed for a year.  I.e.
  the daylight saving start comes later in the year than the daylight
  saving end.  When save is positive, this happens in the southern
  hemisphere (e.g. "Australia/Sydney" -> "AEST-10AEDT,M10.1.0,M4.1.0/3").
  It also happens in the nothern hemisphere when the save is negative
  (e.g. "Europe/Dublin" -> "IST-1GMT0,M10.5.0,M3.5.0/1").

* Added tests for Posix::time_zone.
2021-04-09 11:08:12 -04:00
156e0a786e Fix up namespace error in Posix::time_zone::to_sys 2021-04-07 20:33:01 -04:00
9a9a42db74 Enable streaming output operator for floating-point-based time_points 2021-04-06 17:22:22 -04:00
b5b765f928 Clean up calendrical streaming operators for invalid values 2021-03-31 16:15:53 -04:00
77bd6b92a4 Rattle github.io's cage 2021-03-22 14:59:59 -04:00
654b97091f Change default on C++17 to uncaught_exceptions
* By popular demand.
* If you need to undo this change, compile with
  -DHAS_UNCAUGHT_EXCEPTIONS=0.
2021-03-21 22:41:22 -04:00
b899774303 Run curl_global_cleanup only once per application 2021-03-11 19:58:34 -05:00
0e08b942c8 Correct the streaming output of year_month_day
* Invalid dates were printing out "invalid"
  too many times.
2021-03-11 19:50:14 -05:00
811be52e1c Add global cleanup to curl object destructor (#652)
Co-authored-by: Satish Bhat <sbhat@belvederetrading.com>
2021-03-01 17:00:11 -05:00
1c285d6545 Cast to unsigned char before calling toupper() (#648) 2021-02-24 12:49:46 -05:00
6 changed files with 507 additions and 114 deletions

View File

@ -49,7 +49,7 @@ To use `"tz.h"`, there is a single source file (`src/tz.cpp`) that needs to be c
One can run tests by cd'ing into the `test` subdirectory and running `testit`. There are known failures on all platforms except for macOS. And even on macOS if C++11 is used. If any of these failures present problems for you, there exist workarounds.
Additionally there is unsupported support for [vcpkg](https://github.com/Microsoft/vcpkg) and [CMake](https://cmake.org/). I don't personally use or maintain these systems as for me they cause more problems than they solve (for this small project). If you would like to contribute to these build systems please feel free to file a PR.
Additionally there is _unsupported_ support for [vcpkg](https://github.com/Microsoft/vcpkg) and [CMake](https://cmake.org/). I don't personally use or maintain these systems as for me they cause more problems than they solve (for this small project). If you would like to contribute to these build systems please feel free to file a PR.
You can download and install Date using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
@ -66,7 +66,7 @@ You can optionally build using [CMake](https://cmake.org/). Here is a guide of h
```bash
mkdir build
cd build
cmake ../
cmake -DENABLE_DATE_TESTING=ON -DBUILD_TZ_LIB=ON ../
cmake --build . --target testit # Consider '-- -j4' for multithreading
```
## Projects using this library

View File

@ -136,7 +136,7 @@ namespace date
#endif
#ifndef HAS_UNCAUGHT_EXCEPTIONS
# if __cplusplus > 201703 || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
# if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define HAS_UNCAUGHT_EXCEPTIONS 1
# else
# define HAS_UNCAUGHT_EXCEPTIONS 0
@ -1004,6 +1004,8 @@ struct is_clock<T, std::void_t<decltype(T::now()), typename T::rep, typename T::
: std::true_type
{};
template<class T> inline constexpr bool is_clock_v = is_clock<T>::value;
#endif // HAS_VOID_T
//----------------+
@ -1020,6 +1022,7 @@ protected:
std::basic_ios<CharT, Traits>& is_;
CharT fill_;
std::ios::fmtflags flags_;
std::streamsize precision_;
std::streamsize width_;
std::basic_ostream<CharT, Traits>* tie_;
std::locale loc_;
@ -1029,6 +1032,7 @@ public:
{
is_.fill(fill_);
is_.flags(flags_);
is_.precision(precision_);
is_.width(width_);
is_.imbue(loc_);
is_.tie(tie_);
@ -1041,6 +1045,7 @@ public:
: is_(is)
, fill_(is.fill())
, flags_(is.flags())
, precision_(is.precision())
, width_(is.width(0))
, tie_(is.tie(nullptr))
, loc_(is.getloc())
@ -1501,16 +1506,29 @@ operator-(const day& x, const days& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const day& d)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
os.flags(std::ios::dec | std::ios::right);
os.width(2);
os << static_cast<unsigned>(d);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const day& d)
{
detail::low_level_fmt(os, d);
if (!d.ok())
os << " is not a valid day";
return os;
@ -1628,10 +1646,12 @@ operator-(const month& x, const months& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month& m)
{
if (m.ok())
{
@ -1639,7 +1659,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
os << format(os.getloc(), fmt, m);
}
else
os << static_cast<unsigned>(m) << " is not a valid month";
os << static_cast<unsigned>(m);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month& m)
{
detail::low_level_fmt(os, m);
if (!m.ok())
os << " is not a valid month";
return os;
}
@ -1753,10 +1786,12 @@ operator-(const year& x, const years& y) NOEXCEPT
return year{static_cast<int>(x) - y.count()};
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year& y)
{
detail::save_ostream<CharT, Traits> _(os);
os.fill('0');
@ -1764,6 +1799,17 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
os.width(4 + (y < year{0}));
os.imbue(std::locale::classic());
os << static_cast<int>(y);
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year& y)
{
detail::low_level_fmt(os, y);
if (!y.ok())
os << " is not a valid year";
return os;
@ -1889,10 +1935,12 @@ operator-(const weekday& x, const days& y) NOEXCEPT
return x + -y;
}
namespace detail
{
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
{
if (wd.ok())
{
@ -1900,7 +1948,20 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
os << format(fmt, wd);
}
else
os << static_cast<unsigned>(wd.wd_) << " is not a valid weekday";
os << wd.c_encoding();
return os;
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday& wd)
{
detail::low_level_fmt(os, wd);
if (!wd.ok())
os << " is not a valid weekday";
return os;
}
@ -2009,15 +2070,26 @@ weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCE
# pragma GCC diagnostic pop
#endif // __GNUC__
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
{
return low_level_fmt(os, wdi.weekday()) << '[' << wdi.index() << ']';
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_indexed& wdi)
{
os << wdi.weekday() << '[' << wdi.index();
if (!(1 <= wdi.index() && wdi.index() <= 5))
os << " is not a valid index";
os << ']';
detail::low_level_fmt(os, wdi);
if (!wdi.ok())
os << " is not a valid weekday_indexed";
return os;
}
@ -2067,12 +2139,27 @@ operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
{
return low_level_fmt(os, wdl.weekday()) << "[last]";
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const weekday_last& wdl)
{
return os << wdl.weekday() << "[last]";
detail::low_level_fmt(os, wdl);
if (!wdl.ok())
os << " is not a valid weekday_last";
return os;
}
CONSTCD11
@ -2247,12 +2334,28 @@ operator-(const year_month& ym, const years& dy) NOEXCEPT
return ym + -dy;
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
{
low_level_fmt(os, ym.year()) << '/';
return low_level_fmt(os, ym.month());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month& ym)
{
return os << ym.year() << '/' << ym.month();
detail::low_level_fmt(os, ym);
if (!ym.ok())
os << " is not a valid year_month";
return os;
}
// month_day
@ -2332,12 +2435,28 @@ operator>=(const month_day& x, const month_day& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day& md)
{
low_level_fmt(os, md.month()) << '/';
return low_level_fmt(os, md.day());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day& md)
{
return os << md.month() << '/' << md.day();
detail::low_level_fmt(os, md);
if (!md.ok())
os << " is not a valid month_day";
return os;
}
// month_day_last
@ -2394,12 +2513,27 @@ operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
{
return low_level_fmt(os, mdl.month()) << "/last";
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_day_last& mdl)
{
return os << mdl.month() << "/last";
detail::low_level_fmt(os, mdl);
if (!mdl.ok())
os << " is not a valid month_day_last";
return os;
}
// month_weekday
@ -2446,12 +2580,28 @@ operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
{
low_level_fmt(os, mwd.month()) << '/';
return low_level_fmt(os, mwd.weekday_indexed());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday& mwd)
{
return os << mwd.month() << '/' << mwd.weekday_indexed();
detail::low_level_fmt(os, mwd);
if (!mwd.ok())
os << " is not a valid month_weekday";
return os;
}
// month_weekday_last
@ -2498,12 +2648,28 @@ operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT
return !(x == y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
{
low_level_fmt(os, mwdl.month()) << '/';
return low_level_fmt(os, mwdl.weekday_last());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const month_weekday_last& mwdl)
{
return os << mwdl.month() << '/' << mwdl.weekday_last();
detail::low_level_fmt(os, mwdl);
if (!mwdl.ok())
os << " is not a valid month_weekday_last";
return os;
}
// year_month_day_last
@ -2653,12 +2819,28 @@ operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT
return !(x < y);
}
namespace detail
{
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
low_level_fmt(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
{
low_level_fmt(os, ymdl.year()) << '/';
return low_level_fmt(os, ymdl.month_day_last());
}
} // namespace detail
template<class CharT, class Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day_last& ymdl)
{
return os << ymdl.year() << '/' << ymdl.month_day_last();
detail::low_level_fmt(os, ymdl);
if (!ymdl.ok())
os << " is not a valid year_month_day_last";
return os;
}
template<class>
@ -2889,12 +3071,13 @@ operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_day& ymd)
os.fill('0');
os.flags(std::ios::dec | std::ios::right);
os.imbue(std::locale::classic());
os << ymd.year() << '-';
os << static_cast<int>(ymd.year()) << '-';
os.width(2);
os << static_cast<unsigned>(ymd.month()) << '-';
os << ymd.day();
os.width(2);
os << static_cast<unsigned>(ymd.day());
if (!ymd.ok())
os << " is not a valid date";
os << " is not a valid year_month_day";
return os;
}
@ -3130,8 +3313,12 @@ inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday& ymwdi)
{
return os << ymwdi.year() << '/' << ymwdi.month()
<< '/' << ymwdi.weekday_indexed();
detail::low_level_fmt(os, ymwdi.year()) << '/';
detail::low_level_fmt(os, ymwdi.month()) << '/';
detail::low_level_fmt(os, ymwdi.weekday_indexed());
if (!ymwdi.ok())
os << " is not a valid year_month_weekday";
return os;
}
template<class>
@ -3307,7 +3494,12 @@ inline
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const year_month_weekday_last& ymwdl)
{
return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last();
detail::low_level_fmt(os, ymwdl.year()) << '/';
detail::low_level_fmt(os, ymwdl.month()) << '/';
detail::low_level_fmt(os, ymwdl.weekday_last());
if (!ymwdl.ok())
os << " is not a valid year_month_weekday_last";
return os;
}
template<class>
@ -3810,6 +4002,7 @@ public:
std::chrono::duration<rep> d = s_ + sub_s_;
if (d < std::chrono::seconds{10})
os << '0';
os.precision(width+6);
os << std::fixed << d.count();
return os;
}
@ -4000,9 +4193,7 @@ make24(std::chrono::hours h, bool is_pm) NOEXCEPT
template <class Duration>
using time_of_day = hh_mm_ss<Duration>;
template <class Rep, class Period,
class = typename std::enable_if
<!std::chrono::treat_as_floating_point<Rep>::value>::type>
template <class Rep, class Period>
CONSTCD11
inline
hh_mm_ss<std::chrono::duration<Rep, Period>>
@ -4015,8 +4206,7 @@ template <class CharT, class Traits, class Duration>
inline
typename std::enable_if
<
!std::chrono::treat_as_floating_point<typename Duration::rep>::value &&
std::ratio_less<typename Duration::period, days::period>::value
std::ratio_less<typename Duration::period, days::period>::value
, std::basic_ostream<CharT, Traits>&
>::type
operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
@ -4779,7 +4969,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
is.setstate(std::ios::eofbit);
break;
}
auto c = static_cast<char>(toupper(ic));
auto c = static_cast<char>(toupper(static_cast<unsigned char>(ic)));
bool consume = false;
// For each keyword which might match, see if the indx character is c
// If a match if found, consume c
@ -4792,7 +4982,7 @@ scan_keyword(std::basic_istream<CharT, Traits>& is, FwdIter kb, FwdIter ke)
{
if (*st == might_match)
{
if (c == static_cast<char>(toupper((*ky)[indx])))
if (c == static_cast<char>(toupper(static_cast<unsigned char>((*ky)[indx]))))
{
consume = true;
if (ky->size() == indx+1)
@ -7655,6 +7845,8 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
goto broken;
}
}
else // I is ambiguous, AM or PM?
goto broken;
}
}
if (H != not_a_hour)

View File

@ -269,8 +269,76 @@ public:
std::string name() const;
friend bool operator==(const time_zone& x, const time_zone& y);
private:
date::sys_seconds get_start(date::year y) const;
date::sys_seconds get_prev_start(date::year y) const;
date::sys_seconds get_next_start(date::year y) const;
date::sys_seconds get_end(date::year y) const;
date::sys_seconds get_prev_end(date::year y) const;
date::sys_seconds get_next_end(date::year y) const;
date::sys_info contant_offset() const;
};
inline
date::sys_seconds
time_zone::get_start(date::year y) const
{
return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_start(date::year y) const
{
return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_start(date::year y) const
{
return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_end(date::year y) const
{
return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_end(date::year y) const
{
return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_end(date::year y) const
{
return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
}
date::sys_info
time_zone::contant_offset() const
{
using date::year;
using date::sys_info;
using date::sys_days;
using date::January;
using date::December;
using date::last;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last};
r.abbrev = std_abbrev_;
r.offset = offset_;
return r;
}
inline
time_zone::time_zone(const detail::string_t& s)
{
@ -313,12 +381,10 @@ time_zone::get_info(date::sys_time<Duration> st) const
{
using date::sys_info;
using date::year_month_day;
using date::sys_seconds;
using date::sys_days;
using date::floor;
using date::ceil;
using date::days;
using date::years;
using date::year;
using date::January;
using date::December;
@ -329,36 +395,59 @@ time_zone::get_info(date::sys_time<Duration> st) const
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(st)}.year();
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
if (start <= st && st < end)
auto start = get_start(y);
auto end = get_end(y);
if (start <= end) // (northern hemisphere)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
if (start <= st && st < end)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else if (st < start)
{
r.begin = get_prev_end(y);
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = get_next_start(y);
r.abbrev = std_abbrev_;
}
}
else if (st < start)
else // end < start (southern hemisphere)
{
r.begin = sys_seconds{(end_rule_(y-years{1}) -
(offset_ + save_)).time_since_epoch()};
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = sys_seconds{(start_rule_(y+years{1}) - offset_).time_since_epoch()};
r.abbrev = std_abbrev_;
if (end <= st && st < start)
{
r.begin = end;
r.end = start;
r.abbrev = std_abbrev_;
}
else if (st < end)
{
r.begin = get_prev_start(y);
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else // st >= start
{
r.begin = start;
r.end = get_next_end(y);
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
}
}
else // constant offset
{
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last};
r.abbrev = std_abbrev_;
}
else
r = contant_offset();
return r;
}
@ -371,7 +460,6 @@ time_zone::get_info(date::local_time<Duration> tp) const
using date::days;
using date::sys_days;
using date::sys_seconds;
using date::years;
using date::year;
using date::ceil;
using date::January;
@ -384,19 +472,25 @@ time_zone::get_info(date::local_time<Duration> tp) const
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(tp)}.year();
auto start = sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
auto end = sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
auto start = get_start(y);
auto end = get_end(y);
auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
auto northern = start <= end;
if ((utcs < start) != (utcd < start))
{
r.first.begin = sys_seconds{(end_rule_(y-years{1}) -
(offset_ + save_)).time_since_epoch()};
if (northern)
r.first.begin = get_prev_end(y);
else
r.first.begin = end;
r.first.end = start;
r.first.offset = offset_;
r.first.abbrev = std_abbrev_;
r.second.begin = start;
r.second.end = end;
if (northern)
r.second.end = end;
else
r.second.end = get_next_end(y);
r.second.abbrev = dst_abbrev_;
r.second.offset = offset_ + save_;
r.second.save = ceil<minutes>(save_);
@ -405,51 +499,29 @@ time_zone::get_info(date::local_time<Duration> tp) const
}
else if ((utcs < end) != (utcd < end))
{
r.first.begin = start;
if (northern)
r.first.begin = start;
else
r.first.begin = get_prev_start(y);
r.first.end = end;
r.first.offset = offset_ + save_;
r.first.save = ceil<minutes>(save_);
r.first.abbrev = dst_abbrev_;
r.second.begin = end;
r.second.end = sys_seconds{(start_rule_(y+years{1}) -
offset_).time_since_epoch()};
if (northern)
r.second.end = get_next_start(y);
else
r.second.end = start;
r.second.abbrev = std_abbrev_;
r.second.offset = offset_;
r.result = save_ > seconds{0} ? local_info::ambiguous
: local_info::nonexistent;
}
else if (utcs < start)
{
r.first.begin = sys_seconds{(end_rule_(y-years{1}) -
(offset_ + save_)).time_since_epoch()};
r.first.end = start;
r.first.offset = offset_;
r.first.abbrev = std_abbrev_;
}
else if (utcs < end)
{
r.first.begin = start;
r.first.end = end;
r.first.offset = offset_ + save_;
r.first.save = ceil<minutes>(save_);
r.first.abbrev = dst_abbrev_;
}
else
{
r.first.begin = end;
r.first.end = sys_seconds{(start_rule_(y+years{1}) -
offset_).time_since_epoch()};
r.first.abbrev = std_abbrev_;
r.first.offset = offset_;
}
}
else // constant offset
{
r.first.begin = sys_days{year::min()/January/1};
r.first.end = sys_days{year::max()/December/last};
r.first.abbrev = std_abbrev_;
r.first.offset = offset_;
r.first = get_info(utcs);
}
else
r.first = contant_offset();
return r;
}
@ -460,6 +532,7 @@ time_zone::to_sys(date::local_time<Duration> tp) const
using date::local_info;
using date::sys_time;
using date::ambiguous_local_time;
using date::nonexistent_local_time;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
throw nonexistent_local_time(tp, i);

View File

@ -2846,18 +2846,24 @@ file_exists(const std::string& filename)
// CURL tools
static
int
curl_global()
{
if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
throw std::runtime_error("CURL global initialization failed");
return 0;
}
namespace
{
struct curl_global_init_and_cleanup
{
~curl_global_init_and_cleanup()
{
::curl_global_cleanup();
}
curl_global_init_and_cleanup()
{
if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0)
throw std::runtime_error("CURL global initialization failed");
}
curl_global_init_and_cleanup(curl_global_init_and_cleanup const&) = delete;
curl_global_init_and_cleanup& operator=(curl_global_init_and_cleanup const&) = delete;
};
struct curl_deleter
{
void operator()(CURL* p) const
@ -2872,8 +2878,7 @@ static
std::unique_ptr<CURL, curl_deleter>
curl_init()
{
static const auto curl_is_now_initiailized = curl_global();
(void)curl_is_now_initiailized;
static const curl_global_init_and_cleanup _{};
return std::unique_ptr<CURL, curl_deleter>{::curl_easy_init()};
}

View File

@ -623,6 +623,21 @@ test_p()
assert(!in.bad());
assert(tp == sys_days{2016_y/12/11} + hours{23});
}
{
std::istringstream in{"1986-12-01 01:01:01 pm"};
sys_time<seconds> tp;
in >> parse("%Y-%m-%d %I:%M:%S %p", tp);
assert(!in.fail());
assert(!in.bad());
assert(tp == sys_days{1986_y/12/01} + hours{13} + minutes{01} + seconds{01});
}
{
std::istringstream in{"1986-12-01 01:01:01"};
sys_time<seconds> tp;
in >> parse("%Y-%m-%d %I:%M:%S", tp);
// The test will fail because %I needs the %p option to shows if it is AM or PM
assert(in.fail());
}
}
void

108
test/posix/ptz.pass.cpp Normal file
View File

@ -0,0 +1,108 @@
// The MIT License (MIT)
//
// Copyright (c) 2021 Howard Hinnant
//
// 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.
// Test Posix::time_zone
#include "tz.h"
#include "ptz.h"
#include <cassert>
bool
is_equal(date::sys_info const& x, date::sys_info const& y)
{
return x.begin == y.begin &&
x.end == y.end &&
x.offset == y.offset &&
x.save == y.save &&
x.abbrev == y.abbrev;
}
bool
is_equal(date::local_info const& x, date::local_info const& y)
{
return x.result == y.result && is_equal(x.first, y.first)
&& is_equal(x.second, y.second);
}
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto tzi = locate_zone("Australia/Sydney");
Posix::time_zone tzp{"AEST-10AEDT,M10.1.0,M4.1.0/3"};
auto tp = local_days{2021_y/1/1} + 0s;
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/10/Sunday[1]} + 2h + 30min;
assert(tzp.get_info(tp).result == local_info::nonexistent);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/4/Sunday[1]} + 2h + 30min;
assert(tzp.get_info(tp).result == local_info::ambiguous);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/7/1};
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tzi = locate_zone("America/New_York");
tzp = Posix::time_zone{"EST5EDT,M3.2.0,M11.1.0"};
tp = local_days{2021_y/1/1};
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/3/Sunday[2]} + 2h + 30min;
assert(tzp.get_info(tp).result == local_info::nonexistent);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/11/Sunday[1]} + 1h + 30min;
assert(tzp.get_info(tp).result == local_info::ambiguous);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/7/1};
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tzi = locate_zone("Europe/Dublin");
tzp = Posix::time_zone{"IST-1GMT0,M10.5.0,M3.5.0/1"};
tp = local_days{2021_y/1/1};
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/3/Sunday[last]} + 1h + 30min;
assert(tzp.get_info(tp).result == local_info::nonexistent);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/10/Sunday[last]} + 1h + 30min;
assert(tzp.get_info(tp).result == local_info::ambiguous);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
tp = local_days{2021_y/7/1};
assert(tzp.get_info(tp).result == local_info::unique);
assert(is_equal(tzi->get_info(tp), tzp.get_info(tp)));
}