Przeglądaj źródła

fix: add leapsecond support

Sam Jaffe 7 miesięcy temu
rodzic
commit
71c2c84e49
1 zmienionych plików z 19 dodań i 3 usunięć
  1. 19 3
      include/jvalidate/format.h

+ 19 - 3
include/jvalidate/format.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include <cctype>
+#include <chrono>
 #include <cstddef>
 #include <cstring>
 #include <ctime>
@@ -50,6 +51,21 @@ inline result date(std::string_view dt) {
   }
   return {.consumed = 0L, .valid = false};
 }
+
+inline bool is_leapsecond(std::tm tm) {
+  if (tm.tm_sec != 60) {
+    return true;
+  }
+
+#if __cpp_lib_chrono >= 201907L
+  tm.tm_isdst = -1;
+  std::chrono::seconds time(std::mktime(&tm));
+  auto const &leap_seconds = std::chrono::get_tzdb().leap_seconds;
+  return std::ranges::find(leap_seconds, time) != leap_seconds.end();
+#else
+  return false;
+#endif
+}
 }
 
 namespace jvalidate::format {
@@ -59,7 +75,7 @@ inline bool date(std::string_view dt) {
 }
 
 inline bool time(std::string_view dt) {
-  struct tm tm;
+  std::tm tm;
   char const * end = strptime(dt.data(), "%T", &tm);
   if (end == nullptr || end == dt.end() || (end - dt.data()) < 8) {
     return false;
@@ -76,10 +92,10 @@ inline bool time(std::string_view dt) {
   }
 
   if (dt[0] == 'Z' || dt[0] == 'z') {
-    return dt.size() == 1;
+    return dt.size() == 1 && detail::is_leapsecond(tm);
   }
   if (std::strchr("+-", dt[0])) {
-    return strptime(dt.data() + 1, "%R", &tm) == dt.end();
+    return strptime(dt.data() + 1, "%R", &tm) == dt.end() && detail::is_leapsecond(tm);
   }
   return false;
 }