diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs
index bd8594bb3feb4807a8a80762e73ebc0869b1a980..2fa6cac5672c757b0a4d36cde3ed68885fd82caa 100644
--- a/src/header/time/mod.rs
+++ b/src/header/time/mod.rs
@@ -12,6 +12,10 @@ pub use self::constants::*;
 pub mod constants;
 mod strftime;
 
+const YEARS_PER_ERA: time_t = 400;
+const DAYS_PER_ERA: time_t = 146097;
+const SECS_PER_DAY: time_t = 24 * 60 * 60;
+
 #[repr(C)]
 #[derive(Clone, Copy, Default)]
 pub struct timespec {
@@ -311,9 +315,6 @@ pub unsafe extern "C" fn gmtime_r(clock: *const time_t, result: *mut tm) -> *mut
      * Note that we need 0-based months here, though.
      * Overall, this implementation should generate correct results as
      * long as the tm_year value will fit in a c_int. */
-    const SECS_PER_DAY: time_t = 24 * 60 * 60;
-    const DAYS_PER_ERA: time_t = 146097;
-
     let unix_secs = *clock;
 
     /* Day number here is possibly negative, remainder will always be
@@ -414,44 +415,68 @@ pub unsafe extern "C" fn localtime_r(clock: *const time_t, t: *mut tm) -> *mut t
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn mktime(t: *mut tm) -> time_t {
-    let mut year = (*t).tm_year + 1900;
-    let mut month = (*t).tm_mon;
-    let mut day = (*t).tm_mday as i64 - 1;
-
-    let leap = if leap_year(year) { 1 } else { 0 };
-
-    if year < 1970 {
-        day = MONTH_DAYS[if leap_year(year) { 1 } else { 0 }][(*t).tm_mon as usize] as i64 - day;
-
-        while year < 1969 {
-            year += 1;
-            day += if leap_year(year) { 366 } else { 365 };
-        }
-
-        while month < 11 {
-            month += 1;
-            day += MONTH_DAYS[leap][month as usize] as i64;
-        }
-
-        (-(day * (60 * 60 * 24)
-            - (((*t).tm_hour as i64) * (60 * 60) + ((*t).tm_min as i64) * 60 + (*t).tm_sec as i64)))
-            as time_t
-    } else {
-        while year > 1970 {
-            year -= 1;
-            day += if leap_year(year) { 366 } else { 365 };
+pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t {
+    /* For the details of the algorithm used here, see
+     * https://howardhinnant.github.io/date_algorithms.html#days_from_civil
+     */
+
+    fn inner(timeptr_mut: &mut tm) -> Option<time_t> {
+        let year = time_t::try_from(timeptr_mut.tm_year)
+            .ok()
+            .and_then(|tm_year| tm_year.checked_add(1900))?;
+        let month = time_t::try_from(timeptr_mut.tm_mon).ok()?;
+        let mday = time_t::try_from(timeptr_mut.tm_mday).ok()?;
+
+        let hour = time_t::try_from(timeptr_mut.tm_hour).ok()?;
+        let min = time_t::try_from(timeptr_mut.tm_min).ok()?;
+        let sec = time_t::try_from(timeptr_mut.tm_sec).ok()?;
+
+        // TODO: handle tm_isdst
+
+        let year_transformed = if month < 2 { year - 1 } else { year };
+
+        let era = year_transformed.div_euclid(YEARS_PER_ERA);
+        let year_of_era = year_transformed.rem_euclid(YEARS_PER_ERA);
+
+        let day_of_year =
+            (153 * (if month > 1 { month - 2 } else { month + 10 }) + 2) / 5 + mday - 1; // adapted for zero-based months
+
+        let day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year;
+        let unix_days = era * DAYS_PER_ERA + day_of_era - 719468;
+        let secs_of_day = hour * (60 * 60) + min * 60 + sec;
+
+        let unix_secs = unix_days
+            .checked_mul(SECS_PER_DAY)
+            .and_then(|day_secs| day_secs.checked_add(secs_of_day))?;
+
+        // Normalize input struct with values from their standard ranges
+        let mut normalized_tm = tm {
+            tm_sec: 0,
+            tm_min: 0,
+            tm_hour: 0,
+            tm_mday: 0,
+            tm_mon: 0,
+            tm_year: 0,
+            tm_wday: 0,
+            tm_yday: 0,
+            tm_isdst: 0,
+            tm_gmtoff: 0,
+            tm_zone: UTC,
+        };
+        if unsafe { gmtime_r(&unix_secs, &mut normalized_tm).is_null() } {
+            None
+        } else {
+            *timeptr_mut = normalized_tm;
+            Some(unix_secs)
         }
+    }
 
-        while month > 0 {
-            month -= 1;
-            day += MONTH_DAYS[leap][month as usize] as i64;
+    match inner(&mut *timeptr) {
+        Some(unix_secs) => unix_secs,
+        None => {
+            platform::ERRNO.set(EOVERFLOW);
+            -1 as time_t
         }
-
-        (day * (60 * 60 * 24)
-            + ((*t).tm_hour as i64) * (60 * 60)
-            + ((*t).tm_min as i64) * 60
-            + (*t).tm_sec as i64) as time_t
     }
 }
 
@@ -502,20 +527,7 @@ pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t {
 
 #[no_mangle]
 pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t {
-    let mut y = (*tm).tm_year as time_t + 1900;
-    let mut m = (*tm).tm_mon as time_t + 1;
-    if m <= 2 {
-        y -= 1;
-        m += 12;
-    }
-    let d = (*tm).tm_mday as time_t;
-    let h = (*tm).tm_hour as time_t;
-    let mi = (*tm).tm_min as time_t;
-    let s = (*tm).tm_sec as time_t;
-    (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400
-        + 3600 * h
-        + 60 * mi
-        + s
+    mktime(tm)
 }
 
 // #[no_mangle]
diff --git a/tests/wchar/wcpncpy.c b/tests/wchar/wcpncpy.c
index 9621ad24582b7c054aef97acfad6cc5beb6af720..62c0bdb4e252df6cb9955b6ad5d1b969ee5f4ef0 100644
--- a/tests/wchar/wcpncpy.c
+++ b/tests/wchar/wcpncpy.c
@@ -4,12 +4,34 @@
 
 int main() {
     wchar_t src[] = L"Привет мир";
-    wchar_t dest[10];
-    wchar_t* result = wcpncpy(dest, src, 5);
 
-    assert(wcsncmp(dest, src, 5) == 0);
-    assert(*result == L'\0');
-    assert(result == dest + 5);
+    size_t n_input = 10;
+    size_t n_short = 6;
+    size_t n_long = 15;
+
+    // Initialize with sentinel values to detect exactly how much is overwritten
+    wchar_t dest_short[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78";
+    wchar_t dest_long[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78";
+
+    wchar_t expected_short[] = L"Привет\x34\x56\x78\x90\x12\x34\x56\x78";
+
+    // The "short" test should copy exactly n_short characters without terminating null
+    wchar_t* result_short = wcpncpy(dest_short, src, n_short);
+    assert(wcsncmp(dest_short, src, n_short) == 0);
+    assert(result_short == dest_short + n_short);
+    for (size_t i = n_short; i < n_long; i++) {
+        // Check that the sentinel characters have not been overwritten
+        assert(dest_short[i] == expected_short[i]);
+    }
+
+    // The "long" test should write the input string, with nulls appended up to n_long
+    wchar_t* result_long = wcpncpy(dest_long, src, n_long);
+    assert(wcsncmp(dest_long, src, n_long) == 0);
+    assert(result_long == dest_long + n_input);
+    for (size_t i = n_input; i < n_long; i++) {
+        // Check that nulls are written up to n_long
+        assert(dest_long[i] == L'\0');
+    }
 
     return 0;
 }