From 0a23eb11aeb16a3b7a20e9311cd1a7e78db07f51 Mon Sep 17 00:00:00 2001
From: antonio2368 <antonio2368@users.noreply.github.com>
Date: Mon, 20 Sep 2021 10:59:30 +0200
Subject: [PATCH] Define procedure API for temporal types (#229)

---
 include/mg_procedure.h                    | 396 ++++++++++++++++++
 include/mgp.py                            |  17 +-
 src/query/procedure/cypher_types.hpp      |  40 +-
 src/query/procedure/mg_procedure_impl.cpp | 477 +++++++++++++++++++++-
 src/query/procedure/mg_procedure_impl.hpp | 211 ++++++++++
 src/query/procedure/py_module.cpp         | 140 +++++++
 src/utils/temporal.cpp                    |  72 ++--
 src/utils/temporal.hpp                    |   6 +
 8 files changed, 1313 insertions(+), 46 deletions(-)

diff --git a/include/mg_procedure.h b/include/mg_procedure.h
index 1d09069c1..8ce1d40a5 100644
--- a/include/mg_procedure.h
+++ b/include/mg_procedure.h
@@ -125,6 +125,18 @@ struct mgp_edge;
 /// Path containing mgp_vertex and mgp_edge instances.
 struct mgp_path;
 
+/// Date stored in Memgraph.
+struct mgp_date;
+
+/// Local time stored in Memgraph.
+struct mgp_local_time;
+
+/// Local date-time stored in Memgraph.
+struct mgp_local_date_time;
+
+/// Duration stored in Memgraph.
+struct mgp_duration;
+
 /// All available types that can be stored in a mgp_value
 enum mgp_value_type {
   // NOTE: New types need to be appended, so as not to break ABI.
@@ -138,6 +150,10 @@ enum mgp_value_type {
   MGP_VALUE_TYPE_VERTEX,
   MGP_VALUE_TYPE_EDGE,
   MGP_VALUE_TYPE_PATH,
+  MGP_VALUE_TYPE_DATE,
+  MGP_VALUE_TYPE_LOCAL_TIME,
+  MGP_VALUE_TYPE_LOCAL_DATE_TIME,
+  MGP_VALUE_TYPE_DURATION,
 };
 
 /// Free the memory used by the given mgp_value instance.
@@ -209,6 +225,38 @@ enum mgp_error mgp_value_make_edge(struct mgp_edge *val, struct mgp_value **resu
 /// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
 enum mgp_error mgp_value_make_path(struct mgp_path *val, struct mgp_value **result);
 
+/// Create a mgp_value storing a mgp_date.
+/// You need to free the instance through mgp_value_destroy. The ownership of
+/// the date is transferred to the created mgp_value and destroying the mgp_value will
+/// destroy the mgp_date. Therefore, if a mgp_value is successfully created you
+/// must not call mgp_date_destroy on the given date.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_date(struct mgp_date *val, struct mgp_value **result);
+
+/// Create a mgp_value storing a mgp_local_time.
+/// You need to free the instance through mgp_value_destroy. The ownership of
+/// the local time is transferred to the created mgp_value and destroying the mgp_value will
+/// destroy the mgp_local_time. Therefore, if a mgp_value is successfully created you
+/// must not call mgp_local_time_destroy on the given local time.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_local_time(struct mgp_local_time *val, struct mgp_value **result);
+
+/// Create a mgp_value storing a mgp_local_date_time.
+/// You need to free the instance through mgp_value_destroy. The ownership of
+/// the local date-time is transferred to the created mgp_value and destroying the mgp_value will
+/// destroy the mgp_local_date_time. Therefore, if a mgp_value is successfully created you
+/// must not call mgp_local_date_time_destroy on the given local date-time.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_local_date_time(struct mgp_local_date_time *val, struct mgp_value **result);
+
+/// Create a mgp_value storing a mgp_duration.
+/// You need to free the instance through mgp_value_destroy. The ownership of
+/// the duration is transferred to the created mgp_value and destroying the mgp_value will
+/// destroy the mgp_duration. Therefore, if a mgp_value is successfully created you
+/// must not call mgp_duration_destroy on the given duration.
+/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_value.
+enum mgp_error mgp_value_make_duration(struct mgp_duration *val, struct mgp_value **result);
+
 /// Get the type of the value contained in mgp_value.
 /// Current implementation always returns without errors.
 enum mgp_error mgp_value_get_type(struct mgp_value *val, enum mgp_value_type *result);
@@ -253,6 +301,22 @@ enum mgp_error mgp_value_is_edge(struct mgp_value *val, int *result);
 /// Current implementation always returns without errors.
 enum mgp_error mgp_value_is_path(struct mgp_value *val, int *result);
 
+/// Result is non-zero if the given mgp_value stores a date.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_date(struct mgp_value *val, int *result);
+
+/// Result is non-zero if the given mgp_value stores a local time.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_local_time(struct mgp_value *val, int *result);
+
+/// Result is non-zero if the given mgp_value stores a local date-time.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_local_date_time(struct mgp_value *val, int *result);
+
+/// Result is non-zero if the given mgp_value stores a duration.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_is_duration(struct mgp_value *val, int *result);
+
 /// Get the contained boolean value.
 /// Non-zero values represent `true`, while zero represents `false`.
 /// Result is undefined if mgp_value does not contain the expected type.
@@ -299,6 +363,26 @@ enum mgp_error mgp_value_get_edge(struct mgp_value *val, struct mgp_edge **resul
 /// Current implementation always returns without errors.
 enum mgp_error mgp_value_get_path(struct mgp_value *val, struct mgp_path **result);
 
+/// Get the contained date.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_date(struct mgp_value *val, struct mgp_date **result);
+
+/// Get the contained local time.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_local_time(struct mgp_value *val, struct mgp_local_time **result);
+
+/// Get the contained local date-time.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_local_date_time(struct mgp_value *val, struct mgp_local_date_time **result);
+
+/// Get the contained duration.
+/// Result is undefined if mgp_value does not contain the expected type.
+/// Current implementation always returns without errors.
+enum mgp_error mgp_value_get_duration(struct mgp_value *val, struct mgp_duration **result);
+
 /// Create an empty list with given capacity.
 /// You need to free the created instance with mgp_list_destroy.
 /// The created list will have allocated enough memory for `capacity` elements
@@ -774,6 +858,302 @@ enum mgp_error mgp_vertices_iterator_underlying_graph_is_mutable(struct mgp_vert
 /// Result is NULL if the end of the iteration has been reached.
 enum mgp_error mgp_vertices_iterator_get(struct mgp_vertices_iterator *it, struct mgp_vertex **result);
 
+/// @name Temporal Types
+///
+///@{
+
+struct mgp_date_parameters {
+  int year;
+  int month;
+  int day;
+};
+
+/// Create a date from a string following the ISO 8601 format.
+/// Resulting date must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the string cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_from_string(const char *string, struct mgp_memory *memory, struct mgp_date **date);
+
+/// Create a date from mgp_date_parameter.
+/// Resulting date must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the parameters cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_from_parameters(struct mgp_date_parameters *parameters, struct mgp_memory *memory,
+                                        struct mgp_date **date);
+
+/// Copy a mgp_date.
+/// Resulting pointer must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_copy(struct mgp_date *date, struct mgp_memory *memory, struct mgp_date **result);
+
+/// Free the memory used by a mgp_date.
+void mgp_date_destroy(struct mgp_date *date);
+
+/// Result is non-zero if given dates are equal, otherwise 0.
+enum mgp_error mgp_date_equal(struct mgp_date *first, struct mgp_date *second, int *result);
+
+/// Get the year property of the date.
+enum mgp_error mgp_date_get_year(struct mgp_date *date, int *year);
+
+/// Get the month property of the date.
+enum mgp_error mgp_date_get_month(struct mgp_date *date, int *month);
+
+/// Get the day property of the date.
+enum mgp_error mgp_date_get_day(struct mgp_date *date, int *day);
+
+/// Get the date as microseconds from Unix epoch.
+enum mgp_error mgp_date_timestamp(struct mgp_date *date, int64_t *timestamp);
+
+/// Get the date representing current date.
+/// Resulting date must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_now(struct mgp_memory *memory, struct mgp_date **date);
+
+/// Add a duration to the date.
+/// Resulting date must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid date.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_add_duration(struct mgp_date *date, struct mgp_duration *dur, struct mgp_memory *memory,
+                                     struct mgp_date **result);
+
+/// Subtract a duration from the date.
+/// Resulting date must be freed with mgp_date_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid date.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_sub_duration(struct mgp_date *date, struct mgp_duration *dur, struct mgp_memory *memory,
+                                     struct mgp_date **result);
+
+/// Get a duration between two dates.
+/// Resulting duration must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_date_diff(struct mgp_date *first, struct mgp_date *second, struct mgp_memory *memory,
+                             struct mgp_duration **result);
+
+struct mgp_local_time_parameters {
+  int hour;
+  int minute;
+  int second;
+  int millisecond;
+  int microsecond;
+};
+
+/// Create a local time from a string following the ISO 8601 format.
+/// Resulting local time must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the string cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_from_string(const char *string, struct mgp_memory *memory,
+                                          struct mgp_local_time **local_time);
+
+/// Create a local time from mgp_local_time_parameters.
+/// Resulting local time must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the parameters cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_from_parameters(struct mgp_local_time_parameters *parameters, struct mgp_memory *memory,
+                                              struct mgp_local_time **local_time);
+
+/// Copy a mgp_local_time.
+/// Resulting pointer must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_copy(struct mgp_local_time *local_time, struct mgp_memory *memory,
+                                   struct mgp_local_time **result);
+
+/// Free the memory used by a mgp_local_time.
+void mgp_local_time_destroy(struct mgp_local_time *local_time);
+
+/// Result is non-zero if given local times are equal, otherwise 0.
+enum mgp_error mgp_local_time_equal(struct mgp_local_time *first, struct mgp_local_time *second, int *result);
+
+/// Get the hour property of the local time.
+enum mgp_error mgp_local_time_get_hour(struct mgp_local_time *local_time, int *hour);
+
+/// Get the minute property of the local time.
+enum mgp_error mgp_local_time_get_minute(struct mgp_local_time *local_time, int *minute);
+
+/// Get the second property of the local time.
+enum mgp_error mgp_local_time_get_second(struct mgp_local_time *local_time, int *second);
+
+/// Get the millisecond property of the local time.
+enum mgp_error mgp_local_time_get_millisecond(struct mgp_local_time *local_time, int *millisecond);
+
+/// Get the microsecond property of the local time.
+enum mgp_error mgp_local_time_get_microsecond(struct mgp_local_time *local_time, int *microsecond);
+
+/// Get the local time as microseconds from midnight.
+enum mgp_error mgp_local_time_timestamp(struct mgp_local_time *local_time, int64_t *timestamp);
+
+/// Get the local time representing current time.
+/// Resulting pointer must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_now(struct mgp_memory *memory, struct mgp_local_time **local_time);
+
+/// Add a duration to the local time.
+/// Resulting pointer must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid local time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_add_duration(struct mgp_local_time *local_time, struct mgp_duration *dur,
+                                           struct mgp_memory *memory, struct mgp_local_time **result);
+
+/// Subtract a duration from the local time.
+/// Resulting pointer must be freed with mgp_local_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid local time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_sub_duration(struct mgp_local_time *local_time, struct mgp_duration *dur,
+                                           struct mgp_memory *memory, struct mgp_local_time **result);
+
+/// Get a duration between two local times.
+/// Resulting pointer must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_date.
+enum mgp_error mgp_local_time_diff(struct mgp_local_time *first, struct mgp_local_time *second,
+                                   struct mgp_memory *memory, struct mgp_duration **result);
+
+struct mgp_local_date_time_parameters {
+  struct mgp_date_parameters *date_parameters;
+  struct mgp_local_time_parameters *local_time_parameters;
+};
+
+/// Create a local date-time from a string following the ISO 8601 format.
+/// Resulting local date-time must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the string cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_from_string(const char *string, struct mgp_memory *memory,
+                                               struct mgp_local_date_time **local_date_time);
+
+/// Create a local date-time from mgp_local_date_time_parameters.
+/// Resulting local date-time must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the parameters cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_from_parameters(struct mgp_local_date_time_parameters *parameters,
+                                                   struct mgp_memory *memory,
+                                                   struct mgp_local_date_time **local_date_time);
+
+/// Copy a mgp_local_date_time.
+/// Resulting pointer must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_copy(struct mgp_local_date_time *local_date_time, struct mgp_memory *memory,
+                                        struct mgp_local_date_time **result);
+
+/// Free the memory used by a mgp_local_date_time.
+void mgp_local_date_time_destroy(struct mgp_local_date_time *local_date_time);
+
+/// Result is non-zero if given local date-times are equal, otherwise 0.
+enum mgp_error mgp_local_date_time_equal(struct mgp_local_date_time *first, struct mgp_local_date_time *second,
+                                         int *result);
+
+/// Get the year property of the local date-time.
+enum mgp_error mgp_local_date_time_get_year(struct mgp_local_date_time *local_date_time, int *year);
+
+/// Get the month property of the local date-time.
+enum mgp_error mgp_local_date_time_get_month(struct mgp_local_date_time *local_date_time, int *month);
+
+/// Get the day property of the local date-time.
+enum mgp_error mgp_local_date_time_get_day(struct mgp_local_date_time *local_date_time, int *day);
+
+/// Get the hour property of the local date-time.
+enum mgp_error mgp_local_date_time_get_hour(struct mgp_local_date_time *local_date_time, int *hour);
+
+/// Get the minute property of the local date-time.
+enum mgp_error mgp_local_date_time_get_minute(struct mgp_local_date_time *local_date_time, int *minute);
+
+/// Get the second property of the local date-time.
+enum mgp_error mgp_local_date_time_get_second(struct mgp_local_date_time *local_date_time, int *second);
+
+/// Get the milisecond property of the local date-time.
+enum mgp_error mgp_local_date_time_get_millisecond(struct mgp_local_date_time *local_date_time, int *millisecond);
+
+/// Get the microsecond property of the local date-time.
+enum mgp_error mgp_local_date_time_get_microsecond(struct mgp_local_date_time *local_date_time, int *microsecond);
+
+/// Get the local date-time as microseconds from Unix epoch.
+enum mgp_error mgp_local_date_time_timestamp(struct mgp_local_date_time *local_date_time, int64_t *timestamp);
+
+/// Get the local date-time representing current date and time.
+/// Resulting local date-time must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_now(struct mgp_memory *memory, struct mgp_local_date_time **local_date_time);
+
+/// Add a duration to the local date-time.
+/// Resulting local date-time must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid local date-time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_add_duration(struct mgp_local_date_time *local_date_time, struct mgp_duration *dur,
+                                                struct mgp_memory *memory, struct mgp_local_date_time **result);
+
+/// Subtract a duration from the local date-time.
+/// Resulting local date-time must be freed with mgp_local_date_time_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid local date-time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_sub_duration(struct mgp_local_date_time *local_date_time, struct mgp_duration *dur,
+                                                struct mgp_memory *memory, struct mgp_local_date_time **result);
+
+/// Get a duration between two local date-times.
+/// Resulting duration must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_local_date_time.
+enum mgp_error mgp_local_date_time_diff(struct mgp_local_date_time *first, struct mgp_local_date_time *second,
+                                        struct mgp_memory *memory, struct mgp_duration **result);
+
+struct mgp_duration_parameters {
+  double day;
+  double hour;
+  double minute;
+  double second;
+  double millisecond;
+  double microsecond;
+};
+
+/// Create a duration from a string following the ISO 8601 format.
+/// Resulting duration must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the string cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_from_string(const char *string, struct mgp_memory *memory, struct mgp_duration **duration);
+
+/// Create a duration from mgp_duration_parameters.
+/// Resulting duration must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the parameters cannot be parsed correctly.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_from_parameters(struct mgp_duration_parameters *parameters, struct mgp_memory *memory,
+                                            struct mgp_duration **duration);
+
+/// Create a duration from microseconds.
+/// Resulting duration must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_from_microseconds(int64_t microseconds, struct mgp_memory *memory,
+                                              struct mgp_duration **duration);
+
+/// Copy a mgp_duration.
+/// Resulting pointer must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_copy(struct mgp_duration *duration, struct mgp_memory *memory,
+                                 struct mgp_duration **result);
+
+/// Free the memory used by a mgp_duration.
+void mgp_duration_destroy(struct mgp_duration *duration);
+
+/// Result is non-zero if given durations are equal, otherwise 0.
+enum mgp_error mgp_duration_equal(struct mgp_duration *first, struct mgp_duration *second, int *result);
+
+/// Get the duration as microseconds.
+enum mgp_error mgp_duration_get_microseconds(struct mgp_duration *duration, int64_t *microseconds);
+
+/// Apply unary minus operator to the duration.
+/// Resulting pointer must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_neg(struct mgp_duration *dur, struct mgp_memory *memory, struct mgp_duration **result);
+
+/// Add two durations.
+/// Resulting pointer must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid duration.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_add(struct mgp_duration *first, struct mgp_duration *second, struct mgp_memory *memory,
+                                struct mgp_duration **result);
+
+/// Subtract two durations.
+/// Resulting pointer must be freed with mgp_duration_destroy.
+/// Return MGP_ERROR_INVALID_ARGUMENT if the operation results in an invalid duration.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_duration.
+enum mgp_error mgp_duration_sub(struct mgp_duration *first, struct mgp_duration *second, struct mgp_memory *memory,
+                                struct mgp_duration **result);
+///@}
+
 /// Advance the iterator to the next vertex and return it.
 /// The previous mgp_vertex obtained through mgp_vertices_iterator_get
 /// will be invalidated, and you must not use its value.
@@ -857,6 +1237,22 @@ enum mgp_error mgp_type_path(struct mgp_type **result);
 /// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
 enum mgp_error mgp_type_list(struct mgp_type *element_type, struct mgp_type **result);
 
+/// Get the type representing a date.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_date(struct mgp_type **result);
+
+/// Get the type representing a local time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_local_time(struct mgp_type **result);
+
+/// Get the type representing a local date-time.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_local_date_time(struct mgp_type **result);
+
+/// Get the type representing a duration.
+/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
+enum mgp_error mgp_type_duration(struct mgp_type **result);
+
 /// Build a type representing either a `null` value or a value of given `type`.
 ///
 /// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
diff --git a/include/mgp.py b/include/mgp.py
index 119366a26..b15d0d969 100644
--- a/include/mgp.py
+++ b/include/mgp.py
@@ -21,6 +21,7 @@ from functools import wraps
 import inspect
 import sys
 import typing
+import datetime
 
 
 import _mgp
@@ -951,7 +952,15 @@ Number = typing.Union[int, float]
 
 Map = typing.Union[dict, Edge, Vertex]
 
-Any = typing.Union[bool, str, Number, Map, Path, list]
+Date = datetime.date
+
+LocalTime = datetime.time
+
+LocalDateTime = datetime.datetime
+
+Duration = datetime.timedelta
+
+Any = typing.Union[bool, str, Number, Map, Path, list, Date, LocalTime, LocalDateTime, Duration]
 
 List = typing.List
 
@@ -980,7 +989,11 @@ def _typing_to_cypher_type(type_):
         Map: _mgp.type_map(),
         Vertex: _mgp.type_node(),
         Edge: _mgp.type_relationship(),
-        Path: _mgp.type_path()
+        Path: _mgp.type_path(),
+        Date: _mgp.type_date(),
+        LocalTime: _mgp.type_local_time(),
+        LocalDateTime: _mgp.type_local_date_time(),
+        Duration: _mgp.type_duration()
     }
     try:
         return simple_types[type_]
diff --git a/src/query/procedure/cypher_types.hpp b/src/query/procedure/cypher_types.hpp
index e7a41100d..eb31b3f46 100644
--- a/src/query/procedure/cypher_types.hpp
+++ b/src/query/procedure/cypher_types.hpp
@@ -129,8 +129,6 @@ class PathType : public CypherType {
   bool SatisfiesType(const query::TypedValue &value) const override { return value.IsPath(); }
 };
 
-// TODO: There's also Temporal Types, but we currently do not support those.
-
 // You'd think that MapType would be a composite type like ListType, but nope.
 // Why? No-one really knows. It's defined like that in "CIP2015-09-16 Public
 // Type System and Type Annotations"
@@ -149,6 +147,44 @@ class MapType : public CypherType {
   }
 };
 
+// Temporal Types
+
+class DateType : public CypherType {
+ public:
+  std::string_view GetPresentableName() const override { return "DATE"; }
+
+  bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_DATE; }
+
+  bool SatisfiesType(const query::TypedValue &value) const override { return value.IsDate(); }
+};
+
+class LocalTimeType : public CypherType {
+ public:
+  std::string_view GetPresentableName() const override { return "LOCAL_TIME"; }
+
+  bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_LOCAL_TIME; }
+
+  bool SatisfiesType(const query::TypedValue &value) const override { return value.IsLocalTime(); }
+};
+
+class LocalDateTimeType : public CypherType {
+ public:
+  std::string_view GetPresentableName() const override { return "LOCAL_DATE_TIME"; }
+
+  bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_LOCAL_DATE_TIME; }
+
+  bool SatisfiesType(const query::TypedValue &value) const override { return value.IsLocalDateTime(); }
+};
+
+class DurationType : public CypherType {
+ public:
+  std::string_view GetPresentableName() const override { return "DURATION"; }
+
+  bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_DURATION; }
+
+  bool SatisfiesType(const query::TypedValue &value) const override { return value.IsDuration(); }
+};
+
 // Composite Types
 
 class ListType : public CypherType {
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index 1f2c6f849..3801067fa 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -23,6 +23,7 @@
 #include "utils/math.hpp"
 #include "utils/memory.hpp"
 #include "utils/string.hpp"
+#include "utils/temporal.hpp"
 
 // This file contains implementation of top level C API functions, but this is
 // all actually part of query::procedure. So use that namespace for simplicity.
@@ -163,6 +164,9 @@ template <typename TFunc, typename... Args>
   } catch (const std::exception &e) {
     spdlog::error("Unexpected error during mg API call: {}", e.what());
     return MGP_ERROR_UNKNOWN_ERROR;
+  } catch (const utils::temporal::InvalidArgumentException &e) {
+    spdlog::error("Invalid argument was sent to an mg API call for temporal types: {}", e.what());
+    return MGP_ERROR_INVALID_ARGUMENT;
   } catch (...) {
     spdlog::error("Unexpected error during mg API call");
     return MGP_ERROR_UNKNOWN_ERROR;
@@ -262,8 +266,15 @@ mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
       return MGP_VALUE_TYPE_EDGE;
     case query::TypedValue::Type::Path:
       return MGP_VALUE_TYPE_PATH;
+    case query::TypedValue::Type::Date:
+      return MGP_VALUE_TYPE_DATE;
+    case query::TypedValue::Type::LocalTime:
+      return MGP_VALUE_TYPE_LOCAL_TIME;
+    case query::TypedValue::Type::LocalDateTime:
+      return MGP_VALUE_TYPE_LOCAL_DATE_TIME;
+    case query::TypedValue::Type::Duration:
+      return MGP_VALUE_TYPE_DURATION;
     default:
-      // TODO(antonio2368): Implement this when we add mgp temporal types
       LOG_FATAL("Unsupported value in the procedures");
   }
 }
@@ -312,6 +323,14 @@ query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memo
       }
       return query::TypedValue(std::move(tv_path));
     }
+    case MGP_VALUE_TYPE_DATE:
+      return query::TypedValue(val.date_v->date, memory);
+    case MGP_VALUE_TYPE_LOCAL_TIME:
+      return query::TypedValue(val.local_time_v->local_time, memory);
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME:
+      return query::TypedValue(val.local_date_time_v->local_date_time, memory);
+    case MGP_VALUE_TYPE_DURATION:
+      return query::TypedValue(val.duration_v->duration, memory);
   }
 }
 
@@ -355,6 +374,26 @@ mgp_value::mgp_value(mgp_path *val, utils::MemoryResource *m) noexcept
   MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator.");
 }
 
+mgp_value::mgp_value(mgp_date *val, utils::MemoryResource *m) noexcept
+    : type(MGP_VALUE_TYPE_DATE), memory(m), date_v(val) {
+  MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator.");
+}
+
+mgp_value::mgp_value(mgp_local_time *val, utils::MemoryResource *m) noexcept
+    : type(MGP_VALUE_TYPE_LOCAL_TIME), memory(m), local_time_v(val) {
+  MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator.");
+}
+
+mgp_value::mgp_value(mgp_local_date_time *val, utils::MemoryResource *m) noexcept
+    : type(MGP_VALUE_TYPE_LOCAL_DATE_TIME), memory(m), local_date_time_v(val) {
+  MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator.");
+}
+
+mgp_value::mgp_value(mgp_duration *val, utils::MemoryResource *m) noexcept
+    : type(MGP_VALUE_TYPE_DURATION), memory(m), duration_v(val) {
+  MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator.");
+}
+
 mgp_value::mgp_value(const query::TypedValue &tv, mgp_graph *graph, utils::MemoryResource *m)
     : type(FromTypedValueType(tv.type())), memory(m) {
   switch (type) {
@@ -427,6 +466,26 @@ mgp_value::mgp_value(const query::TypedValue &tv, mgp_graph *graph, utils::Memor
       path_v = allocator.new_object<mgp_path>(std::move(tmp_path));
       break;
     }
+    case MGP_VALUE_TYPE_DATE: {
+      utils::Allocator<mgp_date> allocator(m);
+      date_v = allocator.new_object<mgp_date>(tv.ValueDate());
+      break;
+    }
+    case MGP_VALUE_TYPE_LOCAL_TIME: {
+      utils::Allocator<mgp_local_time> allocator(m);
+      local_time_v = allocator.new_object<mgp_local_time>(tv.ValueLocalTime());
+      break;
+    }
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME: {
+      utils::Allocator<mgp_local_date_time> allocator(m);
+      local_date_time_v = allocator.new_object<mgp_local_date_time>(tv.ValueLocalDateTime());
+      break;
+    }
+    case MGP_VALUE_TYPE_DURATION: {
+      utils::Allocator<mgp_duration> allocator(m);
+      duration_v = allocator.new_object<mgp_duration>(tv.ValueDuration());
+      break;
+    }
   }
 }
 
@@ -481,8 +540,33 @@ 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
-      LOG_FATAL("Unsupported type");
+      const auto &temporal_data = pv.ValueTemporalData();
+      switch (temporal_data.type) {
+        case storage::TemporalType::Date: {
+          type = MGP_VALUE_TYPE_DATE;
+          utils::Allocator<mgp_date> allocator(m);
+          date_v = allocator.new_object<mgp_date>(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalTime: {
+          type = MGP_VALUE_TYPE_LOCAL_TIME;
+          utils::Allocator<mgp_local_time> allocator(m);
+          local_time_v = allocator.new_object<mgp_local_time>(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::LocalDateTime: {
+          type = MGP_VALUE_TYPE_LOCAL_DATE_TIME;
+          utils::Allocator<mgp_local_date_time> allocator(m);
+          local_date_time_v = allocator.new_object<mgp_local_date_time>(temporal_data.microseconds);
+          break;
+        }
+        case storage::TemporalType::Duration: {
+          type = MGP_VALUE_TYPE_DURATION;
+          utils::Allocator<mgp_duration> allocator(m);
+          duration_v = allocator.new_object<mgp_duration>(temporal_data.microseconds);
+          break;
+        }
+      }
     }
   }
 }
@@ -528,6 +612,26 @@ mgp_value::mgp_value(const mgp_value &other, utils::MemoryResource *m) : type(ot
       path_v = allocator.new_object<mgp_path>(*other.path_v);
       break;
     }
+    case MGP_VALUE_TYPE_DATE: {
+      utils::Allocator<mgp_date> allocator(m);
+      date_v = allocator.new_object<mgp_date>(*other.date_v);
+      break;
+    }
+    case MGP_VALUE_TYPE_LOCAL_TIME: {
+      utils::Allocator<mgp_local_time> allocator(m);
+      local_time_v = allocator.new_object<mgp_local_time>(*other.local_time_v);
+      break;
+    }
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME: {
+      utils::Allocator<mgp_local_date_time> allocator(m);
+      local_date_time_v = allocator.new_object<mgp_local_date_time>(*other.local_date_time_v);
+      break;
+    }
+    case MGP_VALUE_TYPE_DURATION: {
+      utils::Allocator<mgp_duration> allocator(m);
+      duration_v = allocator.new_object<mgp_duration>(*other.duration_v);
+      break;
+    }
   }
 }
 
@@ -561,6 +665,18 @@ void DeleteValueMember(mgp_value *value) noexcept {
     case MGP_VALUE_TYPE_PATH:
       allocator.delete_object(value->path_v);
       return;
+    case MGP_VALUE_TYPE_DATE:
+      allocator.delete_object(value->date_v);
+      return;
+    case MGP_VALUE_TYPE_LOCAL_TIME:
+      allocator.delete_object(value->local_time_v);
+      return;
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME:
+      allocator.delete_object(value->local_date_time_v);
+      return;
+    case MGP_VALUE_TYPE_DURATION:
+      allocator.delete_object(value->duration_v);
+      return;
   }
 }
 
@@ -632,6 +748,47 @@ mgp_value::mgp_value(mgp_value &&other, utils::MemoryResource *m) : type(other.t
         path_v = allocator.new_object<mgp_path>(std::move(*other.path_v));
       }
       break;
+    case MGP_VALUE_TYPE_DATE:
+      static_assert(std::is_pointer_v<decltype(date_v)>, "Expected to move date_v by copying pointers.");
+      if (*other.GetMemoryResource() == *m) {
+        date_v = other.date_v;
+        other.type = MGP_VALUE_TYPE_NULL;
+      } else {
+        utils::Allocator<mgp_date> allocator(m);
+        date_v = allocator.new_object<mgp_date>(*other.date_v);
+      }
+      break;
+    case MGP_VALUE_TYPE_LOCAL_TIME:
+      static_assert(std::is_pointer_v<decltype(local_time_v)>, "Expected to move local_time_v by copying pointers.");
+      if (*other.GetMemoryResource() == *m) {
+        local_time_v = other.local_time_v;
+        other.type = MGP_VALUE_TYPE_NULL;
+      } else {
+        utils::Allocator<mgp_local_time> allocator(m);
+        local_time_v = allocator.new_object<mgp_local_time>(*other.local_time_v);
+      }
+      break;
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME:
+      static_assert(std::is_pointer_v<decltype(local_date_time_v)>,
+                    "Expected to move local_date_time_v by copying pointers.");
+      if (*other.GetMemoryResource() == *m) {
+        local_date_time_v = other.local_date_time_v;
+        other.type = MGP_VALUE_TYPE_NULL;
+      } else {
+        utils::Allocator<mgp_local_date_time> allocator(m);
+        local_date_time_v = allocator.new_object<mgp_local_date_time>(*other.local_date_time_v);
+      }
+      break;
+    case MGP_VALUE_TYPE_DURATION:
+      static_assert(std::is_pointer_v<decltype(duration_v)>, "Expected to move duration_v by copying pointers.");
+      if (*other.GetMemoryResource() == *m) {
+        duration_v = other.duration_v;
+        other.type = MGP_VALUE_TYPE_NULL;
+      } else {
+        utils::Allocator<mgp_duration> allocator(m);
+        duration_v = allocator.new_object<mgp_duration>(*other.duration_v);
+      }
+      break;
   }
   DeleteValueMember(&other);
   other.type = MGP_VALUE_TYPE_NULL;
@@ -675,6 +832,10 @@ DEFINE_MGP_VALUE_MAKE(map)
 DEFINE_MGP_VALUE_MAKE(vertex)
 DEFINE_MGP_VALUE_MAKE(edge)
 DEFINE_MGP_VALUE_MAKE(path)
+DEFINE_MGP_VALUE_MAKE(date)
+DEFINE_MGP_VALUE_MAKE(local_time)
+DEFINE_MGP_VALUE_MAKE(local_date_time)
+DEFINE_MGP_VALUE_MAKE(duration)
 
 namespace {
 mgp_value_type MgpValueGetType(const mgp_value &val) noexcept { return val.type; }
@@ -704,6 +865,10 @@ DEFINE_MGP_VALUE_IS(map, MAP)
 DEFINE_MGP_VALUE_IS(vertex, VERTEX)
 DEFINE_MGP_VALUE_IS(edge, EDGE)
 DEFINE_MGP_VALUE_IS(path, PATH)
+DEFINE_MGP_VALUE_IS(date, DATE)
+DEFINE_MGP_VALUE_IS(local_time, LOCAL_TIME)
+DEFINE_MGP_VALUE_IS(local_date_time, LOCAL_DATE_TIME)
+DEFINE_MGP_VALUE_IS(duration, DURATION)
 
 mgp_error mgp_value_get_bool(mgp_value *val, int *result) {
   *result = val->bool_v ? 1 : 0;
@@ -735,6 +900,10 @@ DEFINE_MGP_VALUE_GET(map)
 DEFINE_MGP_VALUE_GET(vertex)
 DEFINE_MGP_VALUE_GET(edge)
 DEFINE_MGP_VALUE_GET(path)
+DEFINE_MGP_VALUE_GET(date)
+DEFINE_MGP_VALUE_GET(local_time)
+DEFINE_MGP_VALUE_GET(local_date_time)
+DEFINE_MGP_VALUE_GET(duration)
 
 mgp_error mgp_list_make_empty(size_t capacity, mgp_memory *memory, mgp_list **result) {
   return WrapExceptions(
@@ -975,6 +1144,274 @@ mgp_error mgp_path_equal(mgp_path *p1, mgp_path *p2, int *result) {
       result);
 }
 
+mgp_error mgp_date_from_string(const char *string, mgp_memory *memory, mgp_date **date) {
+  return WrapExceptions([string, memory] { return NewRawMgpObject<mgp_date>(memory, string); }, date);
+}
+
+mgp_error mgp_date_from_parameters(mgp_date_parameters *parameters, mgp_memory *memory, mgp_date **date) {
+  return WrapExceptions([parameters, memory] { return NewRawMgpObject<mgp_date>(memory, parameters); }, date);
+}
+
+mgp_error mgp_date_copy(mgp_date *date, mgp_memory *memory, mgp_date **result) {
+  return WrapExceptions([date, memory] { return NewRawMgpObject<mgp_date>(memory, *date); }, result);
+}
+
+void mgp_date_destroy(mgp_date *date) { DeleteRawMgpObject(date); }
+
+mgp_error mgp_date_equal(mgp_date *first, mgp_date *second, int *result) {
+  return WrapExceptions([first, second] { return first->date == second->date; }, result);
+}
+
+mgp_error mgp_date_get_year(mgp_date *date, int *year) {
+  return WrapExceptions([date] { return date->date.years; }, year);
+}
+
+mgp_error mgp_date_get_month(mgp_date *date, int *month) {
+  return WrapExceptions([date] { return date->date.months; }, month);
+}
+
+mgp_error mgp_date_get_day(mgp_date *date, int *day) {
+  return WrapExceptions([date] { return date->date.days; }, day);
+}
+
+mgp_error mgp_date_timestamp(mgp_date *date, int64_t *timestamp) {
+  return WrapExceptions([date] { return static_cast<int>(date->date.MicrosecondsSinceEpoch()); }, timestamp);
+}
+
+mgp_error mgp_date_now(mgp_memory *memory, mgp_date **date) {
+  return WrapExceptions([memory] { return NewRawMgpObject<mgp_date>(memory, utils::UtcToday()); }, date);
+}
+
+mgp_error mgp_date_add_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory, mgp_date **result) {
+  return WrapExceptions([date, dur, memory] { return NewRawMgpObject<mgp_date>(memory, date->date + dur->duration); },
+                        result);
+}
+
+mgp_error mgp_date_sub_duration(mgp_date *date, mgp_duration *dur, mgp_memory *memory, mgp_date **result) {
+  return WrapExceptions([date, dur, memory] { return NewRawMgpObject<mgp_date>(memory, date->date - dur->duration); },
+                        result);
+}
+
+mgp_error mgp_date_diff(mgp_date *first, mgp_date *second, mgp_memory *memory, mgp_duration **result) {
+  return WrapExceptions(
+      [first, second, memory] { return NewRawMgpObject<mgp_duration>(memory, first->date - second->date); }, result);
+}
+
+mgp_error mgp_local_time_from_string(const char *string, mgp_memory *memory, mgp_local_time **local_time) {
+  return WrapExceptions([string, memory] { return NewRawMgpObject<mgp_local_time>(memory, string); }, local_time);
+}
+
+mgp_error mgp_local_time_from_parameters(mgp_local_time_parameters *parameters, mgp_memory *memory,
+                                         mgp_local_time **local_time) {
+  return WrapExceptions([parameters, memory] { return NewRawMgpObject<mgp_local_time>(memory, parameters); },
+                        local_time);
+}
+
+mgp_error mgp_local_time_copy(mgp_local_time *local_time, mgp_memory *memory, mgp_local_time **result) {
+  return WrapExceptions([local_time, memory] { return NewRawMgpObject<mgp_local_time>(memory, *local_time); }, result);
+}
+
+void mgp_local_time_destroy(mgp_local_time *local_time) { DeleteRawMgpObject(local_time); }
+
+mgp_error mgp_local_time_equal(mgp_local_time *first, mgp_local_time *second, int *result) {
+  return WrapExceptions([first, second] { return first->local_time == second->local_time; }, result);
+}
+
+mgp_error mgp_local_time_get_hour(mgp_local_time *local_time, int *hour) {
+  return WrapExceptions([local_time] { return local_time->local_time.hours; }, hour);
+}
+
+mgp_error mgp_local_time_get_minue(mgp_local_time *local_time, int *minute) {
+  return WrapExceptions([local_time] { return local_time->local_time.minutes; }, minute);
+}
+
+mgp_error mgp_local_time_get_second(mgp_local_time *local_time, int *second) {
+  return WrapExceptions([local_time] { return local_time->local_time.seconds; }, second);
+}
+
+mgp_error mgp_local_time_get_millisecond(mgp_local_time *local_time, int *millisecond) {
+  return WrapExceptions([local_time] { return local_time->local_time.milliseconds; }, millisecond);
+}
+
+mgp_error mgp_local_time_get_microsecond(mgp_local_time *local_time, int *microsecond) {
+  return WrapExceptions([local_time] { return local_time->local_time.microseconds; }, microsecond);
+}
+
+mgp_error mgp_local_time_timestamp(mgp_local_time *local_time, int64_t *timestamp) {
+  return WrapExceptions([local_time] { return static_cast<int>(local_time->local_time.MicrosecondsSinceEpoch()); },
+                        timestamp);
+}
+
+mgp_error mgp_local_time_now(mgp_memory *memory, mgp_local_time **local_time) {
+  return WrapExceptions([memory] { return NewRawMgpObject<mgp_local_time>(memory, utils::UtcLocalTime()); },
+                        local_time);
+}
+
+mgp_error mgp_local_time_add_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory,
+                                      mgp_local_time **result) {
+  return WrapExceptions(
+      [local_time, dur, memory] {
+        return NewRawMgpObject<mgp_local_time>(memory, local_time->local_time + dur->duration);
+      },
+      result);
+}
+
+mgp_error mgp_local_time_sub_duration(mgp_local_time *local_time, mgp_duration *dur, mgp_memory *memory,
+                                      mgp_local_time **result) {
+  return WrapExceptions(
+      [local_time, dur, memory] {
+        return NewRawMgpObject<mgp_local_time>(memory, local_time->local_time - dur->duration);
+      },
+      result);
+}
+
+mgp_error mgp_local_time_diff(mgp_local_time *first, mgp_local_time *second, mgp_memory *memory,
+                              mgp_duration **result) {
+  return WrapExceptions(
+      [first, second, memory] { return NewRawMgpObject<mgp_duration>(memory, first->local_time - second->local_time); },
+      result);
+}
+
+mgp_error mgp_local_date_time_from_string(const char *string, mgp_memory *memory,
+                                          mgp_local_date_time **local_date_time) {
+  return WrapExceptions([string, memory] { return NewRawMgpObject<mgp_local_date_time>(memory, string); },
+                        local_date_time);
+}
+
+mgp_error mgp_local_date_time_from_parameters(mgp_local_date_time_parameters *parameters, mgp_memory *memory,
+                                              mgp_local_date_time **local_date_time) {
+  return WrapExceptions([parameters, memory] { return NewRawMgpObject<mgp_local_date_time>(memory, parameters); },
+                        local_date_time);
+}
+
+mgp_error mgp_local_date_time_copy(mgp_local_date_time *local_date_time, mgp_memory *memory,
+                                   mgp_local_date_time **result) {
+  return WrapExceptions(
+      [local_date_time, memory] { return NewRawMgpObject<mgp_local_date_time>(memory, *local_date_time); }, result);
+}
+
+void mgp_local_date_time_destroy(mgp_local_date_time *local_date_time) { DeleteRawMgpObject(local_date_time); }
+
+mgp_error mgp_local_date_time_equal(mgp_local_date_time *first, mgp_local_date_time *second, int *result) {
+  return WrapExceptions([first, second] { return first->local_date_time == second->local_date_time; }, result);
+}
+
+mgp_error mgp_local_date_time_get_year(mgp_local_date_time *local_date_time, int *year) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.date.years; }, year);
+}
+
+mgp_error mgp_local_date_time_get_month(mgp_local_date_time *local_date_time, int *month) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.date.months; }, month);
+}
+
+mgp_error mgp_local_date_time_get_day(mgp_local_date_time *local_date_time, int *day) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.date.days; }, day);
+}
+
+mgp_error mgp_local_date_time_get_hour(mgp_local_date_time *local_date_time, int *hour) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.local_time.hours; }, hour);
+}
+
+mgp_error mgp_local_date_time_get_minute(mgp_local_date_time *local_date_time, int *minute) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.local_time.minutes; }, minute);
+}
+
+mgp_error mgp_local_date_time_get_second(mgp_local_date_time *local_date_time, int *second) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.local_time.seconds; }, second);
+}
+
+mgp_error mgp_local_date_time_get_millisecond(mgp_local_date_time *local_date_time, int *millisecond) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.local_time.milliseconds; },
+                        millisecond);
+}
+
+mgp_error mgp_local_date_time_get_microsecond(mgp_local_date_time *local_date_time, int *microsecond) {
+  return WrapExceptions([local_date_time] { return local_date_time->local_date_time.local_time.microseconds; },
+                        microsecond);
+}
+
+mgp_error mgp_local_date_time_timestamp(mgp_local_date_time *local_date_time, int64_t *timestamp) {
+  return WrapExceptions(
+      [local_date_time] { return static_cast<int>(local_date_time->local_date_time.MicrosecondsSinceEpoch()); },
+      timestamp);
+}
+
+mgp_error mgp_local_date_time_now(mgp_memory *memory, mgp_local_date_time **local_date_time) {
+  return WrapExceptions([memory] { return NewRawMgpObject<mgp_local_date_time>(memory, utils::UtcLocalDateTime()); },
+                        local_date_time);
+}
+
+mgp_error mgp_local_date_time_add_duration(mgp_local_date_time *local_date_time, mgp_duration *dur, mgp_memory *memory,
+                                           mgp_local_date_time **result) {
+  return WrapExceptions(
+      [local_date_time, dur, memory] {
+        return NewRawMgpObject<mgp_local_date_time>(memory, local_date_time->local_date_time + dur->duration);
+      },
+      result);
+}
+
+mgp_error mgp_local_date_time_sub_duration(mgp_local_date_time *local_date_time, mgp_duration *dur, mgp_memory *memory,
+                                           mgp_local_date_time **result) {
+  return WrapExceptions(
+      [local_date_time, dur, memory] {
+        return NewRawMgpObject<mgp_local_date_time>(memory, local_date_time->local_date_time - dur->duration);
+      },
+      result);
+}
+
+mgp_error mgp_local_date_time_diff(mgp_local_date_time *first, mgp_local_date_time *second, mgp_memory *memory,
+                                   mgp_duration **result) {
+  return WrapExceptions(
+      [first, second, memory] {
+        return NewRawMgpObject<mgp_duration>(memory, first->local_date_time - second->local_date_time);
+      },
+      result);
+}
+
+mgp_error mgp_duration_from_string(const char *string, mgp_memory *memory, mgp_duration **duration) {
+  return WrapExceptions([memory, string] { return NewRawMgpObject<mgp_duration>(memory, string); }, duration);
+}
+
+mgp_error mgp_duration_from_parameters(mgp_duration_parameters *parameters, mgp_memory *memory,
+                                       mgp_duration **duration) {
+  return WrapExceptions([memory, parameters] { return NewRawMgpObject<mgp_duration>(memory, parameters); }, duration);
+}
+
+mgp_error mgp_duration_from_microseconds(int64_t microseconds, mgp_memory *memory, mgp_duration **duration) {
+  return WrapExceptions([microseconds, memory] { return NewRawMgpObject<mgp_duration>(memory, microseconds); },
+                        duration);
+}
+
+mgp_error mgp_duration_copy(mgp_duration *duration, mgp_memory *memory, mgp_duration **result) {
+  return WrapExceptions([duration, memory] { return NewRawMgpObject<mgp_duration>(memory, *duration); }, result);
+}
+
+void mgp_duration_destroy(mgp_duration *duration) { DeleteRawMgpObject(duration); }
+
+mgp_error mgp_duration_get_microseconds(mgp_duration *duration, int64_t *microseconds) {
+  return WrapExceptions([duration] { return duration->duration.microseconds; }, microseconds);
+}
+
+mgp_error mgp_duration_equal(mgp_duration *first, mgp_duration *second, int *result) {
+  return WrapExceptions([first, second] { return first->duration == second->duration; }, result);
+}
+
+mgp_error mgp_duration_neg(mgp_duration *dur, mgp_memory *memory, mgp_duration **result) {
+  return WrapExceptions([memory, dur] { return NewRawMgpObject<mgp_duration>(memory, -dur->duration); }, result);
+}
+
+mgp_error mgp_duration_add(mgp_duration *first, mgp_duration *second, mgp_memory *memory, mgp_duration **result) {
+  return WrapExceptions(
+      [memory, first, second] { return NewRawMgpObject<mgp_duration>(memory, first->duration + second->duration); },
+      result);
+}
+
+mgp_error mgp_duration_sub(mgp_duration *first, mgp_duration *second, mgp_memory *memory, mgp_duration **result) {
+  return WrapExceptions(
+      [memory, first, second] { return NewRawMgpObject<mgp_duration>(memory, first->duration - second->duration); },
+      result);
+}
+
 /// Plugin Result
 
 mgp_error mgp_result_set_error_msg(mgp_result *res, const char *msg) {
@@ -1106,6 +1543,18 @@ storage::PropertyValue ToPropertyValue(const mgp_value &value) {
       return ToPropertyValue(*value.list_v);
     case MGP_VALUE_TYPE_MAP:
       return ToPropertyValue(*value.map_v);
+    case MGP_VALUE_TYPE_DATE:
+      return storage::PropertyValue{
+          storage::TemporalData{storage::TemporalType::Date, value.date_v->date.MicrosecondsSinceEpoch()}};
+    case MGP_VALUE_TYPE_LOCAL_TIME:
+      return storage::PropertyValue{storage::TemporalData{storage::TemporalType::LocalTime,
+                                                          value.local_time_v->local_time.MicrosecondsSinceEpoch()}};
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME:
+      return storage::PropertyValue{storage::TemporalData{
+          storage::TemporalType::LocalDateTime, value.local_date_time_v->local_date_time.MicrosecondsSinceEpoch()}};
+    case MGP_VALUE_TYPE_DURATION:
+      return storage::PropertyValue{
+          storage::TemporalData{storage::TemporalType::Duration, value.duration_v->duration.microseconds}};
     case MGP_VALUE_TYPE_VERTEX:
       throw ValueConversionException{"A vertex is not a valid property value! "};
     case MGP_VALUE_TYPE_EDGE:
@@ -1754,6 +2203,10 @@ DEFINE_MGP_TYPE_GETTER(Map, map);
 DEFINE_MGP_TYPE_GETTER(Node, node);
 DEFINE_MGP_TYPE_GETTER(Relationship, relationship);
 DEFINE_MGP_TYPE_GETTER(Path, path);
+DEFINE_MGP_TYPE_GETTER(Date, date);
+DEFINE_MGP_TYPE_GETTER(LocalTime, local_time);
+DEFINE_MGP_TYPE_GETTER(LocalDateTime, local_date_time);
+DEFINE_MGP_TYPE_GETTER(Duration, duration);
 
 mgp_error mgp_type_list(mgp_type *type, mgp_type **result) {
   return WrapExceptions(
@@ -1850,6 +2303,10 @@ mgp_error mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, mgp_type *type,
       case MGP_VALUE_TYPE_STRING:
       case MGP_VALUE_TYPE_LIST:
       case MGP_VALUE_TYPE_MAP:
+      case MGP_VALUE_TYPE_DATE:
+      case MGP_VALUE_TYPE_LOCAL_TIME:
+      case MGP_VALUE_TYPE_LOCAL_DATE_TIME:
+      case MGP_VALUE_TYPE_DURATION:
         break;
     }
     // Default value must be of required `type`.
@@ -1941,16 +2398,18 @@ std::ostream &PrintValue(const TypedValue &value, std::ostream *stream) {
         PrintValue(item.second, &stream);
       });
       return (*stream) << "}";
+    case TypedValue::Type::Date:
+      return (*stream) << value.ValueDate();
+    case TypedValue::Type::LocalTime:
+      return (*stream) << value.ValueLocalTime();
+    case TypedValue::Type::LocalDateTime:
+      return (*stream) << value.ValueLocalDateTime();
+    case TypedValue::Type::Duration:
+      return (*stream) << value.ValueDuration();
     case TypedValue::Type::Vertex:
     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/procedure/mg_procedure_impl.hpp b/src/query/procedure/mg_procedure_impl.hpp
index 1dc436ef1..23c993c30 100644
--- a/src/query/procedure/mg_procedure_impl.hpp
+++ b/src/query/procedure/mg_procedure_impl.hpp
@@ -18,6 +18,7 @@
 #include "utils/pmr/map.hpp"
 #include "utils/pmr/string.hpp"
 #include "utils/pmr/vector.hpp"
+#include "utils/temporal.hpp"
 /// Wraps memory resource used in custom procedures.
 ///
 /// This should have been `using mgp_memory = utils::MemoryResource`, but that's
@@ -53,6 +54,11 @@ struct mgp_value {
   /// Take ownership of the mgp_path, MemoryResource must match.
   mgp_value(mgp_path *, utils::MemoryResource *) noexcept;
 
+  mgp_value(mgp_date *, utils::MemoryResource *) noexcept;
+  mgp_value(mgp_local_time *, utils::MemoryResource *) noexcept;
+  mgp_value(mgp_local_date_time *, utils::MemoryResource *) noexcept;
+  mgp_value(mgp_duration *, utils::MemoryResource *) noexcept;
+
   /// Construct by copying query::TypedValue using utils::MemoryResource.
   /// mgp_graph is needed to construct mgp_vertex and mgp_edge.
   /// @throw std::bad_alloc
@@ -102,9 +108,214 @@ struct mgp_value {
     mgp_vertex *vertex_v;
     mgp_edge *edge_v;
     mgp_path *path_v;
+    mgp_date *date_v;
+    mgp_local_time *local_time_v;
+    mgp_local_date_time *local_date_time_v;
+    mgp_duration *duration_v;
   };
 };
 
+inline utils::DateParameters MapDateParameters(const mgp_date_parameters *parameters) {
+  return {.years = parameters->year, .months = parameters->month, .days = parameters->day};
+}
+
+struct mgp_date {
+  /// Allocator type so that STL containers are aware that we need one.
+  /// We don't actually need this, but it simplifies the C API, because we store
+  /// the allocator which was used to allocate `this`.
+  using allocator_type = utils::Allocator<mgp_date>;
+
+  // Hopefully utils::Date copy constructor remains noexcept, so that we can
+  // have everything noexcept here.
+  static_assert(std::is_nothrow_copy_constructible_v<utils::Date>);
+
+  mgp_date(const utils::Date &date, utils::MemoryResource *memory) noexcept : memory(memory), date(date) {}
+
+  mgp_date(const std::string_view string, utils::MemoryResource *memory) noexcept
+      : memory(memory), date(utils::ParseDateParameters(string).first) {}
+
+  mgp_date(const mgp_date_parameters *parameters, utils::MemoryResource *memory) noexcept
+      : memory(memory), date(MapDateParameters(parameters)) {}
+
+  mgp_date(const int64_t microseconds, utils::MemoryResource *memory) noexcept : memory(memory), date(microseconds) {}
+
+  mgp_date(const mgp_date &other, utils::MemoryResource *memory) noexcept : memory(memory), date(other.date) {}
+
+  mgp_date(mgp_date &&other, utils::MemoryResource *memory) noexcept : memory(memory), date(other.date) {}
+
+  mgp_date(mgp_date &&other) noexcept : memory(other.memory), date(other.date) {}
+
+  /// Copy construction without utils::MemoryResource is not allowed.
+  mgp_date(const mgp_date &) = delete;
+
+  mgp_date &operator=(const mgp_date &) = delete;
+  mgp_date &operator=(mgp_date &&) = delete;
+
+  ~mgp_date() = default;
+
+  utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
+
+  utils::MemoryResource *memory;
+  utils::Date date;
+};
+
+inline utils::LocalTimeParameters MapLocalTimeParameters(const mgp_local_time_parameters *parameters) {
+  return {.hours = parameters->hour,
+          .minutes = parameters->minute,
+          .seconds = parameters->second,
+          .milliseconds = parameters->millisecond,
+          .microseconds = parameters->microsecond};
+}
+
+struct mgp_local_time {
+  /// Allocator type so that STL containers are aware that we need one.
+  /// We don't actually need this, but it simplifies the C API, because we store
+  /// the allocator which was used to allocate `this`.
+  using allocator_type = utils::Allocator<mgp_local_time>;
+
+  // Hopefully utils::LocalTime copy constructor remains noexcept, so that we can
+  // have everything noexcept here.
+  static_assert(std::is_nothrow_copy_constructible_v<utils::LocalTime>);
+
+  mgp_local_time(const std::string_view string, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(utils::ParseLocalTimeParameters(string).first) {}
+
+  mgp_local_time(const mgp_local_time_parameters *parameters, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(MapLocalTimeParameters(parameters)) {}
+
+  mgp_local_time(const utils::LocalTime &local_time, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(local_time) {}
+
+  mgp_local_time(const int64_t microseconds, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(microseconds) {}
+
+  mgp_local_time(const mgp_local_time &other, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(other.local_time) {}
+
+  mgp_local_time(mgp_local_time &&other, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_time(other.local_time) {}
+
+  mgp_local_time(mgp_local_time &&other) noexcept : memory(other.memory), local_time(other.local_time) {}
+
+  /// Copy construction without utils::MemoryResource is not allowed.
+  mgp_local_time(const mgp_local_time &) = delete;
+
+  mgp_local_time &operator=(const mgp_local_time &) = delete;
+  mgp_local_time &operator=(mgp_local_time &&) = delete;
+
+  ~mgp_local_time() = default;
+
+  utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
+
+  utils::MemoryResource *memory;
+  utils::LocalTime local_time;
+};
+
+inline utils::LocalDateTime CreateLocalDateTimeFromString(const std::string_view string) {
+  const auto &[date_parameters, local_time_parameters] = utils::ParseLocalDateTimeParameters(string);
+  return utils::LocalDateTime{date_parameters, local_time_parameters};
+}
+
+struct mgp_local_date_time {
+  /// Allocator type so that STL containers are aware that we need one.
+  /// We don't actually need this, but it simplifies the C API, because we store
+  /// the allocator which was used to allocate `this`.
+  using allocator_type = utils::Allocator<mgp_local_date_time>;
+
+  // Hopefully utils::LocalDateTime copy constructor remains noexcept, so that we can
+  // have everything noexcept here.
+  static_assert(std::is_nothrow_copy_constructible_v<utils::LocalDateTime>);
+
+  mgp_local_date_time(const utils::LocalDateTime &local_date_time, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_date_time(local_date_time) {}
+
+  mgp_local_date_time(const std::string_view string, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_date_time(CreateLocalDateTimeFromString(string)) {}
+
+  mgp_local_date_time(const mgp_local_date_time_parameters *parameters, utils::MemoryResource *memory) noexcept
+      : memory(memory),
+        local_date_time(MapDateParameters(parameters->date_parameters),
+                        MapLocalTimeParameters(parameters->local_time_parameters)) {}
+
+  mgp_local_date_time(const int64_t microseconds, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_date_time(microseconds) {}
+
+  mgp_local_date_time(const mgp_local_date_time &other, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_date_time(other.local_date_time) {}
+
+  mgp_local_date_time(mgp_local_date_time &&other, utils::MemoryResource *memory) noexcept
+      : memory(memory), local_date_time(other.local_date_time) {}
+
+  mgp_local_date_time(mgp_local_date_time &&other) noexcept
+      : memory(other.memory), local_date_time(other.local_date_time) {}
+
+  /// Copy construction without utils::MemoryResource is not allowed.
+  mgp_local_date_time(const mgp_local_date_time &) = delete;
+
+  mgp_local_date_time &operator=(const mgp_local_date_time &) = delete;
+  mgp_local_date_time &operator=(mgp_local_date_time &&) = delete;
+
+  ~mgp_local_date_time() = default;
+
+  utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
+
+  utils::MemoryResource *memory;
+  utils::LocalDateTime local_date_time;
+};
+
+inline utils::DurationParameters MapDurationParameters(const mgp_duration_parameters *parameters) {
+  return {.days = parameters->day,
+          .hours = parameters->hour,
+          .minutes = parameters->minute,
+          .seconds = parameters->second,
+          .milliseconds = parameters->millisecond,
+          .microseconds = parameters->microsecond};
+}
+
+struct mgp_duration {
+  /// Allocator type so that STL containers are aware that we need one.
+  /// We don't actually need this, but it simplifies the C API, because we store
+  /// the allocator which was used to allocate `this`.
+  using allocator_type = utils::Allocator<mgp_duration>;
+
+  // Hopefully utils::Duration copy constructor remains noexcept, so that we can
+  // have everything noexcept here.
+  static_assert(std::is_nothrow_copy_constructible_v<utils::Duration>);
+
+  mgp_duration(const std::string_view string, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(utils::ParseDurationParameters(string)) {}
+
+  mgp_duration(const mgp_duration_parameters *parameters, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(MapDurationParameters(parameters)) {}
+
+  mgp_duration(const utils::Duration &duration, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(duration) {}
+
+  mgp_duration(const int64_t microseconds, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(microseconds) {}
+
+  mgp_duration(const mgp_duration &other, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(other.duration) {}
+
+  mgp_duration(mgp_duration &&other, utils::MemoryResource *memory) noexcept
+      : memory(memory), duration(other.duration) {}
+
+  mgp_duration(mgp_duration &&other) noexcept : memory(other.memory), duration(other.duration) {}
+
+  /// Copy construction without utils::MemoryResource is not allowed.
+  mgp_duration(const mgp_duration &) = delete;
+
+  mgp_duration &operator=(const mgp_duration &) = delete;
+  mgp_duration &operator=(mgp_duration &&) = delete;
+
+  ~mgp_duration() = default;
+
+  utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
+
+  utils::MemoryResource *memory;
+  utils::Duration duration;
+};
+
 struct mgp_list {
   /// Allocator type so that STL containers are aware that we need one.
   using allocator_type = utils::Allocator<mgp_list>;
diff --git a/src/query/procedure/py_module.cpp b/src/query/procedure/py_module.cpp
index e41c987e1..ab97d15a6 100644
--- a/src/query/procedure/py_module.cpp
+++ b/src/query/procedure/py_module.cpp
@@ -1,5 +1,6 @@
 #include "query/procedure/py_module.hpp"
 
+#include <datetime.h>
 #include <pyerrors.h>
 #include <array>
 #include <sstream>
@@ -9,6 +10,7 @@
 
 #include "query/procedure/mg_procedure_helpers.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
+#include "utils/memory.hpp"
 #include "utils/on_scope_exit.hpp"
 #include "utils/pmr/vector.hpp"
 
@@ -1127,6 +1129,10 @@ DEFINE_PY_MGP_MODULE_TYPE(Map, map);
 DEFINE_PY_MGP_MODULE_TYPE(Node, node);
 DEFINE_PY_MGP_MODULE_TYPE(Relationship, relationship);
 DEFINE_PY_MGP_MODULE_TYPE(Path, path);
+DEFINE_PY_MGP_MODULE_TYPE(Date, date);
+DEFINE_PY_MGP_MODULE_TYPE(LocalTime, local_time);
+DEFINE_PY_MGP_MODULE_TYPE(LocalDateTime, local_date_time);
+DEFINE_PY_MGP_MODULE_TYPE(Duration, duration);
 
 static PyMethodDef PyMgpModuleMethods[] = {
     {"type_nullable", PyMgpModuleTypeNullable, METH_O,
@@ -1145,6 +1151,10 @@ static PyMethodDef PyMgpModuleMethods[] = {
      "Get the type representing graph relationship values."},
     {"type_path", PyMgpModuleTypePath, METH_NOARGS,
      "Get the type representing a graph path (walk) from one node to another."},
+    {"type_date", PyMgpModuleTypeDate, METH_NOARGS, "Get the type representing a Date."},
+    {"type_local_time", PyMgpModuleTypeLocalTime, METH_NOARGS, "Get the type representing a LocalTime."},
+    {"type_local_date_time", PyMgpModuleTypeLocalDateTime, METH_NOARGS, "Get the type representing a LocalDateTime."},
+    {"type_duration", PyMgpModuleTypeDuration, METH_NOARGS, "Get the type representing a Duration."},
     {nullptr},
 };
 
@@ -1956,6 +1966,10 @@ PyObject *PyInitMgpModule() {
     }
   }
   clean_up.Disable();
+
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+  PyDateTime_IMPORT;
+
   return mgp;
 }
 
@@ -2055,6 +2069,30 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
       py::Object py_path(reinterpret_cast<PyObject *>(MakePyPath(*p, py_graph)));
       return py_mgp.CallMethod("Path", py_path);
     }
+    case MGP_VALUE_TYPE_DATE: {
+      const auto &date = value.date_v->date;
+      py::Object py_date(PyDate_FromDate(date.years, date.months, date.days));
+      return py_date;
+    }
+    case MGP_VALUE_TYPE_LOCAL_TIME: {
+      const auto &local_time = value.local_time_v->local_time;
+      py::Object py_local_time(PyTime_FromTime(local_time.hours, local_time.minutes, local_time.seconds,
+                                               local_time.milliseconds * 1000 + local_time.microseconds));
+      return py_local_time;
+    }
+    case MGP_VALUE_TYPE_LOCAL_DATE_TIME: {
+      const auto &local_time = value.local_date_time_v->local_date_time.local_time;
+      const auto &date = value.local_date_time_v->local_date_time.date;
+      py::Object py_local_date_time(
+          PyDateTime_FromDateAndTime(date.years, date.months, date.days, local_time.hours, local_time.minutes,
+                                     local_time.seconds, local_time.milliseconds * 1000 + local_time.microseconds));
+      return py_local_date_time;
+    }
+    case MGP_VALUE_TYPE_DURATION: {
+      const auto &duration = value.duration_v->duration;
+      py::Object py_duration(PyDelta_FromDSU(0, 0, duration.microseconds));
+      return py_duration;
+    }
   }
 }
 
@@ -2247,6 +2285,108 @@ mgp_value *PyObjectToMgpValue(PyObject *o, mgp_memory *memory) {
       throw std::invalid_argument("'mgp.Path' is missing '_path' attribute");
     }
     return PyObjectToMgpValue(path.Ptr(), memory);
+  } else if (PyDate_CheckExact(o)) {
+    mgp_date_parameters parameters{
+        .year = PyDateTime_GET_YEAR(o),    // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .month = PyDateTime_GET_MONTH(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .day = PyDateTime_GET_DAY(o)};     // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+    MgpUniquePtr<mgp_date> date{nullptr, mgp_date_destroy};
+
+    if (const auto err = CreateMgpObject(date, mgp_date_from_parameters, &parameters, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_date"};
+    }
+    if (const auto err = mgp_value_make_date(date.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_value"};
+    }
+    static_cast<void>(date.release());
+  } else if (PyTime_CheckExact(o)) {
+    mgp_local_time_parameters parameters{
+        .hour = PyDateTime_TIME_GET_HOUR(o),      // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .minute = PyDateTime_TIME_GET_MINUTE(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .second = PyDateTime_TIME_GET_SECOND(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .millisecond =
+            PyDateTime_TIME_GET_MICROSECOND(o) /  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+            1000,
+        .microsecond =
+            PyDateTime_TIME_GET_MICROSECOND(o) %  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+            1000};
+    MgpUniquePtr<mgp_local_time> local_time{nullptr, mgp_local_time_destroy};
+
+    if (const auto err = CreateMgpObject(local_time, mgp_local_time_from_parameters, &parameters, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_local_time"};
+    }
+    if (const auto err = mgp_value_make_local_time(local_time.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_value"};
+    }
+    static_cast<void>(local_time.release());
+  } else if (PyDateTime_CheckExact(o)) {
+    mgp_date_parameters date_parameters{
+        .year = PyDateTime_GET_YEAR(o),    // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .month = PyDateTime_GET_MONTH(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .day = PyDateTime_GET_DAY(o)};     // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+    mgp_local_time_parameters local_time_parameters{
+        .hour = PyDateTime_DATE_GET_HOUR(o),      // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .minute = PyDateTime_DATE_GET_MINUTE(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .second = PyDateTime_DATE_GET_SECOND(o),  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+        .millisecond =
+            PyDateTime_DATE_GET_MICROSECOND(o) /  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+            1000,
+        .microsecond =
+            PyDateTime_DATE_GET_MICROSECOND(o) %  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+            1000};
+
+    mgp_local_date_time_parameters parameters{&date_parameters, &local_time_parameters};
+
+    MgpUniquePtr<mgp_local_date_time> local_date_time{nullptr, mgp_local_date_time_destroy};
+
+    if (const auto err = CreateMgpObject(local_date_time, mgp_local_date_time_from_parameters, &parameters, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_local_date_time"};
+    }
+    if (const auto err = mgp_value_make_local_date_time(local_date_time.get(), &mgp_v);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_value"};
+    }
+    static_cast<void>(local_date_time.release());
+  } else if (PyDelta_CheckExact(o)) {
+    constexpr int64_t microseconds_in_days = static_cast<std::chrono::microseconds>(std::chrono::days{1}).count();
+    const auto days =
+        PyDateTime_DELTA_GET_DAYS(o);  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+    auto microseconds =
+        std::abs(days) * microseconds_in_days +
+        PyDateTime_DELTA_GET_SECONDS(o) * 1000 *  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+            1000 +
+        PyDateTime_DELTA_GET_MICROSECONDS(o);  // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,hicpp-signed-bitwise)
+    microseconds *= days < 0 ? -1 : 1;
+
+    MgpUniquePtr<mgp_duration> duration{nullptr, mgp_duration_destroy};
+
+    if (const auto err = CreateMgpObject(duration, mgp_duration_from_microseconds, microseconds, memory);
+        err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_duration"};
+    }
+    if (const auto err = mgp_value_make_duration(duration.get(), &mgp_v); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
+      throw std::bad_alloc{};
+    } else if (err != MGP_ERROR_NO_ERROR) {
+      throw std::runtime_error{"Unexpected error while creating mgp_value"};
+    }
+    static_cast<void>(duration.release());
   } else {
     throw std::invalid_argument("Unsupported PyObject conversion");
   }
diff --git a/src/utils/temporal.cpp b/src/utils/temporal.cpp
index 438596bfa..ab3842967 100644
--- a/src/utils/temporal.cpp
+++ b/src/utils/temporal.cpp
@@ -47,18 +47,18 @@ Date::Date(const int64_t microseconds) {
 
 Date::Date(const DateParameters &date_parameters) {
   if (!IsInBounds(0, 9999, date_parameters.years)) {
-    throw utils::BasicException(
+    throw temporal::InvalidArgumentException(
         "Creating a Date with invalid year parameter. The value should be an integer between 0 and 9999.");
   }
 
   if (!IsInBounds(1, 12, date_parameters.months)) {
-    throw utils::BasicException(
+    throw temporal::InvalidArgumentException(
         "Creating a Date with invalid month parameter. The value should be an integer between 1 and 12.");
   }
 
   if (!IsInBounds(1, 31, date_parameters.days) ||
       !IsValidDay(date_parameters.days, date_parameters.months, date_parameters.years)) {
-    throw utils::BasicException(
+    throw temporal::InvalidArgumentException(
         "Creating a Date with invalid day parameter. The value should be an integer between 1 and 31, depending on the "
         "month and year.");
   }
@@ -74,7 +74,7 @@ tm GetUtcFromSystemClockOrThrow() {
   const auto today = chrono::system_clock::to_time_t(chrono::system_clock::now());
   tm utc_today;
   if (!gmtime_r(&today, &utc_today)) {
-    throw utils::BasicException("Can't access clock's UTC time");
+    throw temporal::InvalidArgumentException("Can't access clock's UTC time");
   }
   return utc_today;
 }
@@ -132,13 +132,13 @@ std::pair<DateParameters, bool> ParseDateParameters(std::string_view date_string
   if (!std::any_of(
           valid_sizes.begin(), valid_sizes.end(),
           [date_string_size = date_string.size()](const auto valid_size) { return valid_size == date_string_size; })) {
-    throw utils::BasicException("Invalid string for date. {}", kSupportedDateFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid string for date. {}", kSupportedDateFormatsHelpMessage);
   }
 
   DateParameters date_parameters;
   auto maybe_year = ParseNumber<int64_t>(date_string, 4);
   if (!maybe_year) {
-    throw utils::BasicException("Invalid year in the string. {}", kSupportedDateFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid year in the string. {}", kSupportedDateFormatsHelpMessage);
   }
   date_parameters.years = *maybe_year;
   date_string.remove_prefix(4);
@@ -151,7 +151,7 @@ std::pair<DateParameters, bool> ParseDateParameters(std::string_view date_string
 
   auto maybe_month = ParseNumber<int64_t>(date_string, 2);
   if (!maybe_month) {
-    throw utils::BasicException("Invalid month in the string. {}", kSupportedDateFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid month in the string. {}", kSupportedDateFormatsHelpMessage);
   }
   date_parameters.months = *maybe_month;
   date_string.remove_prefix(2);
@@ -159,21 +159,21 @@ std::pair<DateParameters, bool> ParseDateParameters(std::string_view date_string
   if (!date_string.empty()) {
     if (date_string.front() == '-') {
       if (!is_extended_format) {
-        throw utils::BasicException("Invalid format for the date. {}", kSupportedDateFormatsHelpMessage);
+        throw temporal::InvalidArgumentException("Invalid format for the date. {}", kSupportedDateFormatsHelpMessage);
       }
       date_string.remove_prefix(1);
     }
 
     auto maybe_day = ParseNumber<int64_t>(date_string, 2);
     if (!maybe_day) {
-      throw utils::BasicException("Invalid month in the string. {}", kSupportedDateFormatsHelpMessage);
+      throw temporal::InvalidArgumentException("Invalid month in the string. {}", kSupportedDateFormatsHelpMessage);
     }
     date_parameters.days = *maybe_day;
     date_string.remove_prefix(2);
   }
 
   if (!date_string.empty()) {
-    throw utils::BasicException("Invalid format for the date. {}", kSupportedDateFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid format for the date. {}", kSupportedDateFormatsHelpMessage);
   }
 
   return {date_parameters, is_extended_format};
@@ -242,7 +242,7 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
     }
 
     if (*using_colon ^ has_colon) {
-      throw utils::BasicException(
+      throw temporal::InvalidArgumentException(
           "Invalid format for the local time. A separator should be used consistently or not at all. {}",
           kSupportedTimeFormatsHelpMessage);
     }
@@ -252,7 +252,8 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
     }
 
     if (local_time_string.empty()) {
-      throw utils::BasicException("Invalid format for the local time. {}", kSupportedTimeFormatsHelpMessage);
+      throw temporal::InvalidArgumentException("Invalid format for the local time. {}",
+                                               kSupportedTimeFormatsHelpMessage);
     }
   };
 
@@ -260,7 +261,7 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
 
   const auto maybe_hour = ParseNumber<int64_t>(local_time_string, 2);
   if (!maybe_hour) {
-    throw utils::BasicException("Invalid hour in the string. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid hour in the string. {}", kSupportedTimeFormatsHelpMessage);
   }
   local_time_parameters.hours = *maybe_hour;
   local_time_string.remove_prefix(2);
@@ -273,7 +274,7 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
 
   const auto maybe_minute = ParseNumber<int64_t>(local_time_string, 2);
   if (!maybe_minute) {
-    throw utils::BasicException("Invalid minutes in the string. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid minutes in the string. {}", kSupportedTimeFormatsHelpMessage);
   }
   local_time_parameters.minutes = *maybe_minute;
   local_time_string.remove_prefix(2);
@@ -286,7 +287,7 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
 
   const auto maybe_seconds = ParseNumber<int64_t>(local_time_string, 2);
   if (!maybe_seconds) {
-    throw utils::BasicException("Invalid seconds in the string. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid seconds in the string. {}", kSupportedTimeFormatsHelpMessage);
   }
   local_time_parameters.seconds = *maybe_seconds;
   local_time_string.remove_prefix(2);
@@ -296,13 +297,14 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
   }
 
   if (local_time_string.front() != '.') {
-    throw utils::BasicException("Invalid format for local time. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid format for local time. {}", kSupportedTimeFormatsHelpMessage);
   }
   local_time_string.remove_prefix(1);
 
   const auto maybe_milliseconds = ParseNumber<int64_t>(local_time_string, 3);
   if (!maybe_milliseconds) {
-    throw utils::BasicException("Invalid milliseconds in the string. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid milliseconds in the string. {}",
+                                             kSupportedTimeFormatsHelpMessage);
   }
   local_time_parameters.milliseconds = *maybe_milliseconds;
   local_time_string.remove_prefix(3);
@@ -313,13 +315,14 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
 
   const auto maybe_microseconds = ParseNumber<int64_t>(local_time_string, 3);
   if (!maybe_microseconds) {
-    throw utils::BasicException("Invalid microseconds in the string. {}", kSupportedTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid microseconds in the string. {}",
+                                             kSupportedTimeFormatsHelpMessage);
   }
   local_time_parameters.microseconds = *maybe_microseconds;
   local_time_string.remove_prefix(3);
 
   if (!local_time_string.empty()) {
-    throw utils::BasicException("Extra characters present at the end of the string.");
+    throw temporal::InvalidArgumentException("Extra characters present at the end of the string.");
   }
 
   return {local_time_parameters, *using_colon};
@@ -328,12 +331,12 @@ std::pair<LocalTimeParameters, bool> ParseLocalTimeParameters(std::string_view l
 LocalTime::LocalTime(const int64_t microseconds) {
   auto chrono_microseconds = std::chrono::microseconds(microseconds);
   if (chrono_microseconds.count() < 0) {
-    throw utils::BasicException("Negative LocalTime specified in microseconds");
+    throw temporal::InvalidArgumentException("Negative LocalTime specified in microseconds");
   }
 
   const auto parsed_hours = GetAndSubtractDuration<std::chrono::hours>(chrono_microseconds);
   if (parsed_hours > 23) {
-    throw utils::BasicException("Invalid LocalTime specified in microseconds");
+    throw temporal::InvalidArgumentException("Invalid LocalTime specified in microseconds");
   }
 
   hours = parsed_hours;
@@ -345,24 +348,24 @@ LocalTime::LocalTime(const int64_t microseconds) {
 
 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.");
+    throw temporal::InvalidArgumentException("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.");
+    throw temporal::InvalidArgumentException("Creating a LocalTime with invalid minutes parameter.");
   }
 
   // ISO 8601 supports leap seconds, but we ignore it for now to simplify the implementation
   if (!IsInBounds(0, 59, local_time_parameters.seconds)) {
-    throw utils::BasicException("Creating a LocalTime with invalid seconds parameter.");
+    throw temporal::InvalidArgumentException("Creating a LocalTime with invalid seconds parameter.");
   }
 
   if (!IsInBounds(0, 999, local_time_parameters.milliseconds)) {
-    throw utils::BasicException("Creating a LocalTime with invalid milliseconds parameter.");
+    throw temporal::InvalidArgumentException("Creating a LocalTime with invalid milliseconds parameter.");
   }
 
   if (!IsInBounds(0, 999, local_time_parameters.microseconds)) {
-    throw utils::BasicException("Creating a LocalTime with invalid microseconds parameter.");
+    throw temporal::InvalidArgumentException("Creating a LocalTime with invalid microseconds parameter.");
   }
 
   hours = local_time_parameters.hours;
@@ -433,7 +436,8 @@ or both parts should be written in their basic forms without the separators.)hel
 std::pair<DateParameters, LocalTimeParameters> ParseLocalDateTimeParameters(std::string_view string) {
   auto t_position = string.find('T');
   if (t_position == std::string_view::npos) {
-    throw utils::BasicException("Invalid LocalDateTime format. {}", kSupportedLocalDateTimeFormatsHelpMessage);
+    throw temporal::InvalidArgumentException("Invalid LocalDateTime format. {}",
+                                             kSupportedLocalDateTimeFormatsHelpMessage);
   }
 
   try {
@@ -444,20 +448,22 @@ std::pair<DateParameters, LocalTimeParameters> ParseLocalDateTimeParameters(std:
     // which denotes the basic format. The opposite case also aplies.
     auto local_time_substring = string.substr(t_position + 1);
     if (local_time_substring.empty()) {
-      throw utils::BasicException("Invalid LocalDateTime format. {}", kSupportedLocalDateTimeFormatsHelpMessage);
+      throw temporal::InvalidArgumentException("Invalid LocalDateTime format. {}",
+                                               kSupportedLocalDateTimeFormatsHelpMessage);
     }
 
     auto [local_time_parameters, extended_time_format] = ParseLocalTimeParameters(local_time_substring);
 
     if (extended_date_format ^ extended_time_format) {
-      throw utils::BasicException(
+      throw temporal::InvalidArgumentException(
           "Invalid LocalDateTime format. Both date and time should be in the basic or extended format. {}",
           kSupportedLocalDateTimeFormatsHelpMessage);
     }
 
     return {date_parameters, local_time_parameters};
-  } catch (const utils::BasicException &e) {
-    throw utils::BasicException("Invalid LocalDateTime format. {}", kSupportedLocalDateTimeFormatsHelpMessage);
+  } catch (const temporal::InvalidArgumentException &e) {
+    throw temporal::InvalidArgumentException("Invalid LocalDateTime format. {}",
+                                             kSupportedLocalDateTimeFormatsHelpMessage);
   }
 }
 
@@ -630,7 +636,7 @@ DurationParameters ParseDurationParameters(std::string_view string) {
   // The string needs to start with P followed by one of the two options:
   //  - string in a duration specific format
   if (string.empty() || string.front() != 'P') {
-    throw utils::BasicException("Duration string is empty.");
+    throw temporal::InvalidArgumentException("Duration string is empty.");
   }
 
   if (auto maybe_duration_parameters = TryParseDurationString(string); maybe_duration_parameters) {
@@ -705,7 +711,7 @@ int64_t Duration::SubSecondsAsNanoseconds() const {
 
 Duration Duration::operator-() const {
   if (microseconds == std::numeric_limits<decltype(microseconds)>::min()) [[unlikely]] {
-      throw utils::BasicException("Duration arithmetic overflows");
+      throw temporal::InvalidArgumentException("Duration arithmetic overflows");
   }
   Duration result{-microseconds};
   return result;
diff --git a/src/utils/temporal.hpp b/src/utils/temporal.hpp
index 082db6121..8a14d0dd0 100644
--- a/src/utils/temporal.hpp
+++ b/src/utils/temporal.hpp
@@ -40,6 +40,12 @@ bool Underflows(const TType &lhs, const TType &rhs) {
   return false;
 }
 
+namespace temporal {
+struct InvalidArgumentException : public utils::BasicException {
+  using utils::BasicException::BasicException;
+};
+}  // namespace temporal
+
 struct DurationParameters {
   double days{0};
   double hours{0};