diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..8ad74f7
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Normalize EOL for all files that Git considers text files.
+* text=auto eol=lf
diff --git a/.gitignore b/.gitignore
index e88e3be..3126959 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,7 +135,7 @@ publish/
 # Publish Web Output
 *.[Pp]ublish.xml
 *.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings 
+# TODO: Comment the next line if you want to checkin your web deploy settings
 # but database connection strings (with potential passwords) will be unencrypted
 *.pubxml
 *.publishproj
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 012512a..c606f47 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,7 +7,7 @@
      include( FetchContent )
      FetchContent_Declare( date_src
        GIT_REPOSITORY https://github.com/HowardHinnant/date.git
-       GIT_TAG        v3.0.0  # adjust tag/branch/commit as needed
+       GIT_TAG        v3.0.1  # adjust tag/branch/commit as needed
      )
      FetchContent_MakeAvailable(date_src)
      ...
@@ -17,7 +17,7 @@
 
 cmake_minimum_required( VERSION 3.7 )
 
-project( date VERSION 3.0.0 )
+project( date VERSION 3.0.1 )
 set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes
 
 include( GNUInstallDirs )
diff --git a/README.md b/README.md
index 7ef280f..46d6d23 100644
--- a/README.md
+++ b/README.md
@@ -80,5 +80,6 @@ cmake --build . --target testit # Consider '-- -j4' for multithreading
 * https://github.com/KomodoPlatform/atomicDEX-Pro
 * https://github.com/Kotlin/kotlinx-datetime
 * https://github.com/royalbee/jewish_date
+* https://github.com/apache/arrow/
 
 If you would like your project (or product) on this list, just let me know.
diff --git a/debian/changelog b/debian/changelog
index b406cb9..cdaeeb7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+howardhinnant-date (3.0.1+git20220812.1.22ceabf-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+  * Drop patch cmake-update-project-version.patch, present upstream.
+  * Drop patch Zero_initialize_local_info_in_get_info.patch, present upstream.
+  * Drop patch When_comparing_sys_info_in_test.patch, present upstream.
+  * Drop patch Change_default_test_mode_to_C++17.patch, present upstream.
+  * Drop patch test_use_pthread_when_CXX_is_g++.patch, present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 15 Sep 2022 00:22:24 -0000
+
 howardhinnant-date (3.0.1-7) unstable; urgency=medium
 
   * d/tests/cmake: suppress psABI warning on ARM
diff --git a/debian/patches/Change_default_test_mode_to_C++17.patch b/debian/patches/Change_default_test_mode_to_C++17.patch
deleted file mode 100644
index 16bfbef..0000000
--- a/debian/patches/Change_default_test_mode_to_C++17.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-Description: Change default test mode to C++17
-Origin: upstream, https://github.com/HowardHinnant/date/commit/ebb5719cd710c694bf62d45a03b3d13f532a35d1
-Last-Update: 2021-11-02
-
-From ebb5719cd710c694bf62d45a03b3d13f532a35d1 Mon Sep 17 00:00:00 2001
-From: Howard Hinnant <howard.hinnant@gmail.com>
-Date: Wed, 19 May 2021 14:43:19 -0400
-Subject: [PATCH] Change default test mode to C++17
-
----
- test/testit | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/test/testit b/test/testit
-index 21ec3a23..f34ab356 100755
---- a/test/testit
-+++ b/test/testit
-@@ -46,7 +46,7 @@ fi
- 
- if [ -z "$CXX_LANG" ]
- then
--    CXX_LANG=c++14
-+    CXX_LANG=c++17
- fi
- OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall $ROOT/src/tz.cpp -lcurl"
- 
diff --git a/debian/patches/Stop-using-PATH_MAX.patch b/debian/patches/Stop-using-PATH_MAX.patch
index a9c675a..c813393 100644
--- a/debian/patches/Stop-using-PATH_MAX.patch
+++ b/debian/patches/Stop-using-PATH_MAX.patch
@@ -40,10 +40,10 @@ https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
  src/tz.cpp | 35 ++++++++++++++++++++++-------------
  1 file changed, 22 insertions(+), 13 deletions(-)
 
-diff --git a/src/tz.cpp b/src/tz.cpp
-index 9d55d4f..5fa170f 100644
---- a/src/tz.cpp
-+++ b/src/tz.cpp
+Index: howardhinnant-date/src/tz.cpp
+===================================================================
+--- howardhinnant-date.orig/src/tz.cpp
++++ howardhinnant-date/src/tz.cpp
 @@ -484,9 +484,11 @@ discover_tz_dir()
      if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0))
          throw runtime_error("discover_tz_dir failed\n");
@@ -59,7 +59,7 @@ index 9d55d4f..5fa170f 100644
      else
          throw system_error(errno, system_category(), "readlink() failed");
      auto i = result.find("zoneinfo");
-@@ -3917,10 +3919,10 @@ bool
+@@ -3921,10 +3923,10 @@ bool
  sniff_realpath(const char* timezone)
  {
      using namespace std;
@@ -73,7 +73,7 @@ index 9d55d4f..5fa170f 100644
      return result != "posixrules";
  }
  
-@@ -3940,6 +3942,7 @@ tzdb::current_zone() const
+@@ -3944,6 +3946,7 @@ tzdb::current_zone() const
      // exception will be thrown by local_timezone.
      // The path may also take a relative form:
      // "../usr/share/zoneinfo/America/Los_Angeles".
@@ -81,7 +81,7 @@ index 9d55d4f..5fa170f 100644
      {
          struct stat sb;
          CONSTDATA auto timezone = "/etc/localtime";
-@@ -3947,18 +3950,22 @@ tzdb::current_zone() const
+@@ -3951,18 +3954,22 @@ tzdb::current_zone() const
          {
              using namespace std;
              static const bool use_realpath = sniff_realpath(timezone);
@@ -108,7 +108,7 @@ index 9d55d4f..5fa170f 100644
          }
      }
      // On embedded systems e.g. buildroot with uclibc the timezone is linked
-@@ -3977,9 +3984,11 @@ tzdb::current_zone() const
+@@ -3981,9 +3988,11 @@ tzdb::current_zone() const
          if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) {
              using namespace std;
              string result;
@@ -123,6 +123,3 @@ index 9d55d4f..5fa170f 100644
              else
                  throw system_error(errno, system_category(), "readlink() failed");
  
--- 
-2.35.1
-
diff --git a/debian/patches/When_comparing_sys_info_in_test.patch b/debian/patches/When_comparing_sys_info_in_test.patch
deleted file mode 100644
index 9879205..0000000
--- a/debian/patches/When_comparing_sys_info_in_test.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-Description: When comparing sys_info in test...
- only compare whether the
- saves are equal to 0 and not their actual values.
-Origin: upstream, https://github.com/HowardHinnant/date/commit/052eebaf0086e6bbc5ead01c3f1a8f02496aa701
-Bug: https://github.com/HowardHinnant/date/issues/671
-Last-Update: 2021-11-01
-
-From 052eebaf0086e6bbc5ead01c3f1a8f02496aa701 Mon Sep 17 00:00:00 2001
-From: Howard Hinnant <howard.hinnant@gmail.com>
-Date: Tue, 18 May 2021 16:17:37 -0400
-Subject: [PATCH] When comparing sys_info in test... only compare whether the
- saves are equal to 0 and not their actual values.
-
-This allows one to compare against the binary database
-which does not contain actual values of save.
----
- test/posix/ptz.pass.cpp | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/test/posix/ptz.pass.cpp b/test/posix/ptz.pass.cpp
-index 5601c21d..9e15e3a9 100644
---- a/test/posix/ptz.pass.cpp
-+++ b/test/posix/ptz.pass.cpp
-@@ -29,10 +29,11 @@
- bool
- is_equal(date::sys_info const& x, date::sys_info const& y)
- {
-+    using namespace std::chrono;
-     return x.begin == y.begin &&
-            x.end == y.end &&
-            x.offset == y.offset &&
--           x.save == y.save &&
-+           (x.save == minutes{0}) == (y.save == minutes{0}) &&
-            x.abbrev == y.abbrev;
- }
- 
diff --git a/debian/patches/Zero_initialize_local_info_in_get_info.patch b/debian/patches/Zero_initialize_local_info_in_get_info.patch
deleted file mode 100644
index 38afa19..0000000
--- a/debian/patches/Zero_initialize_local_info_in_get_info.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-Description: Zero initialize local_info in get_info
- Even when the result is unique, the second sys_info
- should be zero initialized.
-Origin: upstream, https://github.com/HowardHinnant/date/commit/b49a7575ebbe127e8bd344900a52c14b5d69dd7b
-Bug: https://github.com/HowardHinnant/date/issues/671
-Last-Update: 2021-11-01
-
-From b49a7575ebbe127e8bd344900a52c14b5d69dd7b Mon Sep 17 00:00:00 2001
-From: Howard Hinnant <howard.hinnant@gmail.com>
-Date: Tue, 18 May 2021 16:15:31 -0400
-Subject: [PATCH] Zero initialize local_info in get_info
-
-* Even when the result is unique, the second sys_info
-  should be zero initialized.
----
- src/tz.cpp | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/tz.cpp b/src/tz.cpp
-index 26babbd9..1592bc8f 100644
---- a/src/tz.cpp
-+++ b/src/tz.cpp
-@@ -2164,7 +2164,7 @@ time_zone::get_info_impl(local_seconds tp) const
- {
-     using namespace std::chrono;
-     init();
--    local_info i;
-+    local_info i{};
-     i.result = local_info::unique;
-     auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp,
-                           [](const local_seconds& x, const transition& t)
diff --git a/debian/patches/cmake-install-all-headers.patch b/debian/patches/cmake-install-all-headers.patch
index 9ffa1f5..01d2f84 100644
--- a/debian/patches/cmake-install-all-headers.patch
+++ b/debian/patches/cmake-install-all-headers.patch
@@ -7,10 +7,10 @@ Bug-Debian: https://bugs.debian.org/1001895
 Forwarded: not-needed
 Last-Update: 2022-08-04
 
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index bc0bcb2..0734b5c 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
+Index: howardhinnant-date/CMakeLists.txt
+===================================================================
+--- howardhinnant-date.orig/CMakeLists.txt
++++ howardhinnant-date/CMakeLists.txt
 @@ -75,7 +75,13 @@ target_sources( date INTERFACE
  )
  if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
@@ -46,7 +46,7 @@ index bc0bcb2..0734b5c 100644
          VERSION "${PROJECT_VERSION}"
          SOVERSION "${ABI_VERSION}" )
      if( NOT MSVC )
-@@ -170,7 +178,8 @@ write_basic_package_version_file( "${version_config}"
+@@ -170,7 +178,8 @@ write_basic_package_version_file( "${ver
  
  install( TARGETS date
      EXPORT dateConfig
diff --git a/debian/patches/cmake-update-project-version.patch b/debian/patches/cmake-update-project-version.patch
deleted file mode 100644
index b5284ff..0000000
--- a/debian/patches/cmake-update-project-version.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-Description: cmake: update project version
- howardhinnant-date is at version 3.0.1, but upstream CMakeLists.txt
- says 3.0.0. This patch fixes it.
-Author: Andrea Pappacoda <andrea@pappacoda.it>
-Origin: upstream, https://github.com/HowardHinnant/date/commit/2e19c006e2218447ee31f864191859517603f59f
-Forwarded: https://github.com/HowardHinnant/date/pull/699
-Last-Update: 2021-09-17
-
---- howardhinnant-date-3.0.1.orig/CMakeLists.txt
-+++ howardhinnant-date-3.0.1/CMakeLists.txt
-@@ -7,7 +7,7 @@
-      include( FetchContent )
-      FetchContent_Declare( date_src
-        GIT_REPOSITORY https://github.com/HowardHinnant/date.git
--       GIT_TAG        v3.0.0  # adjust tag/branch/commit as needed
-+       GIT_TAG        v3.0.1  # adjust tag/branch/commit as needed
-      )
-      FetchContent_MakeAvailable(date_src)
-      ...
-@@ -17,7 +17,7 @@
- 
- cmake_minimum_required( VERSION 3.7 )
- 
--project( date VERSION 3.0.0 )
-+project( date VERSION 3.0.1 )
- set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes
- 
- include( GNUInstallDirs )
diff --git a/debian/patches/series b/debian/patches/series
index 13c2316..629c20c 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,8 +1,3 @@
-cmake-update-project-version.patch
-Zero_initialize_local_info_in_get_info.patch
-When_comparing_sys_info_in_test.patch
-Change_default_test_mode_to_C++17.patch
-test_use_pthread_when_CXX_is_g++.patch
 cmake-install-all-headers.patch
 tests-disable-lcurl.patch
 Stop-using-PATH_MAX.patch
diff --git a/debian/patches/test_use_pthread_when_CXX_is_g++.patch b/debian/patches/test_use_pthread_when_CXX_is_g++.patch
deleted file mode 100644
index 3da6f1c..0000000
--- a/debian/patches/test_use_pthread_when_CXX_is_g++.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-Origin: upstream, https://github.com/HowardHinnant/date/commit/655b249b8f463f690c53a19d6b4110297699e3c5
-Bug: https://github.com/HowardHinnant/date/issues/713
-Last-Update: 2021-11-02
-
-From 655b249b8f463f690c53a19d6b4110297699e3c5 Mon Sep 17 00:00:00 2001
-From: Andrea Pappacoda <andrea@pappacoda.it>
-Date: Tue, 2 Nov 2021 18:20:04 +0100
-Subject: [PATCH] test: use -pthread when $CXX is g++
-
-Fixes https://github.com/HowardHinnant/date/issues/713
----
- test/testit | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/test/testit b/test/testit
-index f34ab356..69669ff7 100755
---- a/test/testit
-+++ b/test/testit
-@@ -48,6 +48,12 @@ if [ -z "$CXX_LANG" ]
- then
-     CXX_LANG=c++17
- fi
-+
-+if expr "$CXX" : ".*g++" >/dev/null
-+then
-+	OPTIONS="$OPTIONS -pthread"
-+fi
-+
- OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall $ROOT/src/tz.cpp -lcurl"
- 
- echo $ROOT
diff --git a/debian/patches/tests-disable-lcurl.patch b/debian/patches/tests-disable-lcurl.patch
index 7da1ea4..02f7939 100644
--- a/debian/patches/tests-disable-lcurl.patch
+++ b/debian/patches/tests-disable-lcurl.patch
@@ -5,8 +5,10 @@ Author: Andrea Pappacoda <andrea@pappacoda.it>
 Forwarded: not-needed
 Last-Update: 2022-08-04
 
---- howardhinnant-date-3.0.1.orig/test/testit
-+++ howardhinnant-date-3.0.1/test/testit
+Index: howardhinnant-date/test/testit
+===================================================================
+--- howardhinnant-date.orig/test/testit
++++ howardhinnant-date/test/testit
 @@ -54,7 +54,7 @@ then
  	OPTIONS="$OPTIONS -pthread"
  fi
diff --git a/include/date/date.h b/include/date/date.h
index 7b6b4e4..6960e8c 100644
--- a/include/date/date.h
+++ b/include/date/date.h
@@ -1316,7 +1316,7 @@ CONSTCD11
 std::chrono::duration<Rep, Period>
 abs(std::chrono::duration<Rep, Period> d)
 {
-    return d >= d.zero() ? d : -d;
+    return d >= d.zero() ? d : static_cast<decltype(d)>(-d);
 }
 
 // round down
@@ -4206,8 +4206,8 @@ template <class CharT, class Traits, class Duration>
 inline
 typename std::enable_if
 <
-    std::ratio_less<typename Duration::period, days::period>::value
-    , std::basic_ostream<CharT, Traits>&
+    !std::is_convertible<Duration, days>::value,
+    std::basic_ostream<CharT, Traits>&
 >::type
 operator<<(std::basic_ostream<CharT, Traits>& os, const sys_time<Duration>& tp)
 {
@@ -4787,7 +4787,11 @@ struct fields
     hh_mm_ss<Duration>    tod{};
     bool                  has_tod = false;
 
+#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ <= 409)
+    fields() : ymd{nanyear/0/0}, wd{8u}, tod{}, has_tod{false} {}
+#else
     fields() = default;
+#endif
 
     fields(year_month_day ymd_) : ymd(ymd_) {}
     fields(weekday wd_) : wd(wd_) {}
@@ -6209,8 +6213,13 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
           const std::chrono::seconds* offset_sec = nullptr)
 {
     using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
-    auto ld = floor<days>(tp);
-    fields<CT> fds{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
+    auto ld = std::chrono::time_point_cast<days>(tp);
+    fields<CT> fds;
+    if (ld <= tp)
+        fds = fields<CT>{year_month_day{ld}, hh_mm_ss<CT>{tp-local_seconds{ld}}};
+    else
+        fds = fields<CT>{year_month_day{ld - days{1}},
+                         hh_mm_ss<CT>{days{1} - (local_seconds{ld} - tp)}};
     return to_stream(os, fmt, fds, abbrev, offset_sec);
 }
 
@@ -6223,8 +6232,13 @@ to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
     using CT = typename std::common_type<Duration, seconds>::type;
     const std::string abbrev("UTC");
     CONSTDATA seconds offset{0};
-    auto sd = floor<days>(tp);
-    fields<CT> fds{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
+    auto sd = std::chrono::time_point_cast<days>(tp);
+    fields<CT> fds;
+    if (sd <= tp)
+        fds = fields<CT>{year_month_day{sd}, hh_mm_ss<CT>{tp-sys_seconds{sd}}};
+    else
+        fds = fields<CT>{year_month_day{sd - days{1}},
+                         hh_mm_ss<CT>{days{1} - (sys_seconds{sd} - tp)}};
     return to_stream(os, fmt, fds, &abbrev, &offset);
 }
 
@@ -6401,7 +6415,7 @@ read_long_double(std::basic_istream<CharT, Traits>& is, unsigned m = 1, unsigned
         is.setstate(std::ios::failbit);
         return 0;
     }
-    return i + f/std::pow(10.L, fcount);
+    return static_cast<long double>(i) + static_cast<long double>(f)/std::pow(10.L, fcount);
 }
 
 struct rs
@@ -6767,7 +6781,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                         CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
                         int tH;
                         int tM;
-                        long double S;
+                        long double S{};
                         read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
                                                CharT{':'}, rld{S, 1, w});
                         checked_set(H, tH, not_a_hour, is);
@@ -6847,7 +6861,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                         CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
                         int tH = not_a_hour;
                         int tM = not_a_minute;
-                        long double S;
+                        long double S{};
                         read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
                                                CharT{':'}, rld{S, 1, w});
                         checked_set(H, tH, not_a_hour, is);
@@ -7202,7 +7216,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                         // "%I:%M:%S %p"
                         using dfs = detail::decimal_format_seconds<Duration>;
                         CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
-                        long double S;
+                        long double S{};
                         int tI = not_a_hour_12_value;
                         int tM = not_a_minute;
                         read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2},
@@ -7258,7 +7272,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                     {
                         using dfs = detail::decimal_format_seconds<Duration>;
                         CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
-                        long double S;
+                        long double S{};
                         read(is, rld{S, 1, width == -1 ? w : static_cast<unsigned>(width)});
                         checked_set(s, round_i<Duration>(duration<long double>{S}),
                                     not_a_second, is);
@@ -7292,7 +7306,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                         CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width;
                         int tH = not_a_hour;
                         int tM = not_a_minute;
-                        long double S;
+                        long double S{};
                         read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2},
                                                CharT{':'}, rld{S, 1, w});
                         checked_set(H, tH, not_a_hour, is);
@@ -7755,11 +7769,11 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
             if (j != not_a_doy && Y != not_a_year)
             {
                 auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}};
-                if (m == 0)
+                if (m == not_a_month)
                     m = static_cast<int>(static_cast<unsigned>(ymd_trial.month()));
                 else if (month(static_cast<unsigned>(m)) != ymd_trial.month())
                     goto broken;
-                if (d == 0)
+                if (d == not_a_day)
                     d = static_cast<int>(static_cast<unsigned>(ymd_trial.day()));
                 else if (day(static_cast<unsigned>(d)) != ymd_trial.day())
                     goto broken;
@@ -7891,7 +7905,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year& y,
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.year().ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7907,7 +7921,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month& m,
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.month().ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7923,7 +7937,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, day& d,
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.day().ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7939,7 +7953,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, weekday& wd
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.wd.ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7955,7 +7969,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, year_month&
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.month().ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7971,7 +7985,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt, month_day&
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.month().ok() || !fds.ymd.day().ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -7987,7 +8001,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
 {
     using CT = std::chrono::seconds;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.ok())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -8007,7 +8021,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
     auto offptr = offset ? offset : &offset_local;
     fields<CT> fds{};
     fds.has_tod = true;
-    from_stream(is, fmt, fds, abbrev, offptr);
+    date::from_stream(is, fmt, fds, abbrev, offptr);
     if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -8025,7 +8039,7 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
     using detail::round_i;
     fields<CT> fds{};
     fds.has_tod = true;
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.ymd.ok() || !fds.tod.in_conventional_range())
         is.setstate(std::ios::failbit);
     if (!is.fail())
@@ -8042,12 +8056,13 @@ from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
 {
     using Duration = std::chrono::duration<Rep, Period>;
     using CT = typename std::common_type<Duration, std::chrono::seconds>::type;
+    using detail::round_i;
     fields<CT> fds{};
-    from_stream(is, fmt, fds, abbrev, offset);
+    date::from_stream(is, fmt, fds, abbrev, offset);
     if (!fds.has_tod)
         is.setstate(std::ios::failbit);
     if (!is.fail())
-        d = std::chrono::duration_cast<Duration>(fds.tod.to_duration());
+        d = round_i<Duration>(fds.tod.to_duration());
     return is;
 }
 
@@ -8070,6 +8085,25 @@ public:
         , offset_(offset)
         {}
 
+#if HAS_STRING_VIEW
+    parse_manip(const CharT* format, Parsable& tp,
+                std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+                std::chrono::minutes* offset = nullptr)
+        : format_(format)
+        , tp_(tp)
+        , abbrev_(abbrev)
+        , offset_(offset)
+        {}
+
+    parse_manip(std::basic_string_view<CharT, Traits> format, Parsable& tp,
+                std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
+                std::chrono::minutes* offset = nullptr)
+        : format_(format)
+        , tp_(tp)
+        , abbrev_(abbrev)
+        , offset_(offset)
+        {}
+#endif  // HAS_STRING_VIEW
 };
 
 template <class Parsable, class CharT, class Traits, class Alloc>
@@ -8077,14 +8111,14 @@ std::basic_istream<CharT, Traits>&
 operator>>(std::basic_istream<CharT, Traits>& is,
            const parse_manip<Parsable, CharT, Traits, Alloc>& x)
 {
-    return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
+    return date::from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_);
 }
 
 template <class Parsable, class CharT, class Traits, class Alloc>
 inline
 auto
 parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
                             format.c_str(), tp),
                 parse_manip<Parsable, CharT, Traits, Alloc>{format, tp})
 {
@@ -8096,7 +8130,7 @@ inline
 auto
 parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
       std::basic_string<CharT, Traits, Alloc>& abbrev)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
                             format.c_str(), tp, &abbrev),
                 parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
 {
@@ -8108,7 +8142,7 @@ inline
 auto
 parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
       std::chrono::minutes& offset)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
                             format.c_str(), tp,
                             std::declval<std::basic_string<CharT, Traits, Alloc>*>(),
                             &offset),
@@ -8122,7 +8156,7 @@ inline
 auto
 parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
       std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(),
                             format.c_str(), tp, &abbrev, &offset),
                 parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
 {
@@ -8135,7 +8169,7 @@ template <class Parsable, class CharT>
 inline
 auto
 parse(const CharT* format, Parsable& tp)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format, tp),
                 parse_manip<Parsable, CharT>{format, tp})
 {
     return {format, tp};
@@ -8145,7 +8179,7 @@ template <class Parsable, class CharT, class Traits, class Alloc>
 inline
 auto
 parse(const CharT* format, Parsable& tp, std::basic_string<CharT, Traits, Alloc>& abbrev)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
                             tp, &abbrev),
                 parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev})
 {
@@ -8156,7 +8190,7 @@ template <class Parsable, class CharT>
 inline
 auto
 parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT>&>(), format,
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT>&>(), format,
                             tp, std::declval<std::basic_string<CharT>*>(), &offset),
                 parse_manip<Parsable, CharT>{format, tp, nullptr, &offset})
 {
@@ -8168,7 +8202,7 @@ inline
 auto
 parse(const CharT* format, Parsable& tp,
       std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
-    -> decltype(from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
+    -> decltype(date::from_stream(std::declval<std::basic_istream<CharT, Traits>&>(), format,
                             tp, &abbrev, &offset),
                 parse_manip<Parsable, CharT, Traits, Alloc>{format, tp, &abbrev, &offset})
 {
diff --git a/include/date/ios.h b/include/date/ios.h
index ee54b9d..a9f8636 100644
--- a/include/date/ios.h
+++ b/include/date/ios.h
@@ -36,10 +36,10 @@
     {
     namespace iOSUtils
     {
-    
+
     std::string get_tzdata_path();
     std::string get_current_timezone();
-    
+
     }  // namespace iOSUtils
     }  // namespace date
 
diff --git a/include/date/julian.h b/include/date/julian.h
index d909d69..da692a4 100644
--- a/include/date/julian.h
+++ b/include/date/julian.h
@@ -1655,10 +1655,10 @@ inline
 bool
 month_day::ok() const NOEXCEPT
 {
-    CONSTDATA julian::day d[] = { 
-        julian::day(31), julian::day(29), julian::day(31), julian::day(30), 
-        julian::day(31), julian::day(30), julian::day(31), julian::day(31), 
-        julian::day(30), julian::day(31), julian::day(30), julian::day(31) 
+    CONSTDATA julian::day d[] = {
+        julian::day(31), julian::day(29), julian::day(31), julian::day(30),
+        julian::day(31), julian::day(30), julian::day(31), julian::day(31),
+        julian::day(30), julian::day(31), julian::day(30), julian::day(31)
     };
     return m_.ok() && julian::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
 }
@@ -1949,10 +1949,10 @@ inline
 day
 year_month_day_last::day() const NOEXCEPT
 {
-    CONSTDATA julian::day d[] = { 
-        julian::day(31), julian::day(28), julian::day(31), julian::day(30), 
-        julian::day(31), julian::day(30), julian::day(31), julian::day(31), 
-        julian::day(30), julian::day(31), julian::day(30), julian::day(31) 
+    CONSTDATA julian::day d[] = {
+        julian::day(31), julian::day(28), julian::day(31), julian::day(30),
+        julian::day(31), julian::day(30), julian::day(31), julian::day(31),
+        julian::day(30), julian::day(31), julian::day(30), julian::day(31)
     };
     return month() != feb || !y_.is_leap() ? d[static_cast<unsigned>(month())-1] : julian::day(29);
 }
diff --git a/include/date/ptz.h b/include/date/ptz.h
index ebd6e04..7dd4c16 100644
--- a/include/date/ptz.h
+++ b/include/date/ptz.h
@@ -36,6 +36,11 @@
 // Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
 // zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
 //
+// In C++17 CTAD simplifies this to:
+//
+// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
+// zoned_time zt{tz, system_clock::now()};
+//
 // If the rule set is missing (everything starting with ','), then the rule is that the
 // alternate offset is never enabled.
 //
@@ -322,6 +327,7 @@ time_zone::get_next_end(date::year y) const
     return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
 }
 
+inline
 date::sys_info
 time_zone::contant_offset() const
 {
@@ -678,6 +684,8 @@ read_date(const string_t& s, unsigned i, rule& r)
         ++i;
         unsigned n;
         i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
+        if (!(1 <= n && n <= 365))
+            throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]");
         r.mode_ = rule::J;
         r.n_ = n;
     }
@@ -686,16 +694,22 @@ read_date(const string_t& s, unsigned i, rule& r)
         ++i;
         unsigned m;
         i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
+        if (!(1 <= m && m <= 12))
+            throw_invalid(s, i-1, "Expected month to be in the range [1, 12]");
         if (i == s.size() || s[i] != '.')
             throw_invalid(s, i, "Expected '.' after month");
         ++i;
         unsigned n;
         i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]");
+        if (!(1 <= n && n <= 5))
+            throw_invalid(s, i-1, "Expected week number to be in the range [1, 5]");
         if (i == s.size() || s[i] != '.')
             throw_invalid(s, i, "Expected '.' after weekday index");
         ++i;
         unsigned wd;
         i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]");
+        if (wd > 6)
+            throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]");
         r.mode_ = rule::M;
         r.m_ = month{m};
         r.wd_ = weekday{wd};
@@ -705,6 +719,8 @@ read_date(const string_t& s, unsigned i, rule& r)
     {
         unsigned n;
         i = read_unsigned(s, i, 3, n);
+        if (n > 365)
+            throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]");
         r.mode_ = rule::N;
         r.n_ = n;
     }
@@ -786,16 +802,22 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
         throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
     unsigned x;
     i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
+    if (x > 24)
+        throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]");
     t = hours{x};
     if (i != s.size() && s[i] == ':')
     {
         ++i;
         i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
+        if (x > 59)
+            throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]");
         t += minutes{x};
         if (i != s.size() && s[i] == ':')
         {
             ++i;
             i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
+            if (x > 59)
+                throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]");
             t += seconds{x};
         }
     }
diff --git a/include/date/solar_hijri.h b/include/date/solar_hijri.h
index 6f6c331..8de9cb5 100644
--- a/include/date/solar_hijri.h
+++ b/include/date/solar_hijri.h
@@ -1707,11 +1707,11 @@ inline
 bool
 month_day::ok() const NOEXCEPT
 {
-    CONSTDATA solar_hijri::day d[] = { 
-        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), 
-        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), 
-        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), 
-        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30) 
+    CONSTDATA solar_hijri::day d[] = {
+        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
+        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
+        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
+        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30)
     };
     return m_.ok() && solar_hijri::day(1) <= d_ && d_ <= d[static_cast<unsigned>(m_)-1];
 }
@@ -2002,11 +2002,11 @@ inline
 day
 year_month_day_last::day() const NOEXCEPT
 {
-    CONSTDATA solar_hijri::day d[] = { 
-        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), 
-        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31), 
-        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30), 
-        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29) 
+    CONSTDATA solar_hijri::day d[] = {
+        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
+        solar_hijri::day(31), solar_hijri::day(31), solar_hijri::day(31),
+        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(30),
+        solar_hijri::day(30), solar_hijri::day(30), solar_hijri::day(29)
     };
     return month() != esf || !y_.is_leap() ?
         d[static_cast<unsigned>(month()) - 1] : solar_hijri::day(30);
diff --git a/include/date/tz.h b/include/date/tz.h
index 4921068..ee09b85 100644
--- a/include/date/tz.h
+++ b/include/date/tz.h
@@ -845,7 +845,7 @@ private:
     load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt,
                                  std::int32_t tzh_typecnt, std::int32_t tzh_charcnt);
 #else  // !USE_OS_TZDB
-    DATE_API sys_info   get_info_impl(sys_seconds tp, int timezone) const;
+    DATE_API sys_info   get_info_impl(sys_seconds tp, int tz_int) const;
     DATE_API void adjust_infos(const std::vector<detail::Rule>& rules);
     DATE_API void parse_info(std::istream& in);
 #endif  // !USE_OS_TZDB
diff --git a/src/ios.mm b/src/ios.mm
index fe3bb24..9e5d800 100644
--- a/src/ios.mm
+++ b/src/ios.mm
@@ -51,7 +51,7 @@ namespace date
 {
     namespace iOSUtils
     {
-        
+
         struct TarInfo
         {
             char objType;
@@ -60,14 +60,14 @@ namespace date
             size_t blocksContentSize; // adjusted size to 512 bytes blocks
             bool success;
         };
-        
+
         std::string convertCFStringRefPathToCStringPath(CFStringRef ref);
         bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath);
         TarInfo getTarObjectInfo(std::ifstream &readStream);
         std::string getTarObject(std::ifstream &readStream, int64_t size);
         bool writeFile(const std::string &tzdataPath, const std::string &fileName,
                        const std::string &data, size_t realContentSize);
-        
+
         std::string
         get_current_timezone()
         {
@@ -75,18 +75,18 @@ namespace date
             CFStringRef tzNameRef = CFTimeZoneGetName(tzRef);
             CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1;
             char buffer[bufferSize];
-            
+
             if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8))
             {
                 CFRelease(tzRef);
                 return std::string(buffer);
             }
-            
+
             CFRelease(tzRef);
-            
+
             return "";
         }
-        
+
         std::string
         get_tzdata_path()
         {
@@ -96,7 +96,7 @@ namespace date
                              INTERNAL_DIR + "/" + TZDATA_DIR);
             std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) +
                                     INTERNAL_DIR);
-            
+
             if (access(path.c_str(), F_OK) == 0)
             {
 #if TAR_DEBUG
@@ -104,34 +104,34 @@ namespace date
 #endif
                 CFRelease(homeUrlRef);
                 CFRelease(homePath);
-                
+
                 return result_path;
             }
-            
+
             CFBundleRef mainBundle = CFBundleGetMainBundle();
             CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION),
                                                               NULL);
-            
+
             if (CFArrayGetCount(paths) != 0)
             {
                 // get archive path, assume there is no other tar.gz in bundle
                 CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0));
                 CFStringRef archiveName = CFURLCopyPath(archiveUrl);
                 archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL);
-                
+
                 extractTzdata(homeUrlRef, archiveUrl, path);
-                
+
                 CFRelease(archiveUrl);
                 CFRelease(archiveName);
             }
-            
+
             CFRelease(homeUrlRef);
             CFRelease(homePath);
             CFRelease(paths);
-            
+
             return result_path;
         }
-        
+
         std::string
         convertCFStringRefPathToCStringPath(CFStringRef ref)
         {
@@ -142,55 +142,55 @@ namespace date
             delete[] buffer;
             return result;
         }
-        
+
         bool
         extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath)
         {
             std::string TAR_TMP_PATH = "/tmp.tar";
-            
+
             CFStringRef homeStringRef = CFURLCopyPath(homeUrl);
             auto homePath = convertCFStringRefPathToCStringPath(homeStringRef);
             CFRelease(homeStringRef);
-            
+
             CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl);
             auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef);
             CFRelease(archiveStringRef);
-            
+
             // create Library path
             auto libraryPath = homePath + INTERNAL_DIR;
-            
+
             // create tzdata path
             auto tzdataPath = libraryPath + "/" + TZDATA_DIR;
-            
+
             // -- replace %20 with " "
             const std::string search = "%20";
             const std::string replacement = " ";
             size_t pos = 0;
-            
+
             while ((pos = archivePath.find(search, pos)) != std::string::npos) {
                 archivePath.replace(pos, search.length(), replacement);
                 pos += replacement.length();
             }
-            
+
             gzFile tarFile = gzopen(archivePath.c_str(), "rb");
-            
+
             // create tar unpacking path
             auto tarPath = libraryPath + TAR_TMP_PATH;
-            
+
             // create tzdata directory
             mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-            
+
             // ======= extract tar ========
-            
+
             std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app);
             unsigned int bufferLength = 1024 * 256;  // 256Kb
             unsigned char *buffer = (unsigned char *)malloc(bufferLength);
             bool success = true;
-            
+
             while (true)
             {
                 int readBytes = gzread(tarFile, buffer, bufferLength);
-                
+
                 if (readBytes > 0)
                 {
                     os.write((char *) &buffer[0], readBytes);
@@ -214,21 +214,21 @@ namespace date
                             break;
                         }
             }
-            
+
             os.close();
             free(buffer);
             gzclose(tarFile);
-            
+
             if (!success)
             {
                 remove(tarPath.c_str());
                 return false;
             }
-            
+
             // ======== extract files =========
-            
+
             uint64_t location = 0; // Position in the file
-            
+
             // get file size
             struct stat stat_buf;
             int res = stat(tarPath.c_str(), &stat_buf);
@@ -239,20 +239,20 @@ namespace date
                 return false;
             }
             int64_t tarSize = stat_buf.st_size;
-            
+
             // create read stream
             std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary);
-            
+
             // process files
             while (location < tarSize)
             {
                 TarInfo info = getTarObjectInfo(is);
-                
+
                 if (!info.success || info.realContentSize == 0)
                 {
                     break; // something wrong or all files are read
                 }
-                
+
                 switch (info.objType)
                 {
                     case '0':   // file
@@ -266,17 +266,17 @@ namespace date
 #endif
                         writeFile(tzdataPath, info.objName, obj, info.realContentSize);
                         location += info.blocksContentSize;
-                        
+
                         break;
                     }
                 }
             }
-            
+
             remove(tarPath.c_str());
-            
+
             return true;
         }
-        
+
         TarInfo
         getTarObjectInfo(std::ifstream &readStream)
         {
@@ -285,22 +285,22 @@ namespace date
             char type;
             char name[TAR_NAME_SIZE + 1];
             char sizeBuf[TAR_SIZE_SIZE + 1];
-            
+
             readStream.read(buffer, length);
-            
+
             memcpy(&type, &buffer[TAR_TYPE_POSITION], 1);
-            
+
             memset(&name, '\0', TAR_NAME_SIZE + 1);
             memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE);
-            
+
             memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1);
             memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE);
             size_t realSize = strtol(sizeBuf, NULL, 8);
             size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE));
-            
+
             return {type, std::string(name), realSize, blocksSize, true};
         }
-        
+
         std::string
         getTarObject(std::ifstream &readStream, int64_t size)
         {
@@ -308,29 +308,29 @@ namespace date
             readStream.read(buffer, size);
             return std::string(buffer);
         }
-        
+
         bool
         writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data,
                   size_t realContentSize)
         {
             std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary);
-            
+
             if (!os) {
                 return false;
             }
-            
+
             // trim empty space
             char trimmedData[realContentSize + 1];
             memset(&trimmedData, '\0', realContentSize);
             memcpy(&trimmedData, data.c_str(), realContentSize);
-            
+
             // write
             os.write(trimmedData, realContentSize);
             os.close();
-            
+
             return true;
         }
-        
+
     }  // namespace iOSUtils
 }  // namespace date
 
diff --git a/src/tz.cpp b/src/tz.cpp
index 26babbd..bd9fad4 100644
--- a/src/tz.cpp
+++ b/src/tz.cpp
@@ -198,6 +198,35 @@ namespace
     using co_task_mem_ptr = std::unique_ptr<wchar_t[], task_mem_deleter>;
 }
 
+static
+std::wstring
+convert_utf8_to_utf16(const std::string& s)
+{
+    std::wstring out;
+    const int size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0);
+
+    if (size == 0)
+    {
+        std::string msg = "Failed to determine required size when converting \"";
+        msg += s;
+        msg += "\" to UTF-16.";
+        throw std::runtime_error(msg);
+    }
+
+    out.resize(size);
+    const int check = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, &out[0], size);
+
+    if (size != check)
+    {
+        std::string msg = "Failed to convert \"";
+        msg += s;
+        msg += "\" to UTF-16.";
+        throw std::runtime_error(msg);
+    }
+
+    return out;
+}
+
 // We might need to know certain locations even if not using the remote API,
 // so keep these routines out of that block for now.
 static
@@ -267,6 +296,89 @@ get_download_folder()
 
 #  endif  // !_WIN32
 
+/*
+ * This class is provided to mimic the following usage of `ifstream`:
+ *
+ * std::ifstream is(filename);
+ *
+ * file_streambuf ibuf(filename);
+ * std::istream is(&ibuf);
+ *
+ * This is required because `ifstream` does not support opening files
+ * containing wide characters on Windows. On Windows, `file_streambuf` uses
+ * `file_open()` to convert the file name to UTF-16 before opening it with
+ * `_wfopen()`.
+ *
+ * Note that this is not an exact re-implementation of `ifstream`,
+ * but is enough for usage here.
+ *
+ * It is partially based on these two implementations:
+ * - fdinbuf from http://www.josuttis.com/cppcode/fdstream.html
+ * - stdiobuf https://stackoverflow.com/questions/12342542/convert-file-to-ifstream-c-android-ndk
+ *
+ * Apparently MSVC provides non-standard overloads of `ifstream` that support
+ * a `const wchar_t*` file name, but MinGW does not https://stackoverflow.com/a/822032
+ */
+class file_streambuf
+  : public std::streambuf
+{
+private:
+    FILE* file_;
+    static const int buffer_size_ = 1024;
+    char buffer_[buffer_size_];
+
+public:
+    ~file_streambuf()
+    {
+        if (file_)
+        {
+            ::fclose(file_);
+        }
+    }
+    file_streambuf(const file_streambuf&) = delete;
+    file_streambuf& operator=(const file_streambuf&) = delete;
+
+    file_streambuf(const std::string& filename)
+        : file_(file_open(filename))
+    {
+    }
+
+protected:
+    virtual
+    int_type
+    underflow()
+    {
+        if (gptr() == egptr() && file_)
+        {
+            const size_t size = ::fread(buffer_, 1, buffer_size_, file_);
+            setg(buffer_, buffer_, buffer_ + size);
+        }
+        return (gptr() == egptr())
+            ? traits_type::eof()
+                : traits_type::to_int_type(*gptr());
+    }
+
+private:
+    FILE*
+    file_open(const std::string& filename)
+    {
+#  ifdef _WIN32
+        std::wstring wfilename = convert_utf8_to_utf16(filename);
+        FILE* file = ::_wfopen(wfilename.c_str(), L"rb");
+#  else // !_WIN32
+        FILE* file = ::fopen(filename.c_str(), "rb");
+#  endif // _WIN32
+        if (file == NULL)
+        {
+            std::string msg = "Error opening file \"";
+            msg += filename;
+            msg += "\".";
+            throw std::runtime_error(msg);
+        }
+        return file;
+    }
+};
+
 #endif  // !USE_OS_TZDB
 
 namespace date
@@ -303,9 +415,9 @@ access_install()
 }
 
 void
-set_install(const std::string& s)
+set_install(const std::string& install)
 {
-    access_install() = s;
+    access_install() = install;
 }
 
 static
@@ -559,15 +671,8 @@ load_timezone_mappings_from_xml_file(const std::string& input_path)
     std::vector<detail::timezone_mapping> mappings;
     std::string line;
 
-    std::ifstream is(input_path);
-    if (!is.is_open())
-    {
-        // We don't emit file exceptions because that's an implementation detail.
-        std::string msg = "Error opening time zone mapping file \"";
-        msg += input_path;
-        msg += "\".";
-        throw std::runtime_error(msg);
-    }
+    file_streambuf ibuf(input_path);
+    std::istream is(&ibuf);
 
     auto error = [&input_path, &line_num](const char* info)
     {
@@ -697,7 +802,6 @@ load_timezone_mappings_from_xml_file(const std::string& input_path)
         }
     }
 
-    is.close();
     return mappings;
 }
 
@@ -2164,7 +2268,7 @@ time_zone::get_info_impl(local_seconds tp) const
 {
     using namespace std::chrono;
     init();
-    local_info i;
+    local_info i{};
     i.result = local_info::unique;
     auto tr = upper_bound(transitions_.begin(), transitions_.end(), tp,
                           [](const local_seconds& x, const transition& t)
@@ -2669,16 +2773,16 @@ find_read_and_leap_seconds()
             std::getline(in, line);
             if (!line.empty() && line[0] != '#')
             {
-                std::istringstream in(line);
-                in.exceptions(std::ios::failbit | std::ios::badbit);
+                std::istringstream iss(line);
+                iss.exceptions(std::ios::failbit | std::ios::badbit);
                 std::string word;
-                in >> word;
+                iss >> word;
                 if (word == "Leap")
                 {
                     int y, m, d;
-                    in >> y;
-                    m = static_cast<int>(parse_month(in));
-                    in >> d;
+                    iss >> y;
+                    m = static_cast<int>(parse_month(iss));
+                    iss >> d;
                     leap_seconds.push_back(leap_second(sys_days{year{y}/m/d} + days{1},
                                                                  detail::undocumented{}));
                 }
@@ -2703,11 +2807,11 @@ find_read_and_leap_seconds()
             std::getline(in, line);
             if (!line.empty() && line[0] != '#')
             {
-                std::istringstream in(line);
-                in.exceptions(std::ios::failbit | std::ios::badbit);
+                std::istringstream iss(line);
+                iss.exceptions(std::ios::failbit | std::ios::badbit);
                 using seconds = std::chrono::seconds;
                 seconds::rep s;
-                in >> s;
+                iss >> s;
                 if (s == 2272060800)
                     continue;
                 leap_seconds.push_back(leap_second(sys_seconds{seconds{s}} - offset,
@@ -2836,7 +2940,8 @@ bool
 file_exists(const std::string& filename)
 {
 #ifdef _WIN32
-    return ::_access(filename.c_str(), 0) == 0;
+    std::wstring wfilename = convert_utf8_to_utf16(filename);
+    return ::_waccess(wfilename.c_str(), 0) == 0;
 #else
     return ::access(filename.c_str(), F_OK) == 0;
 #endif
@@ -3413,16 +3518,27 @@ std::string
 get_version(const std::string& path)
 {
     std::string version;
-    std::ifstream infile(path + "version");
-    if (infile.is_open())
+
+    std::string path_version = path + "version";
+
+    if (file_exists(path_version))
     {
+        file_streambuf inbuf(path_version);
+        std::istream infile(&inbuf);
+
         infile >> version;
+
         if (!infile.fail())
             return version;
     }
-    else
+
+    std::string path_news = path + "NEWS";
+
+    if (file_exists(path_news))
     {
-        infile.open(path + "NEWS");
+        file_streambuf inbuf(path_news);
+        std::istream infile(&inbuf);
+
         while (infile)
         {
             infile >> version;
@@ -3433,6 +3549,7 @@ get_version(const std::string& path)
             }
         }
     }
+
     throw std::runtime_error("Unable to get Timezone database version from " + path);
 }
 
@@ -3504,7 +3621,13 @@ init_tzdb()
 
     for (const auto& filename : files)
     {
-        std::ifstream infile(path + filename);
+        std::string file_path = path + filename;
+        if (!file_exists(file_path))
+        {
+          continue;
+        }
+        file_streambuf inbuf(file_path);
+        std::istream infile(&inbuf);
         while (infile)
         {
             std::getline(infile, line);
@@ -3537,6 +3660,10 @@ init_tzdb()
                 {
                     db->zones.back().add(line);
                 }
+                else if (word.size() > 0 && word[0] == '#')
+                {
+                    continue;
+                }
                 else
                 {
                     std::cerr << line << '\n';
@@ -3919,7 +4046,7 @@ tzdb::current_zone() const
             auto p = result.find("ZONE=\"");
             if (p != std::string::npos)
             {
-                result.erase(p, p+6);
+                result.erase(0, p+6);
                 result.erase(result.rfind('"'));
                 return locate_zone(result);
             }
diff --git a/test/date_test/detail/decimal_format_seconds.pass.cpp b/test/date_test/detail/decimal_format_seconds.pass.cpp
index cce6ae8..6769905 100644
--- a/test/date_test/detail/decimal_format_seconds.pass.cpp
+++ b/test/date_test/detail/decimal_format_seconds.pass.cpp
@@ -33,15 +33,15 @@
 // private:
 //     std::chrono::seconds s_;
 //     precision            sub_s_;
-// 
+//
 // public:
 //     constexpr explicit decimal_format_seconds(const Duration& d) noexcept;
-// 
+//
 //     constexpr std::chrono::seconds& seconds() noexcept;
 //     constexpr std::chrono::seconds seconds() const noexcept;
 //     constexpr precision subseconds() const noexcept;
 //     constexpr precision to_duration() const noexcept;
-// 
+//
 //     template <class CharT, class Traits>
 //     friend
 //     std::basic_ostream<CharT, Traits>&
diff --git a/test/date_test/multi_year_duration_addition.pass.cpp b/test/date_test/multi_year_duration_addition.pass.cpp
index 5d1d031..4100615 100644
--- a/test/date_test/multi_year_duration_addition.pass.cpp
+++ b/test/date_test/multi_year_duration_addition.pass.cpp
@@ -83,7 +83,7 @@ main()
     constexpr ConvertibleToMonths custom_month;
     constexpr ConvertibleToYears custom_year;
     constexpr ConvertibleToYearsAndMonths prefer_year;
-    
+
 
     {
        constexpr year_month ym = 2001_y/feb;
@@ -97,7 +97,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += one_month);
        CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan);
        NOEXCEPT_ASSERT(copy(ym) -= one_month);
-      
+
        CPP11_ASSERT(ym + one_year == 2002_y/feb);
        NOEXCEPT_ASSERT(ym + one_year);
        CPP11_ASSERT(one_year + ym == 2002_y/feb);
@@ -141,7 +141,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += custom_month);
        CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan);
        NOEXCEPT_ASSERT(copy(ym) -= custom_month);
-      
+
        CPP11_ASSERT(ym + custom_year == 2002_y/feb);
        NOEXCEPT_ASSERT(ym + custom_year);
        CPP11_ASSERT(custom_year + ym == 2002_y/feb);
@@ -177,7 +177,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += one_month);
        CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/10);
        NOEXCEPT_ASSERT(copy(ym) -= one_month);
-      
+
        CPP11_ASSERT(ym + one_year == 2002_y/feb/10);
        NOEXCEPT_ASSERT(ym + one_year);
        CPP11_ASSERT(one_year + ym == 2002_y/feb/10);
@@ -221,7 +221,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += custom_month);
        CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/10);
        NOEXCEPT_ASSERT(copy(ym) -= custom_month);
-      
+
        CPP11_ASSERT(ym + custom_year == 2002_y/feb/10);
        NOEXCEPT_ASSERT(ym + custom_year);
        CPP11_ASSERT(custom_year + ym == 2002_y/feb/10);
@@ -257,7 +257,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += one_month);
        CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/last);
        NOEXCEPT_ASSERT(copy(ym) -= one_month);
-      
+
        CPP11_ASSERT(ym + one_year == 2002_y/feb/last);
        NOEXCEPT_ASSERT(ym + one_year);
        CPP11_ASSERT(one_year + ym == 2002_y/feb/last);
@@ -301,7 +301,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += custom_month);
        CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/last);
        NOEXCEPT_ASSERT(copy(ym) -= custom_month);
-      
+
        CPP11_ASSERT(ym + custom_year == 2002_y/feb/last);
        NOEXCEPT_ASSERT(ym + custom_year);
        CPP11_ASSERT(custom_year + ym == 2002_y/feb/last);
@@ -337,7 +337,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += one_month);
        CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/fri[4]);
        NOEXCEPT_ASSERT(copy(ym) -= one_month);
-      
+
        CPP11_ASSERT(ym + one_year == 2002_y/feb/fri[4]);
        NOEXCEPT_ASSERT(ym + one_year);
        CPP11_ASSERT(one_year + ym == 2002_y/feb/fri[4]);
@@ -381,7 +381,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += custom_month);
        CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/fri[4]);
        NOEXCEPT_ASSERT(copy(ym) -= custom_month);
-      
+
        CPP11_ASSERT(ym + custom_year == 2002_y/feb/fri[4]);
        NOEXCEPT_ASSERT(ym + custom_year);
        CPP11_ASSERT(custom_year + ym == 2002_y/feb/fri[4]);
@@ -417,7 +417,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += one_month);
        CPP14_ASSERT((copy(ym) -= one_month) == 2001_y/jan/fri[last]);
        NOEXCEPT_ASSERT(copy(ym) -= one_month);
-      
+
        CPP11_ASSERT(ym + one_year == 2002_y/feb/fri[last]);
        NOEXCEPT_ASSERT(ym + one_year);
        CPP11_ASSERT(one_year + ym == 2002_y/feb/fri[last]);
@@ -461,7 +461,7 @@ main()
        NOEXCEPT_ASSERT(copy(ym) += custom_month);
        CPP14_ASSERT((copy(ym) -= custom_month) == 2001_y/jan/fri[last]);
        NOEXCEPT_ASSERT(copy(ym) -= custom_month);
-      
+
        CPP11_ASSERT(ym + custom_year == 2002_y/feb/fri[last]);
        NOEXCEPT_ASSERT(ym + custom_year);
        CPP11_ASSERT(custom_year + ym == 2002_y/feb/fri[last]);
diff --git a/test/date_test/year_month.pass.cpp b/test/date_test/year_month.pass.cpp
index eec28a9..44954c9 100644
--- a/test/date_test/year_month.pass.cpp
+++ b/test/date_test/year_month.pass.cpp
@@ -123,7 +123,7 @@ void test_arithemtic_not_ok()
     assert(ym - months{0} == ym2);
     assert(ym - ym2 == months{0});
     assert(ym2 - ym == months{0});
-   
+
     auto ymc = ym;
     ymc += months{0};
     assert(ymc.ok());
diff --git a/test/posix/ptz.pass.cpp b/test/posix/ptz.pass.cpp
index 5601c21..c17d28a 100644
--- a/test/posix/ptz.pass.cpp
+++ b/test/posix/ptz.pass.cpp
@@ -29,10 +29,11 @@
 bool
 is_equal(date::sys_info const& x, date::sys_info const& y)
 {
+    using namespace std::chrono;
     return x.begin == y.begin &&
            x.end == y.end &&
            x.offset == y.offset &&
-           x.save == y.save &&
+           (x.save == minutes{0}) == (y.save == minutes{0}) &&
            x.abbrev == y.abbrev;
 }
 
@@ -55,7 +56,7 @@ main()
     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)));
diff --git a/test/testit b/test/testit
index 21ec3a2..69669ff 100755
--- a/test/testit
+++ b/test/testit
@@ -46,8 +46,14 @@ fi
 
 if [ -z "$CXX_LANG" ]
 then
-    CXX_LANG=c++14
+    CXX_LANG=c++17
 fi
+
+if expr "$CXX" : ".*g++" >/dev/null
+then
+	OPTIONS="$OPTIONS -pthread"
+fi
+
 OPTIONS="-std=${CXX_LANG} $OPTIONS -I$ROOT -Wall $ROOT/src/tz.cpp -lcurl"
 
 echo $ROOT
diff --git a/test/tz_test/zoned_time.pass.cpp b/test/tz_test/zoned_time.pass.cpp
index 70f3a83..cada23c 100644
--- a/test/tz_test/zoned_time.pass.cpp
+++ b/test/tz_test/zoned_time.pass.cpp
@@ -25,12 +25,12 @@
 // {
 // public:
 //     using duration = typename std::common_type<Duration, std::chrono::seconds>::type;
-// 
+//
 //              zoned_time();
 //              zoned_time(const sys_time<Duration>& st);
 //     explicit zoned_time(const time_zone* z);
 //     explicit zoned_time(std::string_view name);
-// 
+//
 //     template <class Duration2,
 //               class = typename std::enable_if
 //                       <
@@ -38,36 +38,36 @@
 //                                               sys_time<Duration>>::value
 //                       >::type>
 //         zoned_time(const zoned_time<Duration2>& zt) NOEXCEPT;
-// 
+//
 //     zoned_time(const time_zone* z,    const local_time<Duration>& tp);
 //     zoned_time(std::string_view name, const local_time<Duration>& tp);
 //     zoned_time(const time_zone* z,    const local_time<Duration>& tp, choose c);
 //     zoned_time(std::string_view name, const local_time<Duration>& tp, choose c);
-// 
+//
 //     zoned_time(const time_zone* z,    const zoned_time<Duration>& zt);
 //     zoned_time(std::string_view name, const zoned_time<Duration>& zt);
 //     zoned_time(const time_zone* z,    const zoned_time<Duration>& zt, choose);
 //     zoned_time(std::string_view name, const zoned_time<Duration>& zt, choose);
-// 
+//
 //     zoned_time(const time_zone* z,    const sys_time<Duration>& st);
 //     zoned_time(std::string_view name, const sys_time<Duration>& st);
-// 
+//
 //     zoned_time& operator=(const sys_time<Duration>& st);
 //     zoned_time& operator=(const local_time<Duration>& ut);
-// 
+//
 //     explicit operator sys_time<duration>() const;
 //     explicit operator local_time<duration>() const;
-// 
+//
 //     const time_zone*     get_time_zone() const;
 //     local_time<duration> get_local_time() const;
 //     sys_time<duration>   get_sys_time() const;
 //     sys_info             get_info() const;
-// 
+//
 //     template <class Duration1, class Duration2>
 //     friend
 //     bool
 //     operator==(const zoned_time<Duration1>& x, const zoned_time<Duration2>& y);
-// 
+//
 //     template <class CharT, class Traits, class Duration1>
 //     friend
 //     std::basic_ostream<CharT, Traits>&
diff --git a/test/tz_test/zoned_time_deduction.pass.cpp b/test/tz_test/zoned_time_deduction.pass.cpp
index e62e11a..6398bcd 100644
--- a/test/tz_test/zoned_time_deduction.pass.cpp
+++ b/test/tz_test/zoned_time_deduction.pass.cpp
@@ -154,7 +154,7 @@ main()
     {
         zoned_time zt{};
         static_assert(std::is_same<decltype(zt), zoned_time<seconds>>::value, "");
-    } 
+    }
 
     // zoned_time
     {