diff --git a/repos/libports/run/system_rtc.run b/repos/libports/run/system_rtc.run
new file mode 100644
index 000000000..fb18a8433
--- /dev/null
+++ b/repos/libports/run/system_rtc.run
@@ -0,0 +1,99 @@
+# RTC test
+
+assert_spec x86
+
+if {[expr ![have_include power_on/qemu]]} {
+ puts "Test requires Qemu."
+ exit 0
+}
+
+set build_components {
+ core init timer drivers/rtc server/system_rtc test/system_rtc
+ server/report_rom
+}
+
+build $build_components
+
+create_boot_directory
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+set boot_components {
+ core ld.lib.so init timer rtc_drv test-system_rtc system_rtc
+ report_rom
+}
+
+build_boot_image $boot_components
+
+append qemu_args " -nographic "
+
+run_genode_until ".*--- system RTC test finished ---.*\n" 60
diff --git a/repos/libports/src/server/system_rtc/README b/repos/libports/src/server/system_rtc/README
new file mode 100644
index 000000000..58fa79118
--- /dev/null
+++ b/repos/libports/src/server/system_rtc/README
@@ -0,0 +1,17 @@
+The system RTC component provides access to a synthetic real time clock. While
+it can get its initial value from a hardware RTC, it will interpolate the time
+by using a Timer session at runtime.
+
+Like the RTC driver component, if the 'allow_setting_rtc' attribute in the
+'config' node is set to 'yes', it will use the content of the 'set_rtc' ROM to
+update the RTC value. A valid ROM must contain a top node with the following
+attributes:
+
+* 'year' (e.g. 2019)
+* 'month' (1 - 12)
+* 'day' (1 - 31)
+* 'hour' (0 - 23)
+* 'minute' (0 - 59)
+* 'second' (0 - 59)
+
+The component will always report the time as 24h day in UTC.
diff --git a/repos/libports/src/server/system_rtc/main.cc b/repos/libports/src/server/system_rtc/main.cc
new file mode 100644
index 000000000..22d38a557
--- /dev/null
+++ b/repos/libports/src/server/system_rtc/main.cc
@@ -0,0 +1,291 @@
+/*
+ * \brief System RTC server
+ * \author Josef Soentgen
+ * \date 2019-07-15
+ */
+
+/*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+namespace Contrib {
+#include
+}; /* Contrib */
+
+
+namespace Util {
+
+ Genode::int64_t convert(Rtc::Timestamp const &ts)
+ {
+ Contrib::tm tm;
+ Genode::memset(&tm, 0, sizeof(Contrib::tm));
+ tm.tm_sec = ts.second;
+ tm.tm_min = ts.minute;
+ tm.tm_hour = ts.hour;
+ tm.tm_mday = ts.day;
+ tm.tm_mon = ts.month - 1;
+ tm.tm_year = ts.year - 1900;
+
+ return Contrib::tm_to_secs(&tm);
+ }
+
+ Rtc::Timestamp convert(Genode::int64_t t)
+ {
+ Contrib::tm tm;
+ Genode::memset(&tm, 0, sizeof(Contrib::tm));
+
+ int const err = Contrib::secs_to_tm((long long)t, &tm);
+ if (err) { Genode::warning("could not convert timestamp"); }
+
+ return Rtc::Timestamp {
+ .microsecond = 0,
+ .second = err ? 0 : (unsigned)tm.tm_sec,
+ .minute = err ? 0 : (unsigned)tm.tm_min,
+ .hour = err ? 0 : (unsigned)tm.tm_hour,
+ .day = err ? 1 : (unsigned)tm.tm_mday,
+ .month = err ? 1 : (unsigned)tm.tm_mon + 1,
+ .year = err ? 1970 : (unsigned)tm.tm_year + 1900
+ };
+ }
+
+ struct Point_in_time
+ {
+ Genode::int64_t rtc_seconds;
+ Genode::int64_t curr_seconds;
+ };
+
+ Rtc::Timestamp generate(Point_in_time const &p, Genode::uint64_t secs)
+ {
+ Genode::int64_t const s = ((Genode::int64_t)secs - p.curr_seconds) + p.rtc_seconds;
+ return convert(s);
+ }
+
+} /* namespace Util */
+
+
+namespace Rtc {
+ using namespace Genode;
+
+ struct Time;
+ struct Session_component;
+ struct Root;
+ struct Main;
+} /* namespace Rtc */
+
+
+struct Rtc::Time
+{
+ Env &_env;
+
+ Signal_context_capability _notify_sigh;
+
+ Timer::Connection _timer { _env };
+ Rtc::Connection _rtc { _env };
+
+ Util::Point_in_time _time_base { };
+
+ void _update_time(Timestamp const &ts)
+ {
+ _time_base.rtc_seconds = Util::convert(ts);
+ _time_base.curr_seconds = _timer.curr_time().trunc_to_plain_ms().value / 1000;
+ if (_notify_sigh.valid()) {
+ Signal_transmitter(_notify_sigh).submit();
+ }
+ }
+
+ void _handle_rtc_set()
+ {
+ Timestamp ts = _rtc.current_time();
+ log("Set RTC base from RTC driver to ", ts);
+ _update_time(ts);
+ }
+
+ Signal_handler