Repository: nasa/fprime (language: python)

Issue:
`Fw::TimeInterval`
| | |
|:---|:---|
|**_F´ Version_**| |
|**_Affected Component_**|  |
---
## Feature Description

Current `Fw::Time` represents absolute time and is abused for relative time.  We should figure out how to represent time intervals.


Discussion / hints:
relates to #2726 

Fix patch:
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index 1484ddc04d..7d2d958e19 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -15,15 +15,12 @@ actools
 acxz
 addoffset
 adminlist
-aei
 aeiouy
 afterstatinfo
-agg
 aheared
 ALLEXTERNALS
 alphanums
 ampcs
-amsmath
 aname
 ANamespace
 anotherchan
@@ -49,7 +46,6 @@ arijitdas
 arinc
 arpa
 ASTRING
-ATest
 ATester
 ATL
 ATMs
@@ -63,12 +59,10 @@ autocoding
 autodocs
 Autodocumentation
 AUTOGEN
-autoload
 automodule
 AYYYY
 backslashreplace
 baremetal
-BARETASKHANDLE
 bashcompinit
 batchmode
 BDV
@@ -76,16 +70,12 @@ bfree
 bibtex
 Bies
 bindir
-binrel
 bitmaps
-ble
-blogs
 bocchino
 bodychars
 bodytext
 bools
 boolt
-bootlin
 brc
 bre
 bsd
@@ -119,7 +109,6 @@ CCSDS
 ccsparc
 cdefs
 CDH
-cdn
 cerrno
 cface
 CFDP
@@ -130,7 +119,6 @@ cfsetospeed
 changeme
 CHANNELID
 CHANS
-cheetahtemplate
 Chieu
 CHK
 CHNG
@@ -139,27 +127,18 @@ classdiagram
 classdoc
 classtype
 CLion
-CLOCAL
-closedir
 CLOSEFILE
 cloudbees
-cls
-cmake
-Cmdd
 CMDDISP
 CMDDISPATCHER
 CMDPACKET
 CMDREG
 cmds
-CMDSEQ
 cmdsequencer
-cnt
 cntx
 cobj
 CODEFILE
-codegen
 codeql
-coldarm
 COLORSTYLE
 colorwheel
 COMBUFFER
@@ -174,14 +153,12 @@ COMQUEUE
 COMQUEUEIN
 COMSPLITTER
 COMSTUB
-constexpr
 cookiecutter
 cooldown
 coor
 coravy
 coreutils
 cosmosgen
-cov
 Coverity
 CPHA
 Cpkt
@@ -191,8 +168,6 @@ cpos
 cppcheck
 cppcheckxml
 cpplint
-cprogramming
-cpuset
 CRCAMPCS
 crcccitt
 crcdnp
@@ -205,22 +180,16 @@ crsmith
 crt
 CRTSCTS
 cryptsoft
-CSIZE
 cstat
-CSTOPB
 csum
 ctest
 ctu
 culates
-curmsgs
 currentxml
 cuz
 CYCLEOUT
-daringfireball
 Daruwala
-databinding
 DATAROOTDIR
-dawbarton
 DDDTHH
 ddl
 ddmm
@@ -237,7 +206,6 @@ deframing
 delchars
 deployables
 DEPRECATEDLIST
-deregistration
 deser
 Deserial
 dhesikan
@@ -246,19 +214,16 @@ diafile
 dictgen
 dictvalue
 diffs
-digitalcommons
 diles
 dinkel
 dlog
 dnp
-docblocks
 docbook
 docset
 doctag
 doinit
 dontcare
 dontinclude
-DONTNEED
 doodie
 dotfile
 DOTFONTPATH
@@ -272,17 +237,14 @@ doxyindexer
 doxyrules
 doxysearch
 Doxywizard
-dpc
 DPCAT
 DPCATALOG
 DPCFG
-dpi
 DPMANAGER
 dps
 DPWRITER
 DRAINBUFFERS
 drv
-dsdl
 dspal
 Duerschmid
 duey
@@ -294,13 +256,10 @@ eabi
 eabihf
 eay
 ECLIPSEHELP
-ect
-edu
 EGB
 EHAs
 eip
 elist
-ELOG
 Elts
 emoji
 endcode
@@ -309,20 +268,17 @@ endfunction
 endloc
 endmacro
 enduml
-enewcommand
 ENQUEUEX
 enumchan
 enumname
 enumparam
 eobj
 errfile
-errmsg
 ERRORCHECK
 errornum
 ert
 esb
 ethanchee
-etime
 eturn
 EVENTARRAY
 EVENTBOOL
@@ -331,30 +287,23 @@ eventname
 EVENTPRIMITIVE
 evr
 evt
-ewcommand
 EXAMPLECOMPONENTIMPL
 excinfo
 Exoplanets
 exprtokens
 Fabcdef
-FADV
-fadvise
 FAKELOGGER
-fallocate
 fbuild
 fdp
 fdset
 FEEDNAME
 ffff
 Ffs
-filedown
 FILEDOWNLINK
 FILEDOWNLINKCFG
 FILEID
-FILENAMESTRING
 fileopen
 FILEOPENERROR
-FILESTUBS
 FILEWRITEERROR
 fio
 Firefox
@@ -362,9 +311,7 @@ FLDP
 flist
 FNDELAY
 fns
-FONTNAME
 FONTPATH
-FONTSIZE
 foodoodie
 foodoodiehoo
 formatline
@@ -373,7 +320,6 @@ fpi
 FPL
 fpp
 fppi
-FPport
 fpptest
 fprime
 fptr
@@ -385,12 +331,10 @@ frox
 frsize
 fsblkcnt
 fsw
-fus
 FWCASSERT
 Gangianpour
 gaurav
 gbl
-gcc
 gcda
 gcgandhi
 gcov
@@ -404,14 +348,11 @@ getcontext
 getdata
 getdefaultencoding
 getextern
-gethostname
 getinput
 getm
-getpid
 getquaternion
 gett
 gettime
-gettimeofday
 getty
 getu
 ghprb
@@ -419,10 +360,7 @@ gitmodules
 gmock
 Gnc
 Gnd
-GNUC
-gnueabi
 gnueabihf
-google
 googletest
 Gorang
 GPGGA
@@ -467,10 +405,8 @@ huey
 Huynh
 HVisitor
 hyperlinks
-hypermail
 ibd
 idl
-idx
 ieeetr
 ifcomment
 iflag
@@ -489,7 +425,6 @@ inin
 initfiles
 initstate
 inkscape
-INPCK
 Inputline
 installable
 instantiator
@@ -508,16 +443,13 @@ ioctl
 IPCFG
 IPHELPER
 ipp
-IPPROTO
 ipriority
 isequal
 isf
 ishii
 isoschematron
 isr
-iss
 itimerspec
-itle
 itr
 itval
 itype
@@ -535,16 +467,11 @@ joshuaa
 jpl
 jplffs
 jre
-jsdelivr
 JSO
 jsonable
 kermit
 kevin
-kislyuk
-kitware
 Kooi
-kthxbye
-Kubernetes
 Lammert
 lammertbies
 LASTLOG
@@ -566,16 +493,11 @@ lic
 lindent
 linelength
 lineroo
-lineterm
-LINUXTIMEIMPL
 listdir
 Listst
 LJR
-lkml
 lld
 LOCALSTATEDIR
-LOCKGUARDTESTER
-LOGASSERT
 LOGGERRULES
 loglvl
 LOGPACKET
@@ -583,8 +505,6 @@ logpath
 Lps
 lseek
 ltab
-LTIME
-LTIMER
 LTLT
 LVL
 lxml
@@ -600,7 +520,6 @@ Manglapus
 mathjax
 maxdepth
 maxloc
-maxmsg
 maxspew
 mcomment
 MCT
@@ -624,18 +543,13 @@ mman
 MMAPALLOCATOR
 MML
 modbus
-mosa
 MOSI
 MOVEFILE
-mozilla
-mqd
-mqueue
 Mrf
 Msap
 msc
 mscfile
 mseconds
-msgsize
 mstarch
 mstat
 mstring
@@ -650,13 +564,10 @@ Mutexed
 mval
 MVC
 mycompany
-nanosleep
 nasafprime
-nathan
 nbits
 ncsl
 ndiffs
-neascout
 newloc
 newroot
 newself
@@ -664,18 +575,14 @@ newstring
 newtio
 nfds
 Nicolich
-ninjaaron
 NMEA
 nmsgs
 noargport
 NOBLOCK
 nogen
-nonblock
 noncomma
 NONINFRINGEMENT
-Nop
 noparent
-noreturn
 normalwidths
 NOSIZE
 NOSPEC
@@ -683,11 +590,9 @@ nostdlib
 nosubgrouping
 notask
 notchars
-NOTYPE
 Nowicki
 nproc
 NPROCESSORS
-npx
 NSHUFF
 NSPACES
 ntohs
@@ -699,19 +604,14 @@ objcopy
 objdoc
 objdump
 objmodule
-oclc
 odl
-odo
 oflag
 okidocki
 oldeol
 OLDINCLUDEDIR
 OMG
-onlinepubs
 ONLN
 OPCODEBASE
-opengroup
-openmct
 openpyxl
 oper
 optarg
@@ -726,8 +626,7 @@ OSAL
 osaves
 osets
 ostate
-osubgrouping
-OSX
+ote
 otherdictitems
 otheritems
 otherside
@@ -749,8 +648,6 @@ PARAMENUM
 PARAMSTRING
 PARAMSTRUCT
 PARAMU
-PARENB
-PARODD
 parseable
 PASSIVERATEGROUP
 PASSIVERATEGROUPIMPLTESTER
@@ -768,16 +665,12 @@ pkill
 pkts
 plainnat
 plantuml
-plugins
-pname
 PNGs
 POLLERR
 pollfd
 POLLIN
 POLLPRI
 POLYDB
-POLYDBCOMP
-POLYDBIMPL
 popd
 portlist
 PORTOUT
@@ -785,16 +678,13 @@ PORTSELECTOR
 PORTSOUT
 ppandian
 pport
-PQueue
 Prashanth
-prealloc
 PREDEF
 preds
 pregen
 preloc
 preparse
 prepeneding
-pri
 printables
 prioritization
 PRIORITYQUEUE
@@ -818,7 +708,6 @@ projectname
 projectnumber
 propget
 propput
-PROT
 protothreading
 prototypetext
 pstr
@@ -828,7 +717,6 @@ ptestrun
 ptf
 pthread
 ptmcg
-pton
 ptrt
 puml
 punc
@@ -841,32 +729,27 @@ PYS
 pytests
 PYTHONPATH
 qch
-QEvent
 qhelpgenerator
 QHG
 qhp
 QNX
 qsf
-qtest
-qthelpproject
 Qualcomm
 quatchan
 RAbrack
 RAII
 Ramanan
-rancherdesktop
 randtbl
-rapidscat
 raspberrypi
 raspi
 RATEGROUPDRIVER
 RATEGROUPDRIVERIMPLTESTER
 RATEGROUPMEMBEROUT
 RATELIMITERTESTER
+rawtime
 RBF
 RCHILD
 rcvd
-RCVTIMEO
 rdwr
 Readback
 Recvd
@@ -885,12 +768,8 @@ resname
 resultlist
 retobj
 revents
-rewinddir
 RGD
-RGDRV
 RHH
-ridgerun
-riverbankcomputing
 Rizvi
 rootdir
 ror
@@ -914,25 +793,20 @@ saveop
 sbb
 SBF
 SBINDIR
-sbt
 Scatterometer
 schem
 schematron
 sclk
 scm
 scons
-scp
-scrollbars
 sdd
 SDFLIGHT
 searchdata
 SENDPARTIAL
 seqgen
-serafin
 serializables
 setaffinity
 setdata
-SETFL
 setid
 setinheritsched
 SETLOGGING
@@ -944,7 +818,6 @@ setschedparam
 setschedpolicy
 setstacksize
 settime
-settype
 setversion
 sev
 sface
@@ -958,17 +831,13 @@ showinitializer
 sideeffect
 sighandler
 Signedness
-SIMPLEQUEUEREGISTRY
 sinc
 Sinha
 sloc
-smallsat
 Smath
-SNDTIMEO
 socio
 SOCKETHELPER
 SOCKETIPDRIVER
-SOCKETREADTASK
 socklen
 somechan
 SOMEEVENT
@@ -976,14 +845,10 @@ someparam
 sometask
 somevalue
 sommercharles
-soudry
 sourced
 SOURCEDIR
 sourceforge
-Sourcetrail
 sourcing
-spacetech
-sparc
 SPHINXBUILD
 SPHINXOPTS
 spi
@@ -997,7 +862,6 @@ Ssymbols
 STAMEM
 startloc
 startuml
-statvfs
 STDC
 stddef
 stdint
@@ -1005,7 +869,6 @@ stest
 Stl
 STREQ
 STREQUAL
-strerror
 strg
 stri
 stringbuffer
@@ -1014,55 +877,43 @@ stringparam
 STRINGUTILS
 STRNE
 strnlen
-subfolders
 subgrouping
 subhist
 subhistory
-subpage
 subseconds
 subtargets
-subtopology
 subtopologies
-Subtopology
-Subtopologies
+subtopology
 suppr
 suseconds
 SVCLOGFILE
 SVCLOGFILEL
-svipc
 swcaegitadmin
 synchronicity
 synopsys
 sysconf
 SYSCONFDIR
 SYSFS
-sysinfo
 sysroot
 tabbedwidths
 tabccitt
 tabdnp
 tabkermit
 tagfile
-TASKIDREPR
-TASKRUNNER
 tbase
 tcanham
 tcflush
 tcgetattr
-TCIFLUSH
 tclist
 tcomp
-tcort
 TCPCLIENT
 TCPHELPER
 tcpserver
 TCSANOW
-tcsetattr
 tcsh
 tdir
 tdirection
 telem
-testcase
 testcmd
 TESTCOMMAND
 TESTCOMMANDSOURCEIMPL
@@ -1070,14 +921,12 @@ testcomp
 TESTCOMPONENTIMPL
 testerbase
 testgen
-testimpl
 TESTLIST
 TESTLOGRECVIMPL
 TESTPARAMIMPL
 TESTPARAMRECVIMPL
 TESTPTSOURCEIMPL
 teststring
-testsuites
 TESTTELEMRECVIMPL
 TESTTEXTLOGIMPL
 TESTTIMEIMPL
@@ -1086,19 +935,14 @@ TESTUTILS
 tfile
 tflat
 tfn
-thepihut
 Thhmmss
 thiscol
 thisdirdoesnotexist
 thisfiledoesnotexist
 timebase
-timedreceive
-timedsend
 timerfd
-TIMERVAL
 timetag
 timeval
-timothycanham
 Tinternal
 tions
 tjh
@@ -1115,35 +959,26 @@ TLMPACKETIZER
 TLMPACKETIZERCOMPONENTIMPLCFG
 TLMPACKETIZERTYPES
 tlmval
-TLOG
 TML
 tmpl
 tmptokens
 tmptree
 tname
-tnum
 toctree
 TODOLIST
 tokdict
 TOKENBUCKETTESTER
 tokenlist
 toklist
-toolchains
-toolset
 topologyapp
-TOPOLOGYCONFIG
-Torvalds
 totalram
 tparam
-treeview
 trimwhitespace
 trinomials
 truncstring
 tts
-ttype
 Tuszynski
 TXD
-typedef
 typedef'ed
 typelist
 typeslist
@@ -1160,49 +995,34 @@ UDPSENDERCOMPONENTIMPLCFG
 UDPSOCKET
 UML
 UNEXP
-unexport
-UNINIT
 UNINSTANTIATED
 unistd
 Unithem
 UNITTESTASSERT
 unittests
 unprintables
-upcalls
 upfiles
 upl
 usb
-usecond
-usepackage
-usleep
+useconds
 ustr
 utdir
 valgrind
 valud
 vbai
-VERSIONED
 vfd
 VFILE
 vhd
 vhdl
 VID
-viewcontent
-viewforum
-virt
 vla
 vlist
-VMIN
-VTIME
 vtype
-vuejs
 vwong
 vxworks
-VXWORKSLOGASSERT
-WAITALL
 watney
 Wdog
 whitebox
-wikipedia
 WLE
 Wno
 WORKDIR
@@ -1216,9 +1036,7 @@ Xapian
 xdf
 xdffe
 Xelect
-xfer
 xmlfile
-xsh
 xsltproc
 xxxx
 yacgen
diff --git a/.gitignore b/.gitignore
index fdfa3cc063..6e0d0a639b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ build/
 build*/
 gtest/
 docs-cache/
+CMakePresets.json
 
 AutoXML/
 test_harness/src/test_harness-C/application.out
diff --git a/Drv/BlockDriver/BlockDriverImpl.cpp b/Drv/BlockDriver/BlockDriverImpl.cpp
index 9bbddbece4..4a5f95e516 100644
--- a/Drv/BlockDriver/BlockDriverImpl.cpp
+++ b/Drv/BlockDriver/BlockDriverImpl.cpp
@@ -16,10 +16,10 @@ namespace Drv {
 
     void BlockDriverImpl::InterruptReport_internalInterfaceHandler(U32 ip) {
         // get time
-        Svc::TimerVal timer;
-        timer.take();
+        Os::RawTime time;
+        time.now();
         // call output timing signal
-        this->CycleOut_out(0,timer);
+        this->CycleOut_out(0,time);
         // increment cycles and write channel
         this->tlmWrite_BD_Cycles(this->m_cycles);
         this->m_cycles++;
diff --git a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
index ddbae28b26..289bb07adc 100644
--- a/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
+++ b/Drv/LinuxGpioDriver/LinuxGpioDriverComponentImpl.cpp
@@ -383,12 +383,12 @@ namespace Drv {
         }
 
         // call interrupt ports
-        Svc::TimerVal timerVal;
-        timerVal.take();
+        Os::RawTime timestamp;
+        timestamp.now();
 
         for (NATIVE_INT_TYPE port = 0; port < compPtr->getNum_intOut_OutputPorts(); port++) {
             if (compPtr->isConnected_intOut_OutputPort(port)) {
-                compPtr->intOut_out(port,timerVal);
+                compPtr->intOut_out(port,timestamp);
             }
         }
 
diff --git a/Drv/LinuxUartDriver/LinuxUartDriver.cpp b/Drv/LinuxUartDriver/LinuxUartDriver.cpp
index 2fbdcb16a0..ae8a06aad4 100644
--- a/Drv/LinuxUartDriver/LinuxUartDriver.cpp
+++ b/Drv/LinuxUartDriver/LinuxUartDriver.cpp
@@ -351,7 +351,7 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
             status = RecvStatus::RECV_ERROR;
             comp->recv_out(0, buff, status);
             // to avoid spinning, wait 50 ms
-            Os::Task::delay(Fw::Time(0, 50));
+            Os::Task::delay(Fw::TimeInterval(0, 50000));
             continue;
         }
 
diff --git a/Fw/Time/CMakeLists.txt b/Fw/Time/CMakeLists.txt
index fa739f5ad6..91136d2d78 100644
--- a/Fw/Time/CMakeLists.txt
+++ b/Fw/Time/CMakeLists.txt
@@ -8,7 +8,7 @@
 set(SOURCE_FILES
   "${CMAKE_CURRENT_LIST_DIR}/Time.fpp"
   "${CMAKE_CURRENT_LIST_DIR}/Time.cpp"
-  "${CMAKE_CURRENT_LIST_DIR}/Time.cpp"
+  "${CMAKE_CURRENT_LIST_DIR}/TimeInterval.cpp"
 )
 register_fprime_module()
 ### UTs ###
diff --git a/Fw/Time/Time.fpp b/Fw/Time/Time.fpp
index 80b7f1dc46..1385b1fd69 100644
--- a/Fw/Time/Time.fpp
+++ b/Fw/Time/Time.fpp
@@ -4,7 +4,14 @@ module Fw {
 
   @ Time port
   port Time(
-             ref $time: Fw.Time @< The U32 cmd argument
+             ref $time: Fw.Time @< Reference to Time object
+           )
+
+  type TimeInterval
+
+  @ Time interval port
+  port TimeInterval(
+      ref timeInterval: Fw.TimeInterval @< Reference to TimeInterval object
            )
 
 }
diff --git a/Fw/Time/TimeInterval.cpp b/Fw/Time/TimeInterval.cpp
new file mode 100644
index 0000000000..016b17fb70
--- /dev/null
+++ b/Fw/Time/TimeInterval.cpp
@@ -0,0 +1,159 @@
+#include <Fw/Time/TimeInterval.hpp>
+#include <FpConfig.hpp>
+
+namespace Fw {
+    TimeInterval::TimeInterval(const TimeInterval& other) : Serializable() {
+        this->set(other.m_seconds,other.m_useconds);
+    }
+
+    TimeInterval::TimeInterval(U32 seconds, U32 useconds) : Serializable() {
+        this->set(seconds,useconds);
+    }
+
+    void TimeInterval::set(U32 seconds, U32 useconds) {
+        this->m_seconds = seconds;
+        this->m_useconds = useconds;
+    }
+
+    TimeInterval& TimeInterval::operator=(const TimeInterval& other) {
+        this->m_useconds = other.m_useconds;
+        this->m_seconds = other.m_seconds;
+
+        return *this;
+    }
+
+    bool TimeInterval::operator==(const TimeInterval& other) const {
+        return (TimeInterval::compare(*this,other) == EQ);
+    }
+
+    bool TimeInterval::operator!=(const TimeInterval& other) const {
+        return (TimeInterval::compare(*this,other) != EQ);
+    }
+
+    bool TimeInterval::operator>(const TimeInterval& other) const {
+        return (TimeInterval::compare(*this,other) == GT);
+    }
+
+    bool TimeInterval::operator<(const TimeInterval& other) const {
+        return (TimeInterval::compare(*this,other) == LT);
+    }
+
+    bool TimeInterval::operator>=(const TimeInterval& other) const {
+        TimeInterval::Comparison c = TimeInterval::compare(*this,other);
+        return ((GT == c) or (EQ == c));
+    }
+
+    bool TimeInterval::operator<=(const TimeInterval& other) const {
+        TimeInterval::Comparison c = TimeInterval::compare(*this,other);
+        return ((LT == c) or (EQ == c));
+    }
+
+    SerializeStatus TimeInterval::serialize(SerializeBufferBase& buffer) const {
+        // serialize members
+        SerializeStatus stat = Fw::FW_SERIALIZE_OK;
+        stat = buffer.serialize(this->m_seconds);
+        if (stat != FW_SERIALIZE_OK) {
+            return stat;
+        }
+        return buffer.serialize(this->m_useconds);
+    }
+
+    SerializeStatus TimeInterval::deserialize(SerializeBufferBase& buffer) {
+
+        SerializeStatus stat = Fw::FW_SERIALIZE_OK;
+        stat = buffer.deserialize(this->m_seconds);
+        if (stat != FW_SERIALIZE_OK) {
+            return stat;
+        }
+
+        return buffer.deserialize(this->m_useconds);
+    }
+
+    U32 TimeInterval::getSeconds() const {
+        return this->m_seconds;
+    }
+
+    U32 TimeInterval::getUSeconds() const {
+        return this->m_useconds;
+    }
+
+    TimeInterval::Comparison TimeInterval ::
+      compare(
+          const TimeInterval &time1,
+          const TimeInterval &time2
+      )
+    {
+      const U32 s1 = time1.getSeconds();
+      const U32 s2 = time2.getSeconds();
+      const U32 us1 = time1.getUSeconds();
+      const U32 us2 = time2.getUSeconds();
+
+      if (s1 < s2) {
+        return LT;
+      } else if (s1 > s2) {
+        return GT;
+      } else if (us1 < us2) {
+        return LT;
+      } else if (us1 > us2) {
+        return GT;
+      } else {
+        return EQ;
+      }
+    }
+
+    TimeInterval TimeInterval ::
+      add(
+        const TimeInterval& a,
+        const TimeInterval& b
+      )
+    {
+      U32 seconds = a.getSeconds() + b.getSeconds();
+      U32 uSeconds = a.getUSeconds() + b.getUSeconds();
+      FW_ASSERT(uSeconds < 1999999);
+      if (uSeconds >= 1000000) {
+        ++seconds;
+        uSeconds -= 1000000;
+      }
+      TimeInterval c(seconds,uSeconds);
+      return c;
+    }
+
+    TimeInterval TimeInterval ::
+      sub(
+        const TimeInterval& t1, //!< TimeInterval t1
+        const TimeInterval& t2 //!< TimeInterval t2
+    )
+    {
+      const TimeInterval& minuend = (t1 > t2) ? t1 : t2;
+      const TimeInterval& subtrahend = (t1 > t2) ? t2 : t1;
+
+      U32 seconds = minuend.getSeconds() - subtrahend.getSeconds();
+      U32 uSeconds;
+      if (subtrahend.getUSeconds() > minuend.getUSeconds()) {
+          seconds--;
+          uSeconds = minuend.getUSeconds() + 1000000 - subtrahend.getUSeconds();
+      } else {
+          uSeconds = minuend.getUSeconds() - subtrahend.getUSeconds();
+      }
+      return TimeInterval(seconds, static_cast<U32>(uSeconds));
+    }
+
+    void TimeInterval::add(U32 seconds, U32 useconds) {
+        this->m_seconds += seconds;
+        this->m_useconds += useconds;
+        FW_ASSERT(this->m_useconds < 1999999, static_cast<FwAssertArgType>(this->m_useconds));
+        if (this->m_useconds >= 1000000) {
+          this->m_seconds += 1;
+          this->m_useconds -= 1000000;
+        }
+    }
+
+#ifdef BUILD_UT
+    std::ostream& operator<<(std::ostream& os, const TimeInterval& val) {
+
+        os << "(" << val.getSeconds() << "s," << val.getUSeconds() << "us)";
+        return os;
+    }
+#endif
+
+}
diff --git a/Fw/Time/TimeInterval.hpp b/Fw/Time/TimeInterval.hpp
new file mode 100644
index 0000000000..32af67f740
--- /dev/null
+++ b/Fw/Time/TimeInterval.hpp
@@ -0,0 +1,90 @@
+#ifndef FW_TIME_INTERVAL_HPP
+#define FW_TIME_INTERVAL_HPP
+
+#include <FpConfig.hpp>
+#include <Fw/Types/Assert.hpp>
+#include <Fw/Types/Serializable.hpp>
+
+//!
+//! @class TimeInterval
+//! @brief A class to represent a time interval holding two U32 seconds and microseconds values.
+//! 
+//! The TimeInterval class is designed to hold a time interval and provides various methods 
+//! to manipulate and compare time intervals. It supports serialization and deserialization 
+//! for easy storage and transmission.
+//!
+namespace Fw {
+
+    class TimeInterval: public Serializable {
+        public:
+
+            enum {
+                SERIALIZED_SIZE = sizeof(U32) * 2
+            };
+
+            TimeInterval() = default; // !< Default constructor
+            ~TimeInterval() = default; // !< Default destructor
+            TimeInterval(const TimeInterval& other); // !< Copy constructor
+            TimeInterval(U32 seconds, U32 useconds); // !< Constructor with member values as arguments
+            void set(U32 seconds, U32 useconds); // !< Sets value of time stored
+            U32 getSeconds() const; // !< Gets seconds part of time
+            U32 getUSeconds() const; // !< Gets microseconds part of time
+            SerializeStatus serialize(SerializeBufferBase& buffer) const; // !< Serialize method
+            SerializeStatus deserialize(SerializeBufferBase& buffer); // !< Deserialize method
+            void add(U32 seconds, U32 mseconds); // !< Add seconds and microseconds to existing time interval
+            bool operator==(const TimeInterval& other) const;
+            bool operator!=(const TimeInterval& other) const;
+            bool operator>(const TimeInterval& other) const;
+            bool operator<(const TimeInterval& other) const;
+            bool operator>=(const TimeInterval& other) const;
+            bool operator<=(const TimeInterval& other) const;
+            TimeInterval& operator=(const TimeInterval& other);
+
+            //! The type of a comparison result
+            typedef enum {
+              LT = -1,
+              EQ = 0,
+              GT = 1,
+              INCOMPARABLE = 2
+            } Comparison;
+
+            //! Compare two time intervals
+            //! A time interval is considered greater than another if it spans a longer duration
+            //! The comparison is done on the seconds first, then the microseconds if the seconds are equal
+            //! \return TimeInterval result
+            static Comparison compare(
+                const TimeInterval &time1, //!< TimeInterval 1
+                const TimeInterval &time2 //!< TimeInterval 2
+            );
+
+            //! Add two time intervals
+            //! Adds the seconds and microseconds fields of two time intervals together
+            //! \return TimeInterval result
+            static TimeInterval add(
+                const TimeInterval& a, //!< TimeInterval a
+                const TimeInterval& b //!< TimeInterval b
+            );
+
+            //! Subtract two time intervals
+            //! This computes the absolute value of the difference between two time intervals
+            //! For example if t1=(0s, 5us) and t2=(0s, 3us), the result is (0s, 2us).
+            //! This operation is commutative, i.e. the result is the same regardless of the order of the arguments.
+            //! \return TimeInterval result
+            static TimeInterval sub(
+                const TimeInterval& t1, //!< TimeInterval 1
+                const TimeInterval& t2 //!< TimeInterval 2
+            );
+
+
+
+#ifdef BUILD_UT // Stream operators to support Googletest
+            friend std::ostream& operator<<(std::ostream& os,  const TimeInterval& val);
+#endif
+        PRIVATE:
+            U32 m_seconds; // !< seconds portion of TimeInterval
+            U32 m_useconds; // !< microseconds portion of TimeInterval
+    };
+
+}
+
+#endif
diff --git a/Os/CMakeLists.txt b/Os/CMakeLists.txt
index c50596197a..a4aebef595 100644
--- a/Os/CMakeLists.txt
+++ b/Os/CMakeLists.txt
@@ -97,15 +97,13 @@ register_fprime_module(Os_Test_File_SyntheticFileSystem)
 
 # Basic source files used in every OSAL layer.  Contains common code and helpers.
 set(SOURCE_FILES
-    "${CMAKE_CURRENT_LIST_DIR}/IntervalTimerCommon.cpp"
-    "${CMAKE_CURRENT_LIST_DIR}/Posix/IntervalTimer.cpp"
-
     "${CMAKE_CURRENT_LIST_DIR}/ValidateFileCommon.cpp"
     "${CMAKE_CURRENT_LIST_DIR}/ValidatedFile.cpp"
+    "${CMAKE_CURRENT_LIST_DIR}/IntervalTimer.cpp"
+    "${CMAKE_CURRENT_LIST_DIR}/Types.fpp"
     "${CMAKE_CURRENT_LIST_DIR}/Os.cpp"
 )
 set(SOURCE_HEADERS
-    "${CMAKE_CURRENT_LIST_DIR}/Posix/IntervalTimer.hpp"
     "${CMAKE_CURRENT_LIST_DIR}/ValidatedFile.hpp"
     "${CMAKE_CURRENT_LIST_DIR}/Os.hpp"
 )
@@ -119,6 +117,7 @@ add_named_os_module("Mutex;Condition")
 add_named_os_module(Queue)
 add_named_os_module(Cpu)
 add_named_os_module(Memory)
+add_named_os_module(RawTime Fw_Buffer)
 
 ### UTS ### Note: 3 separate UTs registered here.
 set(UT_SOURCE_FILES
diff --git a/Os/IntervalTimer.cpp b/Os/IntervalTimer.cpp
new file mode 100644
index 0000000000..b926c9a13d
--- /dev/null
+++ b/Os/IntervalTimer.cpp
@@ -0,0 +1,34 @@
+// ======================================================================
+// \title Os/IntervalTimer.cpp
+// \brief Implementation for Os::IntervalTimer
+// ======================================================================
+
+#include <Os/IntervalTimer.hpp>
+#include <cstring>
+
+namespace Os {
+
+    IntervalTimer::IntervalTimer() : m_startTime(), m_stopTime() {}
+
+    void IntervalTimer::start() {
+        this->m_startTime.now();
+    }
+
+    void IntervalTimer::stop() {
+        this->m_stopTime.now();
+    }
+
+    U32 IntervalTimer::getDiffUsec() const {
+        U32 result = 0;
+        Os::RawTime::Status status = this->m_stopTime.getDiffUsec(this->m_startTime, result);
+        if (status == Os::RawTime::Status::OP_OVERFLOW) {
+            // If the operation fails due to overflow, we return the max value
+            result = std::numeric_limits<U32>::max();
+        }
+        return result;
+    }
+
+    Os::RawTime::Status IntervalTimer::getTimeInterval(Fw::TimeInterval& interval) const {
+        return this->m_stopTime.getTimeInterval(this->m_startTime, interval);
+    }
+}
diff --git a/Os/IntervalTimer.hpp b/Os/IntervalTimer.hpp
index a606590408..61a30b78a6 100644
--- a/Os/IntervalTimer.hpp
+++ b/Os/IntervalTimer.hpp
@@ -1,78 +1,77 @@
-/**
- * IntervalTimer.hpp:
- *
- * Interval timer provides timing over a set interval to the caller. It is one of the core Os
- * package supplied items.
- */
+// ======================================================================
+// \title Os/IntervalTimer.hpp
+// \brief Definition for Os::IntervalTimer
+// ======================================================================
 #ifndef _IntervalTimer_hpp_
 #define _IntervalTimer_hpp_
 
 #include <FpConfig.hpp>
+#include <Os/RawTime.hpp>
 
 namespace Os {
-    class IntervalTimer {
-        public:
-    		/**
-    		 * RawTime:
-    		 *
-    		 * Most time is stored as an upper and lower part of this raw time object. The
-    		 * semantic meaning of this "RawTime" is platform-dependent.
-    		 */
-            typedef struct {
-                U32 upper;  //!< Upper 32-bits part of time value. Platform dependent.
-                U32 lower; //!< Lower 32-bits part of time value. Platform dependent.
-            } RawTime;
+//! \brief Os::IntervalTimer measures time intervals using start/stop functionality.
+//!
+//! The IntervalTimer class provides methods to capture the start and stop times of an interval
+//! and calculate the difference between these times. It is useful for measuring the duration
+//! of operations or events. Intervals can be returned in Fw::TimeInterval or as a microsecond U32.
+//!
+//! \note The caller must ensure that the start() method is called before the stop() method to get
+//! a relevant time interval.
+//!
+//! \example
+//! IntervalTimer timer;
+//! timer.start();
+//! // Perform some operations
+//! timer.stop();
+//! Fw::TimeInterval interval = timer.getTimeInterval();
+class IntervalTimer {
+  public:
+    //! \brief Constructor
+    IntervalTimer();
 
-            IntervalTimer(); //!<  Constructor
-            virtual ~IntervalTimer(); //!<  Destructor
+    //! \brief Destructor
+    ~IntervalTimer() = default;
 
-            //------------ Common Functions ------------
-            // Common functions, typically do not need to be implemented by an OS support package.
-            // Common implementations in IntervalTimerCommon.cpp.
-            //------------------------------------------
-            /**
-             * Capture a start time of the interval timed by the interval timer. This fills the
-             * start RawTime of the interval.
-             */
-            void start();
-            /**
-             * Capture a stop time of the interval timed by the interval timer. This fills the
-             * stop RawTime of the interval.
-             */
-            void stop();
-            /**
-             * Returns the difference in usecond difference between start and stop times. The caller
-             * must have called start and stop previously.
-             * \return U32: microseconds difference in the interval
-             */
-            U32 getDiffUsec();
+    //! \brief Capture the start time of the interval.
+    //!
+    //! This method records the current time as the start time of the interval for this timer instance.
+    void start();
 
-            //------------ Platform Functions ------------
-            // Platform functions, typically do need to be implemented by an OS support package, as
-            // they are dependent on the platform definition of "RawTime".
-            //------------------------------------------
+    //! \brief Capture the stop time of the interval.
+    //!
+    //! This method records the current time as the stop time of the interval for this timer instance.
+    void stop();
 
-            /**
-             * Returns the difference in microseconds between the supplied times t1, and t2. This
-             * calculation is done with respect to the semantic meaning of the times, and thus is
-             * dependent on the platform's representation of the RawTime object.
-             * \return U32 microsecond difference between two supplied values, t1-t2.
-             */
-            static U32 getDiffUsec(const RawTime& t1, const RawTime& t2);
-            /**
-             * Fills the RawTime object supplied with the current raw time in a platform dependent
-             * way.
-             */
-            static void getRawTime(RawTime& time);
-        PRIVATE:
+    //! \brief Get the difference between start and stop times in microseconds.
+    //!
+    //! This method calculates and returns the time difference between the start and stop times
+    //! in microseconds. The start() and stop() methods must be called before calling this method.
+    //!
+    //! \warning Users should prefer the getTimeInterval() method for better error handling.
+    //! \warning This function will return the maximum U32 value if the time difference is too large to fit in a U32.
+    //! \warning This means the largest time difference that can be measured is 2^32 microseconds (about 71 minutes).
+    //!
+    //! \return U32: The time difference in microseconds.
+    U32 getDiffUsec() const;
 
-		    //------------ Internal Member Variables ------------
-            RawTime m_startTime; //!<  Stored start time
-            RawTime m_stopTime; //!<  Stored end time
+    //! \brief Get the time interval between the start and stop times.
+    //!
+    //! This method calculates and returns the time interval between the recorded start and stop times
+    //! as a Fw::TimeInterval object.
+    //!
+    //! \param interval [out] A reference to a Fw::TimeInterval object where the calculated interval will be stored.
+    //! \return bool: True if the interval was successfully calculated, false otherwise.
+    Os::RawTime::Status getTimeInterval(Fw::TimeInterval& interval) const;
 
-            //------------ Disabled (private) Copy Constructor ------------
-            IntervalTimer(IntervalTimer&); //!<  Disabled copy constructor
-    };
-}
+  PRIVATE:
+    RawTime m_startTime;  //!< Stored start time
+    RawTime m_stopTime;   //!< Stored end time
+
+    //! Disabled (private) Copy Constructor
+    IntervalTimer(IntervalTimer&);
+
+};  // class IntervalTimer
+
+}  // namespace Os
 
 #endif
diff --git a/Os/IntervalTimerCommon.cpp b/Os/IntervalTimerCommon.cpp
deleted file mode 100644
index 66b796bb0f..0000000000
--- a/Os/IntervalTimerCommon.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * IntervalTimerCommon.cpp:
- *
- * Contains the common functions for interval timer. This set of functions makes no assumption on
- * the format of the RawTime objects and thus it operates through functions that abstract that
- * implementation away, or it is working on the raw values, as raw values.
- *
- * *Note:* If the RawTime object is using U32 upper to store seconds and U32 lower to store nano
- * seconds, then X86/IntervalTimer.cpp can be used, and the implementer need only fill in the
- * getRawTime function for the specific OS.
- */
-
-#include <Os/IntervalTimer.hpp>
-
-#include <cstring>
-
-namespace Os {
-
-    IntervalTimer::IntervalTimer() {
-        memset(&this->m_startTime,0,sizeof(this->m_startTime));
-        memset(&this->m_stopTime,0,sizeof(this->m_stopTime));
-    }
-
-    IntervalTimer::~IntervalTimer() {}
-
-    void IntervalTimer::start() {
-        getRawTime(this->m_startTime);
-    }
-
-    void IntervalTimer::stop() {
-        getRawTime(this->m_stopTime);
-    }
-
-    U32 IntervalTimer::getDiffUsec() {
-        return getDiffUsec(this->m_stopTime, this->m_startTime);
-    }
-}
diff --git a/Os/Models/CMakeLists.txt b/Os/Models/CMakeLists.txt
index 4cfb1fe0c6..5bba88e86a 100644
--- a/Os/Models/CMakeLists.txt
+++ b/Os/Models/CMakeLists.txt
@@ -13,6 +13,7 @@ set(SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/Directory.fpp"
     "${CMAKE_CURRENT_LIST_DIR}/FileSystem.fpp"
     "${CMAKE_CURRENT_LIST_DIR}/Generic.fpp"
+    "${CMAKE_CURRENT_LIST_DIR}/RawTime.fpp"
     "${CMAKE_CURRENT_LIST_DIR}/Models.cpp"
 )
 register_fprime_module()
diff --git a/Os/Models/Models.cpp b/Os/Models/Models.cpp
index 5748e8c3be..d997a1039d 100644
--- a/Os/Models/Models.cpp
+++ b/Os/Models/Models.cpp
@@ -8,6 +8,7 @@
 #include "Os/Models/MutexStatusEnumAc.hpp"
 #include "Os/Models/DirectoryStatusEnumAc.hpp"
 #include "Os/Models/DirectoryOpenModeEnumAc.hpp"
+#include "Os/Models/RawTimeStatusEnumAc.hpp"
 #include "Os/Models/FileSystemStatusEnumAc.hpp"
 #include "Os/Models/GenericStatusEnumAc.hpp"
 #include "Os/File.hpp"
@@ -16,6 +17,7 @@
 #include "Os/Directory.hpp"
 #include "Os/FileSystem.hpp"
 #include "Os/Os.hpp"
+#include "Os/RawTime.hpp"
 
 // Check consistency of every constant in the Os::File::Status enum
 static_assert(static_cast<FwIndexType>(Os::File::Status::MAX_STATUS) ==
@@ -167,6 +169,17 @@ static_assert(static_cast<Os::FileSystemStatus::T>(Os::FileSystem::Status::NOT_S
 static_assert(static_cast<Os::FileSystemStatus::T>(Os::FileSystem::Status::OTHER_ERROR) == Os::FileSystemStatus::T::OTHER_ERROR,
               "FileSystem status and FPP shadow enum do not match");
 
+
+// Check consistency of every constant in the Os::RawTime::Status enum
+static_assert(static_cast<Os::RawTimeStatus::T>(Os::RawTime::Status::OP_OK) == Os::RawTimeStatus::T::OP_OK,
+              "RawTime status enums do not match");
+static_assert(static_cast<Os::RawTimeStatus::T>(Os::RawTime::Status::OP_OVERFLOW) == Os::RawTimeStatus::T::OP_OVERFLOW,
+              "RawTime status enums do not match");
+static_assert(static_cast<Os::RawTimeStatus::T>(Os::RawTime::Status::INVALID_PARAMS) == Os::RawTimeStatus::T::INVALID_PARAMS,
+              "RawTime status enums do not match");
+static_assert(static_cast<Os::RawTimeStatus::T>(Os::RawTime::Status::OTHER_ERROR) == Os::RawTimeStatus::T::OTHER_ERROR,
+              "Generic status enums do not match");
+
 // Check Generic mappings
 static_assert(static_cast<Os::GenericStatus::T>(Os::Generic::Status::OP_OK) == Os::GenericStatus::T::OP_OK,
               "Generic status enums do not match");
diff --git a/Os/Models/RawTime.fpp b/Os/Models/RawTime.fpp
new file mode 100644
index 0000000000..6fdf2ddc9a
--- /dev/null
+++ b/Os/Models/RawTime.fpp
@@ -0,0 +1,16 @@
+# ======================================================================
+# \title Os/Models/RawTime.fpp
+# \brief FPP type definitions for Os/RawTime.hpp concepts
+# ======================================================================
+
+module Os {
+
+    @ FPP shadow-enum representing Os::RawTime::Status
+    enum RawTimeStatus {
+        OP_OK,          @<  Operation was successful
+        OP_OVERFLOW,    @< Operation result caused an overflow
+        INVALID_PARAMS, @< Parameters invalid for current platform
+        OTHER_ERROR,    @< All other errors
+    }
+
+}
diff --git a/Os/Os.hpp b/Os/Os.hpp
index 0852697485..1794c6deb9 100644
--- a/Os/Os.hpp
+++ b/Os/Os.hpp
@@ -17,6 +17,7 @@ typedef U8 FileSystemHandleStorage[FW_FILESYSTEM_HANDLE_MAX_SIZE];
 typedef U8 ConditionVariableHandleStorage[FW_CONDITION_VARIABLE_HANDLE_MAX_SIZE];
 typedef U8 CpuHandleStorage[FW_CPU_HANDLE_MAX_SIZE];
 typedef U8 MemoryHandleStorage[FW_MEMORY_HANDLE_MAX_SIZE];
+typedef U8 RawTimeHandleStorage[FW_RAW_TIME_HANDLE_MAX_SIZE];
 
 namespace Os {
 namespace Generic {
diff --git a/Os/Posix/CMakeLists.txt b/Os/Posix/CMakeLists.txt
index 1ba304cb28..75e6854824 100644
--- a/Os/Posix/CMakeLists.txt
+++ b/Os/Posix/CMakeLists.txt
@@ -23,12 +23,13 @@ register_fprime_module(Os_Posix_Shared)
 
 # Set up Posix implementations
 add_fprime_supplied_os_module("File;FileSystem;Directory" Posix Os_Posix_Shared)
-add_fprime_supplied_os_module(Console Posix)
-add_fprime_supplied_os_module(Task Posix Os_Posix_Shared Fw_Time)
+add_fprime_supplied_os_module("Console" Posix)
+add_fprime_supplied_os_module("Task" Posix Os_Posix_Shared Fw_Time)
 add_fprime_supplied_os_module("Mutex;ConditionVariable" Posix Os_Posix_Shared)
+add_fprime_supplied_os_module("RawTime" Posix Os_Posix_Shared)
 
 # -----------------------------------------
-### Os/File/Posix Section
+### Os/File/Posix Test Section
 # -----------------------------------------
 set(UT_SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/../test/ut/file/CommonTests.cpp"
@@ -94,3 +95,13 @@ set(UT_SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixConditionTests.cpp"
 )
 add_fprime_os_test(PosixConditionTests "${UT_SOURCE_FILES}" "Os_Mutex\;Os_Mutex_Posix")
+
+# -----------------------------------------
+### Os/RawTime/Posix Test Section
+# -----------------------------------------
+set(UT_SOURCE_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/../test/ut/rawtime/CommonTests.cpp"
+    "${CMAKE_CURRENT_LIST_DIR}/../test/ut/rawtime/RawTimeRules.cpp"
+    "${CMAKE_CURRENT_LIST_DIR}/test/ut/PosixRawTimeTests.cpp"
+)
+add_fprime_os_test(PosixRawTimeTest "${UT_SOURCE_FILES}" "Os_RawTime\;Os_RawTime_Posix")
diff --git a/Os/Posix/DefaultRawTime.cpp b/Os/Posix/DefaultRawTime.cpp
new file mode 100644
index 0000000000..c252478cf0
--- /dev/null
+++ b/Os/Posix/DefaultRawTime.cpp
@@ -0,0 +1,20 @@
+// ======================================================================
+// \title Os/Posix/DefaultRawTime.cpp
+// \brief sets default Os::RawTime Posix implementation via linker
+// ======================================================================
+#include "Os/Posix/RawTime.hpp"
+#include "Os/Delegate.hpp"
+
+namespace Os {
+
+//! \brief get a delegate for RawTimeInterface that intercepts calls for Posix
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+RawTimeInterface *RawTimeInterface::getDelegate(RawTimeHandleStorage& aligned_new_memory, const RawTimeInterface* to_copy) {
+    return Os::Delegate::makeDelegate<RawTimeInterface, Os::Posix::RawTime::PosixRawTime, RawTimeHandleStorage>(
+            aligned_new_memory, to_copy
+    );
+}
+
+}  // namespace Os
diff --git a/Os/Posix/DefaultTask.cpp b/Os/Posix/DefaultTask.cpp
index 8d76033994..986714198b 100644
--- a/Os/Posix/DefaultTask.cpp
+++ b/Os/Posix/DefaultTask.cpp
@@ -8,7 +8,7 @@
 #include "Os/Delegate.hpp"
 
 namespace Os {
-    Os::Task::Status TaskInterface::delay(Fw::Time interval) {
+    Os::Task::Status TaskInterface::delay(Fw::TimeInterval interval) {
         Os::Task::Status task_status = Os::Task::OP_OK;
         timespec sleep_interval;
         sleep_interval.tv_sec = interval.getSeconds();
diff --git a/Os/Posix/IntervalTimer.cpp b/Os/Posix/IntervalTimer.cpp
deleted file mode 100644
index 4893ee15ce..0000000000
--- a/Os/Posix/IntervalTimer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Posix/IntervalTimer.cpp:
- *
- * The Posix implementation of the interval timer shares the same raw setup as other X86
- * implementations. That is: the lower U32 of the RawTime is nano-seconds, and the upper U32 of
- * RawTime object is seconds. Thus only the "getRawTime" function differs from the base X86
- * version of this file.
- */
-#include <Os/IntervalTimer.hpp>
-#include <Fw/Types/Assert.hpp>
-#include <ctime>
-#include <cerrno>
-
-namespace Os {
-    void IntervalTimer::getRawTime(RawTime& time) {
-        timespec t;
-        PlatformIntType status = clock_gettime(CLOCK_REALTIME,&t);
-        FW_ASSERT(status == 0,errno);
-        time.upper = static_cast<U32>(t.tv_sec);
-        time.lower = static_cast<U32>(t.tv_nsec);
-    }
-
-    // Adapted from: http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
-    // should be t1In - t2In
-    U32 IntervalTimer::getDiffUsec(const RawTime& t1In, const RawTime& t2In) {
-        RawTime result = {t1In.upper - t2In.upper, 0};
-        if (t1In.lower < t2In.lower) {
-            result.upper -= 1; // subtract nsec carry to seconds
-            result.lower = t1In.lower + (1000000000 - t2In.lower);
-        } else {
-            result.lower = t1In.lower - t2In.lower;
-        }
-        return (result.upper * 1000000) + (result.lower / 1000);
-    }
-}
diff --git a/Os/Posix/RawTime.cpp b/Os/Posix/RawTime.cpp
new file mode 100644
index 0000000000..7c474ab183
--- /dev/null
+++ b/Os/Posix/RawTime.cpp
@@ -0,0 +1,80 @@
+// ======================================================================
+// \title Os/Posix/RawTime.cpp
+// \brief Posix implementation for Os::RawTime
+// ======================================================================
+#include <Fw/Logger/Logger.hpp>
+#include <Fw/Types/Assert.hpp>
+#include <Os/Posix/RawTime.hpp>
+#include <Os/Posix/error.hpp>
+#include <cerrno>
+
+namespace Os {
+namespace Posix {
+namespace RawTime {
+
+PosixRawTime::Status PosixRawTime::now() {
+    PlatformIntType status = clock_gettime(CLOCK_REALTIME, &this->m_handle.m_timespec);
+    if (status != 0) {
+        return errno_to_rawtime_status(errno);
+    }
+    return Status::OP_OK;
+}
+
+PosixRawTime::Status PosixRawTime::getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const {
+    timespec t1 = this->m_handle.m_timespec;
+    timespec t2 = static_cast<PosixRawTimeHandle*>(const_cast<Os::RawTime&>(other).getHandle())->m_timespec;
+
+    // Guarantee t1 is the later time to make the calculation below easier
+    if ((t1.tv_sec < t2.tv_sec) or (t1.tv_sec == t2.tv_sec and t1.tv_nsec < t2.tv_nsec)) {
+        t1 = static_cast<PosixRawTimeHandle*>(const_cast<Os::RawTime&>(other).getHandle())->m_timespec;
+        t2 = this->m_handle.m_timespec;
+    }
+
+    // Here we have guaranteed that t1 > t2, so there is no underflow
+    U32 sec = static_cast<U32>(t1.tv_sec - t2.tv_sec);
+    U32 nanosec = 0;
+    if (t1.tv_nsec < t2.tv_nsec) {
+        sec -= 1;  // subtract nsec carry to seconds
+        nanosec = static_cast<U32>(t1.tv_nsec + (1000000000 - t2.tv_nsec));
+    } else {
+        nanosec = static_cast<U32>(t1.tv_nsec - t2.tv_nsec);
+    }
+
+    interval.set(sec, nanosec / 1000);
+    return Status::OP_OK;
+}
+
+Fw::SerializeStatus PosixRawTime::serialize(Fw::SerializeBufferBase& buffer) const {
+    static_assert(PosixRawTime::SERIALIZED_SIZE >= 2 * sizeof(U32),
+                  "PosixRawTime implementation requires at least 2*sizeof(U32) serialization size");
+    Fw::SerializeStatus status = buffer.serialize(static_cast<U32>(this->m_handle.m_timespec.tv_sec));
+    if (status != Fw::SerializeStatus::FW_SERIALIZE_OK) {
+        return status;
+    }
+    return buffer.serialize(static_cast<U32>(this->m_handle.m_timespec.tv_nsec));
+}
+
+Fw::SerializeStatus PosixRawTime::deserialize(Fw::SerializeBufferBase& buffer) {
+    static_assert(PosixRawTime::SERIALIZED_SIZE >= 2 * sizeof(U32),
+                  "PosixRawTime implementation requires at least 2*sizeof(U32) serialization size");
+    U32 sec = 0;
+    U32 nsec = 0;
+    Fw::SerializeStatus status = buffer.deserialize(sec);
+    if (status != Fw::SerializeStatus::FW_SERIALIZE_OK) {
+        return status;
+    }
+    status = buffer.deserialize(nsec);
+    if (status != Fw::SerializeStatus::FW_SERIALIZE_OK) {
+        return status;
+    }
+    this->m_handle.m_timespec = {sec, nsec};
+    return Fw::SerializeStatus::FW_SERIALIZE_OK;
+}
+
+RawTimeHandle* PosixRawTime::getHandle() {
+    return &this->m_handle;
+}
+
+}  // namespace RawTime
+}  // namespace Posix
+}  // namespace Os
diff --git a/Os/Posix/RawTime.hpp b/Os/Posix/RawTime.hpp
new file mode 100644
index 0000000000..5bd421e866
--- /dev/null
+++ b/Os/Posix/RawTime.hpp
@@ -0,0 +1,95 @@
+// ======================================================================
+// \title Os/Posix/RawTime.hpp
+// \brief Posix definitions for Os::RawTime
+// ======================================================================
+#ifndef OS_POSIX_RAWTIME_HPP
+#define OS_POSIX_RAWTIME_HPP
+
+#include <ctime>
+#include <Os/RawTime.hpp>
+
+
+namespace Os {
+namespace Posix {
+namespace RawTime {
+
+struct PosixRawTimeHandle : public RawTimeHandle {
+    timespec m_timespec = {0, 0};
+};
+
+//! \brief Posix implementation of Os::RawTime
+//!
+//! Posix implementation of `RawTimeInterface` for use as a delegate class handling error-only file operations.
+//!
+class PosixRawTime : public RawTimeInterface {
+  public:
+
+    //! \brief constructor
+    PosixRawTime() = default;
+
+    //! \brief destructor
+    ~PosixRawTime() override = default;
+
+    //! \brief return the underlying RawTime handle (implementation specific)
+    //! \return internal RawTime handle representation
+    RawTimeHandle* getHandle() override;
+
+    // ------------------------------------------------------------
+    // Implementation-specific RawTime overrides
+    // ------------------------------------------------------------
+    //! \brief Get the current time.
+    //!
+    //! This function retrieves the current time and stores it in the RawTime object.
+    //! Each implementation should define its RawTimeHandle type for storing the time.
+    //!
+    //! \return Status indicating the result of the operation.
+    Status now() override;
+
+    //! \brief Calculate the time interval between this and another raw time.
+    //!
+    //! This function calculates the time interval between the current raw time and another
+    //! specified raw time. The result is stored in the provided (output) interval object.
+    //!
+    //! \param other The other RawTimeHandle to compare against.
+    //! \param interval Output parameter to store the calculated time interval.
+    //! \return Status indicating the result of the operation.
+    Status getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const override;
+
+    //! \brief Serialize the contents of the RawTimeInterface object into a buffer.
+    //!
+    //! This function serializes the contents of the RawTimeInterface object into the provided
+    //! buffer. 
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to serialize the contents into.
+    //! \return Fw::SerializeStatus indicating the result of the serialization.
+    Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const override;
+
+    //! \brief Deserialize the contents of the RawTimeInterface object from a buffer.
+    //!
+    //! This function deserializes the contents of the RawTimeInterface object from the provided
+    //! buffer.
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to deserialize the contents from.
+    //! \return Fw::SerializeStatus indicating the result of the deserialization.
+    Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer) override;
+
+
+  private:
+    //! Handle for PosixRawTime
+    PosixRawTimeHandle m_handle;
+};
+
+}  // namespace RawTime
+}  // namespace Posix
+}  // namespace Os
+#endif  // OS_POSIX_RAWTIME_HPP
diff --git a/Os/Posix/error.cpp b/Os/Posix/error.cpp
index 9932b6be22..fd7f1d947d 100644
--- a/Os/Posix/error.cpp
+++ b/Os/Posix/error.cpp
@@ -128,6 +128,22 @@ Directory::Status errno_to_directory_status(PlatformIntType errno_input) {
     return status;
 }
 
+RawTime::Status errno_to_rawtime_status(PlatformIntType errno_input) {
+    RawTime::Status status = RawTime::Status::OP_OK;
+    switch (errno_input) {
+        case 0:
+            status = RawTime::Status::OP_OK;
+            break;
+        case EINVAL:
+            status = RawTime::Status::INVALID_PARAMS;
+            break;
+        default:
+            status = RawTime::Status::OTHER_ERROR;
+            break;
+    }
+    return status;
+}
+
 Task::Status posix_status_to_task_status(PlatformIntType posix_status) {
     Task::Status status = Task::Status::OP_OK;
     switch (posix_status) {
diff --git a/Os/Posix/error.hpp b/Os/Posix/error.hpp
index afacabaa8f..b276534604 100644
--- a/Os/Posix/error.hpp
+++ b/Os/Posix/error.hpp
@@ -8,6 +8,7 @@
 #include "Os/Task.hpp"
 #include "Os/FileSystem.hpp"
 #include "Os/Directory.hpp"
+#include "Os/RawTime.hpp"
 
 namespace Os {
 namespace Posix {
@@ -20,16 +21,22 @@ Os::File::Status errno_to_file_status(PlatformIntType errno_input);
 
 //! Convert an errno representation of an error to the Os::FileSystem::Status representation.
 //! \param errno_input: errno representation of the error
-//! \return: Os::File::Status representation of the error
+//! \return: Os::FileSystem::Status representation of the error
 //!
 Os::FileSystem::Status errno_to_filesystem_status(PlatformIntType errno_input);
 
 //! Convert an errno representation of an error to the Os::FileSystem::Status representation.
 //! \param errno_input: errno representation of the error
-//! \return: Os::File::Status representation of the error
+//! \return: Os::Directory::Status representation of the error
 //!
 Os::Directory::Status errno_to_directory_status(PlatformIntType errno_input);
 
+//! Convert an errno representation of an error to the Os::RawTime::Status representation.
+//! \param errno_input: errno representation of the error
+//! \return: Os::RawTime::Status representation of the error
+//!
+Os::RawTime::Status errno_to_rawtime_status(PlatformIntType errno_input);
+
 //! Convert an posix task representation of an error to the Os::Task::Status representation.
 //! \param posix_status: errno representation of the error
 //! \return: Os::Task::Status representation of the error
diff --git a/Os/RawTime.cpp b/Os/RawTime.cpp
new file mode 100644
index 0000000000..f0ef7a51c5
--- /dev/null
+++ b/Os/RawTime.cpp
@@ -0,0 +1,80 @@
+// ======================================================================
+// \title Os/RawTime.cpp
+// \brief common function implementation for Os::RawTime
+// ======================================================================
+#include <Fw/Types/Assert.hpp>
+#include <Os/RawTime.hpp>
+
+namespace Os {
+
+RawTime::RawTime() : m_handle_storage(), m_delegate(*RawTimeInterface::getDelegate(m_handle_storage)) {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<RawTimeInterface*>(&this->m_handle_storage[0]));
+}
+
+RawTime::~RawTime() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<RawTimeInterface*>(&this->m_handle_storage[0]));
+    m_delegate.~RawTimeInterface();
+}
+
+RawTime::RawTime(const RawTime& other)
+    : m_handle_storage(), m_delegate(*RawTimeInterface::getDelegate(m_handle_storage, &other.m_delegate)) {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<RawTimeInterface*>(&this->m_handle_storage[0]));
+}
+
+RawTime& RawTime::operator=(const RawTime& other) {
+    if (this != &other) {
+        this->m_delegate = *RawTimeInterface::getDelegate(m_handle_storage, &other.m_delegate);
+    }
+    return *this;
+}
+
+RawTimeHandle* RawTime::getHandle() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<RawTimeInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.getHandle();
+}
+
+RawTime::Status RawTime::now() {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<RawTimeInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.now();
+}
+
+RawTime::Status RawTime::getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& result) const {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<const RawTimeInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.getTimeInterval(other, result);
+}
+
+Fw::SerializeStatus RawTime::serialize(Fw::SerializeBufferBase& buffer) const {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<const RawTimeInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.serialize(buffer);
+}
+
+Fw::SerializeStatus RawTime::deserialize(Fw::SerializeBufferBase& buffer) {
+    FW_ASSERT(&this->m_delegate == reinterpret_cast<const RawTimeInterface*>(&this->m_handle_storage[0]));
+    return this->m_delegate.deserialize(buffer);
+}
+
+RawTime::Status RawTime::getDiffUsec(const RawTime& other, U32& result) const {
+    Fw::TimeInterval interval;
+    Status status = this->getTimeInterval(other, interval);
+    if (status != Status::OP_OK) {
+        return status;
+    }
+
+    // Check overflows in computation
+    U32 seconds = interval.getSeconds();
+    U32 useconds = interval.getUSeconds();
+    if (seconds > (std::numeric_limits<U32>::max() / 1000000)) {
+        result = std::numeric_limits<U32>::max();
+        return Status::OP_OVERFLOW;
+    }
+    U32 secToUsec = seconds * 1000000;
+    if (secToUsec > (std::numeric_limits<U32>::max() - useconds)) {
+        result = std::numeric_limits<U32>::max();
+        return Status::OP_OVERFLOW;
+    }
+    // No overflow, we can safely add values to get total microseconds
+    result = secToUsec + useconds;
+    return status;
+}
+
+}  // namespace Os
diff --git a/Os/RawTime.hpp b/Os/RawTime.hpp
new file mode 100644
index 0000000000..569c5f1b47
--- /dev/null
+++ b/Os/RawTime.hpp
@@ -0,0 +1,193 @@
+// ======================================================================
+// \title Os/RawTime.hpp
+// \brief common definitions for Os::RawTime
+// ======================================================================
+#ifndef OS_RAWTIME_HPP_
+#define OS_RAWTIME_HPP_
+
+#include <FpConfig.hpp>
+#include <Fw/Types/Serializable.hpp>
+#include <Fw/Time/TimeInterval.hpp>
+#include <Os/Os.hpp>
+
+
+namespace Os {
+
+struct RawTimeHandle {};
+
+class RawTime; // Forward declaration
+
+class RawTimeInterface : public Fw::Serializable {
+  
+  public:
+
+    // Serialization size for RawTime objects, configured in config/FpConfig.h
+    static const FwSizeType SERIALIZED_SIZE = FW_RAW_TIME_SERIALIZATION_MAX_SIZE;
+
+    enum Status {
+      OP_OK, //!<  Operation was successful
+      OP_OVERFLOW, //!< Operation result caused an overflow
+      INVALID_PARAMS, //!< Parameters invalid for current platform
+      OTHER_ERROR //!< All other errors
+    };
+
+    //! \brief default constructor
+    RawTimeInterface() = default;
+
+    //! \brief default virtual destructor
+    virtual ~RawTimeInterface() = default;
+
+    //! \brief return the underlying RawTime handle (implementation specific)
+    //! \return internal RawTime handle representation
+    virtual RawTimeHandle* getHandle() = 0;
+
+    //! \brief provide a pointer to a RawTime delegate object
+    static RawTimeInterface* getDelegate(RawTimeHandleStorage& aligned_new_memory, const RawTimeInterface* to_copy=nullptr);
+
+    // ------------------------------------------------------------------
+    // RawTime operations to be implemented by an OSAL implementation
+    // ------------------------------------------------------------------
+
+    //! \brief Get the current time.
+    //!
+    //! This function retrieves the current time and stores it in the RawTime object.
+    //! Each implementation should define its RawTimeHandle type for storing the time.
+    //!
+    //! \return Status indicating the result of the operation.
+    virtual Status now() = 0;
+
+    //! \brief Calculate the time interval between this and another raw time.
+    //!
+    //! This function calculates the time interval between the current raw time and another
+    //! specified raw time. The result is stored in the provided (output) interval object.
+    //!
+    //! \param other The other RawTimeHandle to compare against.
+    //! \param interval Output parameter to store the calculated time interval.
+    //! \return Status indicating the result of the operation.
+    virtual Status getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const = 0;
+
+    //! \brief Serialize the contents of the RawTimeInterface object into a buffer.
+    //!
+    //! This function serializes the contents of the RawTimeInterface object into the provided
+    //! buffer. 
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to serialize the contents into.
+    //! \return Fw::SerializeStatus indicating the result of the serialization.
+    virtual Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const = 0;
+
+    //! \brief Deserialize the contents of the RawTimeInterface object from a buffer.
+    //!
+    //! This function deserializes the contents of the RawTimeInterface object from the provided
+    //! buffer.
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to deserialize the contents from.
+    //! \return Fw::SerializeStatus indicating the result of the deserialization.
+    virtual Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer) = 0;
+
+};
+
+class RawTime final : public RawTimeInterface {
+  public:
+    RawTime();         //!<  Constructor
+    ~RawTime() final;  //!<  Destructor
+    
+    //! \brief copy constructor that copies the internal representation
+    RawTime(const RawTime& other);
+
+    //! \brief assignment operator that copies the internal representation
+    RawTime& operator=(const RawTime& other);
+
+    //! \brief return the underlying RawTime handle (implementation specific)
+    //! \return internal RawTime handle representation
+    RawTimeHandle* getHandle() override;
+
+    // ------------------------------------------------------------
+    // Implementation-specific RawTime member functions
+    // ------------------------------------------------------------
+
+    //! \brief Get the current time.
+    //!
+    //! This function retrieves the current time and stores it in the RawTime object.
+    //! Each implementation should define its RawTimeHandle type for storing the time.
+    //!
+    //! \return Status indicating the result of the operation.
+    Status now() override;
+
+    //! \brief Calculate the time interval between this and another raw time.
+    //!
+    //! This function calculates the time interval between the current raw time and another
+    //! specified raw time. The result is stored in the provided (output) interval object.
+    //!
+    //! \param other The other RawTimeHandle to compare against.
+    //! \param interval Output parameter to store the calculated time interval.
+    //! \return Status indicating the result of the operation.
+    Status getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const override;
+
+    //! \brief Serialize the contents of the RawTimeInterface object into a buffer.
+    //!
+    //! This function serializes the contents of the RawTimeInterface object into the provided
+    //! buffer. 
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to serialize the contents into.
+    //! \return Fw::SerializeStatus indicating the result of the serialization.
+    Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const override;
+
+    //! \brief Deserialize the contents of the RawTimeInterface object from a buffer.
+    //!
+    //! This function deserializes the contents of the RawTimeInterface object from the provided
+    //! buffer.
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to deserialize the contents from.
+    //! \return Fw::SerializeStatus indicating the result of the deserialization.
+    Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer) override;
+
+    // ------------------------------------------------------------
+    // Common functions built on top of OS-specific functions
+    // ------------------------------------------------------------
+
+    //! \brief Calculate the difference in microseconds between two RawTime objects.
+    //!
+    //! This function calculates the difference in microseconds between the current RawTime object
+    //! and another RawTime object provided as a parameter.
+    //!
+    //! \warning This function will return Status::OP_OVERFLOW if the time difference is too large to fit in a U32.
+    //! \warning This means the largest time difference that can be measured is 2^32 microseconds (about 71 minutes).
+    //! \warning Users should prefer getTimeInterval() for larger intervals.
+    //!
+    //! \param other The other RawTime object to compare against.
+    //! \param result A reference to a U32 variable where the result will be stored.
+    //! \return Status indicating the result of the operation.
+    Status getDiffUsec(const RawTime& other, U32& result) const;
+
+
+  private:
+    // This section is used to store the implementation-defined RawTime handle. To Os::RawTime and fprime, this type is
+    // opaque and thus normal allocation cannot be done. Instead, we allow the implementor to store the handle in
+    // the byte-array here and set `m_handle_storage` to that address for storage.
+    //
+    alignas(FW_HANDLE_ALIGNMENT) RawTimeHandleStorage m_handle_storage;  //!< RawTime handle storage
+    RawTimeInterface& m_delegate;                                   //!< Delegate for the real implementation
+};
+}  // namespace Os
+
+#endif
diff --git a/Os/Stub/CMakeLists.txt b/Os/Stub/CMakeLists.txt
index a549b2a122..568884e213 100644
--- a/Os/Stub/CMakeLists.txt
+++ b/Os/Stub/CMakeLists.txt
@@ -14,6 +14,7 @@ add_fprime_supplied_os_module("Mutex;ConditionVariable" Stub)
 add_fprime_supplied_os_module(Cpu Stub)
 add_fprime_supplied_os_module(Memory Stub)
 add_fprime_supplied_os_module(Queue Stub)
+add_fprime_supplied_os_module(RawTime Stub)
 
 # Remainder of file is specific to UTs
 if (NOT BUILD_TESTING)
@@ -32,4 +33,5 @@ choose_fprime_implementation(Os/File Os_File_Stub)
 choose_fprime_implementation(Os/Console Os_Console_Stub)
 choose_fprime_implementation(Os/Cpu Os_Cpu_Stub)
 choose_fprime_implementation(Os/Memory Os_Memory_Stub)
+choose_fprime_implementation(Os/RawTime Os_RawTime_Stub)
 register_fprime_ut(StubTest)
diff --git a/Os/Stub/DefaultRawTime.cpp b/Os/Stub/DefaultRawTime.cpp
new file mode 100644
index 0000000000..c1df6c51ee
--- /dev/null
+++ b/Os/Stub/DefaultRawTime.cpp
@@ -0,0 +1,20 @@
+// ======================================================================
+// \title Os/Stub/DefaultRawTime.cpp
+// \brief sets default Os::RawTime to no-op stub implementation via linker
+// ======================================================================
+#include "Os/Stub/RawTime.hpp"
+#include "Os/Delegate.hpp"
+
+namespace Os {
+
+//! \brief get a delegate for RawTimeInterface that intercepts calls for stub RawTime usage
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+RawTimeInterface *RawTimeInterface::getDelegate(RawTimeHandleStorage& aligned_placement_new_memory, const RawTimeInterface* to_copy) {
+    return Os::Delegate::makeDelegate<RawTimeInterface, Os::Stub::RawTime::StubRawTime, RawTimeHandleStorage>(
+            aligned_placement_new_memory, to_copy
+    );
+}
+
+} // namespace Os
diff --git a/Os/Stub/DefaultTask.cpp b/Os/Stub/DefaultTask.cpp
index d26472307e..31c0922228 100644
--- a/Os/Stub/DefaultTask.cpp
+++ b/Os/Stub/DefaultTask.cpp
@@ -9,7 +9,7 @@
 #include <sys/time.h>
 
 namespace Os {
-    Os::Task::Status TaskInterface::delay(Fw::Time interval) {
+    Os::Task::Status TaskInterface::delay(Fw::TimeInterval interval) {
         FW_ASSERT(0);
         return Os::Task::Status::UNKNOWN_ERROR;
     }
diff --git a/Os/Stub/RawTime.cpp b/Os/Stub/RawTime.cpp
new file mode 100644
index 0000000000..7aad1cc48e
--- /dev/null
+++ b/Os/Stub/RawTime.cpp
@@ -0,0 +1,33 @@
+// ======================================================================
+// \title Os/Stub/RawTime.cpp
+// \brief stub implementation for Os::RawTime
+// ======================================================================
+#include "Os/Stub/RawTime.hpp"
+
+namespace Os {
+namespace Stub {
+namespace RawTime {
+
+RawTimeHandle* StubRawTime::getHandle() {
+    return &this->m_handle;
+}
+
+StubRawTime::Status StubRawTime::now() {
+    return Status::OP_OK;
+}
+
+StubRawTime::Status StubRawTime::getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const {
+    interval.set(0, 0);
+    return Status::OP_OK;
+}
+
+Fw::SerializeStatus StubRawTime::serialize(Fw::SerializeBufferBase& buffer) const {
+    return Fw::FW_SERIALIZE_OK;
+}
+
+Fw::SerializeStatus StubRawTime::deserialize(Fw::SerializeBufferBase& buffer) {
+    return Fw::FW_SERIALIZE_OK;
+}
+}  // namespace RawTime
+}  // namespace Stub
+}  // namespace Os
diff --git a/Os/Stub/RawTime.hpp b/Os/Stub/RawTime.hpp
new file mode 100644
index 0000000000..05132d4ff5
--- /dev/null
+++ b/Os/Stub/RawTime.hpp
@@ -0,0 +1,91 @@
+// ======================================================================
+// \title Os/Stub/RawTime.hpp
+// \brief stub definitions for Os::RawTime
+// ======================================================================
+#ifndef OS_STUB_RAWTIME_HPP
+#define OS_STUB_RAWTIME_HPP
+
+#include "Os/RawTime.hpp"
+
+namespace Os {
+namespace Stub {
+namespace RawTime {
+
+struct StubRawTimeHandle : public RawTimeHandle {};
+
+//! \brief stub implementation of Os::RawTime
+//!
+//! Stub implementation of `RawTimeInterface`.
+//!
+class StubRawTime : public RawTimeInterface {
+  public:
+    //! \brief constructor
+    //!
+    StubRawTime() = default;
+
+    //! \brief destructor
+    //!
+    ~StubRawTime() override = default;
+
+    //! \brief return the underlying RawTime handle (implementation specific)
+    //! \return internal RawTime handle representation
+    RawTimeHandle* getHandle() override;
+
+    // ------------------------------------------------------------
+    // Implementation-specific RawTime overrides
+    // ------------------------------------------------------------
+    //! \brief Get the current time.
+    //!
+    //! This function retrieves the current time and stores it in the RawTime object.
+    //! Each implementation should define its RawTimeHandle type for storing the time.
+    //!
+    //! \return Status indicating the result of the operation.
+    Status now() override;
+
+    //! \brief Calculate the time interval between this and another raw time.
+    //!
+    //! This function calculates the time interval between the current raw time and another
+    //! specified raw time. The result is stored in the provided (output) interval object.
+    //!
+    //! \param other The other RawTimeHandle to compare against.
+    //! \param interval Output parameter to store the calculated time interval.
+    //! \return Status indicating the result of the operation.
+    Status getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const override;
+
+    //! \brief Serialize the contents of the RawTimeInterface object into a buffer.
+    //!
+    //! This function serializes the contents of the RawTimeInterface object into the provided
+    //! buffer. 
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to serialize the contents into.
+    //! \return Fw::SerializeStatus indicating the result of the serialization.
+    Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const override;
+
+    //! \brief Deserialize the contents of the RawTimeInterface object from a buffer.
+    //!
+    //! This function deserializes the contents of the RawTimeInterface object from the provided
+    //! buffer.
+    //!
+    //! \note The serialization must fit within `FW_RAW_TIME_SERIALIZATION_MAX_SIZE` bytes. This value is
+    //! defined in FpConfig.h. For example, Posix systems use a pair of U32 (sec, nanosec) and can therefore
+    //! serialize in 8 bytes. Should an OSAL implementation require more than this, the project must increase 
+    //! that value in its config/ folder.
+    //!
+    //! \param buffer The buffer to deserialize the contents from.
+    //! \return Fw::SerializeStatus indicating the result of the deserialization.
+    Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer) override;
+
+  private:
+    //! Handle for StubRawTime
+    StubRawTimeHandle m_handle;
+};
+
+} // namespace RawTime
+} // namespace Stub
+} // namespace Os
+#endif // OS_STUB_RAWTIME_HPP
diff --git a/Os/Task.hpp b/Os/Task.hpp
index eb2b610a7f..03a2b4bb56 100644
--- a/Os/Task.hpp
+++ b/Os/Task.hpp
@@ -6,7 +6,7 @@
 #define Os_Task_hpp_
 
 #include <FpConfig.hpp>
-#include <Fw/Time/Time.hpp>
+#include <Fw/Time/TimeInterval.hpp>
 #include <Fw/Types/Serializable.hpp>
 #include <Os/Os.hpp>
 #include <Os/TaskString.hpp>
@@ -112,7 +112,7 @@ namespace Os {
             //!
             //! \param interval: delay time
             //! \return status of the delay
-            static Status delay(Fw::Time interval);
+            static Status delay(Fw::TimeInterval interval);
 
             //! \brief provide a pointer to a task delegate object
             //!
diff --git a/Os/Types.fpp b/Os/Types.fpp
new file mode 100644
index 0000000000..67cd58e64e
--- /dev/null
+++ b/Os/Types.fpp
@@ -0,0 +1,3 @@
+module Os {
+    type RawTime
+}
diff --git a/Os/X86/IntervalTimer.cpp b/Os/X86/IntervalTimer.cpp
deleted file mode 100644
index f27f1d2246..0000000000
--- a/Os/X86/IntervalTimer.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * X86/IntervalTimer.cpp:
- *
- * This file supports the core functions of the IntervalTimer for X86 implementations that support
- * the following specification for the "RawTime" object:
- *
- * RawTime.lower = nanoseconds of time
- * RawTime.upper = seconds of time.
- *
- * Any implementation that fills "RawTime" via this specification can use these basic implementations.
- *
- * Note: this file is cloned from the original Linux implementation.
- */
-#include <Os/IntervalTimer.hpp>
-#include <Fw/Types/Assert.hpp>
-#include <cstring>
-
-namespace Os {
-
-    // Adapted from: http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
-    // should be t1In - t2In
-    U32 IntervalTimer::getDiffUsec(const RawTime& t1In, const RawTime& t2In) {
-        RawTime result = {t1In.upper - t2In.upper, 0};
-        if (t1In.lower < t2In.lower) {
-            result.upper -= 1; // subtract nsec carry to seconds
-            result.lower = t1In.lower + (1000000000 - t2In.lower);
-        } else {
-            result.lower = t1In.lower - t2In.lower;
-        }
-        return (result.upper * 1000000) + (result.lower / 1000);
-    }
-}
diff --git a/RPI/Main.cpp b/RPI/Main.cpp
index 131ae7672c..017487e54c 100644
--- a/RPI/Main.cpp
+++ b/RPI/Main.cpp
@@ -61,7 +61,7 @@ int main(int argc, char* argv[]) {
     // Time to exit the program.
     // Give time for threads to exit.
     (void) printf("Waiting for threads...\n");
-    Os::Task::delay(Fw::Time(1, 0));
+    Os::Task::delay(Fw::TimeInterval(1, 0));
 
     (void) printf("Exiting...\n");
 
diff --git a/Ref/Main.cpp b/Ref/Main.cpp
index f46062688b..69fa163389 100644
--- a/Ref/Main.cpp
+++ b/Ref/Main.cpp
@@ -92,7 +92,7 @@ int main(int argc, char* argv[]) {
 
     // Setup, cycle, and teardown topology
     Ref::setupTopology(inputs);
-    Ref::startSimulatedCycle(Fw::Time(1, 0));  // Program loop cycling rate groups at 1Hz
+    Ref::startSimulatedCycle(Fw::TimeInterval(1, 0));  // Program loop cycling rate groups at 1Hz
     Ref::teardownTopology(inputs);
     (void)printf("Exiting...\n");
     return 0;
diff --git a/Ref/Top/RefTopology.cpp b/Ref/Top/RefTopology.cpp
index 07d6434981..f1fae1c878 100644
--- a/Ref/Top/RefTopology.cpp
+++ b/Ref/Top/RefTopology.cpp
@@ -173,7 +173,7 @@ void setupTopology(const TopologyState& state) {
 Os::Mutex cycleLock;
 volatile bool cycleFlag = true;
 
-void startSimulatedCycle(Fw::Time interval) {
+void startSimulatedCycle(Fw::TimeInterval interval) {
     cycleLock.lock();
     bool cycling = cycleFlag;
     cycleLock.unLock();
diff --git a/Ref/Top/RefTopology.hpp b/Ref/Top/RefTopology.hpp
index 7c7d676348..aeef3105a9 100644
--- a/Ref/Top/RefTopology.hpp
+++ b/Ref/Top/RefTopology.hpp
@@ -69,7 +69,7 @@ void teardownTopology(const TopologyState& state);
  *
  * The reference topology does not have a true 1Hz input clock for the rate group driver because it is designed to
  * operate across various computing endpoints (e.g. laptops) where a clear 1Hz source may not be easily and generically
- * achieved. This function mimics the cycling via a Task::delay(Fw::Time()) loop that manually invokes the ISR call
+ * achieved. This function mimics the cycling via a Task::delay(Fw::TimeInterval()) loop that manually invokes the ISR call
  * to the example block driver.
  *
  * This loop is stopped via a startSimulatedCycle call.
@@ -77,7 +77,7 @@ void teardownTopology(const TopologyState& state);
  * Note: projects should replace this with a component that produces an output port call at the appropriate frequency.
  *
  */
-void startSimulatedCycle(Fw::Time interval);
+void startSimulatedCycle(Fw::TimeInterval interval);
 
 /**
  * \brief stop the simulated cycle started by startSimulatedCycle
diff --git a/Svc/ActiveRateGroup/ActiveRateGroup.cpp b/Svc/ActiveRateGroup/ActiveRateGroup.cpp
index 07c7b9dcf5..8ccfa6433c 100644
--- a/Svc/ActiveRateGroup/ActiveRateGroup.cpp
+++ b/Svc/ActiveRateGroup/ActiveRateGroup.cpp
@@ -52,12 +52,12 @@ namespace Svc {
         this->log_DIAGNOSTIC_RateGroupStarted();
     }
 
-    void ActiveRateGroup::CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart) {
+    void ActiveRateGroup::CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart) {
 
         // Make sure it's been configured
         FW_ASSERT(this->m_numContexts);
 
-        TimerVal end;
+        Os::RawTime endTime;
 
         this->m_cycleStarted = false;
 
@@ -68,15 +68,18 @@ namespace Svc {
             }
         }
 
-        // grab timer for end of cycle
-        end.take();
+        // grab timer for endTime of cycle
+        endTime.now();
 
         // get rate group execution time
-        U32 cycle_time = end.diffUSec(cycleStart);
+        U32 cycleTime;
+        // Cast to void as the only possible error is overflow, which we can't handle other
+        // than capping cycleTime to max value of U32 (which is done in getDiffUsec anyways)
+        (void) endTime.getDiffUsec(cycleStart, cycleTime);
 
         // check to see if the time has exceeded the previous maximum
-        if (cycle_time > this->m_maxTime) {
-            this->m_maxTime = cycle_time;
+        if (cycleTime > this->m_maxTime) {
+            this->m_maxTime = cycleTime;
         }
 
         // update cycle telemetry
@@ -103,7 +106,7 @@ namespace Svc {
 
     }
 
-    void ActiveRateGroup::CycleIn_preMsgHook(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart) {
+    void ActiveRateGroup::CycleIn_preMsgHook(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart) {
         // set flag to indicate cycle has started. Check in thread for overflow.
         this->m_cycleStarted = true;
     }
diff --git a/Svc/ActiveRateGroup/ActiveRateGroup.hpp b/Svc/ActiveRateGroup/ActiveRateGroup.hpp
index 979a17643e..6551fad08e 100644
--- a/Svc/ActiveRateGroup/ActiveRateGroup.hpp
+++ b/Svc/ActiveRateGroup/ActiveRateGroup.hpp
@@ -69,7 +69,7 @@ namespace Svc {
             //!  \param portNum incoming port call. For this class, should always be zero
             //!  \param cycleStart value stored by the cycle driver, used to compute execution time.
 
-            void CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart);
+            void CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart);
 
             //!  \brief Input cycle port pre message hook
             //!
@@ -79,7 +79,7 @@ namespace Svc {
             //!  \param portNum incoming port call. For this class, should always be zero
             //!  \param cycleStart value stored by the cycle driver, used to compute execution time.
 
-            void CycleIn_preMsgHook(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart); //!< CycleIn pre-message hook
+            void CycleIn_preMsgHook(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart); //!< CycleIn pre-message hook
 
             //!  \brief Input ping port handler
             //!
diff --git a/Svc/Cycle/CMakeLists.txt b/Svc/Cycle/CMakeLists.txt
index 32dcbfa8c6..a21a23249c 100644
--- a/Svc/Cycle/CMakeLists.txt
+++ b/Svc/Cycle/CMakeLists.txt
@@ -6,9 +6,9 @@
 #
 # Note: using PROJECT_NAME as EXECUTABLE_NAME
 ####
+
 set(SOURCE_FILES
   "${CMAKE_CURRENT_LIST_DIR}/Cycle.fpp"
-  "${CMAKE_CURRENT_LIST_DIR}/TimerVal.cpp"
 )
 set(MOD_DEPS
     Os
diff --git a/Svc/Cycle/Cycle.fpp b/Svc/Cycle/Cycle.fpp
index b2bcabccd8..7c49c3718e 100644
--- a/Svc/Cycle/Cycle.fpp
+++ b/Svc/Cycle/Cycle.fpp
@@ -1,10 +1,8 @@
 module Svc {
 
-  type TimerVal
-
   @ Time cycle Port with timestamp argument
   port Cycle(
-              ref cycleStart: Svc.TimerVal @< Cycle start timer value
+              ref cycleStart: Os.RawTime @< Cycle start timestamp
             )
 
 }
diff --git a/Svc/Cycle/TimerVal.cpp b/Svc/Cycle/TimerVal.cpp
deleted file mode 100644
index e8a1765688..0000000000
--- a/Svc/Cycle/TimerVal.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * TimerVal.cpp
- *
- *  Created on: Aug 5, 2015
- *      Author: timothycanham
- */
-
-#include <Svc/Cycle/TimerVal.hpp>
-#include <cstring>
-
-namespace Svc {
-
-    TimerVal::TimerVal() : Fw::Serializable() {
-        this->m_timerVal.upper = 0;
-        this->m_timerVal.lower = 0;
-    }
-
-    TimerVal::TimerVal(U32 upper, U32 lower)  {
-        this->m_timerVal.upper = upper;
-        this->m_timerVal.lower = lower;
-    }
-
-    TimerVal::TimerVal(const TimerVal& other) : Fw::Serializable() {
-        this->m_timerVal.upper = other.m_timerVal.upper;
-        this->m_timerVal.lower = other.m_timerVal.lower;
-    }
-
-    TimerVal& TimerVal::operator=(const TimerVal& other) {
-        this->m_timerVal.upper = other.m_timerVal.upper;
-        this->m_timerVal.lower = other.m_timerVal.lower;
-        return *this;
-    }
-
-    Os::IntervalTimer::RawTime TimerVal::getTimerVal() const {
-    	return this->m_timerVal;
-    }
-
-    void TimerVal::take() {
-        Os::IntervalTimer::getRawTime(this->m_timerVal);
-    }
-
-    U32 TimerVal::diffUSec(const TimerVal& time) {
-        return Os::IntervalTimer::getDiffUsec(this->m_timerVal,time.m_timerVal);
-    }
-
-    Fw::SerializeStatus TimerVal::serialize(Fw::SerializeBufferBase& buffer) const {
-        Fw::SerializeStatus stat = buffer.serialize(this->m_timerVal.upper);
-        if (stat != Fw::FW_SERIALIZE_OK) {
-            return stat;
-        }
-        return buffer.serialize(this->m_timerVal.lower);
-    }
-
-    Fw::SerializeStatus TimerVal::deserialize(Fw::SerializeBufferBase& buffer) {
-        Fw::SerializeStatus stat = buffer.deserialize(this->m_timerVal.upper);
-        if (stat != Fw::FW_SERIALIZE_OK) {
-            return stat;
-        }
-        return buffer.deserialize(this->m_timerVal.lower);
-    }
-
-
-
-} /* namespace Svc */
diff --git a/Svc/Cycle/TimerVal.hpp b/Svc/Cycle/TimerVal.hpp
deleted file mode 100644
index e97467ea5a..0000000000
--- a/Svc/Cycle/TimerVal.hpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * TimerVal.hpp
- *
- *  Created on: Aug 5, 2015
- *      Author: timothycanham
- */
-
-#ifndef TIMERVAL_HPP_
-#define TIMERVAL_HPP_
-
-#include <Fw/Types/Serializable.hpp>
-#include <Os/IntervalTimer.hpp>
-
-namespace Svc {
-
-    //! \class TimerVal
-    //! \brief Serializable class for carrying timer values
-    //!
-    //! This class carries a timer value. It is meant to be
-    //! passed through ports that carry timing info.
-
-    class TimerVal: public Fw::Serializable {
-        public:
-
-            enum {
-                SERIALIZED_SIZE = sizeof(U32) + sizeof(U32) //!< size of TimerVal private members
-            };
-
-            TimerVal(); //!< Default constructor
-
-            //!  \brief Timer copy constructor
-            //!
-            //!  constructs a TimerVal with another timer as the source
-            //!
-            //!  \param other source timer value
-
-            TimerVal(const TimerVal& other); //!< copy constructor
-
-            //!  \brief Timer equal operator
-            //!
-            //!  copies timer value from another timer as the source
-            //!
-            //!  \param other source timer value
-
-            TimerVal& operator=(const TimerVal& other); //!< equal operator
-
-            //!  \brief Destructor
-            //!
-            //!  Does nothing
-            //!
-
-            virtual ~TimerVal() {}
-
-            //!  \brief Serialization function
-            //!
-            //!  Serializes timer values
-            //!
-            //!  \param buffer destination buffer for serialization data
-
-            Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const; //!< serialize contents
-
-            //!  \brief Deserialization function
-            //!
-            //!  Deserializes timer values
-            //!
-            //!  \param buffer source buffer for serialization data
-
-            Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer); //!< deserialize to contents
-
-            //!  \brief Returns the current timer value
-		    //!
-		    //!
-            Os::IntervalTimer::RawTime getTimerVal() const;
-
-            //!  \brief Function to store a timer value
-            //!
-            //!  This function calls the Os::IntervalTimer function to store a timer value in the private data of the object
-            //!
-
-            void take(); //!< Take raw time
-
-            //!  \brief Compute difference function
-            //!
-            //!  This function computes the difference in time between the internal
-            //!  value and the passed value. It is computed as internal - time parameter.
-            //!
-            //!  \param time time to compute difference from
-
-            U32 diffUSec(const TimerVal& time); //!< takes difference between stored time and passed time
-
-        PRIVATE:
-            TimerVal(U32 upper, U32 lower); //!< Private constructor for testing
-            Os::IntervalTimer::RawTime m_timerVal; //!< Stored timer value
-    };
-
-} /* namespace Svc */
-
-#endif /* TIMERVAL_HPP_ */
diff --git a/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp b/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
index cb51088160..334c5b934c 100644
--- a/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
+++ b/Svc/FatalHandler/FatalHandlerComponentLinuxImpl.cpp
@@ -28,7 +28,7 @@ namespace Svc {
             FwEventIdType Id) {
         // for **nix, delay then exit with error code
         Fw::Logger::log("FATAL %d handled.\n",Id);
-        (void)Os::Task::delay(Fw::Time(1, 0));
+        (void)Os::Task::delay(Fw::TimeInterval(1, 0));
         Fw::Logger::log("Exiting with abort signal and core dump file.\n");
         (void)raise( SIGABRT );
         exit(1);
diff --git a/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp b/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp
index 75557354f3..4e210332e7 100644
--- a/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp
+++ b/Svc/LinuxTimer/LinuxTimerComponentImpl.hpp
@@ -50,7 +50,7 @@ namespace Svc {
 
       volatile bool m_quit; //!< flag to quit
 
-      Svc::TimerVal m_timer;
+      Os::RawTime m_rawTime; //!< timestamp to pass to CycleOut port calls
 
 
     };
diff --git a/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp b/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
index ef2d02eb92..f67355b461 100644
--- a/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
+++ b/Svc/LinuxTimer/LinuxTimerComponentImplTaskDelay.cpp
@@ -19,15 +19,15 @@ namespace Svc {
 
   void LinuxTimerComponentImpl::startTimer(NATIVE_INT_TYPE interval) {
       while (true) {
-          Os::Task::delay(Fw::Time(static_cast<U32>(interval/1000), static_cast<U32>((interval % 1000) * 1000)));
+          Os::Task::delay(Fw::TimeInterval(static_cast<U32>(interval/1000), static_cast<U32>((interval % 1000) * 1000)));
           this->m_mutex.lock();
           bool quit = this->m_quit;
           this->m_mutex.unLock();
           if (quit) {
               return;
           }
-          this->m_timer.take();
-          this->CycleOut_out(0,this->m_timer);
+          this->m_rawTime.now();
+          this->CycleOut_out(0,this->m_rawTime);
       }
   }
 
diff --git a/Svc/LinuxTimer/LinuxTimerComponentImplTimerFd.cpp b/Svc/LinuxTimer/LinuxTimerComponentImplTimerFd.cpp
index aeaac6deaa..d4a3923e5c 100644
--- a/Svc/LinuxTimer/LinuxTimerComponentImplTimerFd.cpp
+++ b/Svc/LinuxTimer/LinuxTimerComponentImplTimerFd.cpp
@@ -52,8 +52,8 @@ namespace Svc {
               timerfd_settime (fd, 0, &itval, nullptr);
               return;
           }
-          this->m_timer.take();
-          this->CycleOut_out(0,this->m_timer);
+          this->m_rawTime.now();
+          this->CycleOut_out(0,this->m_rawTime);
       }
   }
 
diff --git a/Svc/PassiveRateGroup/PassiveRateGroup.cpp b/Svc/PassiveRateGroup/PassiveRateGroup.cpp
index 5436340557..acf80ea2f1 100644
--- a/Svc/PassiveRateGroup/PassiveRateGroup.cpp
+++ b/Svc/PassiveRateGroup/PassiveRateGroup.cpp
@@ -38,8 +38,8 @@ void PassiveRateGroup::configure(NATIVE_INT_TYPE contexts[], NATIVE_INT_TYPE num
 }
 
 
-void PassiveRateGroup::CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart) {
-    TimerVal end;
+void PassiveRateGroup::CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart) {
+    Os::RawTime endTime;
     FW_ASSERT(this->m_numContexts);
 
     // invoke any members of the rate group
@@ -49,18 +49,20 @@ void PassiveRateGroup::CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& c
         }
     }
 
-    // grab timer for end of cycle
-    end.take();
+    // grab timer for endTime of cycle
+    endTime.now();
 
     // get rate group execution time
-    U32 cycle_time = end.diffUSec(cycleStart);
-
+    U32 cycleTime;
+    // Cast to void as the only possible error is overflow, which we can't handle other
+    // than capping cycleTime to max value of U32 (which is done in getDiffUsec anyways)
+    (void) endTime.getDiffUsec(cycleStart, cycleTime);
     // check to see if the time has exceeded the previous maximum
-    if (cycle_time > this->m_maxTime) {
-        this->m_maxTime = cycle_time;
+    if (cycleTime > this->m_maxTime) {
+        this->m_maxTime = cycleTime;
     }
     this->tlmWrite_MaxCycleTime(this->m_maxTime);
-    this->tlmWrite_CycleTime(cycle_time);
+    this->tlmWrite_CycleTime(cycleTime);
     this->tlmWrite_CycleCount(++this->m_cycles);
 }
 
diff --git a/Svc/PassiveRateGroup/PassiveRateGroup.hpp b/Svc/PassiveRateGroup/PassiveRateGroup.hpp
index 427c4c8786..2d6f29098f 100644
--- a/Svc/PassiveRateGroup/PassiveRateGroup.hpp
+++ b/Svc/PassiveRateGroup/PassiveRateGroup.hpp
@@ -62,7 +62,7 @@ class PassiveRateGroup : public PassiveRateGroupComponentBase {
     //!
     //!  \param portNum incoming port call. For this class, should always be zero
     //!  \param cycleStart value stored by the cycle driver, used to compute execution time.
-    void CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart);
+    void CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart);
 
     U32 m_cycles;                                                     //!< cycles executed
     U32 m_maxTime;                                                    //!< maximum execution time in microseconds
diff --git a/Svc/RateGroupDriver/RateGroupDriver.cpp b/Svc/RateGroupDriver/RateGroupDriver.cpp
index 6875019534..deb58ed63c 100644
--- a/Svc/RateGroupDriver/RateGroupDriver.cpp
+++ b/Svc/RateGroupDriver/RateGroupDriver.cpp
@@ -41,7 +41,7 @@ namespace Svc {
 
     }
 
-    void RateGroupDriver::CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart) {
+    void RateGroupDriver::CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart) {
 
         // Make sure that the dividers have been configured:
         // If this asserts, add the configure() call to initialization.
diff --git a/Svc/RateGroupDriver/RateGroupDriver.hpp b/Svc/RateGroupDriver/RateGroupDriver.hpp
index 8016295145..60f6bef783 100644
--- a/Svc/RateGroupDriver/RateGroupDriver.hpp
+++ b/Svc/RateGroupDriver/RateGroupDriver.hpp
@@ -81,7 +81,7 @@ namespace Svc {
 
             //! downcall for input port
             //! NOTE: This port can execute in ISR context.
-            void CycleIn_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart);
+            void CycleIn_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart);
 
             //! divider array
             Divider m_dividers[NUM_CYCLEOUT_OUTPUT_PORTS];
diff --git a/cmake/platform/Darwin.cmake b/cmake/platform/Darwin.cmake
index 975710fe5e..9ad9cdf6d7 100644
--- a/cmake/platform/Darwin.cmake
+++ b/cmake/platform/Darwin.cmake
@@ -23,6 +23,7 @@ choose_fprime_implementation(Os/Console Os/Console/Posix)
 choose_fprime_implementation(Os/Task Os/Task/Posix)
 choose_fprime_implementation(Os/Mutex Os/Mutex/Posix)
 choose_fprime_implementation(Os/Queue Os/Generic/PriorityQueue)
+choose_fprime_implementation(Os/RawTime Os/RawTime/Posix)
 
 choose_fprime_implementation(Os/Cpu Os/Cpu/Darwin)
 choose_fprime_implementation(Os/Memory Os/Memory/Darwin)
diff --git a/cmake/platform/Linux.cmake b/cmake/platform/Linux.cmake
index fec0b0ba39..f332b988a7 100644
--- a/cmake/platform/Linux.cmake
+++ b/cmake/platform/Linux.cmake
@@ -14,6 +14,7 @@ choose_fprime_implementation(Os/Console Os/Console/Posix)
 choose_fprime_implementation(Os/Task Os/Task/Posix)
 choose_fprime_implementation(Os/Mutex Os/Mutex/Posix)
 choose_fprime_implementation(Os/Queue Os/Generic/PriorityQueue)
+choose_fprime_implementation(Os/RawTime Os/RawTime/Posix)
 
 choose_fprime_implementation(Os/Cpu Os/Cpu/Linux)
 choose_fprime_implementation(Os/Memory Os/Memory/Linux)
diff --git a/config/FpConfig.h b/config/FpConfig.h
index f49f6e17f3..ca65ff2226 100644
--- a/config/FpConfig.h
+++ b/config/FpConfig.h
@@ -416,6 +416,14 @@ typedef FwIndexType FwQueueSizeType;
 #define FW_FILESYSTEM_HANDLE_MAX_SIZE 16  //!< Maximum size of a handle for OS resources (files, queues, locks, etc.)
 #endif
 
+#ifndef FW_RAW_TIME_HANDLE_MAX_SIZE
+#define FW_RAW_TIME_HANDLE_MAX_SIZE 24  //!< Maximum size of a handle for OS::RawTime objects
+#endif
+
+#ifndef FW_RAW_TIME_SERIALIZATION_MAX_SIZE
+#define FW_RAW_TIME_SERIALIZATION_MAX_SIZE 8  //!< Maximum allowed serialization size for Os::RawTime objects
+#endif
+
 #ifndef FW_CONDITION_VARIABLE_HANDLE_MAX_SIZE
 #define FW_CONDITION_VARIABLE_HANDLE_MAX_SIZE 56  //!< Maximum size of a handle for OS condition variables
 #endif
diff --git a/config/IpCfg.hpp b/config/IpCfg.hpp
index 60daae5adb..d7c7fcd827 100644
--- a/config/IpCfg.hpp
+++ b/config/IpCfg.hpp
@@ -9,7 +9,7 @@
 // acknowledged.
 //
 // ======================================================================
-#include <Fw/Time/Time.hpp>
+#include <Fw/Time/TimeInterval.hpp>
 #ifndef REF_IPCFG_HPP
 #define REF_IPCFG_HPP
 
@@ -21,7 +21,7 @@ enum IpCfg {
     SOCKET_MAX_ITERATIONS = 0xFFFF,        // Maximum send/recv attempts before an error is returned
     SOCKET_MAX_HOSTNAME_SIZE = 256         // Maximum stored hostname
 };
-static const Fw::Time SOCKET_RETRY_INTERVAL = Fw::Time(1, 0);
+static const Fw::TimeInterval SOCKET_RETRY_INTERVAL = Fw::TimeInterval(1, 0);
 
 
 #endif //REF_IPCFG_HPP


Test patch:
diff --git a/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.cpp b/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.cpp
index 70e02bd358..8ccea92b60 100644
--- a/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.cpp
+++ b/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.cpp
@@ -131,7 +131,7 @@ namespace Drv {
   void LinuxGpioDriverTester ::
     from_intOut_handler(
         const NATIVE_INT_TYPE portNum,
-        Svc::TimerVal &cycleStart
+        Os::RawTime &cycleStart
     )
   {
     timer.stop();
diff --git a/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.hpp b/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.hpp
index efeb7bed6f..1d9faf1213 100644
--- a/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.hpp
+++ b/Drv/LinuxGpioDriver/test/ut/LinuxGpioDriverTester.hpp
@@ -62,7 +62,7 @@ namespace Drv {
       //!
       void from_intOut_handler(
           const NATIVE_INT_TYPE portNum, /*!< The port number*/
-          Svc::TimerVal &cycleStart /*!< Cycle start timer value*/
+          Os::RawTime &cycleStart /*!< Cycle start timer value*/
       );
 
     private:
diff --git a/Drv/TcpClient/test/ut/TcpClientTester.cpp b/Drv/TcpClient/test/ut/TcpClientTester.cpp
index 51bd53240f..edea68e2d7 100644
--- a/Drv/TcpClient/test/ut/TcpClientTester.cpp
+++ b/Drv/TcpClient/test/ut/TcpClientTester.cpp
@@ -124,7 +124,7 @@ bool TcpClientTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 itera
         if (open == this->component.isOpened()) {
             return true;
         }
-        Os::Task::delay(Fw::Time(0, 10000));
+        Os::Task::delay(Fw::TimeInterval(0, 10000));
     }
     return false;
 }
diff --git a/Drv/TcpServer/test/ut/TcpServerTester.cpp b/Drv/TcpServer/test/ut/TcpServerTester.cpp
index 62ee79d5d7..b820610c0c 100644
--- a/Drv/TcpServer/test/ut/TcpServerTester.cpp
+++ b/Drv/TcpServer/test/ut/TcpServerTester.cpp
@@ -114,7 +114,7 @@ bool TcpServerTester::wait_on_change(bool open, U32 iterations) {
         if (open == this->component.isOpened()) {
             return true;
         }
-        Os::Task::delay(Fw::Time(0, 10000));
+        Os::Task::delay(Fw::TimeInterval(0, 10000));
     }
     return false;
 }
@@ -125,7 +125,7 @@ bool TcpServerTester::wait_on_started(bool open, U32 iterations) {
         if (open == this->component.isStarted()) {
             return true;
         }
-        Os::Task::delay(Fw::Time(0, 10000));
+        Os::Task::delay(Fw::TimeInterval(0, 10000));
     }
     return false;
 }
diff --git a/Drv/Udp/test/ut/UdpTester.cpp b/Drv/Udp/test/ut/UdpTester.cpp
index 6dea06695f..3e1dae4099 100644
--- a/Drv/Udp/test/ut/UdpTester.cpp
+++ b/Drv/Udp/test/ut/UdpTester.cpp
@@ -128,7 +128,7 @@ bool UdpTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations)
         if (open == this->component.isOpened()) {
             return true;
         }
-        Os::Task::delay(Fw::Time(0, 10000));
+        Os::Task::delay(Fw::TimeInterval(0, 10000));
     }
     return false;
 }
diff --git a/Os/Posix/test/ut/PosixRawTimeTests.cpp b/Os/Posix/test/ut/PosixRawTimeTests.cpp
new file mode 100644
index 0000000000..104ab2f1ae
--- /dev/null
+++ b/Os/Posix/test/ut/PosixRawTimeTests.cpp
@@ -0,0 +1,22 @@
+// ======================================================================
+// \title Os/Posix/test/ut/PosixRawTimeTests.cpp
+// \brief tests for posix implementation for Os::RawTime
+// ======================================================================
+#include "Os/test/ut/rawtime/RulesHeaders.hpp"
+#include "Os/test/ut/rawtime/CommonTests.hpp"
+#include "Os/Posix/Task.hpp"
+#include <gtest/gtest.h>
+#include "STest/Scenario/Scenario.hpp"
+#include "STest/Pick/Pick.hpp"
+#include "Fw/Types/String.hpp"
+
+// ----------------------------------------------------------------------
+// Posix Test Cases
+// ----------------------------------------------------------------------
+
+
+int main(int argc, char** argv) {
+    STest::Random::seed();
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/Os/Stub/test/CMakeLists.txt b/Os/Stub/test/CMakeLists.txt
index a2ee495030..d5319d6d99 100644
--- a/Os/Stub/test/CMakeLists.txt
+++ b/Os/Stub/test/CMakeLists.txt
@@ -17,6 +17,7 @@ add_fprime_supplied_os_module("Mutex;ConditionVariable" Test_Stub)
 add_fprime_supplied_os_module(Cpu Test_Stub)
 add_fprime_supplied_os_module(Memory Test_Stub)
 add_fprime_supplied_os_module(Queue Test_Stub)
+add_fprime_supplied_os_module(RawTime Test_Stub) # add Fw_Buffer here?
 
 #### File Stub Testing ####
 set(UT_SOURCE_FILES
@@ -90,3 +91,9 @@ set(UT_SOURCE_FILES
     "${CMAKE_CURRENT_LIST_DIR}/ut/StubConditionTests.cpp"
 )
 add_fprime_os_test(StubConditionVariableTest "${UT_SOURCE_FILES}" "Os_Mutex\;Os_Mutex_Test_Stub" Fw_Time)
+
+#### RawTime Stub Testing ####
+set(UT_SOURCE_FILES
+    "${CMAKE_CURRENT_LIST_DIR}/ut/StubRawTimeTests.cpp"
+)
+add_fprime_os_test(StubRawTimeTest "${UT_SOURCE_FILES}" "Os_RawTime\;Os_RawTime_Test_Stub" Fw_Time)
diff --git a/Os/Stub/test/DefaultRawTime.cpp b/Os/Stub/test/DefaultRawTime.cpp
new file mode 100644
index 0000000000..20cc992cb8
--- /dev/null
+++ b/Os/Stub/test/DefaultRawTime.cpp
@@ -0,0 +1,20 @@
+// ======================================================================
+// \title Os/Stub/test/DefaultRawTime.cpp
+// \brief sets default Os::RawTime to test stub implementation via linker
+// ======================================================================
+#include "Os/Stub/test/RawTime.hpp"
+#include "Os/Delegate.hpp"
+
+namespace Os {
+
+//! \brief get a delegate for RawTimeInterface that intercepts calls for  for stub test file usage
+//! \param aligned_new_memory: aligned memory to fill
+//! \param to_copy: pointer to copy-constructor input
+//! \return: pointer to delegate
+RawTimeInterface *RawTimeInterface::getDelegate(RawTimeHandleStorage& aligned_placement_new_memory, const RawTimeInterface* to_copy) {
+    return Os::Delegate::makeDelegate<RawTimeInterface, Os::Stub::RawTime::Test::TestRawTime, RawTimeHandleStorage>(
+            aligned_placement_new_memory, to_copy
+    );
+}
+
+}  // namespace Os
diff --git a/Os/Stub/test/DefaultTask.cpp b/Os/Stub/test/DefaultTask.cpp
index fea15bf681..86e5628c4c 100644
--- a/Os/Stub/test/DefaultTask.cpp
+++ b/Os/Stub/test/DefaultTask.cpp
@@ -9,7 +9,7 @@
 #include <sys/time.h>
 
 namespace Os {
-    Os::Task::Status TaskInterface::delay(Fw::Time interval) {
+    Os::Task::Status TaskInterface::delay(Fw::TimeInterval interval) {
         Os::Stub::Task::Test::StaticData::data.lastCalled = Os::Stub::Task::Test::StaticData::LastFn::DELAY_FN;
         Os::Stub::Task::Test::StaticData::data.delay = interval;
 
diff --git a/Os/Stub/test/RawTime.cpp b/Os/Stub/test/RawTime.cpp
new file mode 100644
index 0000000000..9332b3b259
--- /dev/null
+++ b/Os/Stub/test/RawTime.cpp
@@ -0,0 +1,53 @@
+// ======================================================================
+// \title Os/Stub/RawTime.cpp
+// \brief stub implementation for Os::RawTime
+// ======================================================================
+#include "Os/Stub/test/RawTime.hpp"
+
+namespace Os {
+namespace Stub {
+namespace RawTime {
+namespace Test {
+
+StaticData StaticData::data;
+
+TestRawTime::TestRawTime() {
+    StaticData::data.lastCalled = StaticData::LastFn::CONSTRUCT_FN;
+}
+
+TestRawTime::TestRawTime(const TestRawTime& other) {
+    StaticData::data.lastCalled = StaticData::LastFn::COPY_CONSTRUCT_FN;
+}
+
+TestRawTime::~TestRawTime() {
+    StaticData::data.lastCalled = StaticData::LastFn::DESTRUCT_FN;
+}
+
+RawTimeHandle* TestRawTime::getHandle() {
+    StaticData::data.lastCalled = StaticData::LastFn::GET_HANDLE_FN;
+    return &this->m_handle;
+}
+
+TestRawTime::Status TestRawTime::now() {
+    StaticData::data.lastCalled = StaticData::LastFn::GET_TIME_FN;
+    return Status::OP_OK;
+}
+
+TestRawTime::Status TestRawTime::getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const {
+    StaticData::data.lastCalled = StaticData::LastFn::GET_INTERVAL_FN;
+    return Status::OP_OK;
+}
+
+Fw::SerializeStatus TestRawTime::serialize(Fw::SerializeBufferBase& buffer) const {
+    StaticData::data.lastCalled = StaticData::LastFn::SERIALIZE_FN;
+    return Fw::FW_SERIALIZE_OK;
+}
+
+Fw::SerializeStatus TestRawTime::deserialize(Fw::SerializeBufferBase& buffer) {
+    StaticData::data.lastCalled = StaticData::LastFn::DESERIALIZE_FN;
+    return Fw::FW_SERIALIZE_OK;
+}
+} // namespace Test
+} // namespace RawTime
+} // namespace Stub
+} // namespace Os
diff --git a/Os/Stub/test/RawTime.hpp b/Os/Stub/test/RawTime.hpp
new file mode 100644
index 0000000000..da3d2a6f8e
--- /dev/null
+++ b/Os/Stub/test/RawTime.hpp
@@ -0,0 +1,82 @@
+// ======================================================================
+// \title Os/Stub/RawTime.hpp
+// \brief stub definitions for Os::RawTime
+// ======================================================================
+#ifndef OS_STUB_RAWTIME_TEST_HPP
+#define OS_STUB_RAWTIME_TEST_HPP
+
+#include "Os/RawTime.hpp"
+
+namespace Os {
+namespace Stub {
+namespace RawTime {
+namespace Test {
+
+//! Data that supports the stubbed Mutex implementation.
+//!/
+struct StaticData {
+    //! Enumeration of last function called
+    //!
+    enum LastFn {
+        NONE_FN,
+        GET_TIME_FN,
+        GET_INTERVAL_FN,
+        SERIALIZE_FN,
+        DESERIALIZE_FN,
+        GET_HANDLE_FN,
+        CONSTRUCT_FN,
+        COPY_CONSTRUCT_FN,
+        DESTRUCT_FN
+    };
+    StaticData() = default;
+    ~StaticData() = default;
+
+    //! Last function called
+    LastFn lastCalled = NONE_FN;
+
+    Os::RawTime::Status lastStatus = Os::RawTime::Status::OP_OK;
+
+    // Singleton data
+    static StaticData data;
+};
+struct TestRawTimeHandle : public RawTimeHandle {};
+
+//! \brief Test implementation of Os::RawTime
+//!
+//! Test implementation of `RawTimeInterface`.
+//!
+class TestRawTime : public RawTimeInterface {
+  public:
+    //! \brief constructor
+    //!
+    TestRawTime();
+
+    //! \brief copy constructor
+    TestRawTime(const TestRawTime& other);
+
+    //! \brief destructor
+    //!
+    ~TestRawTime() override;
+
+    //! \brief return the underlying RawTime handle (implementation specific)
+    //! \return internal RawTime handle representation
+    RawTimeHandle* getHandle() override;
+
+    // ------------------------------------------------------------
+    // Implementation-specific RawTime overrides
+    // ------------------------------------------------------------
+    Status now() override;
+    Status getTimeInterval(const Os::RawTime& other, Fw::TimeInterval& interval) const override;
+    Fw::SerializeStatus serialize(Fw::SerializeBufferBase& buffer) const override;
+    Fw::SerializeStatus deserialize(Fw::SerializeBufferBase& buffer) override;
+
+  private:
+    //! Handle for TestRawTime
+    TestRawTimeHandle m_handle;
+};
+
+}  // namespace Test
+}  // namespace RawTime
+}  // namespace Stub
+}  // namespace Os
+#endif  // OS_STUB_RAWTIME_TEST_HPP
diff --git a/Os/Stub/test/Task.hpp b/Os/Stub/test/Task.hpp
index 2a972e65ab..f3b2d1d64b 100644
--- a/Os/Stub/test/Task.hpp
+++ b/Os/Stub/test/Task.hpp
@@ -27,9 +27,9 @@ struct StaticData {
 
     //! Last function called
     LastFn lastCalled = NONE_FN;
-    //Os::Task::Arguments arguments();
+
     Os::Task::SuspensionType suspensionType;
-    Fw::Time delay;
+    Fw::TimeInterval delay;
 
     Os::Task::Status delayStatus = Os::Task::Status::OP_OK;
     Os::Task::Status joinStatus = Os::Task::Status::OP_OK;
diff --git a/Os/Stub/test/ut/StubRawTimeTests.cpp b/Os/Stub/test/ut/StubRawTimeTests.cpp
new file mode 100644
index 0000000000..431548d9c3
--- /dev/null
+++ b/Os/Stub/test/ut/StubRawTimeTests.cpp
@@ -0,0 +1,91 @@
+// ======================================================================
+// \title Os/Stub/test/ut/StubRawTimeTests.cpp
+// \brief stub implementation for Os::RawTimeInterface testing
+// This ensures the delegation of function calls happens properly
+// ======================================================================
+#include <gtest/gtest.h>
+#include "Os/test/ut/rawtime/CommonTests.hpp"
+#include "Os/test/ut/rawtime/RulesHeaders.hpp"
+#include "Os/Stub/test/RawTime.hpp"
+#include "Fw/Buffer/Buffer.hpp"
+
+using namespace Os::Stub::RawTime::Test;
+
+
+// Basic file tests
+class Interface : public ::testing::Test {
+public:
+    //! Setup function delegating to UT setUp function
+    void SetUp() override {
+        StaticData::data = StaticData();
+    }
+
+    //! Setup function delegating to UT tearDown function
+    void TearDown() override {
+        StaticData::data = StaticData();
+    }
+};
+
+
+// Ensure that Os::RawTime properly calls the implementation constructor
+TEST_F(Interface, Construction) {
+    Os::RawTime rawtime;
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::CONSTRUCT_FN);
+}
+
+// Ensure that Os::RawTime properly calls the implementation constructor
+TEST_F(Interface, CopyConstruction) {
+    Os::RawTime rawtime;
+    Os::RawTime rawtime_copy(rawtime);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::COPY_CONSTRUCT_FN);
+}
+
+// Ensure that Os::RawTime properly calls the implementation destructor
+TEST_F(Interface, Destruction) {
+    delete (new Os::RawTime);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DESTRUCT_FN);
+}
+
+// Ensure that Os::RawTime properly calls the following delegate function
+TEST_F(Interface, Now) {
+    Os::RawTime rawtime;
+    ASSERT_EQ(rawtime.now(), Os::RawTime::Status::OP_OK);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_TIME_FN);
+}
+
+// Ensure that Os::RawTime properly calls the following delegate function
+TEST_F(Interface, GetTimeInterval) {
+    Os::RawTime rawtime;
+    Fw::TimeInterval unused;
+    ASSERT_EQ(rawtime.getTimeInterval(rawtime, unused), Os::RawTime::Status::OP_OK);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_INTERVAL_FN);
+}
+
+// Ensure that Os::RawTime properly calls the following delegate function
+TEST_F(Interface, Serialize) {
+    Os::RawTime rawtime;
+    Fw::Buffer buffer;
+    ASSERT_EQ(rawtime.serialize(buffer.getSerializeRepr()), Fw::FW_SERIALIZE_OK);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::SERIALIZE_FN);
+}
+
+// Ensure that Os::RawTime properly calls the following delegate function
+TEST_F(Interface, Deserialize) {
+    Os::RawTime rawtime;
+    Fw::Buffer buffer;
+    ASSERT_EQ(rawtime.deserialize(buffer.getSerializeRepr()), Fw::FW_SERIALIZE_OK);
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DESERIALIZE_FN);
+}
+
+// Ensure that Os::RawTime properly calls the implementation getHandle()
+TEST_F(Interface, GetHandle) {
+    Os::RawTime rawtime;
+    rawtime.getHandle();
+    ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::GET_HANDLE_FN);
+}
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    STest::Random::seed();
+    return RUN_ALL_TESTS();
+}
diff --git a/Os/Stub/test/ut/StubTaskTests.cpp b/Os/Stub/test/ut/StubTaskTests.cpp
index a969b27e8d..75affe6b4b 100644
--- a/Os/Stub/test/ut/StubTaskTests.cpp
+++ b/Os/Stub/test/ut/StubTaskTests.cpp
@@ -116,7 +116,7 @@ TEST_F(Interface, GetHandle) {
 TEST_F(Interface, Delay) {
     Os::Task task;
     StaticData::data.delayStatus = Os::Task::Status::DELAY_ERROR;
-    ASSERT_EQ(Os::Task::delay(Fw::Time(0, 1)), StaticData::data.delayStatus);
+    ASSERT_EQ(Os::Task::delay(Fw::TimeInterval(0, 1)), StaticData::data.delayStatus);
     ASSERT_EQ(StaticData::data.lastCalled, StaticData::LastFn::DELAY_FN);
 }
 
diff --git a/Os/Stub/test/ut/StubTests.cpp b/Os/Stub/test/ut/StubTests.cpp
index f8978221b9..c2946b3f40 100644
--- a/Os/Stub/test/ut/StubTests.cpp
+++ b/Os/Stub/test/ut/StubTests.cpp
@@ -9,6 +9,7 @@
 #include <Os/Directory.hpp>
 #include <Os/Cpu.hpp>
 #include <Os/Memory.hpp>
+#include <Os/RawTime.hpp>
 
 
 TEST(Stub, File) {
@@ -51,6 +52,10 @@ TEST(Stub, Memory) {
     Os::Memory::getSingleton();
 }
 
+TEST(Stub, RawTime) {
+    Os::RawTime _;
+}
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
diff --git a/Os/test/ut/IntervalTimerTest.cpp b/Os/test/ut/IntervalTimerTest.cpp
index 94359d6506..c5ab722ad2 100644
--- a/Os/test/ut/IntervalTimerTest.cpp
+++ b/Os/test/ut/IntervalTimerTest.cpp
@@ -8,10 +8,39 @@ extern "C" {
 }
 
 void intervalTimerTest() {
+    // Os::RawTime is already tested thoroughly, here we only test the IntervalTimer
+
+    // We are using RawTimes to get upper and lower bounds for what the interval should be
+    // This test expects functional RawTime (i.e. not Stubs)
+
     Os::IntervalTimer timer;
-    timer.start();
-    Os::Task::delay(Fw::Time(1, 0));
-    timer.stop();
-    ASSERT_GE(timer.getDiffUsec(), 1000000);
-    ASSERT_LT(timer.getDiffUsec(), 1005000);
+
+    Os::RawTime minStart;
+    Os::RawTime minStop;
+    Os::RawTime maxStart;
+    Os::RawTime maxStop;
+
+    maxStart.now(); // max bound
+    timer.start();  // actual timer under test
+    minStart.now(); // low bound
+
+    // Delay 5ms (5000us) for good measure's sake, value should not affect test
+    Os::Task::delay(Fw::TimeInterval(0, 5000));
+
+    minStop.now(); // low bound
+    timer.stop();  // actual timer under test
+    maxStop.now(); // max bound
+
+    Fw::TimeInterval testInterval;
+    Fw::TimeInterval minInterval;
+    Fw::TimeInterval maxInterval;
+
+    ASSERT_EQ(maxStop.getTimeInterval(maxStart, maxInterval), Os::RawTime::Status::OP_OK);
+    ASSERT_EQ(minStop.getTimeInterval(minStart, minInterval), Os::RawTime::Status::OP_OK);
+    ASSERT_EQ(timer.getTimeInterval(testInterval), Os::RawTime::Status::OP_OK);
+
+    // Compare intervals to make sure the IntervalTimer is returning within the bounds
+    // Note: this also tests the comparison operators of Fw::TimeInterval
+    ASSERT_TRUE(testInterval >= minInterval) << "Test interval: " << testInterval << " min interval: " << minInterval;
+    ASSERT_TRUE(testInterval <= maxInterval) << "Test interval: " << testInterval << " max interval: " << maxInterval;
 }
diff --git a/Os/test/ut/OsTestMain.cpp b/Os/test/ut/OsTestMain.cpp
index 7a8bef8b29..f4a3cade56 100644
--- a/Os/test/ut/OsTestMain.cpp
+++ b/Os/test/ut/OsTestMain.cpp
@@ -11,7 +11,7 @@ const char* filename;
 // (~0.99 s) or longer (~10 sec) than requested, causing the test to fail. The interval timer should
 // be rewritten to not directly utilize the OS clock, but in the mean time disabling this test on
 // Mac OS prevents intermittent unit test failures.
-TEST(Nominal, DISABLED_IntervalTimerTest) {
+TEST(Nominal, IntervalTimerTest) {
    intervalTimerTest();
 }
 TEST(Nominal, ValidateFileTest) {
diff --git a/Os/test/ut/rawtime/CommonTests.cpp b/Os/test/ut/rawtime/CommonTests.cpp
new file mode 100644
index 0000000000..d444e36064
--- /dev/null
+++ b/Os/test/ut/rawtime/CommonTests.cpp
@@ -0,0 +1,115 @@
+// ======================================================================
+// \title Os/test/ut/rawtime/CommonTests.cpp
+// \brief common test implementations
+// ======================================================================
+#include "Os/test/ut/rawtime/CommonTests.hpp"
+#include "Fw/Buffer/Buffer.hpp"
+
+// ----------------------------------------------------------------------
+// Test Fixture
+// ----------------------------------------------------------------------
+
+std::unique_ptr<Os::Test::RawTime::Tester> get_tester_implementation() {
+    return std::unique_ptr<Os::Test::RawTime::Tester>(new Os::Test::RawTime::Tester());
+}
+
+Functionality::Functionality() : tester(get_tester_implementation()) {
+
+    tester->m_times.reserve(tester->TEST_TIME_COUNT);
+    tester->m_shadow_times.reserve(tester->TEST_TIME_COUNT);
+
+    for (U32 i = 0; i < tester->TEST_TIME_COUNT; ++i) {
+        tester->m_times.emplace_back();
+        tester->m_shadow_times.emplace_back();
+        tester->m_times[i].now();
+        tester->m_shadow_times[i] = std::chrono::system_clock::now();
+    }
+}
+
+void Functionality::SetUp() {
+    // All setup is done in the constructor (recommended by GTest)
+}
+
+void Functionality::TearDown() {
+    // No teardown required
+}
+
+// ----------------------------------------------------------------------
+// Test Cases
+// ----------------------------------------------------------------------
+
+// Now
+TEST_F(Functionality, Now) {
+    Os::Test::RawTime::Tester::Now get_time_rule;
+    get_time_rule.apply(*tester);
+}
+
+// SelfDiffIsZero
+TEST_F(Functionality, SelfDiffIsZero) {
+    Os::Test::RawTime::Tester::SelfDiffIsZero self_diff_rule;
+    self_diff_rule.apply(*tester);
+}
+
+// GetTimeDiffU32
+TEST_F(Functionality, GetTimeDiffU32) {
+    Os::Test::RawTime::Tester::GetTimeDiffU32 get_diff_rule;
+    get_diff_rule.apply(*tester);
+}
+
+// GetTimeInterval
+TEST_F(Functionality, GetTimeInterval) {
+    Os::Test::RawTime::Tester::GetTimeInterval get_interval_rule;
+    get_interval_rule.apply(*tester);
+}
+
+// Serialization
+TEST_F(Functionality, Serialization) {
+    Os::Test::RawTime::Tester::Serialization serialization_rule;
+    serialization_rule.apply(*tester);
+}
+
+// DiffU32 overflows if times are too far apart
+TEST_F(Functionality, DiffU32Overflow) {
+    Os::Test::RawTime::Tester::DiffU32Overflow overflow_rule;
+    overflow_rule.apply(*tester);
+}
+
+// RandomizedTesting
+TEST_F(Functionality, RandomizedTesting) {
+    // Enumerate all rules and construct an instance of each
+    Os::Test::RawTime::Tester::Now get_time_rule;
+    Os::Test::RawTime::Tester::SelfDiffIsZero diff_zero_rule;
+    Os::Test::RawTime::Tester::GetTimeDiffU32 get_diff_rule;
+    Os::Test::RawTime::Tester::GetTimeInterval get_interval_rule;
+    Os::Test::RawTime::Tester::Serialization serialization_rule;
+    Os::Test::RawTime::Tester::DiffU32Overflow overflow_rule;
+
+    // Place these rules into a list of rules
+    STest::Rule<Os::Test::RawTime::Tester>* rules[] = {
+            &get_time_rule,
+            &diff_zero_rule,
+            &get_diff_rule,
+            &get_interval_rule,
+            &serialization_rule,
+            &overflow_rule,
+    };
+
+    // Take the rules and place them into a random scenario
+    STest::RandomScenario<Os::Test::RawTime::Tester> random(
+            "Random Rules",
+            rules,
+            FW_NUM_ARRAY_ELEMENTS(rules)
+    );
+
+    // Create a bounded scenario wrapping the random scenario
+    STest::BoundedScenario<Os::Test::RawTime::Tester> bounded(
+            "Bounded Random Rules Scenario",
+            random,
+            5000
+    );
+    // Run!
+    const U32 numSteps = bounded.run(*tester);
+    printf("Ran %u steps for RawTime.\n", numSteps);
+
+}
+
diff --git a/Os/test/ut/rawtime/CommonTests.hpp b/Os/test/ut/rawtime/CommonTests.hpp
new file mode 100644
index 0000000000..951fd774bd
--- /dev/null
+++ b/Os/test/ut/rawtime/CommonTests.hpp
@@ -0,0 +1,36 @@
+// ======================================================================
+// \title Os/test/ut/rawtime/CommonTests.hpp
+// \brief GoogleTest fixture definitions used in common RawTime testing
+// ======================================================================
+#ifndef OS_TEST_UT_COMMON_RAWTIME_TESTS_HPP
+#define OS_TEST_UT_COMMON_RAWTIME_TESTS_HPP
+
+#include <Os/RawTime.hpp>
+#include <gtest/gtest.h>
+#include <Os/test/ut/rawtime/RulesHeaders.hpp>
+
+namespace Os {
+namespace Test {
+namespace RawTime {
+
+
+}  // namespace RawTime
+}  // namespace Test
+}  // namespace Os
+
+class Functionality : public ::testing::Test {
+  public:
+    //! Constructor
+    Functionality();
+
+    //! SetUp test fixture
+    void SetUp() override;
+
+    //! TearDown test fixture for safe destruction
+    void TearDown() override;
+
+    //! Tester/state implementation
+    std::unique_ptr<Os::Test::RawTime::Tester> tester;
+};
+
+#endif  // OS_TEST_UT_COMMON_RAWTIME_TESTS_HPP
diff --git a/Os/test/ut/rawtime/RawTimeRules.cpp b/Os/test/ut/rawtime/RawTimeRules.cpp
new file mode 100644
index 0000000000..402424244c
--- /dev/null
+++ b/Os/test/ut/rawtime/RawTimeRules.cpp
@@ -0,0 +1,146 @@
+// ======================================================================
+// \title Os/test/ut/rawtime/RawTimeRules.cpp
+// \brief rule implementations for common testing of RawTime
+// ======================================================================
+
+#include "RulesHeaders.hpp"
+#include "RawTimeRules.hpp"
+#include "STest/Pick/Pick.hpp"
+#include "Fw/Buffer/Buffer.hpp"
+
+
+// ------------------------------------------------------------------------------------------------------
+// Rule SelfDiffIsZero
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::SelfDiffIsZero::SelfDiffIsZero() :
+    STest::Rule<Os::Test::RawTime::Tester>("SelfDiffIsZero") {}
+
+bool Os::Test::RawTime::Tester::SelfDiffIsZero::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::SelfDiffIsZero::action(Os::Test::RawTime::Tester &state) {
+    U32 result;
+    FwIndexType index = state.pick_random_index();
+    Os::RawTime& raw_time = state.m_times[index];
+    Os::RawTime::Status status = raw_time.getDiffUsec(raw_time, result);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OK);
+    ASSERT_EQ(result, 0);
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule Now
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::Now::Now() :
+    STest::Rule<Os::Test::RawTime::Tester>("Now") {}
+
+bool Os::Test::RawTime::Tester::Now::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::Now::action(Os::Test::RawTime::Tester &state) {
+    FwIndexType index = state.pick_random_index();
+
+    Os::RawTime::Status status = state.m_times[index].now();
+    state.shadow_now(index);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OK);
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule GetTimeDiffU32
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::GetTimeDiffU32::GetTimeDiffU32() :
+    STest::Rule<Os::Test::RawTime::Tester>("GetTimeDiffU32") {}
+
+bool Os::Test::RawTime::Tester::GetTimeDiffU32::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::GetTimeDiffU32::action(Os::Test::RawTime::Tester &state) {
+    U32 result;
+    FwIndexType index1 = state.pick_random_index();
+    FwIndexType index2 = state.pick_random_index();
+
+    Os::RawTime::Status status = state.m_times[index1].getDiffUsec(state.m_times[index2], result);
+    U32 shadow_result = state.shadow_getDiffUsec(state.m_shadow_times[index1], state.m_shadow_times[index2]);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OK);
+
+    state.shadow_validate_diff_result(result, shadow_result);
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule GetTimeInterval
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::GetTimeInterval::GetTimeInterval() :
+    STest::Rule<Os::Test::RawTime::Tester>("GetTimeInterval") {}
+
+bool Os::Test::RawTime::Tester::GetTimeInterval::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::GetTimeInterval::action(Os::Test::RawTime::Tester &state) {
+    Fw::TimeInterval interval;
+    FwIndexType index1 = state.pick_random_index();
+    FwIndexType index2 = state.pick_random_index();
+
+    Os::RawTime::Status status = state.m_times[index1].getTimeInterval(state.m_times[index2], interval);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OK);
+
+    state.shadow_validate_interval_result(index1, index2, interval);
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule Serialization
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::Serialization::Serialization() :
+    STest::Rule<Os::Test::RawTime::Tester>("Serialization") {}
+
+bool Os::Test::RawTime::Tester::Serialization::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::Serialization::action(Os::Test::RawTime::Tester &state) {
+    // In this test, we:
+    //    1. Serialize a RawTime object in a buffer
+    //    2. Deserialize the buffer into a new (stack-allocated) RawTime object
+    //    3. Compare the deserialized RawTime object with the original RawTime object
+    U8 data[Os::RawTime::SERIALIZED_SIZE] = {0};
+    Fw::Buffer buffer(data, sizeof(data));
+
+    FwIndexType index = state.pick_random_index();
+
+    state.m_times[index].serialize(buffer.getSerializeRepr());
+
+    Os::RawTime raw_time;
+    raw_time.deserialize(buffer.getSerializeRepr());
+
+    // We make sure that serialization and deserialization are successful by deserializing
+    // into a new Os::RawTime object and comparing the difference between the original RawTime
+    // test and the deserialized RawTime
+    U32 result = 1; // initialize to non-zero value to ensure result is set by getDiffUsec
+    Os::RawTime::Status status = state.m_times[index].getDiffUsec(raw_time, result);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OK);
+    // We expect the result to be 0 since the deserialized RawTime should be the same as the original
+    ASSERT_EQ(result, 0) << "Serialization test failed";
+}
+
+// ------------------------------------------------------------------------------------------------------
+// Rule DiffU32Overflow
+// ------------------------------------------------------------------------------------------------------
+Os::Test::RawTime::Tester::DiffU32Overflow::DiffU32Overflow() :
+    STest::Rule<Os::Test::RawTime::Tester>("DiffU32Overflow") {}
+
+bool Os::Test::RawTime::Tester::DiffU32Overflow::precondition(const Os::Test::RawTime::Tester &state) {
+    return true;
+}
+
+void Os::Test::RawTime::Tester::DiffU32Overflow::action(Os::Test::RawTime::Tester &state) {
+    U32 result;
+    Os::RawTime zero_time; // uninitialized time object is zero
+
+    FwIndexType index = state.pick_random_index();
+    Os::RawTime& now_time = state.m_times[index];
+
+    Os::RawTime::Status status = zero_time.getDiffUsec(now_time, result);
+    ASSERT_EQ(status, Os::RawTime::Status::OP_OVERFLOW);
+}
\ No newline at end of file
diff --git a/Os/test/ut/rawtime/RawTimeRules.hpp b/Os/test/ut/rawtime/RawTimeRules.hpp
new file mode 100644
index 0000000000..3fb4b49944
--- /dev/null
+++ b/Os/test/ut/rawtime/RawTimeRules.hpp
@@ -0,0 +1,75 @@
+// ======================================================================
+// \title Os/test/ut/rawtime/RawTimeRules.hpp
+// \brief rule definitions for common testing of RawTime
+// ======================================================================
+// Stripped when compiled, here for IDEs
+#include "RulesHeaders.hpp"
+
+// ------------------------------------------------------------------------------------------------------
+// Rule SelfDiffIsZero: RawTime diff with itself is zero
+// ------------------------------------------------------------------------------------------------------
+struct SelfDiffIsZero : public STest::Rule<Os::Test::RawTime::Tester> {
+    // ----------------------------------------------------------------------
+    // Construction
+    // ----------------------------------------------------------------------
+
+    //! Constructor
+    SelfDiffIsZero();
+
+    // ----------------------------------------------------------------------
+    // Public member functions
+    // ----------------------------------------------------------------------
+
+    //! Precondition
+    bool precondition(const Os::Test::RawTime::Tester &state  //!< The test state
+    );
+
+    //! Action
+    void action(Os::Test::RawTime::Tester &state  //!< The test state
+    );
+};
+
+// ------------------------------------------------------------------------------------------------------
+// Rule Now: Get current RawTime
+// ------------------------------------------------------------------------------------------------------
+struct Now : public STest::Rule<Os::Test::RawTime::Tester> {
+    Now();
+    bool precondition(const Os::Test::RawTime::Tester &state);
+    void action(Os::Test::RawTime::Tester &state);
+};
+
+// ------------------------------------------------------------------------------------------------------
+// Rule GetTimeDiffU32: Get diff between two RawTime
+// ------------------------------------------------------------------------------------------------------
+struct GetTimeDiffU32 : public STest::Rule<Os::Test::RawTime::Tester> {
+    GetTimeDiffU32();
+    bool precondition(const Os::Test::RawTime::Tester &state);
+    void action(Os::Test::RawTime::Tester &state);
+};
+
+// ------------------------------------------------------------------------------------------------------
+// Rule GetTimeInterval: Get diff between two RawTime
+// ------------------------------------------------------------------------------------------------------
+struct GetTimeInterval : public STest::Rule<Os::Test::RawTime::Tester> {
+    GetTimeInterval();
+    bool precondition(const Os::Test::RawTime::Tester &state);
+    void action(Os::Test::RawTime::Tester &state);
+};
+
+// ------------------------------------------------------------------------------------------------------
+// Rule Serialization: Serialize and Deserialize RawTime
+// ------------------------------------------------------------------------------------------------------
+struct Serialization : public STest::Rule<Os::Test::RawTime::Tester> {
+    Serialization();
+    bool precondition(const Os::Test::RawTime::Tester &state);
+    void action(Os::Test::RawTime::Tester &state);
+};
+
+// ------------------------------------------------------------------------------------------------------
+// Rule DiffU32Overflow: DiffU32 overflows if times are too far apart
+// ------------------------------------------------------------------------------------------------------
+struct DiffU32Overflow : public STest::Rule<Os::Test::RawTime::Tester> {
+    DiffU32Overflow();
+    bool precondition(const Os::Test::RawTime::Tester &state);
+    void action(Os::Test::RawTime::Tester &state);
+};
diff --git a/Os/test/ut/rawtime/RulesHeaders.hpp b/Os/test/ut/rawtime/RulesHeaders.hpp
new file mode 100644
index 0000000000..1781596db2
--- /dev/null
+++ b/Os/test/ut/rawtime/RulesHeaders.hpp
@@ -0,0 +1,106 @@
+// ======================================================================
+// \title Os/test/ut/rawtime/RulesHeaders.hpp
+// \brief rule definitions for common testing
+// ======================================================================
+
+#ifndef __RULES_HEADERS__
+#define __RULES_HEADERS__
+#include <gtest/gtest.h>
+#include <vector>
+#include <chrono>
+
+#include "Os/RawTime.hpp"
+#include "STest/Rule/Rule.hpp"
+#include "STest/Pick/Pick.hpp"
+#include "STest/Scenario/RandomScenario.hpp"
+#include "STest/Scenario/BoundedScenario.hpp"
+
+
+
+namespace Os {
+namespace Test {
+namespace RawTime {
+
+struct Tester {
+    // Constructors that ensures the mutex is always valid
+    Tester() = default;
+
+    // Destructor must be virtual
+    virtual ~Tester() = default;
+
+    // Number of instances of RawTime under test
+    static constexpr U32 TEST_TIME_COUNT = 5;
+
+    // Threshold for time differences, in microseconds
+    // This value was selected empirically, some platforms may need to adjust
+    static constexpr U32 INTERVAL_DIFF_THRESHOLD = 20;
+
+    //! RawTime (array thereof) under test
+    std::vector<Os::RawTime> m_times;
+
+    //! Shadow time for testing (vector of std time points)
+    std::vector<std::chrono::time_point<std::chrono::system_clock>> m_shadow_times;
+
+    //! Get time for shadow state, at specified index
+    void shadow_now(FwIndexType index) {
+        this->m_shadow_times[index] = std::chrono::system_clock::now();
+    }
+
+    U32 shadow_getDiffUsec(std::chrono::time_point<std::chrono::system_clock>& t1, std::chrono::time_point<std::chrono::system_clock>& t2) const {
+        // Signedness is important here so we compare and substract accordingly
+        if (t1 < t2) {
+            return std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
+        }
+        return std::chrono::duration_cast<std::chrono::microseconds>(t1 - t2).count();
+    }
+
+    void shadow_getTimeInterval(FwIndexType index1, FwIndexType index2, Fw::TimeInterval& interval) {
+        auto duration = this->m_shadow_times[index1] - this->m_shadow_times[index2];
+        if (duration < std::chrono::system_clock::duration::zero()) {
+            duration = -duration;
+        }
+
+        U32 microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
+        U32 seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
+
+        interval.set(seconds, microseconds);
+    }
+
+    void shadow_validate_interval_result(FwIndexType index1, FwIndexType index2, const Fw::TimeInterval& interval) {
+        Fw::TimeInterval shadow_interval;
+        Fw::TimeInterval result;
+        this->shadow_getTimeInterval(index1, index2, shadow_interval);
+        // Signedness is important here so we compare and substract accordingly
+        if (interval < shadow_interval) {
+            result = Fw::TimeInterval::sub(shadow_interval, interval);
+        } else {
+            result = Fw::TimeInterval::sub(interval, shadow_interval);
+        }
+        // Check that difference between 2 intervals is less than threshold
+        ASSERT_TRUE(result < Fw::TimeInterval(0, INTERVAL_DIFF_THRESHOLD)) 
+            << "Interval difference: " << result;
+    }
+
+    void shadow_validate_diff_result(U32 result, U32 shadow_result) {
+        U32 result_diff;
+        if (result > shadow_result) {
+            result_diff = result - shadow_result;
+        } else {
+            result_diff = shadow_result - result;
+        }
+        ASSERT_TRUE(result_diff < INTERVAL_DIFF_THRESHOLD)
+            << "Difference between results: " << result_diff << " microseconds";
+    }
+
+    FwIndexType pick_random_index() const {
+        return STest::Pick::lowerUpper(0, TEST_TIME_COUNT - 1);;
+    }
+
+// Do NOT alter, adds rules to Tester as inner classes
+#include "RawTimeRules.hpp"
+};
+
+}  // namespace RawTime
+}  // namespace Test
+}  // namespace Os
+#endif  // __RULES_HEADERS__
diff --git a/Os/test/ut/task/TaskRules.cpp b/Os/test/ut/task/TaskRules.cpp
index 4fef313bc3..9923798537 100644
--- a/Os/test/ut/task/TaskRules.cpp
+++ b/Os/test/ut/task/TaskRules.cpp
@@ -11,7 +11,7 @@ void wait_for_state_with_timeout(Os::Test::Task::TestTaskInfo &info, const Os::T
     // Loop waiting for transition in preparation for join (with timeout)
     FwSizeType i = 0;
     for (i = 0; i < (delay_ms * 100); i++) {
-        Os::Task::delay(Fw::Time(0, 10));
+        Os::Task::delay(Fw::TimeInterval(0, 10));
         if (info.stage() == stage) {
             break;
         }
@@ -151,7 +151,7 @@ void Os::Test::Task::Tester::Delay::action(
         Os::Test::Task::Tester &state //!< The test state
 ) {
     const U32 delay_micro_seconds = 5; //STest::Pick::lowerUpper(0, MAX_DELAY_MICRO_SECONDS);
-    Fw::Time delay(delay_micro_seconds / 1000000, delay_micro_seconds % 1000000);
+    Fw::TimeInterval delay(delay_micro_seconds / 1000000, delay_micro_seconds % 1000000);
 
     timeval start;
     timeval end;
diff --git a/Svc/ActiveRateGroup/test/ut/ActiveRateGroupImplTester.cpp b/Svc/ActiveRateGroup/test/ut/ActiveRateGroupImplTester.cpp
index f2e0972624..a4edfd15ae 100644
--- a/Svc/ActiveRateGroup/test/ut/ActiveRateGroupImplTester.cpp
+++ b/Svc/ActiveRateGroup/test/ut/ActiveRateGroupImplTester.cpp
@@ -50,7 +50,7 @@ namespace Svc {
         // we can cause an overrun by calling the cycle port in the middle of the rate
         // group execution
         if (this->m_causeOverrun) {
-            TimerVal zero(0,0);
+            Os::RawTime zero;
             this->invoke_to_CycleIn(0,zero);
             this->m_causeOverrun = false;
         }
@@ -78,15 +78,15 @@ namespace Svc {
         ASSERT_EVENTS_SIZE(1);
         ASSERT_EVENTS_RateGroupStarted_SIZE(1);
 
-        Svc::TimerVal timer;
-        timer.take();
+        Os::RawTime time;
+        time.now();
 
         // clear port call log
         this->clearPortCalls();
         // verify cycle start flag is NOT set
         ASSERT_FALSE(this->m_impl.m_cycleStarted);
-        // call active rate group with timer val
-        this->invoke_to_CycleIn(0,timer);
+        // call active rate group with time val
+        this->invoke_to_CycleIn(0,time);
         // verify cycle started flag is set
         ASSERT_TRUE(this->m_impl.m_cycleStarted);
         // call doDispatch() for ActiveRateGroup
@@ -123,7 +123,8 @@ namespace Svc {
         ASSERT_EVENTS_SIZE(1);
         ASSERT_EVENTS_RateGroupStarted_SIZE(1);
 
-        Svc::TimerVal timer(1,2);
+        // NOTE: The value of the timestamp is not relevant to this test ?
+        Os::RawTime zero_time;
 
         // run some more cycles to verify that event is sent and telemetry is updated
         for (NATIVE_INT_TYPE cycle = 0; cycle < ACTIVE_RATE_GROUP_OVERRUN_THROTTLE; cycle++) {
@@ -142,7 +143,7 @@ namespace Svc {
             // set flag to cause overrun
             this->m_causeOverrun = true;
             // call active rate group with timer val
-            this->invoke_to_CycleIn(0,timer);
+            this->invoke_to_CycleIn(0,zero_time);
             // verify cycle started flag is set
             ASSERT_TRUE(this->m_impl.m_cycleStarted);
             // call doDispatch() for ActiveRateGroup
@@ -189,7 +190,7 @@ namespace Svc {
         // set flag to cause overrun
         this->m_causeOverrun = true;
         // call active rate group with timer val
-        this->invoke_to_CycleIn(0,timer);
+        this->invoke_to_CycleIn(0,zero_time);
         // verify cycle started flag is set from previous cycle slip
         ASSERT_TRUE(this->m_impl.m_cycleStarted);
         // call doDispatch() for ActiveRateGroup
@@ -233,7 +234,7 @@ namespace Svc {
         // set flag to prevent overrun
         this->m_causeOverrun = false;
         // call active rate group with timer val
-        this->invoke_to_CycleIn(0,timer);
+        this->invoke_to_CycleIn(0,zero_time);
         // verify cycle started flag is set from previous cycle slip
         ASSERT_TRUE(this->m_impl.m_cycleStarted);
         // call doDispatch() for ActiveRateGroup
@@ -271,7 +272,7 @@ namespace Svc {
         // set flag to cause overrun
         this->m_causeOverrun = true;
         // call active rate group with timer val
-        this->invoke_to_CycleIn(0,timer);
+        this->invoke_to_CycleIn(0,zero_time);
         // verify cycle started flag is set from port call
         ASSERT_TRUE(this->m_impl.m_cycleStarted);
         // call doDispatch() for ActiveRateGroup
diff --git a/Svc/LinuxTimer/test/ut/LinuxTimerTester.cpp b/Svc/LinuxTimer/test/ut/LinuxTimerTester.cpp
index 1e31c28003..972c48beff 100644
--- a/Svc/LinuxTimer/test/ut/LinuxTimerTester.cpp
+++ b/Svc/LinuxTimer/test/ut/LinuxTimerTester.cpp
@@ -55,7 +55,7 @@ namespace Svc {
   void LinuxTimerTester ::
     from_CycleOut_handler(
         const NATIVE_INT_TYPE portNum,
-        Svc::TimerVal &cycleStart
+        Os::RawTime &cycleStart
     )
   {
       printf("TICK\n");
diff --git a/Svc/LinuxTimer/test/ut/LinuxTimerTester.hpp b/Svc/LinuxTimer/test/ut/LinuxTimerTester.hpp
index d9fc47f96c..32b0ba77c8 100644
--- a/Svc/LinuxTimer/test/ut/LinuxTimerTester.hpp
+++ b/Svc/LinuxTimer/test/ut/LinuxTimerTester.hpp
@@ -56,7 +56,7 @@ namespace Svc {
       //!
       void from_CycleOut_handler(
           const NATIVE_INT_TYPE portNum, /*!< The port number*/
-          Svc::TimerVal &cycleStart /*!< Cycle start timer value*/
+          Os::RawTime &cycleStart /*!< Cycle start timer value*/
       );
 
     private:
diff --git a/Svc/PassiveRateGroup/test/ut/PassiveRateGroupImplTester.cpp b/Svc/PassiveRateGroup/test/ut/PassiveRateGroupImplTester.cpp
index 3afea5c476..b39607b27c 100644
--- a/Svc/PassiveRateGroup/test/ut/PassiveRateGroupImplTester.cpp
+++ b/Svc/PassiveRateGroup/test/ut/PassiveRateGroupImplTester.cpp
@@ -55,15 +55,15 @@ void PassiveRateGroupTester::runNominal(NATIVE_INT_TYPE contexts[],
     // clear events
     this->clearTlm();
 
-    Svc::TimerVal timer;
-    timer.take();
+    Os::RawTime timestamp;
+    timestamp.now();
 
     // clear port call log
     this->clearPortCalls();
 
     REQUIREMENT("FPRIME-PRG-001");
-    // call active rate group with timer val
-    this->invoke_to_CycleIn(0, timer);
+    // call active rate group with timestamp val
+    this->invoke_to_CycleIn(0, timestamp);
 
 
     // check calls
diff --git a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.cpp b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.cpp
index 70e3102af5..11f455232e 100644
--- a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.cpp
+++ b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.cpp
@@ -33,7 +33,7 @@ namespace Svc {
     RateGroupDriverImplTester::~RateGroupDriverImplTester() {
     }
 
-    void RateGroupDriverImplTester::from_CycleOut_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart) {
+    void RateGroupDriverImplTester::from_CycleOut_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart) {
         this->m_portCalls[portNum] = true;
     }
 
@@ -59,7 +59,7 @@ namespace Svc {
 
         for (NATIVE_INT_TYPE cycle = 0; cycle < iters; cycle++) {
             this->clearPortCalls();
-            TimerVal t;
+            Os::RawTime t;
             this->invoke_to_CycleIn(0,t);
             // make sure ticks are counting correctly
             ASSERT_EQ((cycle+1)%expected_rollover,this->m_impl.m_ticks);
diff --git a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp
index a7e32f6c7f..493b0843f2 100644
--- a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp
+++ b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp
@@ -24,7 +24,7 @@ namespace Svc {
 
         private:
 
-            void from_CycleOut_handler(NATIVE_INT_TYPE portNum, Svc::TimerVal& cycleStart);
+            void from_CycleOut_handler(NATIVE_INT_TYPE portNum, Os::RawTime& cycleStart);
 
             Svc::RateGroupDriver& m_impl;