diff --git a/CMakeLists.txt b/CMakeLists.txt
index 37cdc1f4354b5c0253f91ad8887d46dc9b871910..037f051530f4e6a8d23967a464d3a5b11667110a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,8 +29,7 @@ add_library(common OBJECT
         src/MessageParser.cpp
         src/ServicePool.cpp
         src/Helpers/CRCHelper.cpp
-        src/Helpers/TimeAndDate.cpp
-        src/Helpers/TimeHelper.cpp
+        src/Helpers/TimeFormats.cpp
         src/Services/EventReportService.cpp
         src/Services/MemoryManagementService.cpp
         src/Services/ParameterService.cpp
diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp
index 2936e1f2ffb4d7c5f7f41e281cde943c9936969a..b05d465fedb11f28a8b2cd827586b7321fae94c3 100644
--- a/inc/ErrorHandler.hpp
+++ b/inc/ErrorHandler.hpp
@@ -85,6 +85,10 @@ public:
 		 * Attempt to access an invalid parameter in ST[03]
 		 */
 		NonExistentParameter = 13,
+		/**
+		 * Invalid TimeStamp parameters at creation
+		 */
+		InvalidTimeStampInput = 14
 	};
 
 	/**
diff --git a/inc/Helpers/TimeAndDate.hpp b/inc/Helpers/TimeAndDate.hpp
deleted file mode 100644
index 03fb0833d3d542b81d48b0f9d31e707220a88fa7..0000000000000000000000000000000000000000
--- a/inc/Helpers/TimeAndDate.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#ifndef ECSS_SERVICES_TIMEANDDATE_HPP
-#define ECSS_SERVICES_TIMEANDDATE_HPP
-
-#include <cstdint>
-#include "macros.hpp"
-
-/**
- * A class that represents the time and date.
- *
- * @note
- * This class represents UTC (Coordinated Universal Time) date
- */
-class TimeAndDate {
-public:
-	uint16_t year;
-	uint8_t month;
-	uint8_t day;
-	uint8_t hour;
-	uint8_t minute;
-	uint8_t second;
-
-	/**
-	 * Assign the instances with the Unix epoch 1/1/1970 00:00:00
-	 */
-	TimeAndDate();
-
-	/**
-	 * @param year the year as it used in Gregorian calendar
-	 * @param month the month as it used in Gregorian calendar
-	 * @param day the day as it used in Gregorian calendar
-	 * @param hour UTC hour in 24 format
-	 * @param minute UTC minutes
-	 * @param second UTC seconds
-	 */
-	TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is smaller than \p Date
-	 */
-	bool operator<(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is greater than \p Date
-	 */
-	bool operator>(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is equal to \p Date
-	 */
-	bool operator==(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is smaller than or equal to \p Date
-	 */
-	bool operator<=(const TimeAndDate& Date);
-
-	/**
-	 * Compare two timestamps.
-	 *
-	 * @param Date the date that will be compared with the pointer `this`
-	 * @return true if the pointer `this` is greater than or equal to \p Date
-	 */
-	bool operator>=(const TimeAndDate& Date);
-};
-
-#endif // ECSS_SERVICES_TIMEANDDATE_HPP
diff --git a/inc/Helpers/TimeFormats.hpp b/inc/Helpers/TimeFormats.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..969c52da5ce3787801fb6a7cbf57f2591c98df25
--- /dev/null
+++ b/inc/Helpers/TimeFormats.hpp
@@ -0,0 +1,221 @@
+#include <cstdint>
+#include <Message.hpp>
+#include "macros.hpp"
+#include "etl/String.hpp"
+
+// SEE CCSDS 301.0-B-4
+
+//////////////// CONSTANTS ////////////////////
+inline constexpr uint8_t SECONDS_PER_MINUTE = 60;
+inline constexpr uint16_t SECONDS_PER_HOUR = 3600;
+inline constexpr uint32_t SECONDS_PER_DAY = 86400;
+static constexpr uint8_t DAYSOFMONTH[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+inline constexpr uint8_t ACUBESAT_CUC_SECONDS_COUNTER_BYTES = 2; // PER DDJF_TTC
+inline constexpr uint8_t ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES = 2; // PER DDJF_TTC
+// Unix Epoch includes leap seconds.
+// Currently set at 01 Jan 2019 00h00m00s, TODO correct to set to 01 Jan. 2020 00h00m00s.
+// May be broken, see Issue #121 on Gitlab.
+inline constexpr uint32_t UNIX_TO_ACUBESAT_EPOCH_ELAPSED_SECONDS = 1546300800;
+//
+inline constexpr uint16_t ACUBESAT_EPOCH_YEAR = 2019;
+inline constexpr uint8_t ACUBESAT_EPOCH_MONTH = 1;
+inline constexpr uint8_t ACUBESAT_EPOCH_DAY = 1;
+
+inline constexpr uint8_t MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP = 9;
+
+static_assert(ACUBESAT_EPOCH_YEAR >= 2019);
+static_assert(ACUBESAT_EPOCH_MONTH < 11 && ACUBESAT_EPOCH_MONTH >= 0);
+static_assert(ACUBESAT_EPOCH_DAY < DAYSOFMONTH[ACUBESAT_EPOCH_MONTH]);
+//////////////////////////////////////////////
+
+//////// HELPER CONSTEXPR ////////
+// Build CUC header at compile time for timestamps that have a short (1 byte) header
+template <int seconds_counter_bytes, int fractional_counter_bytes>
+inline constexpr uint8_t build_short_CUC_header() {
+	static_assert(seconds_counter_bytes <= 4, "Use build_long_CUC_header instead");
+	static_assert(fractional_counter_bytes <= 3, "Use build_long_CUC_header instead");
+
+	uint8_t header = 0;
+
+	// P-Field extension is 0, CUC header is not extended
+	header += 0;
+
+	// AcubeSAT is using custom TAI epoch at 01 Jan 2020
+	header = header << 3;
+	header += 0b010;
+
+	// Number of bytes in the basic time unit
+	header = header << 2;
+	header += seconds_counter_bytes - 1;
+
+	// Number of bytes in the fractional unit
+	header = header << 2;
+	header += fractional_counter_bytes;
+
+	return header;
+}
+
+// Build CUC header at compile time for timestamps that have a long (2 byte) header
+template <int seconds_counter_bytes, int fractional_counter_bytes>
+inline constexpr uint16_t build_long_CUC_header() {
+	// cppcheck-suppress redundantCondition
+	static_assert(seconds_counter_bytes > 4 || fractional_counter_bytes > 3, "Use build_short_CUC_header instead");
+	static_assert(seconds_counter_bytes <= 7,
+	              "Number of bytes for seconds over maximum number of octets allowed by CCSDS");
+	static_assert(fractional_counter_bytes <= 6,
+	              "Number of bytes for seconds over maximum number of octets allowed by CCSDS");
+
+	uint16_t header = 0;
+
+	uint8_t first_octet_number_of_seconds_bytes = std::min(4, seconds_counter_bytes);
+	uint8_t second_octet_number_of_seconds_bytes = seconds_counter_bytes - first_octet_number_of_seconds_bytes;
+
+	uint8_t first_octet_number_of_fractional_bytes = std::min(3, fractional_counter_bytes);
+	uint8_t second_octet_number_of_fractional_bytes = fractional_counter_bytes - first_octet_number_of_fractional_bytes;
+
+	// P-Field extension is 1, CUC header is extended
+	header += 1;
+
+	// AcubeSAT is using custom TAI epoch at 01 Jan 2020
+	header = header << 3;
+	header += 0b010;
+
+	// // Number of bytes in the basic time unit
+	header = header << 2;
+	header += first_octet_number_of_seconds_bytes - 1;
+
+	// Number of bytes in the fractional unit
+	header = header << 2;
+	header += first_octet_number_of_fractional_bytes;
+
+	// P-Field extension is 1, CUC header was extended
+	header = header << 1;
+	header += 1;
+
+	// Number of bytes in the extended basic time unit
+	header = header << 2;
+	header += second_octet_number_of_seconds_bytes;
+
+	// Number of bytes in the extended fractional unit
+	header = header << 3;
+	header += second_octet_number_of_fractional_bytes;
+
+	// Last 3 LSb are reserved for custom mission use
+	header = header << 2;
+	header += 0;
+
+	return header;
+}
+
+template <typename T, int seconds_counter_bytes, int fractional_counter_bytes>
+inline constexpr T build_CUC_header() {
+	static_assert((seconds_counter_bytes + fractional_counter_bytes) <= 8,
+	              "Complete arbitrary precision not yet supported"); // TODO: see Issue #106 on Gitlab
+	// cppcheck-suppress syntaxError
+	// cppcheck-suppress redundantCondition
+	if constexpr (seconds_counter_bytes <= 4 &&
+	              fractional_counter_bytes <= 3) // if constexpr not supported yet in cppcheck
+		return build_short_CUC_header<seconds_counter_bytes, fractional_counter_bytes>();
+	else
+		return build_long_CUC_header<seconds_counter_bytes, fractional_counter_bytes>();
+}
+
+inline constexpr uint8_t build_AcubeSAT_CDS_header() {
+	uint8_t header = 0;
+
+	// bit 0 is at 0
+	header += 0;
+	header << 1;
+
+	// timecode identification
+	header += 0b100;
+	header << 3;
+
+	// AcubeSAT is using custom TAI epoch at 01 Jan 2020
+	header += 1;
+	header << 1;
+
+	// AcubeSAT is using 16 bits day count segment
+	header += 0;
+	header << 1;
+
+	// AcubeSAT is using picosecond resolution
+	header += 0b10;
+	// header << 2;
+
+	return header;
+}
+////////////////////////////////////////////////
+
+////////// CONVENIENCE FUNCTIONS ////////////
+bool is_leap_year(uint16_t year);
+
+////////// Transitory timestamps ////////////
+// CUSTOM EPOCH FOR ALL ACUBESAT TIMESTAMPS IS 01 JAN 2020, EXCEPT UTC (UNIX)
+class AcubeSAT_CDS_timestamp {
+public:
+	static constexpr uint8_t P_FIELD = build_AcubeSAT_CDS_header();
+	uint16_t day;
+	uint16_t ms_of_day;
+	uint32_t submilliseconds;
+
+	uint64_t to_CDS_timestamp();
+	void from_CDS_timestamp(uint64_t);
+};
+
+// CUSTOM EPOCH FOR ALL ACUBESAT TIMESTAMPS IS 01 JAN 2020, EXCEPT UTC (UNIX)
+/**
+ * A class that represents UTC time and date, from UNIX EPOCH
+ *
+ * @note
+ * This class represents UTC (Coordinated Universal Time) date
+ */
+class UTC_Timestamp {
+public:
+	uint16_t year;
+	uint8_t month;
+	uint8_t day;
+	uint8_t hour;
+	uint8_t minute;
+	uint8_t second;
+
+	/**
+	 * Assign the instances with the Unix epoch 1/1/1970 00:00:00
+	 */
+	UTC_Timestamp();
+
+	/**
+	 * @param year the year as it used in Gregorian calendar
+	 * @param month the month as it used in Gregorian calendar
+	 * @param day the day as it used in Gregorian calendar
+	 * @param hour UTC hour in 24 format
+	 * @param minute UTC minutes
+	 * @param second UTC seconds
+	 */
+	UTC_Timestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
+
+	/**
+	 * @param text_timestamp the timestamp to parse into a UTC date
+	 */
+	UTC_Timestamp(etl::string<32> text_timestamp);
+
+	/**
+	 * Compare two timestamps.
+	 *
+	 * @param Date the date that will be compared with the pointer `this`
+	 * @return true if the condition is satisfied
+	 */
+	bool operator<(const UTC_Timestamp& Date);
+	bool operator>(const UTC_Timestamp& Date);
+	bool operator==(const UTC_Timestamp& Date);
+	bool operator<=(const UTC_Timestamp& Date);
+	bool operator>=(const UTC_Timestamp& Date);
+
+	/**
+	 * Pretty-print timestamp.
+	 *
+	 * @param Date the date that will be output
+	 */
+	friend std::ostream& operator<<(std::ostream& o, UTC_Timestamp const& Date);
+};
diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp
deleted file mode 100644
index 42b5baf1204b01c58b3e964c5d413a6a973ea26f..0000000000000000000000000000000000000000
--- a/inc/Helpers/TimeHelper.hpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#ifndef ECSS_SERVICES_TIMEHELPER_HPP
-#define ECSS_SERVICES_TIMEHELPER_HPP
-
-#include <cstdint>
-#include <Message.hpp>
-#include "TimeAndDate.hpp"
-
-inline const uint8_t SecondsPerMinute = 60U;
-inline const uint16_t SecondsPerHour = 3600U;
-inline const uint32_t SecondsPerDay = 86400U;
-
-/**
- * @todo If we use CUC time format then we should keep leap seconds up to date. Leap seconds are added in undefined
- * periods of time, so we should find a way to adjust to these changes either in runtime using GPS or sending a new
- * compiled code (changing the defined leap seconds) from the ground segment
- */
-inline const uint8_t LeapSeconds = 27;
-
-
-/**
- * This class formats the spacecraft time and cooperates closely with the ST[09] time management.
- *
- * The ECSS standard supports two time formats: the CUC and CSD that are described in CCSDS
- * 301.0-B-4 standard.
- *
- * The CDS is UTC-based (UTC: Coordinated Universal Time). It consists of two main fields: the
- * time code preamble field (P-field) and the time specification field (T-field). The P-Field is the metadata for the
- * T-Field. The T-Field is consisted of two segments: 1) the `DAY` and the 2) `ms of day` segments. The P-field won't
- * be included in the code, because as the ECSS standards claims, it can be just implicitly declared.
- *
- * The CUC is TAI-based (TAI: International Atomic Time). It consists of two main fields: the time code preamble field
- * (P-field) and the time specification field(T-field). The T-Field contains the value of the time unit and the designer
- * decides what the time unit will be, so this is a subject for discussion. The recommended time unit from the
- * standard is the second and it is probably the best solution for accuracy.
- *
- * @note
- * The defined epoch for both time formats is 1 January 1958 00:00:00
- *
- * Since CDS format is UTC-based, the leap second correction must be made. The leap seconds that
- * have been occurred between timestamps should be considered if a critical time-difference is
- * needed
- *
- */
-class TimeHelper {
-public:
-	static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-	TimeHelper() = default;
-
-	/**
-	 * @param year The year that will be examined if it is a leap year (366 days)
-	 * @return if the \p year is a leap year returns true and if it isn't returns false
-	 */
-	static bool IsLeapYear(uint16_t year);
-
-	/**
-	 * Convert UTC date to elapsed seconds since Unix epoch (1/1/1970 00:00:00).
-	 *
-	 * This is a reimplemented `mktime()` of the `<ctime>` library in an embedded compatible way
-	 *
-	 * @note
-	 * This function can convert UTC dates after 1 January 2019 to elapsed seconds since Unix epoch
-	 *
-	 * @param TimeInfo the time information/data from the RTC (UTC format)
-	 * @return the elapsed seconds between a given UTC date (after the Unix epoch) and Unix epoch
-	 * @todo check if we need to change the epoch to the recommended one from the standard, 1
-	 * January 1958
-	 */
-	static uint32_t utcToSeconds(const TimeAndDate& TimeInfo);
-
-	/**
-	 * Convert elapsed seconds since Unix epoch to UTC date.
-	 *
-	 * This is a reimplemented `gmtime()` of the `<ctime>` library in an embedded compatible way
-	 *
-	 * @note
-	 * This function can convert elapsed seconds since Unix epoch to UTC dates after 1 January 2019
-	 *
-	 * @param seconds elapsed seconds since Unix epoch
-	 * @return the UTC date based on the \p seconds
-	 * @todo check if we need to change the epoch to the recommended one from the standard, 1
-	 * January 1958
-	 */
-	static TimeAndDate secondsToUTC(uint32_t seconds);
-
-	/**
-	 * Generate the CDS time format (3.3 in CCSDS 301.0-B-4 standard).
-	 *
-	 * Converts a UTC date to CDS time format.
-	 *
-	 * @param TimeInfo is the data provided from RTC (UTC)
-	 * @return TimeFormat the CDS time format. More specific, 48 bits are used for the T-field
-	 * (16 for the `DAY` and 32 for the `ms of day`)
-	 * @todo time security for critical time operations
-	 * @todo declare the implicit P-field
-	 */
-	static uint64_t generateCDSTimeFormat(const struct TimeAndDate& TimeInfo);
-
-	/**
-	 * Parse the CDS time format (3.3 in CCSDS 301.0-B-4 standard)
-	 *
-	 * @param data time information provided from the ground segment. The length of the data is a
-	 * fixed size of 48 bits
-	 * @return the UTC date
-	 */
-	static TimeAndDate parseCDStimeFormat(const uint8_t* data);
-
-	/**
-	 * Generate the CUC time format (3.3 in CCSDS 301.0-B-4 standard).
-	 *
-	 * Converts a UTC date to CUC time format.
-	 *
-	 * @note
-	 * The T-field is specified for the seconds passed from the defined epoch 1 January 1958. We use 4 octets(32
-	 * bits) for the time unit (seconds) because 32 bits for the seconds are enough to count 136 years! But if we use 24
-	 * bits for the seconds then it will count 0,5 years and this isn't enough. Remember we can use only integers
-	 * numbers of octets for the time unit (second)
-	 *
-	 * The CUC time format doesn't include leap seconds, so we need to add them because we assume that
-	 * the RTC will provide UTC format.
-	 *
-	 * @param TimeInfo is the data provided from RTC (UTC)
-	 * @return TimeFormat the CUC time format. More specific, 32 bits are used for the T-field (seconds since 1/1/1958)
-	 * @todo time security for critical time operations
-	 * @todo declare the implicit P-field
-	 */
-	static uint32_t generateCUCTimeFormat(const struct TimeAndDate& TimeInfo);
-
-	/**
-	 * Parse the CUC time format (3.3 in CCSDS 301.0-B-4 standard)
-	 *
-	 * @param data time information provided from the ground segment. The length of the data is a
-	 * fixed size of 32 bits
-	 * @return the UTC date
-	 */
-	static TimeAndDate parseCUCTimeFormat(const uint8_t* data);
-};
-
-#endif // ECSS_SERVICES_TIMEHELPER_HPP
diff --git a/inc/Helpers/TimeStamp.hpp b/inc/Helpers/TimeStamp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef8190952f6ceefbac0a3a848093836c34a1b6bd
--- /dev/null
+++ b/inc/Helpers/TimeStamp.hpp
@@ -0,0 +1,120 @@
+#ifndef ECSS_SERVICES_TIME_HPP
+#define ECSS_SERVICES_TIME_HPP
+
+#include <cstdint>
+#include <algorithm>
+#include "macros.hpp"
+#include <etl/array.h>
+#include "Helpers/TimeFormats.hpp"
+
+// SEE CCSDS 301.0-B-4
+// CUSTOM EPOCH FOR ALL ACUBESAT TIMESTAMPS IS 01 JAN 2020, EXCEPT UTC (UNIX)
+
+///////////// CLASS DECLARATION ////////////////
+/**
+ * A class that represents an instant in time, with convenient conversion
+ * to and from usual time and date representations
+ *
+ * @note
+ * This class uses internally TAI time, and handles UTC leap seconds at conversion to and
+ * from UTC time system.
+ */
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+class TimeStamp {
+private:
+	static_assert(seconds_counter_bytes + fractional_counter_bytes <= 8,
+	              "Currently, this class is not suitable for storage on internal counter larger than uint64_t");
+	typedef typename std::conditional
+			<
+			seconds_counter_bytes < 4 && fractional_counter_bytes < 3,
+			uint8_t,
+			uint16_t
+			>
+			::type CUC_header_t;
+	typedef typename std::conditional
+			<
+			(seconds_counter_bytes + fractional_counter_bytes) < 4,
+			uint32_t,
+			uint64_t
+			>
+			::type tai_counter_t;
+	tai_counter_t tai_counter;
+	CUC_header_t CUC_header = build_CUC_header<CUC_header_t, seconds_counter_bytes, fractional_counter_bytes>();
+
+public:
+	/**
+	 * Initialize the instant at epoch
+	 *
+	 */
+	TimeStamp() : tai_counter(0){};
+
+	/**
+	 * Initialize the instant from a duration from epoch in TAI
+	 *
+	 * @param seconds an integer number of seconds from AcubeSAT custom epoch
+	 */
+	TimeStamp(int tai_seconds_from_AcubeSAT_epoch);
+	
+	/**
+	 * Initialize the instant from the bytes of a CUC time stamp
+	 *
+	 * @param timestamp a complete CUC time stamp including header, of the maximum possible size, zero padded to the
+	 * right
+	 */
+	TimeStamp(etl::array<uint8_t, MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP> timestamp);
+
+	/**
+	 * Initialize the instant from a UTC timestamp struct
+	 *
+	 * @param timestamp a UTC timestamp, from Unix Epoch
+	 */
+	TimeStamp(UTC_Timestamp timestamp);
+
+	/**
+	 * Initialize the instant from an AcubeSAT CDS timestamp
+	 *
+	 * @param timestamp a CDS timestamp, in the format from DDJF_TTC, from epoch 2020.01.01
+	 */
+	TimeStamp(AcubeSAT_CDS_timestamp timestamp);
+
+	/**
+	 * Get the representation as seconds from epoch in TAI
+	 *
+	 * @return the seconds elapsed in TAI since 1 Jan 1958, cut to the integer part
+	 */
+	const int as_TAI_seconds();
+
+	/**
+	 * Get the representation as CUC formatted bytes
+	 *
+	 * @return the instant, represented in the CCSDS CUC format
+	 */
+	const etl::array<uint8_t, MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP> as_CUC_timestamp();
+
+	/**
+	 * Get the representation as a UTC timestamp
+	 *
+	 * @return the instant, represented in the structure that holds UTC timestamps
+	 */
+	const UTC_Timestamp as_UTC_timestamp();
+
+	/**
+	 * Compare two instants.
+	 *
+	 * @param TimeStamp the date that will be compared with the pointer `this`
+	 * @return true if the condition is satisfied
+	 */
+	bool operator<(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+	bool operator>(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+	bool operator==(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+	bool operator!=(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+	bool operator<=(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+	bool operator>=(const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const;
+};
+////////////////////////////////////////////////
+
+////// TEMPLATED METHODS INSTANTIATION /////////
+#include "TimeStamp.tpp"
+////////////////////////////////////////////////
+
+#endif
diff --git a/inc/Helpers/TimeStamp.tpp b/inc/Helpers/TimeStamp.tpp
new file mode 100644
index 0000000000000000000000000000000000000000..1d70e753cd94a215fa6d9f368e05c70d23f65423
--- /dev/null
+++ b/inc/Helpers/TimeStamp.tpp
@@ -0,0 +1,240 @@
+#define SECONDS_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1 0b00001100
+#define SECONDS_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE2 0b01100000
+#define FRACTIONAL_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1 0b00000011
+#define FRACTIONAL_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE2 0b00011100
+
+#define SECONDS_FROM_SINGLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1 0b00001100
+#define FRACTIONAL_FROM_SINGLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1 0b00000011
+
+#include <math.h>
+
+bool is_leap_year(uint16_t year);
+
+////////////: CONSTRUCTORS ////////////
+//// FROM TAI SECONDS
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(int tai_seconds_from_AcubeSAT_epoch) {
+  static constexpr uint64_t max_seconds_counter_value = static_cast<uint64_t>(seconds_counter_bytes << 2) - 1;
+  // cppcheck-suppress useInitializationList
+  tai_counter = static_cast<tai_counter_t>(tai_seconds_from_AcubeSAT_epoch) << 8 * fractional_counter_bytes;
+  ASSERT_INTERNAL(tai_seconds_from_AcubeSAT_epoch >= max_seconds_counter_value, ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+}
+
+//// FROM CDS TIMESTAMP
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(AcubeSAT_CDS_timestamp timestamp) {
+	// tai_counter = 0; //TODO: see Issue #105 on Gitlab
+}
+
+//// FROM CUC TIMESTAMP
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(
+    etl::array<uint8_t, MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP> timestamp) {
+	// process header
+	int header_size = 1;
+	if (timestamp[0] & 0b10000000) {
+		header_size = 2;
+	};
+
+	int timestamp_fractional_bytes_count = 0;
+	int timestamp_seconds_bytes_count = 1;
+
+	if (header_size == 2) {
+		timestamp_seconds_bytes_count += (timestamp[0] & SECONDS_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1) >> 2;
+		timestamp_seconds_bytes_count += (timestamp[1] & SECONDS_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE2) >> 5;
+		timestamp_fractional_bytes_count =
+		    ((timestamp[0] & FRACTIONAL_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1) >> 0) +
+		    ((timestamp[1] & FRACTIONAL_FROM_DOUBLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE2) >> 2);
+	} else if (header_size == 1) {
+		timestamp_seconds_bytes_count += (timestamp[0] & SECONDS_FROM_SINGLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1) >> 2;
+		timestamp_fractional_bytes_count =
+		    (timestamp[0] & FRACTIONAL_FROM_SINGLE_BYTE_CUC_TIMESTAMP_BITMASK_BYTE1) >> 0;
+	} else {
+		ASSERT_INTERNAL(true, ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+	}
+
+	// check input validity (useless bytes set to 0)
+	int err = 0;
+	for (int i = header_size + timestamp_seconds_bytes_count + timestamp_fractional_bytes_count; i < 9; i++) {
+		if (timestamp[i] != 0) {
+			err += 1;
+			break;
+		}
+	}
+	ASSERT_INTERNAL(err == 0, ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+
+	// do checks wrt template precision parameters
+	ASSERT_INTERNAL(timestamp_seconds_bytes_count <= seconds_counter_bytes,
+	                ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+	ASSERT_INTERNAL(timestamp_fractional_bytes_count <= fractional_counter_bytes,
+	                ErrorHandler::InternalErrorType::InvalidTimeStampInput);
+
+	// put timestamp into internal counter
+	tai_counter = 0;
+	// add seconds until run out of bytes on input array
+	for (auto i = 0; i < timestamp_seconds_bytes_count; i++) {
+		tai_counter = tai_counter << 8;
+		tai_counter += timestamp[header_size + i];
+	}
+	// add fractional until run out of bytes on input array
+	for (auto i = 0; i < timestamp_fractional_bytes_count; i++) {
+		tai_counter = tai_counter << 8;
+		tai_counter += timestamp[header_size + timestamp_seconds_bytes_count + i];
+	}
+	// pad rightmost bytes to full length
+	tai_counter = tai_counter << 8 * (fractional_counter_bytes - timestamp_fractional_bytes_count);
+}
+
+//// FROM UTC TIMESTAMP
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(UTC_Timestamp timestamp) {
+	int seconds = UNIX_TO_ACUBESAT_EPOCH_ELAPSED_SECONDS;
+	for (int year = ACUBESAT_EPOCH_YEAR; year < timestamp.year; ++year) {
+		seconds += (is_leap_year(year) ? 366 : 365) * SECONDS_PER_DAY;
+	}
+	for (int month = ACUBESAT_EPOCH_MONTH; month < timestamp.month; ++month) {
+		seconds += DAYSOFMONTH[month - 1] * SECONDS_PER_DAY;
+		if ((month == 2U) && is_leap_year(timestamp.year)) {
+			seconds += SECONDS_PER_DAY;
+		}
+	}
+	seconds += (timestamp.day - ACUBESAT_EPOCH_DAY) * SECONDS_PER_DAY;
+	seconds += timestamp.hour * SECONDS_PER_HOUR;
+	seconds += timestamp.minute * SECONDS_PER_MINUTE;
+	seconds += timestamp.second;
+	tai_counter = static_cast<tai_counter_t>(seconds) << 8 * fractional_counter_bytes;
+}
+
+////////////// GETTER ///////////////
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+const int TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::as_TAI_seconds() {
+	return tai_counter >> (8 * fractional_counter_bytes);
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+const etl::array<uint8_t, MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP>
+TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::as_CUC_timestamp() {
+	etl::array<uint8_t, MAXIMUM_BYTES_FOR_COMPLETE_CUC_TIMESTAMP> return_array = {0};
+	int index_first_non_header_byte;
+
+  // cppcheck-suppress redundantCondition
+	static constexpr uint8_t header_size = seconds_counter_bytes < 4 && fractional_counter_bytes < 3 ? 1 : 2; // number of bytes in CUC header
+
+	if (header_size == 1) {
+		return_array[0] = static_cast<uint8_t>(CUC_header);
+		index_first_non_header_byte = 1;
+	}
+
+	else { // two-bytes CUC header
+		return_array[1] = static_cast<uint8_t>(CUC_header);
+		return_array[0] = static_cast<uint8_t>(CUC_header >> 8);
+		index_first_non_header_byte = 2;
+	}
+
+	for (auto byte_being_filled_in_return = 0;
+	     byte_being_filled_in_return < seconds_counter_bytes + fractional_counter_bytes;
+	     byte_being_filled_in_return++) {
+		int index_of_interesting_byte_in_tai_counter =
+		    8 * (seconds_counter_bytes + fractional_counter_bytes - byte_being_filled_in_return - 1);
+		return_array[index_first_non_header_byte + byte_being_filled_in_return] =
+		    tai_counter >> index_of_interesting_byte_in_tai_counter;
+	}
+
+	return return_array;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+const UTC_Timestamp TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::as_UTC_timestamp() {
+	int seconds = as_TAI_seconds();
+
+	// elapsed seconds should be between dates, that are after 1/1/2019 and Unix epoch
+	ASSERT_INTERNAL(seconds >= UNIX_TO_ACUBESAT_EPOCH_ELAPSED_SECONDS, ErrorHandler::InternalErrorType::InvalidDate);
+
+	seconds -= UNIX_TO_ACUBESAT_EPOCH_ELAPSED_SECONDS; // elapsed seconds from Unix epoch until AcubeSAT custom epoch 00:00:00 (UTC)
+	int year_utc = ACUBESAT_EPOCH_YEAR;
+	int month_utc = ACUBESAT_EPOCH_MONTH;
+	int day_utc = ACUBESAT_EPOCH_DAY;
+	int hour = 0;
+	int minute = 0;
+	int second = 0;
+
+	// calculate years
+	while (seconds >= (is_leap_year(year_utc) ? 366 : 365) * SECONDS_PER_DAY) {
+		seconds -= (is_leap_year(year_utc) ? 366 : 365) * SECONDS_PER_DAY;
+		year_utc++;
+	}
+
+	// calculate months
+	int current_month = 0;
+	while (seconds >= (DAYSOFMONTH[current_month] * SECONDS_PER_DAY)) {
+		month_utc++;
+		seconds -= (DAYSOFMONTH[current_month] * SECONDS_PER_DAY);
+		current_month++;
+		if ((current_month == 1U) && is_leap_year(year_utc)) {
+			if (seconds <= (28 * SECONDS_PER_DAY)) {
+				break;
+			}
+			month_utc++;
+			seconds -= 29 * SECONDS_PER_DAY;
+			current_month++;
+		}
+	}
+
+	// calculate days
+	day_utc = seconds / SECONDS_PER_DAY;
+	seconds -= day_utc * SECONDS_PER_DAY;
+	day_utc++; // add 1 day because we start count from 1 January (and not 0 January!)
+
+	// calculate hours
+	hour = seconds / SECONDS_PER_HOUR;
+	seconds -= hour * SECONDS_PER_HOUR;
+
+	// calculate minutes
+	minute = seconds / SECONDS_PER_MINUTE;
+	seconds -= minute * SECONDS_PER_MINUTE;
+
+	// calculate seconds
+	second = seconds;
+
+	return UTC_Timestamp(year_utc, month_utc, day_utc, hour, minute, second);
+}
+
+////////////// OPERATORS ///////////
+/// COMPARISON
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator==(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter == TimeStamp.tai_counter;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator!=(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter != TimeStamp.tai_counter;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator<(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter < TimeStamp.tai_counter;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator>(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter > TimeStamp.tai_counter;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator<=(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter <= TimeStamp.tai_counter;
+}
+
+template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes>
+bool TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::operator>=(
+    const TimeStamp<seconds_counter_bytes, fractional_counter_bytes>& TimeStamp) const {
+	return tai_counter >= TimeStamp.tai_counter;
+}
+/// ARITHMETIC
+// TODO: see Issue #104 on Gitlab repository
diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp
index 751b6438dac6109d05245340cdb23ac8a19d1c0e..f49d8067a02a738a521c71ffcbee1d7b4718fc19 100644
--- a/inc/Services/TimeBasedSchedulingService.hpp
+++ b/inc/Services/TimeBasedSchedulingService.hpp
@@ -6,7 +6,6 @@
 #include "ErrorHandler.hpp"
 #include "MessageParser.hpp"
 #include "Helpers/CRCHelper.hpp"
-#include "Helpers/TimeHelper.hpp"
 
 // Include platform specific files
 #include "Platform/x86/TimeGetter.hpp"
diff --git a/src/Helpers/TimeAndDate.cpp b/src/Helpers/TimeFormats.cpp
similarity index 69%
rename from src/Helpers/TimeAndDate.cpp
rename to src/Helpers/TimeFormats.cpp
index 54a17b6b17f0ca2838f741222858fc97777f25ae..a699de5ee859949d4586683ffa19fdea33ec8872 100644
--- a/src/Helpers/TimeAndDate.cpp
+++ b/src/Helpers/TimeFormats.cpp
@@ -1,6 +1,17 @@
-#include "Helpers/TimeHelper.hpp"
+#include "Helpers/TimeFormats.hpp"
+#include <iomanip>
 
-TimeAndDate::TimeAndDate() {
+bool is_leap_year(uint16_t year) {
+	if ((year % 4) != 0) {
+		return false;
+	}
+	if ((year % 100) != 0) {
+		return true;
+	}
+	return (year % 400) == 0;
+}
+
+UTC_Timestamp::UTC_Timestamp() {
 	// Unix epoch 1/1/1970
 	year = 1970;
 	month = 1;
@@ -10,7 +21,7 @@ TimeAndDate::TimeAndDate() {
 	second = 0;
 }
 
-TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
+UTC_Timestamp::UTC_Timestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
 	// check if the parameters make sense
 	ASSERT_INTERNAL(2019 <= year, ErrorHandler::InternalErrorType::InvalidDate);
 	ASSERT_INTERNAL((1 <= month) && (month <= 12), ErrorHandler::InternalErrorType::InvalidDate);
@@ -28,7 +39,7 @@ TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour
 	this->second = second;
 }
 
-bool TimeAndDate::operator<(const TimeAndDate& Date) {
+bool UTC_Timestamp::operator<(const UTC_Timestamp& Date) {
 	// compare years
 	if (this->year < Date.year) {
 		return true;
@@ -77,48 +88,37 @@ bool TimeAndDate::operator<(const TimeAndDate& Date) {
 	return false;
 }
 
-bool TimeAndDate::operator>(const TimeAndDate& Date) {
-	// compare years
+bool UTC_Timestamp::operator>(const UTC_Timestamp& Date) {
 	if (this->year > Date.year) {
 		return true;
 	}
 	if (this->year < Date.year) {
 		return false;
 	}
-
-	// compare months
 	if (this->month > Date.month) {
 		return true;
 	}
 	if (this->month < Date.month) {
 		return false;
 	}
-
-	// compare days
 	if (this->day > Date.day) {
 		return true;
 	}
 	if (this->day < Date.day) {
 		return false;
 	}
-
-	// compare hours
 	if (this->hour > Date.hour) {
 		return true;
 	}
 	if (this->hour < Date.hour) {
 		return false;
 	}
-
-	// compare minutes
 	if (this->minute > Date.minute) {
 		return true;
 	}
 	if (this->minute < Date.minute) {
 		return false;
 	}
-
-	// compare seconds
 	if (this->second > Date.second) {
 		return true;
 	}
@@ -126,44 +126,41 @@ bool TimeAndDate::operator>(const TimeAndDate& Date) {
 	return false;
 }
 
-bool TimeAndDate::operator==(const TimeAndDate& Date) {
-	// compare years
+bool UTC_Timestamp::operator==(const UTC_Timestamp& Date) {
 	if (this->year != Date.year) {
 		return false;
 	}
-
-	// compare months
 	if (this->month != Date.month) {
 		return false;
 	}
-
-	// compare days
 	if (this->day != Date.day) {
 		return false;
 	}
-
-	// compare hours
 	if (this->hour != Date.hour) {
 		return false;
 	}
-
-	// compare minutes
 	if (this->minute != Date.minute) {
 		return false;
 	}
-
-	// compare seconds
 	if (this->second != Date.second) {
 		return false;
 	}
-
 	return true;
 }
 
-bool TimeAndDate::operator<=(const TimeAndDate& Date) {
+bool UTC_Timestamp::operator<=(const UTC_Timestamp& Date) {
 	return ((*this < Date) || (*this == Date));
 }
 
-bool TimeAndDate::operator>=(const TimeAndDate& Date) {
+bool UTC_Timestamp::operator>=(const UTC_Timestamp& Date) {
 	return ((*this > Date) || (*this == Date));
 }
+
+std::ostream& operator<<(std::ostream& o, UTC_Timestamp const& Date) { // NOLINT
+	// YYYY-MM-DDTHH:mm:ss.sssZ
+	o.fill('0');
+	o << static_cast<int>(Date.year) << "-" << std::setw(2) << static_cast<int>(Date.month) << "-" << std::setw(2)
+	  << static_cast<int>(Date.day) << "T" << std::setw(2) << static_cast<int>(Date.hour) << ":" << std::setw(2)
+	  << static_cast<int>(Date.minute) << ":" << std::setw(2) << static_cast<int>(Date.second) << ":000Z" << std::endl;
+	return o;
+}
diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp
deleted file mode 100644
index 942635140f585f3bb9d9e11d0bae32516610683c..0000000000000000000000000000000000000000
--- a/src/Helpers/TimeHelper.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-#include "Helpers/TimeHelper.hpp"
-
-bool TimeHelper::IsLeapYear(uint16_t year) {
-	if ((year % 4) != 0) {
-		return false;
-	}
-	if ((year % 100) != 0) {
-		return true;
-	}
-	return (year % 400) == 0;
-}
-
-uint32_t TimeHelper::utcToSeconds(const TimeAndDate& TimeInfo) {
-	// the date, that \p TimeInfo represents, should be greater than or equal to 1/1/2019 and the
-	// date should be valid according to Gregorian calendar
-	ASSERT_INTERNAL(TimeInfo.year >= 2019, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL((1 <= TimeInfo.month) && (TimeInfo.month <= 12), ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL((1 <= TimeInfo.day) && (TimeInfo.day <= 31), ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.hour <= 24, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.minute <= 60, ErrorHandler::InternalErrorType::InvalidDate);
-	ASSERT_INTERNAL(TimeInfo.second <= 60, ErrorHandler::InternalErrorType::InvalidDate);
-
-	uint32_t secs = 1546300800; // elapsed seconds from Unix epoch until 1/1/2019 00:00:00 (UTC)
-	for (uint16_t y = 2019; y < TimeInfo.year; ++y) {
-		secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay;
-	}
-	for (uint16_t m = 1; m < TimeInfo.month; ++m) {
-		secs += DaysOfMonth[m - 1U] * SecondsPerDay;
-		if ((m == 2U) && IsLeapYear(TimeInfo.year)) {
-			secs += SecondsPerDay;
-		}
-	}
-	secs += (TimeInfo.day - 1) * SecondsPerDay;
-	secs += TimeInfo.hour * SecondsPerHour;
-	secs += TimeInfo.minute * SecondsPerMinute;
-	secs += TimeInfo.second;
-	return secs;
-}
-
-struct TimeAndDate TimeHelper::secondsToUTC(uint32_t seconds) {
-	// elapsed seconds should be between dates, that are after 1/1/2019 and Unix epoch
-	ASSERT_INTERNAL(seconds >= 1546300800, ErrorHandler::InternalErrorType::InvalidDate);
-
-	seconds -= 1546300800; // elapsed seconds from Unix epoch until 1/1/2019 00:00:00 (UTC)
-	TimeAndDate TimeInfo;
-	TimeInfo.year = 2019;
-	TimeInfo.month = 1;
-	TimeInfo.day = 0;
-	TimeInfo.hour = 0;
-	TimeInfo.minute = 0;
-	TimeInfo.second = 0;
-
-	// calculate years
-	while (seconds >= (IsLeapYear(TimeInfo.year) ? 366 : 365) * SecondsPerDay) {
-		seconds -= (IsLeapYear(TimeInfo.year) ? 366 : 365) * SecondsPerDay;
-		TimeInfo.year++;
-	}
-
-	// calculate months
-	uint8_t i = 0;
-	while (seconds >= (DaysOfMonth[i] * SecondsPerDay)) {
-		TimeInfo.month++;
-		seconds -= (DaysOfMonth[i] * SecondsPerDay);
-		i++;
-		if ((i == 1U) && IsLeapYear(TimeInfo.year)) {
-			if (seconds <= (28 * SecondsPerDay)) {
-				break;
-			}
-			TimeInfo.month++;
-			seconds -= 29 * SecondsPerDay;
-			i++;
-		}
-	}
-
-	// calculate days
-	TimeInfo.day = seconds / SecondsPerDay;
-	seconds -= TimeInfo.day * SecondsPerDay;
-	TimeInfo.day++; // add 1 day because we start count from 1 January (and not 0 January!)
-
-	// calculate hours
-	TimeInfo.hour = seconds / SecondsPerHour;
-	seconds -= TimeInfo.hour * SecondsPerHour;
-
-	// calculate minutes
-	TimeInfo.minute = seconds / SecondsPerMinute;
-	seconds -= TimeInfo.minute * SecondsPerMinute;
-
-	// calculate seconds
-	TimeInfo.second = seconds;
-
-	return TimeInfo;
-}
-
-uint64_t TimeHelper::generateCDSTimeFormat(const TimeAndDate& TimeInfo) {
-	/**
-	 * Define the T-field. The total number of octets for the implementation of T-field is 6(2 for
-	 * the `DAY` and 4 for the `ms of day`
-	 */
-
-	uint32_t seconds = utcToSeconds(TimeInfo);
-
-	/**
-	 * The `DAY` segment, 16 bits as defined from standard. Actually the days passed since Unix
-	 * epoch
-	 */
-	auto elapsedDays = static_cast<uint16_t>(seconds / SecondsPerDay);
-
-	/**
-	 * The `ms of day` segment, 32 bits as defined in standard. The `ms of the day` and DAY`
-	 * should give the time passed since Unix epoch
-	 */
-	auto msOfDay = static_cast<uint32_t>((seconds % SecondsPerDay) * 1000);
-
-	uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32) | msOfDay;
-
-	return timeFormat;
-}
-
-TimeAndDate TimeHelper::parseCDStimeFormat(const uint8_t* data) {
-	uint16_t elapsedDays = ((static_cast<uint16_t>(data[0])) << 8) | (static_cast<uint16_t>(data[1]));
-	uint32_t msOfDay = ((static_cast<uint32_t>(data[2])) << 24) | ((static_cast<uint32_t>(data[3]))) << 16 |
-	                   ((static_cast<uint32_t>(data[4]))) << 8 | (static_cast<uint32_t>(data[5]));
-
-	uint32_t seconds = (elapsedDays * SecondsPerDay) + (msOfDay / 1000U);
-
-	return secondsToUTC(seconds);
-}
-
-uint32_t TimeHelper::generateCUCTimeFormat(const struct TimeAndDate& TimeInfo) {
-	return (utcToSeconds(TimeInfo) + LeapSeconds);
-}
-
-TimeAndDate TimeHelper::parseCUCTimeFormat(const uint8_t* data) {
-	uint32_t seconds = ((static_cast<uint32_t>(data[0])) << 24) | ((static_cast<uint32_t>(data[1]))) << 16 |
-	                   ((static_cast<uint32_t>(data[2]))) << 8 | (static_cast<uint32_t>(data[3]));
-	seconds -= LeapSeconds;
-
-	return secondsToUTC(seconds);
-}
diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp
index 52cdd073d92de1bae7b19804365e74fcf6ffec73..90f36f8b4f5869182d9cfc78c1546837186caa66 100644
--- a/src/Platform/x86/main.cpp
+++ b/src/Platform/x86/main.cpp
@@ -1,7 +1,6 @@
 #include <iostream>
 #include <Logger.hpp>
 #include "Helpers/CRCHelper.hpp"
-#include "Helpers/TimeHelper.hpp"
 #include "Services/TestService.hpp"
 #include "Services/ParameterService.hpp"
 #include "Services/RequestVerificationService.hpp"
diff --git a/test/Helpers/TimeAndDate.cpp b/test/Helpers/TimeAndDate.cpp
deleted file mode 100644
index 13866f29ebff1432aa88562fdb7f4020690fc37a..0000000000000000000000000000000000000000
--- a/test/Helpers/TimeAndDate.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-#include "catch2/catch.hpp"
-#include "Helpers/TimeAndDate.hpp"
-#include "../Services/ServiceTests.hpp"
-
-TEST_CASE("Date comparison", "[operands]") {
-	SECTION("Invalid date") {
-		TimeAndDate InvalidDate0(1900, 2, 2, 4, 5, 6); // error in year
-		TimeAndDate InvalidDate1(2030, 70, 2, 4, 5, 6); // error in month
-		TimeAndDate InvalidDate2(2030, 2, 73, 4, 5, 6); // error in day
-		TimeAndDate InvalidDate3(2030, 2, 2, 74, 5, 6); // error in hour
-		TimeAndDate InvalidDate4(2030, 2, 2, 4, 75, 6); // error in minute
-		TimeAndDate InvalidDate5(2030, 2, 2, 4, 5, 76); // error in seconds
-
-		CHECK(ServiceTests::countErrors() == 6);
-		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
-	}
-
-	SECTION("Different year") {
-		TimeAndDate Now;
-		// 10/04/2021 10:15:00
-		Now.year = 2021;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different month") {
-		TimeAndDate Now;
-		// 10/05/2020 10:15:00
-		Now.year = 2020;
-		Now.month = 5;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different day") {
-		TimeAndDate Now;
-		// 11/04/2020 10:15:00
-		Now.year = 2020;
-		Now.month = 5;
-		Now.day = 11;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different hour") {
-		TimeAndDate Now;
-		// 10/04/2020 11:15:00
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 11;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different minute") {
-		TimeAndDate Now;
-		// 10/04/2020 10:16:00
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 16;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Different second") {
-		TimeAndDate Now;
-		// 10/04/2020 10:15:01
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 1;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now < Date) == false);
-		CHECK((Now > Date) == true);
-		CHECK((Now > Date) == true);
-		CHECK((Now < Date) == false);
-
-		CHECK((Now <= Date) == false);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == false);
-
-		CHECK((Now == Date) == false);
-	}
-
-	SECTION("Same date") {
-		TimeAndDate Now;
-		// 10/04/2020 10:15:01
-		Now.year = 2020;
-		Now.month = 4;
-		Now.day = 10;
-		Now.hour = 10;
-		Now.minute = 15;
-		Now.second = 0;
-
-		TimeAndDate Date;
-		// 10/04/2020 10:15:00
-		Date.year = 2020;
-		Date.month = 4;
-		Date.day = 10;
-		Date.hour = 10;
-		Date.minute = 15;
-		Date.second = 0;
-
-		CHECK((Now == Date) == true);
-		CHECK((Now <= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now >= Date) == true);
-		CHECK((Now <= Date) == true);
-	}
-}
diff --git a/test/Helpers/TimeFormats.cpp b/test/Helpers/TimeFormats.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6562503f6f20fbf3e210623e0226e7705822734
--- /dev/null
+++ b/test/Helpers/TimeFormats.cpp
@@ -0,0 +1,26 @@
+#include "catch2/catch.hpp"
+#include "Helpers/TimeFormats.hpp"
+#include "../Services/ServiceTests.hpp"
+
+TEST_CASE("UTC timestamps") {
+	// invalid year
+	UTC_Timestamp Timestamp0{2018, 4, 10, 10, 15, 0};
+
+	// invalid month
+	UTC_Timestamp Timestamp1{2020, 60, 10, 10, 15, 0};
+
+	// invalid day
+	UTC_Timestamp Timestamp2{2020, 4, 35, 10, 15, 0};
+
+	// invalid hour
+	UTC_Timestamp Timestamp3{2020, 4, 10, 100, 15, 0};
+
+	// invalid minute
+	UTC_Timestamp Timestamp4{2020, 4, 10, 10, 200, 0};
+
+	// invalid second
+	UTC_Timestamp Timestamp5{2020, 4, 10, 10, 15, 100};
+
+	CHECK(ServiceTests::countErrors() == 6);
+	CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
+}
diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp
deleted file mode 100644
index e9d0fd1f22d532553de2925a1cb6ccf3657522ec..0000000000000000000000000000000000000000
--- a/test/Helpers/TimeHelper.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-#include "catch2/catch.hpp"
-#include "Helpers/TimeHelper.hpp"
-#include "../Services/ServiceTests.hpp"
-
-TEST_CASE("Time format implementation for CDS", "[CDS]") {
-	SECTION("Invalid date") {
-		TimeAndDate TimeInfo;
-
-		// invalid year
-		TimeInfo.year = 2018;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid month
-		TimeInfo.year = 2019;
-		TimeInfo.month = 60;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid day
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 35;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid hour
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 100;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid minute
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 200;
-		TimeInfo.second = 0;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		// invalid second
-		TimeInfo.year = 2019;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 122;
-
-		TimeHelper::utcToSeconds(TimeInfo);
-
-		CHECK(ServiceTests::countErrors() == 6);
-		CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate));
-	}
-
-	SECTION("Convert UTC date to elapsed seconds since Unix epoch") {
-		TimeAndDate TimeInfo;
-		// 10/04/2020 10:15:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 4;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 10;
-		TimeInfo.minute = 15;
-		TimeInfo.second = 0;
-
-		uint32_t currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-		uint16_t elapsedDays = currTime / 86400;
-		uint32_t msOfDay = currTime % 86400 * 1000;
-		uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
-		CHECK(TimeHelper::generateCDSTimeFormat(TimeInfo) == timeFormat);
-
-		// 1/1/2019 00:00:00
-		TimeInfo.year = 2019;
-		TimeInfo.month = 1;
-		TimeInfo.day = 1;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-		elapsedDays = currTime / 86400;
-		msOfDay = currTime % 86400 * 1000;
-		timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay);
-		CHECK(TimeHelper::generateCDSTimeFormat(TimeInfo) == timeFormat);
-
-		// 5/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 5;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607126400);
-
-		// 10/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 10;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607558400);
-
-		// 15/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 15;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1607990400);
-
-		// 20/12/2020 00:00:00
-		TimeInfo.year = 2020;
-		TimeInfo.month = 12;
-		TimeInfo.day = 20;
-		TimeInfo.hour = 0;
-		TimeInfo.minute = 0;
-		TimeInfo.second = 0;
-
-		currTime = TimeHelper::utcToSeconds(TimeInfo);
-		CHECK(currTime == 1608422400);
-	}
-
-	SECTION("Convert elapsed seconds since Unix epoch to UTC date") {
-		uint32_t seconds = 1586513700; // elapsed seconds between 10/04/2020 10:15:00 and Unix epoch
-
-		TimeAndDate TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 4);
-		CHECK(TimeInfo.day == 10);
-		CHECK(TimeInfo.hour == 10);
-		CHECK(TimeInfo.minute == 15);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1546300800; // elapsed seconds between 1/1/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 1);
-		CHECK(TimeInfo.day == 1);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1550966400; // elapsed seconds between 24/2/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 2);
-		CHECK(TimeInfo.day == 24);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1551571200; // elapsed seconds between 3/3/2019 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2019);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 3);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1742907370; // elapsed seconds between 25/3/2025 12:56:10 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2025);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 25);
-		CHECK(TimeInfo.hour == 12);
-		CHECK(TimeInfo.minute == 56);
-		CHECK(TimeInfo.second == 10);
-
-		seconds = 1583020800; // elapsed seconds between 1/3/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 3);
-		CHECK(TimeInfo.day == 1);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1582934400; // elapsed seconds between 2/29/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 2);
-		CHECK(TimeInfo.day == 29);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-
-		seconds = 1577923200; // elapsed seconds between 2/1/2020 00:00:00 and Unix epoch
-
-		TimeInfo = TimeHelper::secondsToUTC(seconds);
-		CHECK(TimeInfo.year == 2020);
-		CHECK(TimeInfo.month == 1);
-		CHECK(TimeInfo.day == 2);
-		CHECK(TimeInfo.hour == 0);
-		CHECK(TimeInfo.minute == 0);
-		CHECK(TimeInfo.second == 0);
-	}
-}
-
-TEST_CASE("Time format implementation for CUC", "[CUC]") {
-	TimeAndDate TimeInfo;
-	// 10/04/2020 10:15:00
-	TimeInfo.year = 2020;
-	TimeInfo.month = 4;
-	TimeInfo.day = 10;
-	TimeInfo.hour = 10;
-	TimeInfo.minute = 15;
-	TimeInfo.second = 0;
-
-	uint32_t currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-	uint32_t timeFormat = currTime + LeapSeconds;
-	CHECK(TimeHelper::generateCUCTimeFormat(TimeInfo) == timeFormat);
-
-	Message message = Message(9, 128, Message::TC, 3);
-	message.appendWord(timeFormat);
-	CHECK((TimeHelper::parseCUCTimeFormat(message.data) == TimeInfo) == true);
-
-	// 1/1/2019 00:00:00
-	TimeInfo.year = 2019;
-	TimeInfo.month = 1;
-	TimeInfo.day = 1;
-	TimeInfo.hour = 0;
-	TimeInfo.minute = 0;
-	TimeInfo.second = 0;
-
-	currTime = TimeHelper::utcToSeconds(TimeInfo);
-
-	timeFormat = currTime + LeapSeconds; // TAI format
-	CHECK(TimeHelper::generateCUCTimeFormat(TimeInfo) == timeFormat);
-
-	message = Message(9, 128, Message::TC, 3);
-	message.appendWord(timeFormat);
-	CHECK((TimeHelper::parseCUCTimeFormat(message.data) == TimeInfo) == true);
-}
diff --git a/test/Helpers/TimeStamp.cpp b/test/Helpers/TimeStamp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4202cb166cd78afd21d9f10103979e4866db2d92
--- /dev/null
+++ b/test/Helpers/TimeStamp.cpp
@@ -0,0 +1,128 @@
+#include "catch2/catch.hpp"
+#include "Helpers/TimeStamp.hpp"
+#include "../Services/ServiceTests.hpp"
+#include <typeinfo>
+#include <iostream>
+
+TEST_CASE("TimeStamp class construction") {
+	// SECTION("Initialize with excessive precision, breaks at compile time"){
+	//   TimeStamp<5, 10> Epoch3;
+	//   TimeStamp<4, 4> Epoch4;
+	// }
+
+	SECTION("Test CUC headers generation") {
+		uint8_t cuc_header1 = build_CUC_header<uint8_t, 2, 2>();
+		CHECK(cuc_header1 == 0b00100110);
+
+		uint8_t cuc_header2 = build_CUC_header<uint8_t, 4, 1>();
+		CHECK(cuc_header2 == 0b00101101);
+
+		uint8_t cuc_header3 = build_CUC_header<uint8_t, 1, 1>();
+		CHECK(cuc_header3 == 0b00100001);
+
+		uint16_t cuc_header4 = build_CUC_header<uint16_t, 5, 1>();
+		CHECK(cuc_header4 == 0b1010110110100000);
+
+		uint16_t cuc_header5 = build_CUC_header<uint16_t, 1, 6>();
+		CHECK(cuc_header5 == 0b1010001110001100);
+
+		uint16_t cuc_header6 = build_CUC_header<uint16_t, 7, 1>();
+		CHECK(cuc_header6 == 0b1010110111100000);
+	}
+
+	SECTION("Check TAI idempotence") {
+		int input_time = 1000;
+		TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(input_time);
+		REQUIRE(Epoch.as_TAI_seconds() == input_time); // check initialization has intended effect
+	}
+
+	SECTION("Check CUC idempotence") {
+		etl::array<uint8_t, 9> input_time1 = {0b00100110, 0, 1, 1, 3, 0, 0, 0, 0};
+		TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch1(input_time1);
+		etl::array<uint8_t, 9> test_return1 = Epoch1.as_CUC_timestamp();
+
+		for (auto i = 0; i < 9; i++) {
+			bool test = input_time1[i] == test_return1[i];
+			REQUIRE(test);
+		}
+
+		etl::array<uint8_t, 9> input_time2 = {0b10101101, 0b10100000, 218, 103, 11, 0, 3, 23, 0};
+		TimeStamp<5, 1> Epoch2(input_time2);
+		etl::array<uint8_t, 9> test_return2 = Epoch2.as_CUC_timestamp();
+
+		for (auto i = 0; i < 9; i++) {
+			bool test = input_time2[i] == test_return2[i];
+			REQUIRE(test);
+		}
+
+		etl::array<uint8_t, 9> input_time3 = {0b10100011, 0b10001100, 218, 103, 11, 0, 3, 23, 2};
+		TimeStamp<1, 6> Epoch3(input_time3);
+		etl::array<uint8_t, 9> test_return3 = Epoch3.as_CUC_timestamp();
+
+		for (auto i = 0; i < 9; i++) {
+			bool test = input_time3[i] == test_return3[i];
+			REQUIRE(test);
+		}
+	}
+
+	SECTION("Check UTC idempotence") {
+		{
+			UTC_Timestamp timestamp1(2020, 4, 10, 10, 15, 0); // 10 Apr 2020, 10:15:00;
+			TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(timestamp1);
+			UTC_Timestamp timestamp2 = Epoch.as_UTC_timestamp();
+			bool cond = (timestamp2 == timestamp1);
+			REQUIRE(cond);
+		}
+		{
+			UTC_Timestamp timestamp1(2035, 1, 1, 0, 0, 1); // 1 Jan 2035 midnight passed;
+			TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(timestamp1);
+			UTC_Timestamp timestamp2 = Epoch.as_UTC_timestamp();
+			bool cond = (timestamp2 == timestamp1);
+			REQUIRE(cond);
+		}
+	}
+
+	SECTION("UTC conversion to and from seconds timestamps") {
+		{
+			UTC_Timestamp timestamp1(2020, 12, 5, 0, 0, 0); // 5 Dec 2020, midnight;
+			TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(timestamp1);
+			REQUIRE(Epoch.as_TAI_seconds() == 1607126400);
+		}
+		{
+			UTC_Timestamp timestamp1(2020, 2, 29, 0, 0, 0); // 5 Dec 2020, midnight;
+			TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(timestamp1);
+			REQUIRE(Epoch.as_TAI_seconds() == 1582934400);
+		}
+	}
+
+	// SECTION("Check different templates, should break at compile"){
+	//   TimeStamp<1, 2> Epoch1;
+	//   TimeStamp<4, 4> Epoch2;
+	//   REQUIRE(Epoch1==Epoch2);
+	// }
+
+	SECTION("Check operators") {
+		TimeStamp<1, 2> Epoch1;
+		TimeStamp<1, 2> Epoch2;
+		TimeStamp<1, 2> Epoch3(10);
+		TimeStamp<1, 2> Epoch4(15);
+		TimeStamp<1, 2> Epoch5(12);
+		TimeStamp<1, 2> Epoch6(10);
+		TimeStamp<2, 2> Epoch7;
+		REQUIRE(Epoch1 == Epoch2);
+		REQUIRE(Epoch2 == Epoch1);
+		REQUIRE(Epoch3 == Epoch6);
+		REQUIRE(Epoch1 != Epoch3);
+		REQUIRE(Epoch3 != Epoch5);
+		REQUIRE(Epoch3 <= Epoch5);
+		REQUIRE(Epoch3 < Epoch5);
+
+		// REQUIRE(Epoch1==Epoch7); //should fail at compile, different templates
+	}
+
+	SECTION("Check runtime class size") {
+		int input_time = 1000;
+		TimeStamp<ACUBESAT_CUC_SECONDS_COUNTER_BYTES, ACUBESAT_CUC_FRACTIONAL_COUNTER_BYTES> Epoch(input_time);
+		REQUIRE(sizeof(Epoch) < 32);
+	}
+}