diff --git a/backend/src/baserow/contrib/database/api/fields/serializers.py b/backend/src/baserow/contrib/database/api/fields/serializers.py
index aeab2a5a4..813ea3dd1 100644
--- a/backend/src/baserow/contrib/database/api/fields/serializers.py
+++ b/backend/src/baserow/contrib/database/api/fields/serializers.py
@@ -301,4 +301,9 @@ class DurationFieldSerializer(serializers.Field):
         )
 
     def to_representation(self, value):
-        return value.total_seconds()
+        if isinstance(value, (int, float)):
+            # Durations are stored as the number of seconds for lookups/arrays in
+            # formula land, so just return the value in that case.
+            return value
+        else:
+            return value.total_seconds()
diff --git a/backend/src/baserow/contrib/database/fields/field_helpers.py b/backend/src/baserow/contrib/database/fields/field_helpers.py
index 56854abb1..b1fa89219 100644
--- a/backend/src/baserow/contrib/database/fields/field_helpers.py
+++ b/backend/src/baserow/contrib/database/fields/field_helpers.py
@@ -1,6 +1,7 @@
 from typing import Any, Dict, List
 
 from baserow.contrib.database.fields.registries import field_type_registry
+from baserow.contrib.database.fields.utils.duration import D_H_M
 
 
 def construct_all_possible_field_kwargs(
@@ -194,7 +195,11 @@ def construct_all_possible_field_kwargs(
             {"name": "formula_int", "formula": "1"},
             {"name": "formula_bool", "formula": "true"},
             {"name": "formula_decimal", "formula": "100/3"},
-            {"name": "formula_dateinterval", "formula": "date_interval('1 day')"},
+            {
+                "name": "formula_dateinterval",
+                "formula": "date_interval('1 day')",
+                "duration_format": D_H_M,
+            },
             {"name": "formula_date", "formula": "todate('20200101', 'YYYYMMDD')"},
             {"name": "formula_singleselect", "formula": "field('single_select')"},
             {"name": "formula_email", "formula": "field('email')"},
diff --git a/backend/src/baserow/contrib/database/fields/field_types.py b/backend/src/baserow/contrib/database/fields/field_types.py
index 2cd4c36be..e178a0ddf 100755
--- a/backend/src/baserow/contrib/database/fields/field_types.py
+++ b/backend/src/baserow/contrib/database/fields/field_types.py
@@ -104,6 +104,7 @@ from baserow.core.utils import list_to_comma_separated_string
 
 from ..formula.types.formula_types import (
     BaserowFormulaArrayType,
+    BaserowFormulaDurationType,
     BaserowFormulaMultipleSelectType,
     BaserowFormulaSingleFileType,
 )
@@ -1796,6 +1797,14 @@ class DurationFieldType(FieldType):
 
         return {**base, "duration_format": field.duration_format}
 
+    def to_baserow_formula_type(self, field) -> BaserowFormulaType:
+        return BaserowFormulaDurationType(
+            duration_format=field.duration_format, nullable=True
+        )
+
+    def from_baserow_formula_type(self, formula_type: BaserowFormulaCharType):
+        return self.model_class(duration_format=formula_type.duration_format)
+
 
 class LinkRowFieldType(ManyToManyFieldTypeSerializeToInputValueMixin, FieldType):
     """
diff --git a/backend/src/baserow/contrib/database/fields/models.py b/backend/src/baserow/contrib/database/fields/models.py
index 4a6a769f0..e512815bc 100644
--- a/backend/src/baserow/contrib/database/fields/models.py
+++ b/backend/src/baserow/contrib/database/fields/models.py
@@ -489,6 +489,13 @@ class FormulaField(Field):
         null=True,
         help_text="Force a timezone for the field overriding user profile settings.",
     )
+    duration_format = models.CharField(
+        choices=DURATION_FORMAT_CHOICES,
+        default=DURATION_FORMAT_CHOICES[0][0],
+        max_length=32,
+        null=True,
+        help_text=_("The format of the duration."),
+    )
     needs_periodic_update = models.BooleanField(
         default=False,
         help_text="Indicates if the field needs to be periodically updated.",
diff --git a/backend/src/baserow/contrib/database/formula/ast/function_defs.py b/backend/src/baserow/contrib/database/formula/ast/function_defs.py
index fdba92e48..15198f40d 100644
--- a/backend/src/baserow/contrib/database/formula/ast/function_defs.py
+++ b/backend/src/baserow/contrib/database/formula/ast/function_defs.py
@@ -105,8 +105,8 @@ from baserow.contrib.database.formula.types.formula_types import (
     BaserowFormulaBooleanType,
     BaserowFormulaButtonType,
     BaserowFormulaCharType,
-    BaserowFormulaDateIntervalType,
     BaserowFormulaDateType,
+    BaserowFormulaDurationType,
     BaserowFormulaLinkType,
     BaserowFormulaMultipleSelectType,
     BaserowFormulaNumberType,
@@ -1237,9 +1237,12 @@ class BaserowEqual(TwoArgumentBaserowFunction):
             return func_call.with_valid_type(BaserowFormulaBooleanType())
 
     def to_django_expression(self, arg1: Expression, arg2: Expression) -> Expression:
-        return EqualsExpr(
-            arg1,
-            arg2,
+        return Case(
+            When(
+                condition=IsNullExpr(arg1, output_field=fields.BooleanField()),
+                then=IsNullExpr(arg2, output_field=fields.BooleanField()),
+            ),
+            default=EqualsExpr(arg1, arg2, output_field=fields.BooleanField()),
             output_field=fields.BooleanField(),
         )
 
@@ -1728,7 +1731,7 @@ class BaserowDateInterval(OneArgumentBaserowFunction):
         func_call: BaserowFunctionCall[UnTyped],
         arg: BaserowExpression[BaserowFormulaValidType],
     ) -> BaserowExpression[BaserowFormulaType]:
-        return func_call.with_valid_type(BaserowFormulaDateIntervalType(nullable=True))
+        return func_call.with_valid_type(BaserowFormulaDurationType(nullable=True))
 
     def to_django_expression(self, arg: Expression) -> Expression:
         return Func(
diff --git a/backend/src/baserow/contrib/database/formula/expression_generator/generator.py b/backend/src/baserow/contrib/database/formula/expression_generator/generator.py
index 87ce241df..4564f46e4 100644
--- a/backend/src/baserow/contrib/database/formula/expression_generator/generator.py
+++ b/backend/src/baserow/contrib/database/formula/expression_generator/generator.py
@@ -18,7 +18,7 @@ from django.db.models import (
     When,
     fields,
 )
-from django.db.models.functions import Cast, Coalesce, JSONObject
+from django.db.models.functions import Cast, Coalesce, Extract, JSONObject
 
 from baserow.contrib.database.formula.ast.exceptions import UnknownFieldReference
 from baserow.contrib.database.formula.ast.tree import (
@@ -372,6 +372,7 @@ class BaserowExpressionToDjangoExpressionGenerator(
         self, db_column: str, model_field: fields.Field, already_in_subquery: bool
     ) -> Expression:
         from baserow.contrib.database.fields.fields import (
+            DurationField,
             MultipleSelectManyToManyField,
             SingleSelectForeignKey,
         )
@@ -417,6 +418,14 @@ class BaserowExpressionToDjangoExpressionGenerator(
                     ),
                     Value([], output_field=JSONField()),
                 )
+        elif isinstance(model_field, DurationField) and already_in_subquery:
+            # already_in_subquery is set to True in a lookup, but the JSON produced by
+            # looking up a duration field cannot contains intervals/timedelta, so we
+            # need to convert the value to a number of seconds instead.
+            return ExpressionWrapper(
+                Extract(db_column, "epoch"),
+                output_field=model_field,
+            )
         else:
             return ExpressionWrapper(
                 F(db_column),
diff --git a/backend/src/baserow/contrib/database/formula/types/formula_types.py b/backend/src/baserow/contrib/database/formula/types/formula_types.py
index 343298974..8c66be40f 100644
--- a/backend/src/baserow/contrib/database/formula/types/formula_types.py
+++ b/backend/src/baserow/contrib/database/formula/types/formula_types.py
@@ -21,6 +21,7 @@ from baserow.contrib.database.fields.expressions import (
 )
 from baserow.contrib.database.fields.field_sortings import OptionallyAnnotatedOrderBy
 from baserow.contrib.database.fields.mixins import get_date_time_format
+from baserow.contrib.database.fields.utils.duration import D_H_M_S
 from baserow.contrib.database.formula.ast.tree import (
     BaserowBooleanLiteral,
     BaserowDecimalLiteral,
@@ -414,12 +415,12 @@ def _calculate_addition_interval_type(
 ) -> BaserowFormulaValidType:
     arg1_type = arg1.expression_type
     arg2_type = arg2.expression_type
-    if isinstance(arg1_type, BaserowFormulaDateIntervalType) and isinstance(
-        arg2_type, BaserowFormulaDateIntervalType
+    if isinstance(arg1_type, BaserowFormulaDateIntervalTypeMixin) and isinstance(
+        arg2_type, BaserowFormulaDateIntervalTypeMixin
     ):
         # interval + interval = interval
         resulting_type = arg1_type
-    elif isinstance(arg1_type, BaserowFormulaDateIntervalType):
+    elif isinstance(arg1_type, BaserowFormulaDateIntervalTypeMixin):
         # interval + date = date
         resulting_type = arg2_type
     else:
@@ -429,9 +430,20 @@ def _calculate_addition_interval_type(
     return resulting_type
 
 
-# noinspection PyMethodMayBeStatic
+class BaserowFormulaDateIntervalTypeMixin:
+    """
+    Empty mixin to allow us to check if a type is a date interval type or a duration
+    type. NOTE: This can be removed once the BaserowFormulaDateIntervalType is removed.
+    """
+
+    pass
+
+
+# Deprecated, use BaserowFormulaDurationType instead
 class BaserowFormulaDateIntervalType(
-    BaserowFormulaTypeHasEmptyBaserowExpression, BaserowFormulaValidType
+    BaserowFormulaTypeHasEmptyBaserowExpression,
+    BaserowFormulaValidType,
+    BaserowFormulaDateIntervalTypeMixin,
 ):
     type = "date_interval"
     baserow_field_type = None
@@ -535,6 +547,74 @@ class BaserowFormulaDateIntervalType(
         return Cast(field.db_column, output_field=models.TextField())
 
 
+class BaserowFormulaDurationType(
+    BaserowFormulaTypeHasEmptyBaserowExpression,
+    BaserowFormulaValidType,
+    BaserowFormulaDateIntervalTypeMixin,
+):
+    type = "duration"
+    baserow_field_type = "duration"
+    user_overridable_formatting_option_fields = ["duration_format"]
+    can_group_by = True
+    can_order_by_in_array = True
+
+    def __init__(self, duration_format: str = D_H_M_S, **kwargs):
+        super().__init__(**kwargs)
+        self.duration_format = duration_format
+
+    @property
+    def comparable_types(self) -> List[Type["BaserowFormulaValidType"]]:
+        return [type(self)]
+
+    @property
+    def limit_comparable_types(self) -> List[Type["BaserowFormulaValidType"]]:
+        return [type(self)]
+
+    @property
+    def addable_types(self) -> List[Type["BaserowFormulaValidType"]]:
+        return [type(self), BaserowFormulaDateType]
+
+    @property
+    def subtractable_types(self) -> List[Type["BaserowFormulaValidType"]]:
+        return [type(self)]
+
+    def add(
+        self,
+        add_func_call: "BaserowFunctionCall[UnTyped]",
+        arg1: "BaserowExpression[BaserowFormulaValidType]",
+        arg2: "BaserowExpression[BaserowFormulaValidType]",
+    ):
+        return add_func_call.with_valid_type(
+            _calculate_addition_interval_type(arg1, arg2)
+        )
+
+    def minus(
+        self,
+        minus_func_call: "BaserowFunctionCall[UnTyped]",
+        arg1: "BaserowExpression[BaserowFormulaValidType]",
+        arg2: "BaserowExpression[BaserowFormulaValidType]",
+    ):
+        return minus_func_call.with_valid_type(
+            BaserowFormulaDurationType(
+                duration_format=self.duration_format,
+                nullable=arg1.expression_type.nullable or arg2.expression_type.nullable,
+            )
+        )
+
+    def placeholder_empty_value(self):
+        return Value(datetime.timedelta(hours=0), output_field=models.DurationField())
+
+    def placeholder_empty_baserow_expression(
+        self,
+    ) -> "BaserowExpression[BaserowFormulaValidType]":
+        return literal(datetime.timedelta(hours=0))
+
+    def get_order_by_in_array_expr(self, field, field_name, order_direction):
+        return JSONBSingleKeyArrayExpression(
+            field_name, "value", "interval", output_field=models.DurationField()
+        )
+
+
 class BaserowFormulaDateType(BaserowFormulaValidType):
     type = "date"
     baserow_field_type = "date"
@@ -579,11 +659,11 @@ class BaserowFormulaDateType(BaserowFormulaValidType):
 
     @property
     def addable_types(self) -> List[Type["BaserowFormulaValidType"]]:
-        return [BaserowFormulaDateIntervalType]
+        return [BaserowFormulaDateIntervalType, BaserowFormulaDurationType]
 
     @property
     def subtractable_types(self) -> List[Type["BaserowFormulaValidType"]]:
-        return [type(self), BaserowFormulaDateIntervalType]
+        return [type(self), BaserowFormulaDateIntervalType, BaserowFormulaDurationType]
 
     def add(
         self,
@@ -604,12 +684,12 @@ class BaserowFormulaDateType(BaserowFormulaValidType):
         arg1_type = arg1.expression_type
         arg2_type = arg2.expression_type
         if isinstance(arg2_type, BaserowFormulaDateType):
-            # date - date = interval
-            resulting_type = BaserowFormulaDateIntervalType(
+            # date - date = duration
+            resulting_type = BaserowFormulaDurationType(
                 nullable=arg1_type.nullable or arg2_type.nullable
             )
         else:
-            # date - interval = date
+            # date - duration = date
             resulting_type = arg1_type
         return minus_func_call.with_valid_type(resulting_type)
 
@@ -1003,6 +1083,11 @@ class BaserowFormulaArrayType(BaserowFormulaValidType):
                 # strings, we need to reparse them back first before giving it to
                 # the date field type.
                 list_item = parser.isoparse(list_item)
+            elif list_item is not None and self.sub_type.type == "duration":
+                # Arrays are stored as JSON which means the durations are converted to
+                # the number of seconds, we need to reparse them back first before
+                # giving the duration field type.
+                list_item = datetime.timedelta(seconds=list_item)
             export_value = map_func(list_item)
             if export_value is None:
                 export_value = ""
@@ -1268,7 +1353,8 @@ BASEROW_FORMULA_TYPES = [
     BaserowFormulaCharType,
     BaserowFormulaButtonType,
     BaserowFormulaLinkType,
-    BaserowFormulaDateIntervalType,
+    BaserowFormulaDateIntervalType,  # Deprecated in favor of BaserowFormulaDurationType
+    BaserowFormulaDurationType,
     BaserowFormulaDateType,
     BaserowFormulaBooleanType,
     BaserowFormulaNumberType,
diff --git a/backend/src/baserow/contrib/database/migrations/0150_formulafield_duration_format_and_more.py b/backend/src/baserow/contrib/database/migrations/0150_formulafield_duration_format_and_more.py
new file mode 100644
index 000000000..4993b5bc5
--- /dev/null
+++ b/backend/src/baserow/contrib/database/migrations/0150_formulafield_duration_format_and_more.py
@@ -0,0 +1,78 @@
+# Generated by Django 4.0.10 on 2024-01-05 15:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("database", "0149_alter_durationfield_duration_format"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="formulafield",
+            name="duration_format",
+            field=models.CharField(
+                choices=[
+                    ("h:mm", "hours:minutes"),
+                    ("h:mm:ss", "hours:minutes:seconds"),
+                    ("h:mm:ss.s", "hours:minutes:seconds:deciseconds"),
+                    ("h:mm:ss.ss", "hours:minutes:seconds:centiseconds"),
+                    ("h:mm:ss.sss", "hours:minutes:seconds:milliseconds"),
+                    ("d h", "days:hours"),
+                    ("d h:mm", "days:hours:minutes"),
+                    ("d h:mm:ss", "days:hours:minutes:seconds"),
+                ],
+                default="h:mm",
+                help_text="The format of the duration.",
+                max_length=32,
+                null=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="formulafield",
+            name="array_formula_type",
+            field=models.TextField(
+                choices=[
+                    ("invalid", "invalid"),
+                    ("text", "text"),
+                    ("char", "char"),
+                    ("button", "button"),
+                    ("link", "link"),
+                    ("date_interval", "date_interval"),
+                    ("duration", "duration"),
+                    ("date", "date"),
+                    ("boolean", "boolean"),
+                    ("number", "number"),
+                    ("single_select", "single_select"),
+                    ("multiple_select", "multiple_select"),
+                    ("single_file", "single_file"),
+                ],
+                default=None,
+                null=True,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="formulafield",
+            name="formula_type",
+            field=models.TextField(
+                choices=[
+                    ("invalid", "invalid"),
+                    ("text", "text"),
+                    ("char", "char"),
+                    ("button", "button"),
+                    ("link", "link"),
+                    ("date_interval", "date_interval"),
+                    ("duration", "duration"),
+                    ("date", "date"),
+                    ("boolean", "boolean"),
+                    ("number", "number"),
+                    ("array", "array"),
+                    ("single_select", "single_select"),
+                    ("multiple_select", "multiple_select"),
+                    ("single_file", "single_file"),
+                ],
+                default="invalid",
+            ),
+        ),
+    ]
diff --git a/backend/src/baserow/contrib/database/views/view_filters.py b/backend/src/baserow/contrib/database/views/view_filters.py
index b7fd19d72..43de31091 100644
--- a/backend/src/baserow/contrib/database/views/view_filters.py
+++ b/backend/src/baserow/contrib/database/views/view_filters.py
@@ -55,6 +55,7 @@ from baserow.contrib.database.formula import (
 )
 from baserow.contrib.database.formula.types.formula_types import (
     BaserowFormulaDateIntervalType,
+    BaserowFormulaDurationType,
     BaserowFormulaSingleFileType,
 )
 from baserow.core.datetime import get_timezones
@@ -1405,6 +1406,7 @@ class EmptyViewFilterType(ViewFilterType):
             BaserowFormulaDateType.type,
             BaserowFormulaBooleanType.type,
             BaserowFormulaDateIntervalType.type,
+            BaserowFormulaDurationType.type,
             FormulaFieldType.array_of(BaserowFormulaSingleFileType.type),
         ),
     ]
diff --git a/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py b/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py
index d70463a17..1d101f0b6 100644
--- a/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py
+++ b/backend/tests/baserow/contrib/database/api/fields/test_formula_views.py
@@ -948,7 +948,7 @@ def test_can_type_a_valid_formula_field(data_fixture, api_client):
             "api:database:formula:type_formula",
             kwargs={"table_id": table.id},
         ),
-        {f"formula": "1+1", "name": formula_field_name},
+        {"formula": "1+1", "name": formula_field_name},
         format="json",
         HTTP_AUTHORIZATION=f"JWT {token}",
     )
@@ -960,6 +960,7 @@ def test_can_type_a_valid_formula_field(data_fixture, api_client):
         "date_time_format": None,
         "date_show_tzinfo": None,
         "date_force_timezone": None,
+        "duration_format": None,
         "error": None,
         "formula": "1+1",
         "formula_type": "number",
diff --git a/backend/tests/baserow/contrib/database/api/rows/test_row_serializers.py b/backend/tests/baserow/contrib/database/api/rows/test_row_serializers.py
index c972666c2..695720491 100644
--- a/backend/tests/baserow/contrib/database/api/rows/test_row_serializers.py
+++ b/backend/tests/baserow/contrib/database/api/rows/test_row_serializers.py
@@ -245,14 +245,14 @@ def test_get_row_serializer_with_user_field_names(data_fixture):
                     {"id": 2, "value": "-123.456"},
                     {"id": 3, "value": ""},
                 ],
-                "duration_hm": 3660,
-                "duration_hms": 3666,
+                "duration_hm": 3660.0,
+                "duration_hms": 3666.0,
                 "duration_hms_s": 3666.6,
                 "duration_hms_ss": 3666.66,
                 "duration_hms_sss": 3666.666,
-                "duration_dh": 90000,
-                "duration_dhm": 90060,
-                "duration_dhms": 90066,
+                "duration_dh": 90000.0,
+                "duration_dhm": 90060.0,
+                "duration_dhms": 90066.0,
                 "email": "test@example.com",
                 "file": [
                     {
@@ -333,7 +333,7 @@ def test_get_row_serializer_with_user_field_names(data_fixture):
                 "url": "https://www.google.com",
                 "formula_bool": True,
                 "formula_date": "2020-01-01",
-                "formula_dateinterval": "1 day",
+                "formula_dateinterval": 86400.0,
                 "formula_decimal": "33.3333333333",
                 "formula_email": "test@example.com",
                 "formula_int": "1",
diff --git a/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py b/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py
index 082df2e61..e62dc2f1a 100644
--- a/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py
+++ b/backend/tests/baserow/contrib/database/api/views/test_view_serializers.py
@@ -207,7 +207,7 @@ def test_serialize_group_by_metadata_on_all_fields_in_interesting_table(data_fix
         "formula_int": [{"count": 2, "field_formula_int": "1"}],
         "formula_bool": [{"count": 2, "field_formula_bool": True}],
         "formula_decimal": [{"count": 2, "field_formula_decimal": "33.3333333333"}],
-        "formula_dateinterval": [{"count": 2, "field_formula_dateinterval": "1 day"}],
+        "formula_dateinterval": [{"count": 2, "field_formula_dateinterval": 24 * 3600}],
         "formula_date": [{"count": 2, "field_formula_date": "2020-01-01"}],
         "formula_email": [
             {"count": 1, "field_formula_email": ""},
diff --git a/backend/tests/baserow/contrib/database/export/test_export_handler.py b/backend/tests/baserow/contrib/database/export/test_export_handler.py
index 91e0062a7..db03a6263 100755
--- a/backend/tests/baserow/contrib/database/export/test_export_handler.py
+++ b/backend/tests/baserow/contrib/database/export/test_export_handler.py
@@ -236,7 +236,7 @@ def test_can_export_every_interesting_different_field_to_csv(
         "1,,,,,,,,,0,False,,,,,,,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,"
         "02/01/2021 13:00,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,02/01/2021 13:00,"
         "user@example.com,user@example.com,,,,,,,,,,,,,,,,,,,test FORMULA,1,True,33.3333333333,"
-        "1 day,2020-01-01,,,label (https://google.com),https://google.com,,0,0.000,,"
+        "1d 0:00,2020-01-01,,,label (https://google.com),https://google.com,,0,0.000,,"
         "00000000-0000-4000-8000-000000000001,1\r\n"
         "2,text,long_text,https://www.google.com,test@example.com,-1,1,-1.2,1.2,3,True,"
         "02/01/2020 01:23,02/01/2020,01/02/2020 01:23,01/02/2020,01/02/2020 02:23,"
@@ -250,7 +250,7 @@ def test_can_export_every_interesting_different_field_to_csv(
         '"a.txt (http://localhost:8000/media/user_files/hashed_name.txt),'
         'b.txt (http://localhost:8000/media/user_files/other_name.txt)",A,"D,C,E",'
         '"user2@example.com,user3@example.com",\'+4412345678,test FORMULA,1,True,33.3333333333,'
-        "1 day,2020-01-01,A,test@example.com,label (https://google.com),https://google.com,"
+        "1d 0:00,2020-01-01,A,test@example.com,label (https://google.com),https://google.com,"
         '"D,C,E",3,-122.222,"linked_row_1,linked_row_2,",00000000-0000-4000-8000-000000000002,2\r\n'
     )
 
diff --git a/backend/tests/baserow/contrib/database/field/test_duration_field_type.py b/backend/tests/baserow/contrib/database/field/test_duration_field_type.py
index 27424cff2..f2e4c4085 100644
--- a/backend/tests/baserow/contrib/database/field/test_duration_field_type.py
+++ b/backend/tests/baserow/contrib/database/field/test_duration_field_type.py
@@ -662,3 +662,126 @@ def test_get_group_by_metadata_in_rows_with_duration_field(data_fixture):
             ]
         )
     }
+
+
+@pytest.mark.field_duration
+@pytest.mark.django_db
+def test_duration_field_can_be_used_in_formulas(data_fixture):
+    user = data_fixture.create_user()
+    table = data_fixture.create_database_table(user=user)
+    duration_field = data_fixture.create_duration_field(
+        table=table, name="duration", duration_format="h:mm:ss"
+    )
+    ref_field = data_fixture.create_formula_field(
+        table=table, name="ref", formula=f"field('{duration_field.name}')"
+    )
+    add_field = data_fixture.create_formula_field(
+        table=table,
+        formula=f"field('{duration_field.name}') + field('{ref_field.name}')",
+    )
+    sub_field = data_fixture.create_formula_field(
+        table=table,
+        formula=f"field('{duration_field.name}') - field('{ref_field.name}')",
+    )
+    compare_field = data_fixture.create_formula_field(
+        table=table,
+        formula=f"field('{duration_field.name}') = field('{ref_field.name}')",
+    )
+
+    RowHandler().create_rows(
+        user,
+        table,
+        rows_values=[
+            {duration_field.db_column: 3600},
+            {duration_field.db_column: 0},
+            {},
+        ],
+    )
+
+    model = table.get_model()
+
+    assert list(
+        model.objects.all().values(
+            ref_field.db_column,
+            add_field.db_column,
+            sub_field.db_column,
+            compare_field.db_column,
+        )
+    ) == [
+        {
+            ref_field.db_column: timedelta(seconds=3600),
+            add_field.db_column: timedelta(seconds=7200),
+            sub_field.db_column: timedelta(seconds=0),
+            compare_field.db_column: True,
+        },
+        {
+            ref_field.db_column: timedelta(seconds=0),
+            add_field.db_column: timedelta(seconds=0),
+            sub_field.db_column: timedelta(seconds=0),
+            compare_field.db_column: True,
+        },
+        {
+            ref_field.db_column: None,
+            add_field.db_column: timedelta(seconds=0),
+            sub_field.db_column: timedelta(seconds=0),
+            compare_field.db_column: True,
+        },
+    ]
+
+
+@pytest.mark.field_duration
+@pytest.mark.django_db
+def test_duration_field_can_be_looked_up(data_fixture):
+    user = data_fixture.create_user()
+    table_a, table_b, link_field = data_fixture.create_two_linked_tables(user=user)
+    duration_field = data_fixture.create_duration_field(
+        table=table_b, name="duration", duration_format="h:mm:ss"
+    )
+    lookup_field = data_fixture.create_formula_field(
+        table=table_a, formula=f"lookup('{link_field.name}', 'duration')"
+    )
+
+    # Also a formula field referencing a duration can be looked up
+    duration_formula = data_fixture.create_formula_field(
+        table=table_b,
+        name="formula",
+        formula=f"field('{duration_field.name}') + date_interval('60s')",
+    )
+    lookup_formula = data_fixture.create_formula_field(
+        table=table_a, formula=f"lookup('{link_field.name}', 'formula')"
+    )
+
+    model_b = table_b.get_model()
+    row_b_1, row_b_2 = RowHandler().create_rows(
+        user=user,
+        table=table_b,
+        rows_values=[
+            {duration_field.db_column: 3600},
+            {duration_field.db_column: 60},
+        ],
+        model=model_b,
+    )
+
+    assert list(model_b.objects.values_list(duration_formula.db_column, flat=True)) == [
+        timedelta(seconds=3600 + 60),
+        timedelta(seconds=60 + 60),
+    ]
+
+    model_a = table_a.get_model()
+    (row,) = RowHandler().create_rows(
+        user=user,
+        table=table_a,
+        rows_values=[
+            {f"field_{link_field.id}": [row_b_1.id, row_b_2.id]},
+        ],
+        model=model_a,
+    )
+    assert getattr(row, f"field_{lookup_field.id}") == [
+        {"id": row_b_1.id, "value": 3600},
+        {"id": row_b_2.id, "value": 60},
+    ]
+
+    assert getattr(row, f"field_{lookup_formula.id}") == [
+        {"id": row_b_1.id, "value": 3600 + 60},
+        {"id": row_b_2.id, "value": 60 + 60},
+    ]
diff --git a/backend/tests/baserow/contrib/database/field/test_field_types.py b/backend/tests/baserow/contrib/database/field/test_field_types.py
index 4d4523121..57034b938 100644
--- a/backend/tests/baserow/contrib/database/field/test_field_types.py
+++ b/backend/tests/baserow/contrib/database/field/test_field_types.py
@@ -567,7 +567,7 @@ def test_human_readable_values(data_fixture):
         "url": "",
         "formula_bool": "True",
         "formula_date": "2020-01-01",
-        "formula_dateinterval": "1 day",
+        "formula_dateinterval": "1d 0:00",
         "formula_decimal": "33.3333333333",
         "formula_email": "",
         "formula_int": "1",
@@ -622,7 +622,7 @@ def test_human_readable_values(data_fixture):
         "url": "https://www.google.com",
         "formula_bool": "True",
         "formula_date": "2020-01-01",
-        "formula_dateinterval": "1 day",
+        "formula_dateinterval": "1d 0:00",
         "formula_decimal": "33.3333333333",
         "formula_email": "test@example.com",
         "formula_int": "1",
diff --git a/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py b/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py
index 420db65d8..68bc03262 100644
--- a/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py
+++ b/backend/tests/baserow/contrib/database/formula/test_baserow_formula_results.py
@@ -147,7 +147,7 @@ VALID_FORMULA_TESTS = [
     ("or(false, true)", True),
     ("or(true, true)", True),
     ("'a' + 'b'", "ab"),
-    ("date_interval('1 year')", "1 year"),
+    ("date_interval('1 year')", 365 * 24 * 3600),
     ("date_interval('1 year') > date_interval('1 day')", True),
     ("date_interval('1 invalid')", None),
     ("todate('20200101', 'YYYYMMDD') + date_interval('1 year')", "2021-01-01"),
@@ -163,8 +163,11 @@ VALID_FORMULA_TESTS = [
         ")",
         "6",
     ),
-    ("todate('20200101', 'YYYYMMDD') - todate('20210101', 'YYYYMMDD')", "-366 days"),
-    ("date_interval('1 year') - date_interval('1 day')", "1 year -1 days"),
+    (
+        "todate('20200101', 'YYYYMMDD') - todate('20210101', 'YYYYMMDD')",
+        -366 * 24 * 3600,
+    ),
+    ("date_interval('1 year') - date_interval('1 day')", 364 * 24 * 3600),
     ("now() > todate('20200101', 'YYYYMMDD')", True),
     ("todate('01123456', 'DDMMYYYY') < now()", False),
     ("todate('01123456', 'DDMMYYYY') < today()", False),
@@ -582,7 +585,7 @@ def test_can_lookup_date_intervals(data_fixture, api_client):
     )
     assert response.status_code == HTTP_200_OK
     assert [o[lookup_formula.db_column] for o in response.json()["results"]] == [
-        [{"id": row_1.id, "value": "2 days"}]
+        [{"id": row_1.id, "value": 2 * 24 * 3600}]
     ]
 
 
@@ -964,13 +967,13 @@ INVALID_FORMULA_TESTS = [
         "todate('20200101', 'YYYYMMDD') + todate('20210101', 'YYYYMMDD')",
         "ERROR_WITH_FORMULA",
         "Error with formula: argument number 2 given to operator + was of type date "
-        "but the only usable type for this argument is date_interval.",
+        "but the only usable types for this argument are date_interval,duration.",
     ),
     (
         "date_interval('1 second') - todate('20210101', 'YYYYMMDD')",
         "ERROR_WITH_FORMULA",
         "Error with formula: argument number 2 given to operator - was of type date "
-        "but the only usable type for this argument is date_interval.",
+        "but the only usable type for this argument is duration.",
     ),
     (
         "when_empty(1, 'a')",
diff --git a/backend/tests/baserow/contrib/database/view/test_view_handler.py b/backend/tests/baserow/contrib/database/view/test_view_handler.py
index 908749b75..81a74a414 100755
--- a/backend/tests/baserow/contrib/database/view/test_view_handler.py
+++ b/backend/tests/baserow/contrib/database/view/test_view_handler.py
@@ -4181,7 +4181,9 @@ def test_get_group_by_on_all_fields_in_interesting_table(data_fixture):
         "formula_decimal": [
             {"field_formula_decimal": Decimal("33.3333333333"), "count": 2}
         ],
-        "formula_dateinterval": [{"field_formula_dateinterval": "1 day", "count": 2}],
+        "formula_dateinterval": [
+            {"field_formula_dateinterval": datetime.timedelta(days=1), "count": 2}
+        ],
         "formula_date": [{"field_formula_date": datetime.date(2020, 1, 1), "count": 2}],
         "formula_email": [
             {"field_formula_email": "", "count": 1},
diff --git a/changelog/entries/unreleased/breaking_change/2190_new_formulas_returning_a_date_intervalduration_are_now_sent_.json b/changelog/entries/unreleased/breaking_change/2190_new_formulas_returning_a_date_intervalduration_are_now_sent_.json
new file mode 100644
index 000000000..dfb656afb
--- /dev/null
+++ b/changelog/entries/unreleased/breaking_change/2190_new_formulas_returning_a_date_intervalduration_are_now_sent_.json
@@ -0,0 +1,7 @@
+{
+    "type": "breaking_change",
+    "message": "New formulas returning a date_interval/duration are sent as number of seconds instead of a formatted string.",
+    "issue_number": 2190,
+    "bullet_points": [],
+    "created_at": "2024-01-12"
+}
\ No newline at end of file
diff --git a/changelog/entries/unreleased/feature/2190_add_formula_support_for_the_duration_field_type.json b/changelog/entries/unreleased/feature/2190_add_formula_support_for_the_duration_field_type.json
new file mode 100644
index 000000000..c44a2fef8
--- /dev/null
+++ b/changelog/entries/unreleased/feature/2190_add_formula_support_for_the_duration_field_type.json
@@ -0,0 +1,7 @@
+{
+    "type": "feature",
+    "message": "Add support to reference duration fields in the formula language.",
+    "issue_number": 2190,
+    "bullet_points": [],
+    "created_at": "2024-01-08"
+}
\ No newline at end of file
diff --git a/premium/backend/tests/baserow_premium_tests/export/test_premium_export_types.py b/premium/backend/tests/baserow_premium_tests/export/test_premium_export_types.py
index da74a1af2..146901c81 100644
--- a/premium/backend/tests/baserow_premium_tests/export/test_premium_export_types.py
+++ b/premium/backend/tests/baserow_premium_tests/export/test_premium_export_types.py
@@ -88,7 +88,7 @@ def test_can_export_every_interesting_different_field_to_json(
     "formula_int": 1,
     "formula_bool": true,
     "formula_decimal": "33.3333333333",
-    "formula_dateinterval": "1 day",
+    "formula_dateinterval": "1d 0:00",
     "formula_date": "2020-01-01",
     "formula_singleselect": "",
     "formula_email": "",
@@ -195,7 +195,7 @@ def test_can_export_every_interesting_different_field_to_json(
     "formula_int": 1,
     "formula_bool": true,
     "formula_decimal": "33.3333333333",
-    "formula_dateinterval": "1 day",
+    "formula_dateinterval": "1d 0:00",
     "formula_date": "2020-01-01",
     "formula_singleselect": "A",
     "formula_email": "test@example.com",
@@ -351,7 +351,7 @@ def test_can_export_every_interesting_different_field_to_xml(
       <formula-int>1</formula-int>
       <formula-bool>true</formula-bool>
       <formula-decimal>33.3333333333</formula-decimal>
-      <formula-dateinterval>1 day</formula-dateinterval>
+      <formula-dateinterval>1d 0:00</formula-dateinterval>
       <formula-date>2020-01-01</formula-date>
       <formula-singleselect/>
       <formula-email/>
@@ -458,7 +458,7 @@ def test_can_export_every_interesting_different_field_to_xml(
       <formula-int>1</formula-int>
       <formula-bool>true</formula-bool>
       <formula-decimal>33.3333333333</formula-decimal>
-      <formula-dateinterval>1 day</formula-dateinterval>
+      <formula-dateinterval>1d 0:00</formula-dateinterval>
       <formula-date>2020-01-01</formula-date>
       <formula-singleselect>A</formula-singleselect>
       <formula-email>test@example.com</formula-email>
diff --git a/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue b/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue
index 82d61ef9d..81aeebe20 100644
--- a/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue
+++ b/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue
@@ -15,17 +15,29 @@
       :view="view"
     >
     </FieldDateSubForm>
+    <FieldDurationSubForm
+      v-else-if="formulaType === 'duration'"
+      :default-values="defaultValues"
+      :table="table"
+      :view="view"
+    >
+    </FieldDurationSubForm>
   </div>
 </template>
 <script>
 import FieldNumberSubForm from '@baserow/modules/database/components/field/FieldNumberSubForm'
 import FieldDateSubForm from '@baserow/modules/database/components/field/FieldDateSubForm'
+import FieldDurationSubForm from '@baserow/modules/database/components/field/FieldDurationSubForm'
 import form from '@baserow/modules/core/mixins/form'
 import fieldSubForm from '@baserow/modules/database/mixins/fieldSubForm'
 
 export default {
   name: 'FormulaTypeSubForms',
-  components: { FieldNumberSubForm, FieldDateSubForm },
+  components: {
+    FieldNumberSubForm,
+    FieldDateSubForm,
+    FieldDurationSubForm,
+  },
   mixins: [form, fieldSubForm],
   props: {
     table: {
diff --git a/web-frontend/modules/database/components/formula/array/FunctionalFormulaArrayDurationItem.vue b/web-frontend/modules/database/components/formula/array/FunctionalFormulaArrayDurationItem.vue
new file mode 100644
index 000000000..4465c13e7
--- /dev/null
+++ b/web-frontend/modules/database/components/formula/array/FunctionalFormulaArrayDurationItem.vue
@@ -0,0 +1,15 @@
+<template functional>
+  <div v-if="props.value" class="array-field__item">
+    <span>
+      {{ $options.methods.formatValue(props.field, props.value) }}
+    </span>
+  </div>
+</template>
+
+<script>
+import durationField from '@baserow/modules/database/mixins/durationField'
+export default {
+  name: 'FunctionalFormulaArrayDurationItem',
+  mixins: [durationField],
+}
+</script>
diff --git a/web-frontend/modules/database/components/row/RowEditFieldDurationReadOnly.vue b/web-frontend/modules/database/components/row/RowEditFieldDurationReadOnly.vue
new file mode 100644
index 000000000..14c4c140d
--- /dev/null
+++ b/web-frontend/modules/database/components/row/RowEditFieldDurationReadOnly.vue
@@ -0,0 +1,28 @@
+<template>
+  <div class="control__elements">
+    <div>{{ formattedValue }}</div>
+  </div>
+</template>
+
+<script>
+import rowEditField from '@baserow/modules/database/mixins/rowEditField'
+import durationField from '@baserow/modules/database/mixins/durationField'
+
+export default {
+  mixins: [rowEditField, durationField],
+  watch: {
+    'field.duration_format': {
+      handler() {
+        this.updateCopy(this.field, this.value)
+        this.updateFormattedValue(this.field, this.copy)
+      },
+    },
+    value: {
+      handler(newValue) {
+        this.updateCopy(this.field, newValue)
+        this.updateFormattedValue(this.field, this.copy)
+      },
+    },
+  },
+}
+</script>
diff --git a/web-frontend/modules/database/components/view/grid/fields/GridViewFieldDuration.vue b/web-frontend/modules/database/components/view/grid/fields/GridViewFieldDuration.vue
index 462c96175..8cb5d2a81 100644
--- a/web-frontend/modules/database/components/view/grid/fields/GridViewFieldDuration.vue
+++ b/web-frontend/modules/database/components/view/grid/fields/GridViewFieldDuration.vue
@@ -18,7 +18,7 @@
         class="grid-field-duration__input"
         :placeholder="field.duration_format"
         @keypress="onKeyPress(field, $event)"
-        @keyup="updateCopy(field, $event.target.value)"
+        @input="onInput(field, $event)"
       />
       <div v-show="!isValid()" class="grid-view__cell--error align-right">
         {{ getError() }}
@@ -43,7 +43,11 @@ export default {
       this.updateFormattedValue(this.field, value)
       return this.$super(gridFieldInput).beforeSave(value)
     },
-    afterEdit() {
+    afterEdit(event, value) {
+      if (value !== null) {
+        this.updateCopy(this.field, value)
+        this.updateFormattedValue(this.field, this.copy)
+      }
       this.$nextTick(() => {
         this.$refs.input.focus()
         this.$refs.input.selectionStart = this.$refs.input.selectionEnd = 100000
diff --git a/web-frontend/modules/database/fieldTypes.js b/web-frontend/modules/database/fieldTypes.js
index 4be2511d2..b8ab30c15 100644
--- a/web-frontend/modules/database/fieldTypes.js
+++ b/web-frontend/modules/database/fieldTypes.js
@@ -2261,6 +2261,10 @@ export class DurationFieldType extends FieldType {
     return DURATION_FORMATS.get(field.duration_format).example
   }
 
+  canBeReferencedByFormulaField() {
+    return true
+  }
+
   getDocsDescription(field) {
     return this.app.i18n.t('fieldDocs.duration', {
       format: field.duration_format,
diff --git a/web-frontend/modules/database/formula/formulaTypes.js b/web-frontend/modules/database/formula/formulaTypes.js
index 95eed5e0c..7afdecadb 100644
--- a/web-frontend/modules/database/formula/formulaTypes.js
+++ b/web-frontend/modules/database/formula/formulaTypes.js
@@ -17,7 +17,9 @@ import RowEditFieldMultipleSelectReadOnly from '@baserow/modules/database/compon
 import RowEditFieldArray from '@baserow/modules/database/components/row/RowEditFieldArray'
 import RowEditFieldLinkURL from '@baserow/modules/database/components/row/RowEditFieldLinkURL'
 import RowEditFieldButton from '@baserow/modules/database/components/row/RowEditFieldButton'
+import RowEditFieldDurationReadOnly from '@baserow/modules/database/components/row/RowEditFieldDurationReadOnly.vue'
 import FunctionalFormulaArrayItem from '@baserow/modules/database/components/formula/array/FunctionalFormulaArrayItem'
+import FunctionalFormulaArrayDurationItem from '@baserow/modules/database/components/formula/array/FunctionalFormulaArrayDurationItem'
 import FunctionalFormulaBooleanArrayItem from '@baserow/modules/database/components/formula/array/FunctionalFormulaBooleanArrayItem'
 import FunctionalFormulaDateArrayItem from '@baserow/modules/database/components/formula/array/FunctionalFormulaDateArrayItem'
 import FunctionalFormulaSingleSelectArrayItem from '@baserow/modules/database/components/formula/array/FunctionalFormulaSingleSelectArrayItem'
@@ -351,6 +353,41 @@ export class BaserowFormulaDateType extends BaserowFormulaTypeDefinition {
   }
 }
 
+export class BaserowFormulaDurationType extends BaserowFormulaTypeDefinition {
+  static getType() {
+    return 'duration'
+  }
+
+  getFieldType() {
+    return 'duration'
+  }
+
+  getIconClass() {
+    return 'iconoir-clock-rotate-right'
+  }
+
+  getRowEditFieldComponent(field) {
+    return RowEditFieldDurationReadOnly
+  }
+
+  getSortOrder() {
+    return 5
+  }
+
+  canGroupByInView() {
+    return true
+  }
+
+  getFunctionalGridViewFieldArrayComponent() {
+    return FunctionalFormulaArrayDurationItem
+  }
+
+  canBeSortedWhenInArray(field) {
+    return true
+  }
+}
+
+// Deprecated, use BaserowFormulaDurationType instead.
 export class BaserowFormulaDateIntervalType extends BaserowFormulaTypeDefinition {
   static getType() {
     return 'date_interval'
diff --git a/web-frontend/modules/database/mixins/durationField.js b/web-frontend/modules/database/mixins/durationField.js
index dd9918cab..eadd75f35 100644
--- a/web-frontend/modules/database/mixins/durationField.js
+++ b/web-frontend/modules/database/mixins/durationField.js
@@ -47,11 +47,17 @@ export default {
         return newCopy
       }
     },
-    onKeyPress(field, event) {
+    isValidChar(char) {
       const allowedChars = ['.', ':', ' ', 'd', 'h']
-      if (!/\d/.test(event.key) && !allowedChars.includes(event.key)) {
+      return /\d/.test(char) || allowedChars.includes(char)
+    },
+    onKeyPress(field, event) {
+      if (!this.isValidChar(event.key)) {
         return event.preventDefault()
       }
     },
+    onInput(field, event) {
+      this.updateCopy(field, event.target.value)
+    },
   },
 }
diff --git a/web-frontend/modules/database/mixins/gridFieldInput.js b/web-frontend/modules/database/mixins/gridFieldInput.js
index b04cef5a6..7f068c89a 100644
--- a/web-frontend/modules/database/mixins/gridFieldInput.js
+++ b/web-frontend/modules/database/mixins/gridFieldInput.js
@@ -144,7 +144,7 @@ export default {
 
       this.editing = true
       this.copy = value === null ? this.value : value
-      this.afterEdit(event)
+      this.afterEdit(event, value)
     },
     /**
      * Method that can be called when in the editing state. It will bring the
diff --git a/web-frontend/modules/database/plugin.js b/web-frontend/modules/database/plugin.js
index 7cb9ab5f8..149b3c266 100644
--- a/web-frontend/modules/database/plugin.js
+++ b/web-frontend/modules/database/plugin.js
@@ -217,7 +217,8 @@ import {
   BaserowFormulaButtonType,
   BaserowFormulaCharType,
   BaserowFormulaLinkType,
-  BaserowFormulaDateIntervalType,
+  BaserowFormulaDateIntervalType, // Deprecated
+  BaserowFormulaDurationType,
   BaserowFormulaDateType,
   BaserowFormulaInvalidType,
   BaserowFormulaNumberType,
@@ -613,6 +614,10 @@ export default (context) => {
     'formula_type',
     new BaserowFormulaDateIntervalType(context)
   )
+  app.$registry.register(
+    'formula_type',
+    new BaserowFormulaDurationType(context)
+  )
   app.$registry.register('formula_type', new BaserowFormulaNumberType(context))
   app.$registry.register('formula_type', new BaserowFormulaArrayType(context))
   app.$registry.register('formula_type', new BaserowFormulaSpecialType(context))
diff --git a/web-frontend/modules/database/utils/duration.js b/web-frontend/modules/database/utils/duration.js
index f878c0b45..7a92c8818 100644
--- a/web-frontend/modules/database/utils/duration.js
+++ b/web-frontend/modules/database/utils/duration.js
@@ -117,7 +117,7 @@ export const DURATION_FORMATS = new Map([
       example: '1:23:40',
       toString(d, h, m, s) {
         return `${d * 24 + h}:${m.toString().padStart(2, '0')}:${s
-          .toString()
+          .toFixed(0)
           .padStart(2, '0')}`
       },
       round: (value) => Math.round(value),
@@ -191,7 +191,7 @@ export const DURATION_FORMATS = new Map([
       example: '1d 2:34:56',
       toString(d, h, m, s) {
         return `${d}d ${h}:${m.toString().padStart(2, '0')}:${s
-          .toString()
+          .toFixed(0)
           .padStart(2, '0')}`
       },
       round: (value) => Math.round(value),