time.hxx 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /** Support for date/time values.
  2. *
  3. * At the moment this supports dates, but not times.
  4. */
  5. #ifndef PQXX_H_TIME
  6. #define PQXX_H_TIME
  7. #if !defined(PQXX_HEADER_PRE)
  8. # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
  9. #endif
  10. #include <chrono>
  11. #include <cstdlib>
  12. #include "pqxx/internal/concat.hxx"
  13. #include "pqxx/strconv.hxx"
  14. #if defined(PQXX_HAVE_YEAR_MONTH_DAY)
  15. namespace pqxx
  16. {
  17. using namespace std::literals;
  18. template<>
  19. struct nullness<std::chrono::year_month_day>
  20. : no_null<std::chrono::year_month_day>
  21. {};
  22. /// String representation for a Gregorian date in ISO-8601 format.
  23. /** @warning Experimental. There may still be design problems, particularly
  24. * when it comes to BC years.
  25. *
  26. * PostgreSQL supports a choice of date formats, but libpqxx does not. The
  27. * other formats in turn support a choice of "month before day" versus "day
  28. * before month," meaning that it's not necessarily known which format a given
  29. * date is supposed to be. So I repeat: ISO-8601-style format only!
  30. *
  31. * Invalid dates will not convert. This includes February 29 on non-leap
  32. * years, which is why it matters that `year_month_day` represents a
  33. * _Gregorian_ date.
  34. *
  35. * The range of years is limited. At the time of writing, PostgreSQL 14
  36. * supports years from 4713 BC to 294276 AD inclusive, and C++20 supports
  37. * a range of 32767 BC to 32767 AD inclusive. So in practice, years must fall
  38. * between 4713 BC and 32767 AD, inclusive.
  39. *
  40. * @warning Support for BC (or BCE) years is still experimental. I still need
  41. * confirmation on this issue: it looks as if C++ years are astronomical years,
  42. * which means they have a Year Zero. Regular BC/AD years do not have a year
  43. * zero, so the year 1 AD follows directly after 1 BC.
  44. *
  45. * So, what to our calendars (and to PostgreSQL) is the year "0001 BC" seems to
  46. * count as year "0" in a `std::chrono::year_month_day`. The year 0001 AD is
  47. * still equal to 1 as you'd expect, and all AD years work normally, but all
  48. * years before then are shifted by one. For instance, the year 543 BC would
  49. * be -542 in C++.
  50. */
  51. template<> struct PQXX_LIBEXPORT string_traits<std::chrono::year_month_day>
  52. {
  53. [[nodiscard]] static zview
  54. to_buf(char *begin, char *end, std::chrono::year_month_day const &value)
  55. {
  56. return generic_to_buf(begin, end, value);
  57. }
  58. static char *
  59. into_buf(char *begin, char *end, std::chrono::year_month_day const &value);
  60. [[nodiscard]] static std::chrono::year_month_day
  61. from_string(std::string_view text);
  62. [[nodiscard]] static std::size_t
  63. size_buffer(std::chrono::year_month_day const &) noexcept
  64. {
  65. static_assert(int{(std::chrono::year::min)()} >= -99999);
  66. static_assert(int{(std::chrono::year::max)()} <= 99999);
  67. return 5 + 1 + 2 + 1 + 2 + std::size(s_bc) + 1;
  68. }
  69. private:
  70. /// The "BC" suffix for years before 1 AD.
  71. static constexpr std::string_view s_bc{" BC"sv};
  72. };
  73. } // namespace pqxx
  74. #endif // PQXX_HAVE_YEAR_MONTH_DAY
  75. #endif