diff --git a/python/vyos/utils/convert.py b/python/vyos/utils/convert.py
index c02f0071e..41e65081f 100644
--- a/python/vyos/utils/convert.py
+++ b/python/vyos/utils/convert.py
@@ -1,200 +1,205 @@
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 def seconds_to_human(s, separator=""):
     """ Converts number of seconds passed to a human-readable
     interval such as 1w4d18h35m59s
     """
     s = int(s)
 
+    year = 60 * 60 * 24 * 365.25
     week = 60 * 60 * 24 * 7
     day = 60 * 60 * 24
     hour = 60 * 60
 
-    remainder = 0
-    result = ""
+    result = []
+
+    years = s // year
+    if years > 0:
+        result.append(f'{int(years)}y')
+        s = int(s % year)
 
     weeks = s // week
     if weeks > 0:
-        result = "{0}w".format(weeks)
+        result.append(f'{weeks}w')
         s = s % week
 
     days = s // day
     if days > 0:
-        result = "{0}{1}{2}d".format(result, separator, days)
+        result.append(f'{days}d')
         s = s % day
 
     hours = s // hour
     if hours > 0:
-        result = "{0}{1}{2}h".format(result, separator, hours)
+        result.append(f'{hours}h')
         s = s % hour
 
     minutes = s // 60
     if minutes > 0:
-        result = "{0}{1}{2}m".format(result, separator, minutes)
+        result.append(f'{minutes}m')
         s = s % 60
 
     seconds = s
     if seconds > 0:
-        result = "{0}{1}{2}s".format(result, separator, seconds)
+        result.append(f'{seconds}s')
 
-    return result
+    return separator.join(result)
 
 def bytes_to_human(bytes, initial_exponent=0, precision=2,
                    int_below_exponent=0):
     """ Converts a value in bytes to a human-readable size string like 640 KB
 
     The initial_exponent parameter is the exponent of 2,
     e.g. 10 (1024) for kilobytes, 20 (1024 * 1024) for megabytes.
     """
 
     if bytes == 0:
         return "0 B"
 
     from math import log2
 
     bytes = bytes * (2**initial_exponent)
 
     # log2 is a float, while range checking requires an int
     exponent = int(log2(bytes))
     if exponent < int_below_exponent:
         precision = 0
 
     if exponent < 10:
         value = bytes
         suffix = "B"
     elif exponent in range(10, 20):
         value = bytes / 1024
         suffix = "KB"
     elif exponent in range(20, 30):
         value = bytes / 1024**2
         suffix = "MB"
     elif exponent in range(30, 40):
         value = bytes / 1024**3
         suffix = "GB"
     else:
         value = bytes / 1024**4
         suffix = "TB"
     # Add a new case when the first machine with petabyte RAM
     # hits the market.
 
     size_string = "{0:.{1}f} {2}".format(value, precision, suffix)
     return size_string
 
 def human_to_bytes(value):
     """ Converts a data amount with a unit suffix to bytes, like 2K to 2048 """
 
     from re import match as re_match
 
     res = re_match(r'^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$', value)
 
     if not res:
         raise ValueError(f"'{value}' is not a valid data amount")
     else:
         amount = float(res.group(1))
         unit = res.group(2).lower()
 
         if unit == 'b':
             res = amount
         elif (unit == 'k') or (unit == 'kb'):
             res = amount * 1024
         elif (unit == 'm') or (unit == 'mb'):
             res = amount * 1024**2
         elif (unit == 'g') or (unit == 'gb'):
             res = amount * 1024**3
         elif (unit == 't') or (unit == 'tb'):
             res = amount * 1024**4
         else:
             raise ValueError(f"Unsupported data unit '{unit}'")
 
     # There cannot be fractional bytes, so we convert them to integer.
     # However, truncating causes problems with conversion back to human unit,
     # so we round instead -- that seems to work well enough.
     return round(res)
 
 def mac_to_eui64(mac, prefix=None):
     """
     Convert a MAC address to a EUI64 address or, with prefix provided, a full
     IPv6 address.
     Thankfully copied from https://gist.github.com/wido/f5e32576bb57b5cc6f934e177a37a0d3
     """
     import re
     from ipaddress import ip_network
     # http://tools.ietf.org/html/rfc4291#section-2.5.1
     eui64 = re.sub(r'[.:-]', '', mac).lower()
     eui64 = eui64[0:6] + 'fffe' + eui64[6:]
     eui64 = hex(int(eui64[0:2], 16) ^ 2)[2:].zfill(2) + eui64[2:]
 
     if prefix is None:
         return ':'.join(re.findall(r'.{4}', eui64))
     else:
         try:
             net = ip_network(prefix, strict=False)
             euil = int('0x{0}'.format(eui64), 16)
             return str(net[euil])
         except:  # pylint: disable=bare-except
             return
 
 
 def convert_data(data) -> dict | list | tuple | str | int | float | bool | None:
     """Filter and convert multiple types of data to types usable in CLI/API
 
     WARNING: Must not be used for anything except formatting output for API or CLI
 
     On the output allowed everything supported in JSON.
 
     Args:
         data (Any): input data
 
     Returns:
         dict | list | tuple | str | int | float | bool | None: converted data
     """
     from base64 import b64encode
 
     # return original data for types which do not require conversion
     if isinstance(data, str | int | float | bool | None):
         return data
 
     if isinstance(data, list):
         list_tmp = []
         for item in data:
             list_tmp.append(convert_data(item))
         return list_tmp
 
     if isinstance(data, tuple):
         list_tmp = list(data)
         tuple_tmp = tuple(convert_data(list_tmp))
         return tuple_tmp
 
     if isinstance(data, bytes | bytearray):
         try:
             return data.decode()
         except UnicodeDecodeError:
             return b64encode(data).decode()
 
     if isinstance(data, set | frozenset):
         list_tmp = convert_data(list(data))
         return list_tmp
 
     if isinstance(data, dict):
         dict_tmp = {}
         for key, value in data.items():
             dict_tmp[key] = convert_data(value)
         return dict_tmp
 
     # do not return anything for other types
     # which cannot be converted to JSON
     # for example: complex | range | memoryview
     return
diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py
index d6adf6f4d..059a4c3f6 100755
--- a/src/op_mode/uptime.py
+++ b/src/op_mode/uptime.py
@@ -1,82 +1,82 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2021-2023 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 as
 # published by the Free Software Foundation.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import sys
 
 import vyos.opmode
 
 def _get_uptime_seconds():
   from re import search
   from vyos.utils.file import read_file
 
   data = read_file("/proc/uptime")
   seconds = search("([0-9\.]+)\s", data).group(1)
 
   return int(float(seconds))
 
 def _get_load_averages():
     from re import search
     from vyos.utils.process import cmd
     from vyos.cpu import get_core_count
 
     data = cmd("uptime")
     matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data)
 
     core_count = get_core_count()
 
     res = {}
     res[1]  = float(matches["one"]) / core_count
     res[5]  = float(matches["five"]) / core_count
     res[15] = float(matches["fifteen"]) / core_count
 
     return res
 
 def _get_raw_data():
     from vyos.utils.convert import seconds_to_human
 
     res = {}
     res["uptime_seconds"] = _get_uptime_seconds()
-    res["uptime"] = seconds_to_human(_get_uptime_seconds())
+    res["uptime"] = seconds_to_human(_get_uptime_seconds(), separator=' ')
     res["load_average"] = _get_load_averages()
 
     return res
 
 def _get_formatted_output(data):
     out = "Uptime: {}\n\n".format(data["uptime"])
     avgs = data["load_average"]
     out += "Load averages:\n"
     out += "1  minute:   {:.01f}%\n".format(avgs[1]*100)
     out += "5  minutes:  {:.01f}%\n".format(avgs[5]*100)
     out += "15 minutes:  {:.01f}%\n".format(avgs[15]*100)
 
     return out
 
 def show(raw: bool):
     uptime_data = _get_raw_data()
 
     if raw:
         return uptime_data
     else:
         return _get_formatted_output(uptime_data)
 
 if __name__ == '__main__':
     try:
         res = vyos.opmode.run(sys.modules[__name__])
         if res:
             print(res)
     except (ValueError, vyos.opmode.Error) as e:
         print(e)
         sys.exit(1)