@@ -212,6 +212,26 @@ inline std::string format_datetime(std::chrono::system_clock::time_point tp, uin
212212 return std::format (" '{:%Y-%m-%d %H:%M:%S}.{:0{}d}'" , secs, scaled_fraction, precision);
213213}
214214
215+ inline std::string format_time (std::chrono::microseconds dur, uint32_t fractional_second_precision = 0 ) {
216+ auto const precision = normalize_fractional_second_precision (fractional_second_precision);
217+ bool const negative = dur.count () < 0 ;
218+ auto const abs_dur = negative ? -dur : dur;
219+ auto const total_secs = std::chrono::duration_cast<std::chrono::seconds>(abs_dur).count ();
220+ auto const hours = total_secs / 3600 ;
221+ auto const mins = (total_secs % 3600 ) / 60 ;
222+ auto const secs = total_secs % 60 ;
223+ std::string result = std::format (" '{}{:02d}:{:02d}:{:02d}" , negative ? " -" : " " , hours, mins, secs);
224+ if (precision > 0U ) {
225+ constexpr std::array<uint32_t , 7 > divisors{1000000U , 100000U , 10000U , 1000U , 100U , 10U , 1U };
226+ auto const fractional_us =
227+ static_cast <uint32_t >((abs_dur - std::chrono::duration_cast<std::chrono::seconds>(abs_dur)).count ());
228+ auto const scaled_fraction = fractional_us / divisors[precision];
229+ result += std::format (" .{:0{}d}" , scaled_fraction, precision);
230+ }
231+ result += ' \' ' ;
232+ return result;
233+ }
234+
215235/* *
216236 * to_sql_value — convert a typed C++ value to its SQL literal representation.
217237 *
@@ -248,6 +268,8 @@ std::string to_sql_value(T const& v) {
248268 return format_datetime (v.time_point (), v.fractional_second_precision ());
249269 } else if constexpr (std::same_as<T, std::chrono::system_clock::time_point>) {
250270 return format_datetime (v);
271+ } else if constexpr (std::same_as<T, sql_time>) {
272+ return format_time (v.duration (), v.fractional_second_precision ());
251273 } else if constexpr (std::same_as<T, bool >) {
252274 return v ? " 1" : " 0" ;
253275 } else if constexpr (is_formatted_numeric_type_v<T>) {
@@ -266,7 +288,7 @@ std::string to_sql_value(T const& v) {
266288 " Supported: column_field<T>, optional<T>, sql_datetime, sql_timestamp, bool, "
267289 " integral types, floating-point types, float_type<P,S>, double_type<P,S>, "
268290 " decimal_type<P,S>, varchar_field<N>, std::string, "
269- " std::chrono::system_clock::time_point" );
291+ " std::chrono::system_clock::time_point, sql_time " );
270292 }
271293}
272294
@@ -277,10 +299,10 @@ std::string to_sql_value(T const& v) {
277299// Constrains template parameters that must produce a valid SQL literal.
278300// ===================================================================
279301template <typename T>
280- concept SqlValue =
281- ColumnFieldType<T> || is_optional_v<T> || std::same_as<T, sql_datetime > || std::same_as<T, sql_timestamp > ||
282- std::same_as<T, std::chrono::system_clock::time_point > || std::same_as<T, bool > || std::integral<T> ||
283- std::floating_point<T> || is_formatted_numeric_type_v<T> || is_varchar_field_v<T> || std::same_as<T, std::string>;
302+ concept SqlValue = ColumnFieldType<T> || is_optional_v<T> || std::same_as<T, sql_datetime> ||
303+ std::same_as<T, sql_timestamp > || std::same_as<T, std::chrono::system_clock::time_point > ||
304+ std::same_as<T, sql_time > || std::same_as<T, bool > || std::integral<T> || std::floating_point <T> ||
305+ is_formatted_numeric_type_v<T> || is_varchar_field_v<T> || std::same_as<T, std::string>;
284306
285307// ===================================================================
286308// where_condition — a typed SQL WHERE fragment
@@ -4933,12 +4955,33 @@ T from_mysql_value_nonnull(std::string_view sv) {
49334955 std::tm tm{};
49344956 ss >> std::get_time (&tm, " %Y-%m-%d %H:%M:%S" );
49354957 return std::chrono::system_clock::from_time_t (std::mktime (&tm));
4958+ } else if constexpr (std::same_as<T, sql_time>) {
4959+ bool const negative = !sv.empty () && sv[0 ] == ' -' ;
4960+ std::string_view const s = negative ? sv.substr (1 ) : sv;
4961+ auto const c1 = s.find (' :' );
4962+ auto const c2 = s.find (' :' , c1 + 1 );
4963+ int64_t const hours = std::stoll (std::string{s.substr (0 , c1)});
4964+ int64_t const mins = std::stoll (std::string{s.substr (c1 + 1 , c2 - c1 - 1 )});
4965+ auto const secs_frac = s.substr (c2 + 1 );
4966+ auto const dot = secs_frac.find (' .' );
4967+ int64_t const secs =
4968+ std::stoll (std::string{secs_frac.substr (0 , dot == std::string_view::npos ? secs_frac.size () : dot)});
4969+ int64_t frac_us = 0 ;
4970+ if (dot != std::string_view::npos) {
4971+ std::string frac{secs_frac.substr (dot + 1 )};
4972+ frac.resize (6 , ' 0' );
4973+ frac_us = std::stoll (frac);
4974+ }
4975+ int64_t total_us = (hours * 3600LL + mins * 60LL + secs) * 1000000LL + frac_us;
4976+ if (negative)
4977+ total_us = -total_us;
4978+ return sql_time{std::chrono::microseconds{total_us}};
49364979 } else {
49374980 static_assert (false ,
49384981 " Unsupported type for MySQL deserialization. "
49394982 " Supported: uint32_t, int32_t, uint64_t, int64_t, float, double, float_type<P,S>, "
49404983 " double_type<P,S>, decimal_type<P,S>, bool, std::string, varchar_field<N>, "
4941- " std::chrono::system_clock::time_point, "
4984+ " std::chrono::system_clock::time_point (for DATETIME/TIMESTAMP), sql_time , "
49424985 " and their std::optional variants." );
49434986 }
49444987}
0 commit comments