diff --git a/src/audit/log.cpp b/src/audit/log.cpp
index 4680bee1f..62a34d65e 100644
--- a/src/audit/log.cpp
+++ b/src/audit/log.cpp
@@ -46,7 +46,7 @@ inline nlohmann::json PropertyValueToJson(const storage::PropertyValue &pv) {
     case storage::PropertyValue::Type::TemporalData: {
       ret = nlohmann::json::object();
       const auto temporal_data = pv.ValueTemporalData();
-      // TODO (antonio2368): Maybe we want to have custom format for each type
+      // TODO(antonio2368): Maybe we want to have custom format for each type
       ret.emplace("type", storage::TemporalTypeTostring(temporal_data.type));
       ret.emplace("microseconds", temporal_data.microseconds);
       break;
diff --git a/src/glue/communication.cpp b/src/glue/communication.cpp
index ef69f642f..aa26bd953 100644
--- a/src/glue/communication.cpp
+++ b/src/glue/communication.cpp
@@ -99,6 +99,13 @@ storage::Result<Value> ToBoltValue(const query::TypedValue &value, const storage
       if (maybe_path.HasError()) return maybe_path.GetError();
       return Value(std::move(*maybe_path));
     }
+    case query::TypedValue::Type::Date:
+    case query::TypedValue::Type::LocalTime:
+    case query::TypedValue::Type::LocalDateTime:
+    case query::TypedValue::Type::Duration:
+      // TODO(antonio2368): Change this when Bolt value for temporal types
+      // are implemented
+      LOG_FATAL("Temporal types not yet supported");
   }
 }
 
diff --git a/src/query/CMakeLists.txt b/src/query/CMakeLists.txt
index 157359850..363f47bcb 100644
--- a/src/query/CMakeLists.txt
+++ b/src/query/CMakeLists.txt
@@ -33,6 +33,7 @@ set(mg_query_sources
     procedure/py_module.cpp
     serialization/property_value.cpp
     streams.cpp
+    temporal.cpp
     trigger.cpp
     trigger_context.cpp
     typed_value.cpp)
diff --git a/src/query/frontend/ast/pretty_print.cpp b/src/query/frontend/ast/pretty_print.cpp
index a57351cb3..76f4377a0 100644
--- a/src/query/frontend/ast/pretty_print.cpp
+++ b/src/query/frontend/ast/pretty_print.cpp
@@ -142,7 +142,7 @@ void PrintObject(std::ostream *out, const storage::PropertyValue &value) {
       PrintObject(out, value.ValueMap());
       break;
     case storage::PropertyValue::Type::TemporalData:
-      // TODO (antonio2368): Print out the temporal data based on the type
+      // TODO(antonio2368): Print out the temporal data based on the type
       LOG_FATAL("Not implemented!");
   }
 }
diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp
index a826aef82..74a5008b9 100644
--- a/src/query/interpret/awesome_memgraph_functions.cpp
+++ b/src/query/interpret/awesome_memgraph_functions.cpp
@@ -542,6 +542,14 @@ TypedValue ValueType(const TypedValue *args, int64_t nargs, const FunctionContex
       return TypedValue("RELATIONSHIP", ctx.memory);
     case TypedValue::Type::Path:
       return TypedValue("PATH", ctx.memory);
+    case TypedValue::Type::Date:
+      return TypedValue("DATE", ctx.memory);
+    case TypedValue::Type::LocalTime:
+      return TypedValue("LOCAL_TIME", ctx.memory);
+    case TypedValue::Type::LocalDateTime:
+      return TypedValue("LOCAL_DATE_TIME", ctx.memory);
+    case TypedValue::Type::Duration:
+      return TypedValue("DURATION", ctx.memory);
   }
 }
 
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index 0cfd151e3..1f2c6f849 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -262,6 +262,9 @@ mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
       return MGP_VALUE_TYPE_EDGE;
     case query::TypedValue::Type::Path:
       return MGP_VALUE_TYPE_PATH;
+    default:
+      // TODO(antonio2368): Implement this when we add mgp temporal types
+      LOG_FATAL("Unsupported value in the procedures");
   }
 }
 
@@ -478,7 +481,7 @@ mgp_value::mgp_value(const storage::PropertyValue &pv, utils::MemoryResource *m)
       break;
     }
     case storage::PropertyValue::Type::TemporalData: {
-      // TODO (antonio2368): Add support for temporala data types
+      // TODO(antonio2368): Add support for temporala data types
       LOG_FATAL("Unsupported type");
     }
   }
@@ -1942,6 +1945,12 @@ std::ostream &PrintValue(const TypedValue &value, std::ostream *stream) {
     case TypedValue::Type::Edge:
     case TypedValue::Type::Path:
       LOG_FATAL("value must not be a graph element");
+    case TypedValue::Type::Date:
+    case TypedValue::Type::LocalTime:
+    case TypedValue::Type::LocalDateTime:
+    case TypedValue::Type::Duration:
+      // TODO(antonio2368): Check how to print out nicely temporal types
+      LOG_FATAL("Temporal types not imlemented yet");
   }
 }
 
diff --git a/src/query/temporal.cpp b/src/query/temporal.cpp
new file mode 100644
index 000000000..e13f691b9
--- /dev/null
+++ b/src/query/temporal.cpp
@@ -0,0 +1,198 @@
+#include "query/temporal.hpp"
+
+#include <chrono>
+
+#include "utils/exceptions.hpp"
+#include "utils/fnv.hpp"
+
+namespace query {
+namespace {
+template <typename T>
+concept Chrono = requires(T) {
+  typename T::rep;
+  typename T::period;
+};
+
+template <Chrono TFirst, Chrono TSecond>
+constexpr auto GetAndSubtractDuration(TSecond &base_duration) {
+  const auto duration = std::chrono::duration_cast<TFirst>(base_duration);
+  base_duration -= duration;
+  return duration.count();
+}
+
+constexpr std::chrono::microseconds epoch{std::chrono::years{1970} + std::chrono::months{1} + std::chrono::days{1}};
+
+constexpr bool IsInBounds(const auto low, const auto high, const auto value) { return low <= value && value <= high; }
+
+constexpr bool IsValidDay(const auto day, const auto month, const auto year) {
+  constexpr std::array<uint8_t, 12> days{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+  MG_ASSERT(IsInBounds(1, 12, month), "Invalid month!");
+
+  if (day <= 0) {
+    return false;
+  }
+
+  const auto is_leap_year = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+
+  uint8_t leap_day = month == 2 && is_leap_year;
+
+  return day <= (days[month - 1] + leap_day);
+}
+
+}  // namespace
+
+Date::Date(const int64_t microseconds) {
+  auto chrono_microseconds = std::chrono::microseconds(microseconds);
+  chrono_microseconds += epoch;
+  MG_ASSERT(chrono_microseconds.count() >= 0, "Invalid Date specified in microseconds");
+  years = GetAndSubtractDuration<std::chrono::years>(chrono_microseconds);
+  months = GetAndSubtractDuration<std::chrono::months>(chrono_microseconds);
+  days = GetAndSubtractDuration<std::chrono::days>(chrono_microseconds);
+}
+
+Date::Date(const DateParameters &date_parameters) {
+  if (!IsInBounds(0, 9999, date_parameters.years)) {
+    throw utils::BasicException("Creating a Date with invalid year parameter.");
+  }
+
+  // TODO(antonio2368): Replace with year_month_day when it's implemented
+  // https://en.cppreference.com/w/cpp/chrono/year_month_day/ok
+  if (!IsInBounds(1, 12, date_parameters.months)) {
+    throw utils::BasicException("Creating a Date with invalid month parameter.");
+  }
+
+  if (!IsValidDay(date_parameters.days, date_parameters.months, date_parameters.years)) {
+    throw utils::BasicException("Creating a Date with invalid day parameter.");
+  }
+
+  years = date_parameters.years;
+  months = date_parameters.months;
+  days = date_parameters.days;
+}
+
+int64_t Date::MicrosecondsSinceEpoch() const {
+  auto result = std::chrono::duration_cast<std::chrono::microseconds>(
+      std::chrono::years{years} + std::chrono::months{months} + std::chrono::days{days});
+
+  result -= epoch;
+  return result.count();
+}
+
+size_t DateHash::operator()(const Date &date) const {
+  utils::HashCombine<uint64_t, uint64_t> hasher;
+  size_t result = hasher(0, date.years);
+  result = hasher(result, date.months);
+  result = hasher(result, date.days);
+  return result;
+}
+
+LocalTime::LocalTime(const int64_t microseconds) {
+  auto chrono_microseconds = std::chrono::microseconds(microseconds);
+  MG_ASSERT(chrono_microseconds.count() >= 0, "Negative LocalTime specified in microseconds");
+
+  const auto parsed_hours = GetAndSubtractDuration<std::chrono::hours>(chrono_microseconds);
+  MG_ASSERT(parsed_hours <= 23, "Invalid LocalTime specified in microseconds");
+
+  hours = parsed_hours;
+  minutes = GetAndSubtractDuration<std::chrono::minutes>(chrono_microseconds);
+  seconds = GetAndSubtractDuration<std::chrono::seconds>(chrono_microseconds);
+  milliseconds = GetAndSubtractDuration<std::chrono::milliseconds>(chrono_microseconds);
+  this->microseconds = chrono_microseconds.count();
+}
+
+LocalTime::LocalTime(const LocalTimeParameters &local_time_parameters) {
+  if (!IsInBounds(0, 23, local_time_parameters.hours)) {
+    throw utils::BasicException("Creating a LocalTime with invalid hour parameter.");
+  }
+
+  if (!IsInBounds(0, 59, local_time_parameters.minutes)) {
+    throw utils::BasicException("Creating a LocalTime with invalid minutes parameter.");
+  }
+
+  if (!IsInBounds(0, 59, local_time_parameters.seconds)) {
+    throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
+  }
+
+  if (!IsInBounds(0, 999, local_time_parameters.milliseconds)) {
+    throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
+  }
+
+  if (!IsInBounds(0, 999, local_time_parameters.microseconds)) {
+    throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
+  }
+
+  hours = local_time_parameters.hours;
+  minutes = local_time_parameters.minutes;
+  seconds = local_time_parameters.seconds;
+  milliseconds = local_time_parameters.milliseconds;
+  microseconds = local_time_parameters.microseconds;
+}
+
+int64_t LocalTime::MicrosecondsSinceEpoch() const {
+  return std::chrono::duration_cast<std::chrono::microseconds>(
+             std::chrono::hours{hours} + std::chrono::minutes{minutes} + std::chrono::seconds{seconds} +
+             std::chrono::milliseconds{milliseconds} + std::chrono::microseconds{microseconds})
+      .count();
+}
+
+size_t LocalTimeHash::operator()(const LocalTime &local_time) const {
+  utils::HashCombine<uint64_t, uint64_t> hasher;
+  size_t result = hasher(0, local_time.hours);
+  result = hasher(result, local_time.minutes);
+  result = hasher(result, local_time.seconds);
+  result = hasher(result, local_time.milliseconds);
+  result = hasher(result, local_time.microseconds);
+  return result;
+}
+
+LocalDateTime::LocalDateTime(const int64_t microseconds) {
+  auto chrono_microseconds = std::chrono::microseconds(microseconds);
+  date = Date(chrono_microseconds.count());
+  chrono_microseconds -= std::chrono::microseconds{date.MicrosecondsSinceEpoch()};
+  local_time = LocalTime(chrono_microseconds.count());
+}
+
+// return microseconds normilized with regard to epoch time point
+int64_t LocalDateTime::MicrosecondsSinceEpoch() const {
+  return date.MicrosecondsSinceEpoch() + local_time.MicrosecondsSinceEpoch();
+}
+
+LocalDateTime::LocalDateTime(const DateParameters date_parameters, const LocalTimeParameters &local_time_parameters)
+    : date(date_parameters), local_time(local_time_parameters) {}
+
+size_t LocalDateTimeHash::operator()(const LocalDateTime &local_date_time) const {
+  utils::HashCombine<uint64_t, uint64_t> hasher;
+  size_t result = hasher(0, LocalTimeHash{}(local_date_time.local_time));
+  result = hasher(result, DateHash{}(local_date_time.date));
+  return result;
+}
+
+Duration::Duration(int64_t microseconds) { this->microseconds = microseconds; }
+
+namespace {
+template <Chrono From, Chrono To>
+constexpr To CastChronoDouble(const double value) {
+  return std::chrono::duration_cast<To>(std::chrono::duration<double, typename From::period>(value));
+};
+
+}  // namespace
+
+Duration::Duration(const DurationParameters &parameters) {
+  microseconds = (CastChronoDouble<std::chrono::years, std::chrono::microseconds>(parameters.years) +
+                  CastChronoDouble<std::chrono::months, std::chrono::microseconds>(parameters.months) +
+                  CastChronoDouble<std::chrono::days, std::chrono::microseconds>(parameters.days) +
+                  CastChronoDouble<std::chrono::hours, std::chrono::microseconds>(parameters.hours) +
+                  CastChronoDouble<std::chrono::minutes, std::chrono::microseconds>(parameters.minutes) +
+                  CastChronoDouble<std::chrono::seconds, std::chrono::microseconds>(parameters.seconds))
+                     .count();
+}
+
+Duration Duration::operator-() const {
+  Duration result{-microseconds};
+  return result;
+}
+
+size_t DurationHash::operator()(const Duration &duration) const { return std::hash<int64_t>{}(duration.microseconds); }
+
+}  // namespace query
diff --git a/src/query/temporal.hpp b/src/query/temporal.hpp
new file mode 100644
index 000000000..705c50c1f
--- /dev/null
+++ b/src/query/temporal.hpp
@@ -0,0 +1,105 @@
+#pragma once
+#include <chrono>
+#include <cstdint>
+
+#include "utils/exceptions.hpp"
+#include "utils/logging.hpp"
+
+namespace query {
+
+struct DateParameters {
+  int64_t years{0};
+  int64_t months{1};
+  int64_t days{1};
+};
+
+struct Date {
+  explicit Date() : Date{DateParameters{}} {}
+  // we assume we accepted date in microseconds which was normilized using the epoch time point
+  explicit Date(int64_t microseconds);
+  explicit Date(const DateParameters &date_parameters);
+
+  int64_t MicrosecondsSinceEpoch() const;
+
+  auto operator<=>(const Date &) const = default;
+
+  uint16_t years;
+  uint8_t months;
+  uint8_t days;
+};
+
+struct DateHash {
+  size_t operator()(const Date &date) const;
+};
+
+struct LocalTimeParameters {
+  int64_t hours{0};
+  int64_t minutes{0};
+  int64_t seconds{0};
+  int64_t milliseconds{0};
+  int64_t microseconds{0};
+};
+
+struct LocalTime {
+  explicit LocalTime() : LocalTime{LocalTimeParameters{}} {}
+  explicit LocalTime(int64_t microseconds);
+  explicit LocalTime(const LocalTimeParameters &local_time_parameters);
+
+  int64_t MicrosecondsSinceEpoch() const;
+
+  auto operator<=>(const LocalTime &) const = default;
+
+  uint8_t hours;
+  uint8_t minutes;
+  uint8_t seconds;
+  uint16_t milliseconds;
+  uint16_t microseconds;
+};
+
+struct LocalTimeHash {
+  size_t operator()(const LocalTime &local_time) const;
+};
+
+struct LocalDateTime {
+  explicit LocalDateTime(int64_t microseconds);
+  explicit LocalDateTime(DateParameters date, const LocalTimeParameters &local_time);
+
+  int64_t MicrosecondsSinceEpoch() const;
+
+  auto operator<=>(const LocalDateTime &) const = default;
+
+  Date date;
+  LocalTime local_time;
+};
+
+struct LocalDateTimeHash {
+  size_t operator()(const LocalDateTime &local_date_time) const;
+};
+
+struct DurationParameters {
+  double years{0};
+  double months{0};
+  double days{0};
+  double hours{0};
+  double minutes{0};
+  double seconds{0};
+  // TODO(antonio2368): Check how to include milliseconds/microseconds
+  // ISO 8601 does not specify string format for them
+};
+
+struct Duration {
+  explicit Duration(int64_t microseconds);
+  explicit Duration(const DurationParameters &parameters);
+
+  auto operator<=>(const Duration &) const = default;
+
+  Duration operator-() const;
+
+  int64_t microseconds;
+};
+
+struct DurationHash {
+  size_t operator()(const Duration &duration) const;
+};
+
+}  // namespace query
diff --git a/src/query/typed_value.cpp b/src/query/typed_value.cpp
index 573467e5b..7e1ab5e69 100644
--- a/src/query/typed_value.cpp
+++ b/src/query/typed_value.cpp
@@ -1,12 +1,15 @@
 #include "query/typed_value.hpp"
 
 #include <fmt/format.h>
+#include <chrono>
 #include <cmath>
 #include <iostream>
 #include <memory>
 #include <string_view>
 #include <utility>
 
+#include "query/temporal.hpp"
+#include "storage/v2/temporal.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/fnv.hpp"
 
@@ -52,9 +55,32 @@ TypedValue::TypedValue(const storage::PropertyValue &value, utils::MemoryResourc
       for (const auto &kv : map) map_v.emplace(kv.first, kv.second);
       return;
     }
-    case storage::PropertyValue::Type::TemporalData:
-      // TODO (antonio2368): Add support for Temporal types in TypedValues
-      break;
+    case storage::PropertyValue::Type::TemporalData: {
+      const auto &temporal_data = value.ValueTemporalData();
+      switch (temporal_data.type) {
+        case storage::TemporalType::Date: {
+          type_ = Type::Date;
+          new (&date_v) Date(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalTime: {
+          type_ = Type::LocalTime;
+          new (&local_time_v) LocalTime(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalDateTime: {
+          type_ = Type::LocalDateTime;
+          new (&local_date_time_v) LocalDateTime(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::Duration: {
+          type_ = Type::Duration;
+          new (&duration_v) Duration(temporal_data.microseconds);
+          break;
+        }
+      }
+      return;
+    }
   }
   LOG_FATAL("Unsupported type");
 }
@@ -99,9 +125,32 @@ TypedValue::TypedValue(storage::PropertyValue &&other, utils::MemoryResource *me
       for (auto &kv : map) map_v.emplace(kv.first, std::move(kv.second));
       break;
     }
-    case storage::PropertyValue::Type::TemporalData:
-      // TODO (antonio2368): Add support for Temporal types in TypedValues
-      LOG_FATAL("Unsupported type");
+    case storage::PropertyValue::Type::TemporalData: {
+      const auto &temporal_data = other.ValueTemporalData();
+      switch (temporal_data.type) {
+        case storage::TemporalType::Date: {
+          type_ = Type::Date;
+          new (&date_v) Date(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalTime: {
+          type_ = Type::LocalTime;
+          new (&local_time_v) LocalTime(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalDateTime: {
+          type_ = Type::LocalDateTime;
+          new (&local_date_time_v) LocalDateTime(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::Duration: {
+          type_ = Type::Duration;
+          new (&duration_v) Duration(temporal_data.microseconds);
+          break;
+        }
+      }
+      break;
+    }
   }
 
   other = storage::PropertyValue();
@@ -143,6 +192,18 @@ TypedValue::TypedValue(const TypedValue &other, utils::MemoryResource *memory) :
     case Type::Path:
       new (&path_v) Path(other.path_v, memory_);
       return;
+    case Type::Date:
+      new (&date_v) Date(other.date_v);
+      return;
+    case Type::LocalTime:
+      new (&local_time_v) LocalTime(other.local_time_v);
+      return;
+    case Type::LocalDateTime:
+      new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
+      return;
+    case Type::Duration:
+      new (&duration_v) Duration(other.duration_v);
+      return;
   }
   LOG_FATAL("Unsupported TypedValue::Type");
 }
@@ -180,6 +241,18 @@ TypedValue::TypedValue(TypedValue &&other, utils::MemoryResource *memory) : memo
     case Type::Path:
       new (&path_v) Path(std::move(other.path_v), memory_);
       break;
+    case Type::Date:
+      new (&date_v) Date(other.date_v);
+      break;
+    case Type::LocalTime:
+      new (&local_time_v) LocalTime(other.local_time_v);
+      break;
+    case Type::LocalDateTime:
+      new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
+      break;
+    case Type::Duration:
+      new (&duration_v) Duration(other.duration_v);
+      break;
   }
   other.DestroyValue();
 }
@@ -203,6 +276,17 @@ TypedValue::operator storage::PropertyValue() const {
       for (const auto &kv : map_v) map.emplace(kv.first, kv.second);
       return storage::PropertyValue(std::move(map));
     }
+    case Type::Date:
+      return storage::PropertyValue(
+          storage::TemporalData{storage::TemporalType::Date, date_v.MicrosecondsSinceEpoch()});
+    case Type::LocalTime:
+      return storage::PropertyValue(
+          storage::TemporalData{storage::TemporalType::LocalTime, local_time_v.MicrosecondsSinceEpoch()});
+    case Type::LocalDateTime:
+      return storage::PropertyValue(
+          storage::TemporalData{storage::TemporalType::LocalDateTime, local_date_time_v.MicrosecondsSinceEpoch()});
+    case Type::Duration:
+      return storage::PropertyValue(storage::TemporalData{storage::TemporalType::Duration, duration_v.microseconds});
     default:
       break;
   }
@@ -233,6 +317,10 @@ DEFINE_VALUE_AND_TYPE_GETTERS(TypedValue::TMap, Map, map_v)
 DEFINE_VALUE_AND_TYPE_GETTERS(VertexAccessor, Vertex, vertex_v)
 DEFINE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge, edge_v)
 DEFINE_VALUE_AND_TYPE_GETTERS(Path, Path, path_v)
+DEFINE_VALUE_AND_TYPE_GETTERS(Date, Date, date_v)
+DEFINE_VALUE_AND_TYPE_GETTERS(LocalTime, LocalTime, local_time_v)
+DEFINE_VALUE_AND_TYPE_GETTERS(LocalDateTime, LocalDateTime, local_date_time_v)
+DEFINE_VALUE_AND_TYPE_GETTERS(Duration, Duration, duration_v)
 
 #undef DEFINE_VALUE_AND_TYPE_GETTERS
 
@@ -249,6 +337,10 @@ bool TypedValue::IsPropertyValue() const {
     case Type::String:
     case Type::List:
     case Type::Map:
+    case Type::Date:
+    case Type::LocalTime:
+    case Type::LocalDateTime:
+    case Type::Duration:
       return true;
     default:
       return false;
@@ -277,6 +369,14 @@ std::ostream &operator<<(std::ostream &os, const TypedValue::Type &type) {
       return os << "edge";
     case TypedValue::Type::Path:
       return os << "path";
+    case TypedValue::Type::Date:
+      return os << "date";
+    case TypedValue::Type::LocalTime:
+      return os << "local_time";
+    case TypedValue::Type::LocalDateTime:
+      return os << "local_date_time";
+    case TypedValue::Type::Duration:
+      return os << "duration";
   }
   LOG_FATAL("Unsupported TypedValue::Type");
 }
@@ -325,6 +425,10 @@ TypedValue &TypedValue::operator=(const std::map<std::string, TypedValue> &other
 DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const VertexAccessor &, Vertex, vertex_v)
 DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const EdgeAccessor &, Edge, edge_v)
 DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Path &, Path, path_v)
+DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Date &, Date, date_v)
+DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const LocalTime &, LocalTime, local_time_v)
+DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const LocalDateTime &, LocalDateTime, local_date_time_v)
+DEFINE_TYPED_VALUE_COPY_ASSIGNMENT(const Duration &, Duration, duration_v)
 
 #undef DEFINE_TYPED_VALUE_COPY_ASSIGNMENT
 
@@ -408,6 +512,18 @@ TypedValue &TypedValue::operator=(const TypedValue &other) {
       case TypedValue::Type::Path:
         new (&path_v) Path(other.path_v, memory_);
         return *this;
+      case Type::Date:
+        new (&date_v) Date(other.date_v);
+        return *this;
+      case Type::LocalTime:
+        new (&local_time_v) LocalTime(other.local_time_v);
+        return *this;
+      case Type::LocalDateTime:
+        new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
+        return *this;
+      case Type::Duration:
+        new (&duration_v) Duration(other.duration_v);
+        return *this;
     }
     LOG_FATAL("Unsupported TypedValue::Type");
   }
@@ -455,6 +571,18 @@ TypedValue &TypedValue::operator=(TypedValue &&other) noexcept(false) {
       case TypedValue::Type::Path:
         new (&path_v) Path(std::move(other.path_v), memory_);
         break;
+      case Type::Date:
+        new (&date_v) Date(other.date_v);
+        break;
+      case Type::LocalTime:
+        new (&local_time_v) LocalTime(other.local_time_v);
+        break;
+      case Type::LocalDateTime:
+        new (&local_date_time_v) LocalDateTime(other.local_date_time_v);
+        break;
+      case Type::Duration:
+        new (&duration_v) Duration(other.duration_v);
+        break;
     }
     other.DestroyValue();
   }
@@ -490,6 +618,11 @@ void TypedValue::DestroyValue() {
     case Type::Path:
       path_v.~Path();
       break;
+    case Type::Date:
+    case Type::LocalTime:
+    case Type::LocalDateTime:
+    case Type::Duration:
+      break;
   }
 
   type_ = TypedValue::Type::Null;
@@ -515,6 +648,15 @@ double ToDouble(const TypedValue &value) {
   }
 }
 
+namespace {
+bool IsTemporalType(const TypedValue::Type type) {
+  constexpr std::array temporal_types{TypedValue::Type::Date, TypedValue::Type::LocalTime,
+                                      TypedValue::Type::LocalDateTime, TypedValue::Type::Duration};
+  return std::any_of(temporal_types.begin(), temporal_types.end(),
+                     [type](const auto temporal_type) { return temporal_type == type; });
+};
+}  // namespace
+
 TypedValue operator<(const TypedValue &a, const TypedValue &b) {
   auto is_legal = [](TypedValue::Type type) {
     switch (type) {
@@ -522,6 +664,10 @@ TypedValue operator<(const TypedValue &a, const TypedValue &b) {
       case TypedValue::Type::Int:
       case TypedValue::Type::Double:
       case TypedValue::Type::String:
+      case TypedValue::Type::Date:
+      case TypedValue::Type::LocalTime:
+      case TypedValue::Type::LocalDateTime:
+      case TypedValue::Type::Duration:
         return true;
       default:
         return false;
@@ -540,6 +686,29 @@ TypedValue operator<(const TypedValue &a, const TypedValue &b) {
     }
   }
 
+  if (IsTemporalType(a.type()) || IsTemporalType(b.type())) {
+    if (a.type() != b.type()) {
+      throw TypedValueException("Invalid 'less' operand types({} + {})", a.type(), b.type());
+    }
+
+    switch (a.type()) {
+      case TypedValue::Type::Date:
+        // NOLINTNEXTLINE(modernize-use-nullptr)
+        return TypedValue(a.ValueDate() < b.ValueDate(), a.GetMemoryResource());
+      case TypedValue::Type::LocalTime:
+        // NOLINTNEXTLINE(modernize-use-nullptr)
+        return TypedValue(a.ValueLocalTime() < b.ValueLocalTime(), a.GetMemoryResource());
+      case TypedValue::Type::LocalDateTime:
+        // NOLINTNEXTLINE(modernize-use-nullptr)
+        return TypedValue(a.ValueLocalDateTime() < b.ValueLocalDateTime(), a.GetMemoryResource());
+      case TypedValue::Type::Duration:
+        // NOLINTNEXTLINE(modernize-use-nullptr)
+        return TypedValue(a.ValueDuration() < b.ValueDuration(), a.GetMemoryResource());
+      default:
+        LOG_FATAL("Invalid temporal type");
+    }
+  }
+
   // at this point we only have int and double
   if (a.IsDouble() || b.IsDouble()) {
     return TypedValue(ToDouble(a) < ToDouble(b), a.GetMemoryResource());
@@ -605,6 +774,14 @@ TypedValue operator==(const TypedValue &a, const TypedValue &b) {
     }
     case TypedValue::Type::Path:
       return TypedValue(a.ValuePath() == b.ValuePath(), a.GetMemoryResource());
+    case TypedValue::Type::Date:
+      return TypedValue(a.ValueDate() == b.ValueDate(), a.GetMemoryResource());
+    case TypedValue::Type::LocalTime:
+      return TypedValue(a.ValueLocalTime() == b.ValueLocalTime(), a.GetMemoryResource());
+    case TypedValue::Type::LocalDateTime:
+      return TypedValue(a.ValueLocalDateTime() == b.ValueLocalDateTime(), a.GetMemoryResource());
+    case TypedValue::Type::Duration:
+      return TypedValue(a.ValueDuration() == b.ValueDuration(), a.GetMemoryResource());
     default:
       LOG_FATAL("Unhandled comparison for types");
   }
@@ -635,6 +812,7 @@ TypedValue operator-(const TypedValue &a) {
   if (a.IsNull()) return TypedValue(a.GetMemoryResource());
   if (a.IsInt()) return TypedValue(-a.ValueInt(), a.GetMemoryResource());
   if (a.IsDouble()) return TypedValue(-a.ValueDouble(), a.GetMemoryResource());
+  if (a.IsDuration()) return TypedValue(-a.ValueDuration(), a.GetMemoryResource());
   throw TypedValueException("Invalid unary minus operand type (-{})", a.type());
 }
 
@@ -666,6 +844,7 @@ inline void EnsureArithmeticallyOk(const TypedValue &a, const TypedValue &b, boo
   // checked here because they are handled before this check is performed in
   // arithmetic op implementations.
 
+  // TODO(antonio2368): Introduce typed value arithmetic
   if (!is_legal(a) || !is_legal(b))
     throw TypedValueException("Invalid {} operand types {}, {}", op_name, a.type(), b.type());
 }
@@ -699,6 +878,7 @@ TypedValue operator+(const TypedValue &a, const TypedValue &b) {
   } else {
     return TypedValue(a.ValueInt() + b.ValueInt(), a.GetMemoryResource());
   }
+  // TODO(antonio2368): Introduce typed value arithmetic
 }
 
 TypedValue operator-(const TypedValue &a, const TypedValue &b) {
@@ -711,6 +891,7 @@ TypedValue operator-(const TypedValue &a, const TypedValue &b) {
   } else {
     return TypedValue(a.ValueInt() - b.ValueInt(), a.GetMemoryResource());
   }
+  // TODO(antonio2368): Introduce typed value arithmetic
 }
 
 TypedValue operator/(const TypedValue &a, const TypedValue &b) {
@@ -838,6 +1019,15 @@ size_t TypedValue::Hash::operator()(const TypedValue &value) const {
       return utils::FnvCollection<decltype(vertices), VertexAccessor>{}(vertices) ^
              utils::FnvCollection<decltype(edges), EdgeAccessor>{}(edges);
     }
+    case TypedValue::Type::Date:
+      return DateHash{}(value.ValueDate());
+    case TypedValue::Type::LocalTime:
+      return LocalTimeHash{}(value.ValueLocalTime());
+    case TypedValue::Type::LocalDateTime:
+      return LocalDateTimeHash{}(value.ValueLocalDateTime());
+    case TypedValue::Type::Duration:
+      return DurationHash{}(value.ValueDuration());
+      break;
   }
   LOG_FATAL("Unhandled TypedValue.type() in hash function");
 }
diff --git a/src/query/typed_value.hpp b/src/query/typed_value.hpp
index 903088904..083ffcd5d 100644
--- a/src/query/typed_value.hpp
+++ b/src/query/typed_value.hpp
@@ -11,6 +11,7 @@
 
 #include "query/db_accessor.hpp"
 #include "query/path.hpp"
+#include "query/temporal.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/memory.hpp"
 #include "utils/pmr/map.hpp"
@@ -56,7 +57,22 @@ class TypedValue {
   };
 
   /** A value type. Each type corresponds to exactly one C++ type */
-  enum class Type : unsigned { Null, Bool, Int, Double, String, List, Map, Vertex, Edge, Path };
+  enum class Type : unsigned {
+    Null,
+    Bool,
+    Int,
+    Double,
+    String,
+    List,
+    Map,
+    Vertex,
+    Edge,
+    Path,
+    Date,
+    LocalTime,
+    LocalDateTime,
+    Duration
+  };
 
   // TypedValue at this exact moment of compilation is an incomplete type, and
   // the standard says that instantiating a container with an incomplete type
@@ -124,6 +140,26 @@ class TypedValue {
     double_v = value;
   }
 
+  explicit TypedValue(const Date &value, utils::MemoryResource *memory = utils::NewDeleteResource())
+      : memory_(memory), type_(Type::Date) {
+    date_v = value;
+  }
+
+  explicit TypedValue(const LocalTime &value, utils::MemoryResource *memory = utils::NewDeleteResource())
+      : memory_(memory), type_(Type::LocalTime) {
+    local_time_v = value;
+  }
+
+  explicit TypedValue(const LocalDateTime &value, utils::MemoryResource *memory = utils::NewDeleteResource())
+      : memory_(memory), type_(Type::LocalDateTime) {
+    local_date_time_v = value;
+  }
+
+  explicit TypedValue(const Duration &value, utils::MemoryResource *memory = utils::NewDeleteResource())
+      : memory_(memory), type_(Type::Duration) {
+    duration_v = value;
+  }
+
   // conversion function to storage::PropertyValue
   explicit operator storage::PropertyValue() const;
 
@@ -381,6 +417,10 @@ class TypedValue {
   TypedValue &operator=(const VertexAccessor &);
   TypedValue &operator=(const EdgeAccessor &);
   TypedValue &operator=(const Path &);
+  TypedValue &operator=(const Date &);
+  TypedValue &operator=(const LocalTime &);
+  TypedValue &operator=(const LocalDateTime &);
+  TypedValue &operator=(const Duration &);
 
   /** Copy assign other, utils::MemoryResource of `this` is used */
   TypedValue &operator=(const TypedValue &other);
@@ -431,6 +471,11 @@ class TypedValue {
   DECLARE_VALUE_AND_TYPE_GETTERS(EdgeAccessor, Edge)
   DECLARE_VALUE_AND_TYPE_GETTERS(Path, Path)
 
+  DECLARE_VALUE_AND_TYPE_GETTERS(Date, Date)
+  DECLARE_VALUE_AND_TYPE_GETTERS(LocalTime, LocalTime)
+  DECLARE_VALUE_AND_TYPE_GETTERS(LocalDateTime, LocalDateTime)
+  DECLARE_VALUE_AND_TYPE_GETTERS(Duration, Duration)
+
 #undef DECLARE_VALUE_AND_TYPE_GETTERS
 
   /**  Checks if value is a TypedValue::Null. */
@@ -468,6 +513,10 @@ class TypedValue {
     VertexAccessor vertex_v;
     EdgeAccessor edge_v;
     Path path_v;
+    Date date_v;
+    LocalTime local_time_v;
+    LocalDateTime local_date_time_v;
+    Duration duration_v;
   };
 
   /**
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index d0cff86ce..0aeefb5cb 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -125,6 +125,8 @@ target_link_libraries(${test_prefix}query_serialization_property_value mg-query)
 add_unit_test(query_streams.cpp)
 target_link_libraries(${test_prefix}query_streams mg-query kafka-mock)
 
+add_unit_test(query_temporal query_temporal.cpp)
+target_link_libraries(${test_prefix}query_temporal mg-query)
 
 # Test query/procedure
 add_unit_test(query_procedure_mgp_type.cpp)
diff --git a/tests/unit/formatters.hpp b/tests/unit/formatters.hpp
index d5c9712b7..c6884d59a 100644
--- a/tests/unit/formatters.hpp
+++ b/tests/unit/formatters.hpp
@@ -3,6 +3,7 @@
 #include <sstream>
 #include <string>
 
+#include "query/temporal.hpp"
 #include "query/typed_value.hpp"
 #include "utils/algorithm.hpp"
 
@@ -61,6 +62,15 @@ inline std::string ToString(const query::Path &path, const TAccessor &acc) {
   return os.str();
 }
 
+// TODO(antonio2368): Define printing of dates
+inline std::string ToString(const query::Date) { return ""; }
+
+inline std::string ToString(const query::LocalTime) { return ""; }
+
+inline std::string ToString(const query::LocalDateTime) { return ""; }
+
+inline std::string ToString(const query::Duration) { return ""; }
+
 template <class TAccessor>
 inline std::string ToString(const query::TypedValue &value, const TAccessor &acc) {
   std::ostringstream os;
@@ -102,6 +112,18 @@ inline std::string ToString(const query::TypedValue &value, const TAccessor &acc
     case query::TypedValue::Type::Path:
       os << ToString(value.ValuePath(), acc);
       break;
+    case query::TypedValue::Type::Date:
+      os << ToString(value.ValueDate());
+      break;
+    case query::TypedValue::Type::LocalTime:
+      os << ToString(value.ValueLocalTime());
+      break;
+    case query::TypedValue::Type::LocalDateTime:
+      os << ToString(value.ValueLocalDateTime());
+      break;
+    case query::TypedValue::Type::Duration:
+      os << ToString(value.ValueDuration());
+      break;
   }
   return os.str();
 }
diff --git a/tests/unit/query_temporal.cpp b/tests/unit/query_temporal.cpp
new file mode 100644
index 000000000..98ac2adea
--- /dev/null
+++ b/tests/unit/query_temporal.cpp
@@ -0,0 +1,161 @@
+#include <optional>
+
+#include <gtest/gtest.h>
+
+#include "query/temporal.hpp"
+#include "utils/exceptions.hpp"
+
+namespace {
+struct TestDateParameters {
+  query::DateParameters date_parameters;
+  bool should_throw;
+};
+
+constexpr std::array test_dates{TestDateParameters{{-1996, 11, 22}, true}, TestDateParameters{{1996, -11, 22}, true},
+                                TestDateParameters{{1996, 11, -22}, true}, TestDateParameters{{1, 13, 3}, true},
+                                TestDateParameters{{1, 12, 32}, true},     TestDateParameters{{1, 2, 29}, true},
+                                TestDateParameters{{2020, 2, 29}, false},  TestDateParameters{{1700, 2, 29}, true},
+                                TestDateParameters{{1200, 2, 29}, false},  TestDateParameters{{10000, 12, 3}, true}};
+
+struct TestLocalTimeParameters {
+  query::LocalTimeParameters local_time_parameters;
+  bool should_throw;
+};
+
+constexpr std::array test_local_times{
+    TestLocalTimeParameters{{.hours = 24}, true},           TestLocalTimeParameters{{.hours = -1}, true},
+    TestLocalTimeParameters{{.minutes = -1}, true},         TestLocalTimeParameters{{.minutes = 60}, true},
+    TestLocalTimeParameters{{.seconds = -1}, true},         TestLocalTimeParameters{{.minutes = 60}, true},
+    TestLocalTimeParameters{{.milliseconds = -1}, true},    TestLocalTimeParameters{{.milliseconds = 1000}, true},
+    TestLocalTimeParameters{{.microseconds = -1}, true},    TestLocalTimeParameters{{.microseconds = 1000}, true},
+    TestLocalTimeParameters{{23, 59, 59, 999, 999}, false}, TestLocalTimeParameters{{0, 0, 0, 0, 0}, false}};
+}  // namespace
+
+TEST(TemporalTest, DateConstruction) {
+  std::optional<query::Date> test_date;
+
+  for (const auto [date_parameters, should_throw] : test_dates) {
+    if (should_throw) {
+      ASSERT_THROW(test_date.emplace(date_parameters), utils::BasicException);
+    } else {
+      ASSERT_NO_THROW(test_date.emplace(date_parameters));
+    }
+  }
+}
+
+TEST(TemporalTest, DateMicrosecondsSinceEpochConversion) {
+  const auto check_microseconds = [](const auto date_parameters) {
+    query::Date initial_date{date_parameters};
+    const auto microseconds = initial_date.MicrosecondsSinceEpoch();
+    query::Date new_date{microseconds};
+    ASSERT_EQ(initial_date, new_date);
+  };
+
+  check_microseconds(query::DateParameters{2020, 11, 22});
+  check_microseconds(query::DateParameters{1900, 2, 22});
+  check_microseconds(query::DateParameters{0, 1, 1});
+
+  ASSERT_THROW(check_microseconds(query::DateParameters{-10, 1, 1}), utils::BasicException);
+
+  {
+    query::Date date{query::DateParameters{1970, 1, 1}};
+    ASSERT_EQ(date.MicrosecondsSinceEpoch(), 0);
+  }
+  {
+    query::Date date{query::DateParameters{1910, 1, 1}};
+    ASSERT_LT(date.MicrosecondsSinceEpoch(), 0);
+  }
+  {
+    query::Date date{query::DateParameters{2021, 1, 1}};
+    ASSERT_GT(date.MicrosecondsSinceEpoch(), 0);
+  }
+}
+
+TEST(TemporalTest, LocalTimeConstruction) {
+  std::optional<query::LocalTime> test_local_time;
+
+  for (const auto [local_time_parameters, should_throw] : test_local_times) {
+    if (should_throw) {
+      ASSERT_THROW(test_local_time.emplace(local_time_parameters), utils::BasicException);
+    } else {
+      ASSERT_NO_THROW(test_local_time.emplace(local_time_parameters));
+    }
+  }
+}
+
+TEST(TemporalTest, LocalTimeMicrosecondsSinceEpochConversion) {
+  const auto check_microseconds = [](const query::LocalTimeParameters &parameters) {
+    query::LocalTime initial_local_time{parameters};
+    const auto microseconds = initial_local_time.MicrosecondsSinceEpoch();
+    query::LocalTime new_local_time{microseconds};
+    ASSERT_EQ(initial_local_time, new_local_time);
+  };
+
+  check_microseconds(query::LocalTimeParameters{23, 59, 59, 999, 999});
+  check_microseconds(query::LocalTimeParameters{0, 0, 0, 0, 0});
+  check_microseconds(query::LocalTimeParameters{14, 8, 55, 321, 452});
+}
+
+TEST(TemporalTest, LocalDateTimeMicrosecondsSinceEpochConversion) {
+  const auto check_microseconds = [](const query::DateParameters date_parameters,
+                                     const query::LocalTimeParameters &local_time_parameters) {
+    query::LocalDateTime initial_local_date_time{date_parameters, local_time_parameters};
+    const auto microseconds = initial_local_date_time.MicrosecondsSinceEpoch();
+    query::LocalDateTime new_local_date_time{microseconds};
+    ASSERT_EQ(initial_local_date_time, new_local_date_time);
+  };
+
+  check_microseconds(query::DateParameters{2020, 11, 22}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+  check_microseconds(query::DateParameters{1900, 2, 22}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+  check_microseconds(query::DateParameters{0, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+  {
+    query::LocalDateTime local_date_time(query::DateParameters{1970, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+    ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(), 0);
+  }
+  {
+    query::LocalDateTime local_date_time(query::DateParameters{1970, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 1});
+    ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0);
+  }
+  {
+    query::LocalTimeParameters local_time_parameters{12, 10, 40, 42, 42};
+    query::LocalDateTime local_date_time{query::DateParameters{1970, 1, 1}, local_time_parameters};
+    ASSERT_EQ(local_date_time.MicrosecondsSinceEpoch(),
+              query::LocalTime{local_time_parameters}.MicrosecondsSinceEpoch());
+  }
+  {
+    query::LocalDateTime local_date_time(query::DateParameters{1910, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+    ASSERT_LT(local_date_time.MicrosecondsSinceEpoch(), 0);
+  }
+  {
+    query::LocalDateTime local_date_time(query::DateParameters{2021, 1, 1}, query::LocalTimeParameters{0, 0, 0, 0, 0});
+    ASSERT_GT(local_date_time.MicrosecondsSinceEpoch(), 0);
+  }
+}
+
+TEST(TemporalTest, DurationConversion) {
+  {
+    query::Duration duration{{.years = 2.5}};
+    const auto microseconds = duration.microseconds;
+    query::LocalDateTime local_date_time{microseconds};
+    ASSERT_EQ(local_date_time.date.years, 2 + 1970);
+    ASSERT_EQ(local_date_time.date.months, 6 + 1);
+  };
+  {
+    query::Duration duration{{.months = 26}};
+    const auto microseconds = duration.microseconds;
+    query::LocalDateTime local_date_time{microseconds};
+    ASSERT_EQ(local_date_time.date.years, 2 + 1970);
+    ASSERT_EQ(local_date_time.date.months, 2 + 1);
+  };
+  {
+    query::Duration duration{{.minutes = 123.25}};
+    const auto microseconds = duration.microseconds;
+    query::LocalDateTime local_date_time{microseconds};
+    ASSERT_EQ(local_date_time.date.years, 1970);
+    ASSERT_EQ(local_date_time.date.months, 1);
+    ASSERT_EQ(local_date_time.date.days, 1);
+    ASSERT_EQ(local_date_time.local_time.hours, 2);
+    ASSERT_EQ(local_date_time.local_time.minutes, 3);
+    ASSERT_EQ(local_date_time.local_time.seconds, 15);
+  };
+}