diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 5e6d0e1b335c05589ff03dacea5255aae439b46c..96159a11d50c055a1702ff09a2ba87b1be9a14cd 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -1308,6 +1308,18 @@ map_to_datetime(struct Mem *mem)
 	return 0;
 }
 
+/** Convert MEM from MAP to INTERVAL. */
+static inline int
+map_to_interval(struct Mem *mem)
+{
+	assert(mem->type == MEM_TYPE_MAP);
+	struct interval itv;
+	if (interval_from_map(&itv, mem->z) != 0)
+		return -1;
+	mem_set_interval(mem, &itv);
+	return 0;
+}
+
 int
 mem_to_int(struct Mem *mem)
 {
@@ -1490,6 +1502,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
 		mem->flags = 0;
 		return 0;
 	case FIELD_TYPE_INTERVAL:
+		if (mem->type == MEM_TYPE_MAP)
+			return map_to_interval(mem);
 		if (mem->type != MEM_TYPE_INTERVAL)
 			return -1;
 		mem->flags = 0;
diff --git a/src/lib/core/datetime.c b/src/lib/core/datetime.c
index 7cfbd95e9d371a02c44ba2557c0469fb5f40ae9d..07f9988a1f219759f66fd9b734a7f0ecbfcf4767 100644
--- a/src/lib/core/datetime.c
+++ b/src/lib/core/datetime.c
@@ -929,6 +929,58 @@ get_double_from_mp(const char **data, double *value)
 	return 0;
 }
 
+/** Parse msgpack value and convert it to int32, if possible. */
+static int
+get_int32_from_mp(const char **data, int32_t *value)
+{
+	switch (mp_typeof(**data)) {
+	case MP_INT: {
+		int64_t val = mp_decode_int(data);
+		if (val < INT32_MIN)
+			return -1;
+		*value = val;
+		break;
+	}
+	case MP_UINT: {
+		uint64_t val = mp_decode_uint(data);
+		if (val > INT32_MAX)
+			return -1;
+		*value = val;
+		break;
+	}
+	case MP_DOUBLE: {
+		double val = mp_decode_double(data);
+		if (val > (double)INT32_MAX || val < (double)INT32_MIN)
+			return -1;
+		if (val != floor(val))
+			return -1;
+		*value = val;
+		break;
+	}
+	case MP_EXT: {
+		int8_t type;
+		uint32_t len = mp_decode_extl(data, &type);
+		if (type != MP_DECIMAL)
+			return -1;
+		decimal_t dec;
+		if (decimal_unpack(data, len, &dec) == NULL)
+			return -1;
+		if (!decimal_is_int(&dec))
+			return -1;
+		int64_t val;
+		if (decimal_to_int64(&dec, &val) == NULL)
+			return -1;
+		if (val < INT32_MIN || val > INT32_MAX)
+			return -1;
+		*value = val;
+		break;
+	}
+	default:
+		return -1;
+	}
+	return 0;
+}
+
 /** Define field of DATETIME value from field of given MAP value.*/
 static int
 map_field_to_dt_field(struct dt_fields *fields, const char **data)
@@ -1050,3 +1102,77 @@ datetime_from_map(struct datetime *dt, const char *data)
 	}
 	return datetime_from_fields(dt, &fields);
 }
+
+/** Define field of INTERVAL value from field of given MAP value.*/
+static int
+map_field_to_itv_field(struct interval *itv, const char **data)
+{
+	if (mp_typeof(**data) != MP_STR) {
+		mp_next(data);
+		mp_next(data);
+		return 0;
+	}
+	uint32_t size;
+	const char *str = mp_decode_str(data, &size);
+	double *dvalue = NULL;
+	int32_t *ivalue = NULL;
+	if (strncmp(str, "year", size) == 0) {
+		ivalue = &itv->year;
+	} else if (strncmp(str, "month", size) == 0) {
+		ivalue = &itv->month;
+	} else if (strncmp(str, "week", size) == 0) {
+		ivalue = &itv->week;
+	} else if (strncmp(str, "day", size) == 0) {
+		dvalue = &itv->day;
+	} else if (strncmp(str, "hour", size) == 0) {
+		dvalue = &itv->hour;
+	} else if (strncmp(str, "min", size) == 0) {
+		dvalue = &itv->min;
+	} else if (strncmp(str, "sec", size) == 0) {
+		dvalue = &itv->sec;
+	} else if (strncmp(str, "nsec", size) == 0) {
+		ivalue = &itv->nsec;
+	} else if (strncmp(str, "adjust", size) == 0) {
+		if (mp_typeof(**data) != MP_STR)
+			return -1;
+		uint32_t vsize;
+		const char *val = mp_decode_str(data, &vsize);
+		if (strncasecmp(val, "none", vsize) == 0)
+			itv->adjust = DT_LIMIT;
+		else if (strncasecmp(val, "last", vsize) == 0)
+			itv->adjust = DT_SNAP;
+		else if (strncasecmp(val, "excess", vsize) == 0)
+			itv->adjust = DT_EXCESS;
+		else
+			return -1;
+		return 0;
+	} else {
+		mp_next(data);
+		return 0;
+	}
+	if (dvalue != NULL) {
+		double val;
+		if (get_double_from_mp(data, &val) != 0)
+			return -1;
+		if (val != floor(val))
+			return -1;
+		*dvalue = val;
+		return 0;
+	}
+	assert(ivalue != NULL);
+	return get_int32_from_mp(data, ivalue);
+}
+
+int
+interval_from_map(struct interval *itv, const char *data)
+{
+	assert(mp_typeof(*data) == MP_MAP);
+	uint32_t len = mp_decode_map(&data);
+	memset(itv, 0, sizeof(*itv));
+	itv->adjust = DT_LIMIT;
+	for (uint32_t i = 0; i < len; ++i) {
+		if (map_field_to_itv_field(itv, &data) != 0)
+			return -1;
+	}
+	return interval_check_args(itv) == 0 ? 0 : -1;
+}
diff --git a/src/lib/core/datetime.h b/src/lib/core/datetime.h
index 03288c2e7b3c02823e55235f8a4fdfcd0fbe7efe..9b7a8a087d010c879f1fbf28b090a20643e12064 100644
--- a/src/lib/core/datetime.h
+++ b/src/lib/core/datetime.h
@@ -385,6 +385,10 @@ datetime_usec(const struct datetime *date)
 int
 datetime_from_map(struct datetime *dt, const char *data);
 
+/** Parse MAP value and construct INTERVAL value. */
+int
+interval_from_map(struct interval *itv, const char *data);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/test/sql-luatest/datetime_test.lua b/test/sql-luatest/datetime_test.lua
index 51bfbbd1ac3c9176e67c1ae543101c744975697a..ad04e4adecf93fd0438bc49d31e32cef4acd182d 100644
--- a/test/sql-luatest/datetime_test.lua
+++ b/test/sql-luatest/datetime_test.lua
@@ -2680,3 +2680,183 @@ g.test_datetime_33_3 = function()
         t.assert_equals(box.execute(sql).rows, {{dt1}})
     end)
 end
+
+-- Make sure cast from MAP to INTERVAL works as intended.
+
+--
+-- The result of CAST() from MAP value to INTERVAL must be equal to the result
+-- of calling require('datetime').interval.new() with the corresponding table as
+-- an argument.
+--
+g.test_datetime_34_1 = function()
+    g.server:exec(function()
+        local t = require('luatest')
+        local itv = require('datetime').interval
+        local v = setmetatable({}, { __serialize = 'map' })
+        local sql = [[SELECT CAST(#v AS INTERVAL);]]
+        t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
+
+        v.something = 1
+        t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
+
+        v = {year = 1, month = 1, week = 1, day = 1, hour = 1, min = 1, sec = 1,
+             nsec = 1, adjust = 'none'}
+        t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
+    end)
+end
+
+--
+-- Make sure an error is thrown if the INTERVAL value cannot be constructed from
+-- the corresponding table.
+--
+g.test_datetime_34_2 = function()
+    g.server:exec(function()
+        local t = require('luatest')
+        local sql = [[SELECT CAST(#v AS INTERVAL);]]
+
+        -- "year" cannot be more than 11759221.
+        local v = {year = 11759222}
+        local _, err = box.execute(sql, {{['#v'] = v}})
+        local res = [[Type mismatch: can not convert ]]..
+                    [[map({"year": 11759222}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "year" cannot be less than -11759221.
+        v = {year = -11759222}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"year": -11759222}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "month" cannot be more than 141110652.
+        v = {month = 141110653}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"month": 141110653}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "month" cannot be less than -141110652.
+        v = {month = -141110653}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"month": -141110653}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "week" cannot be more than 613579352.
+        v = {week = 613579353}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"week": 613579353}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "week" cannot be less than -613579352.
+        v = {week = -613579353}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"week": -613579353}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "day" cannot be more than 4295055470.
+        v = {day = 4295055471}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"day": 4295055471}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "day" cannot be less than -4295055470.
+        v = {day = -4295055471}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"day": -4295055471}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "hour" cannot be more than 103081331286.
+        v = {hour = 103081331287}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"hour": 103081331287}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "hour" cannot be less than 103081331286.
+        v = {hour = -103081331287}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"hour": -103081331287}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "min" cannot be more than 6184879877160.
+        v = {min = 6184879877161}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"min": 6184879877161}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "min" cannot be less than -6184879877160.
+        v = {min = -6184879877161}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"min": -6184879877161}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "sec" cannot be more than 371092792629600.
+        v = {sec = 371092792629601}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"sec": 371092792629601}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "sec" cannot be less than -371092792629600.
+        v = {sec = -371092792629601}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert ]]..
+              [[map({"sec": -371092792629601}) to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "nsec" cannot be more than 2147483647.
+        v = {nsec = 2147483648}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert map({"nsec": 2147483648}) ]]..
+              [[to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "nsec" cannot be less than -2147483647.
+        v = {nsec = -2147483648}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert map({"nsec": -2147483648}) ]]..
+              [[to interval]]
+        t.assert_equals(err.message, res)
+
+        -- "adjust" cannot be anything other than "none", "excess", "last".
+        v = {adjust = 1}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert map({"adjust": 1}) ]]..
+              [[to interval]]
+        t.assert_equals(err.message, res)
+
+        v = {adjust = 'asd'}
+        _, err = box.execute(sql, {{['#v'] = v}})
+        res = [[Type mismatch: can not convert map({"adjust": "asd"}) ]]..
+              [[to interval]]
+        t.assert_equals(err.message, res)
+    end)
+end
+
+--
+-- Make sure that any of the DECIMAL, INTEGER, and DOUBLE values can be used as
+-- values in the MAP converted to a INTERVAL.
+--
+g.test_datetime_34_3 = function()
+    g.server:exec(function()
+        local t = require('luatest')
+        local itv = require('datetime').interval
+        local dt1 = itv.new({year = 1})
+        local sql = [[SELECT CAST({'year': 1.0} AS INTERVAL);]]
+        t.assert_equals(box.execute(sql).rows, {{dt1}})
+
+        sql = [[SELECT CAST({'year': 1.e0} AS INTERVAL);]]
+        t.assert_equals(box.execute(sql).rows, {{dt1}})
+
+        sql = [[SELECT CAST({'year': 1} AS INTERVAL);]]
+        t.assert_equals(box.execute(sql).rows, {{dt1}})
+    end)
+end