diff --git a/changelogs/unreleased/gh-7700-interval-arithmetic-across-dst.md b/changelogs/unreleased/gh-7700-interval-arithmetic-across-dst.md
new file mode 100644
index 0000000000000000000000000000000000000000..05b1a0d0d098c4de32369b93b9038a55407aa7fc
--- /dev/null
+++ b/changelogs/unreleased/gh-7700-interval-arithmetic-across-dst.md
@@ -0,0 +1,19 @@
+## bugfix/datetime
+
+* Fixed interval arithmetic for boundaries crossing DST (gh-7700).
+
+  We did not take into consideration the fact that
+  as result of date/time arithmetic we could get
+  in a different timezone, if DST boundary has been
+  crossed during operation.
+
+  ```
+  tarantool> datetime.new{year=2008, month=1, day=1,
+                          tz='Europe/Moscow'} +
+             datetime.interval.new{month=6}
+  ---
+  - 2008-07-01T01:00:00 Europe/Moscow
+  ...
+  ```
+  Now we resolve tzoffset at the end of operation if
+  tzindex is not 0.
diff --git a/src/lib/core/datetime.c b/src/lib/core/datetime.c
index ab4f5ce6e55d90c8f5ff503c7dee738963ce0614..e1e85457a945965bbfbca8dfbf5b161c62640eb8 100644
--- a/src/lib/core/datetime.c
+++ b/src/lib/core/datetime.c
@@ -49,18 +49,19 @@ local_secs(const struct datetime *date)
 
 /**
  * Resolve tzindex encoded timezone from @sa date using Olson facilities.
- * @param[in] date decode input datetime value.
+ * @param[in] epoch decode input epoch time (in seconds).
+ * @param[in] tzindex use timezone index for decode.
  * @param[out] gmtoff return resolved timezone offset (in seconds).
  * @param[out] isdst return resolved daylight saving time status for the zone.
  */
 static inline bool
-datetime_timezone_lookup(const struct datetime *date, long *gmtoff, int *isdst)
+epoch_timezone_lookup(int64_t epoch, int16_t tzindex, long *gmtoff, int *isdst)
 {
-	if (date->tzindex == 0)
+	if (tzindex == 0)
 		return false;
 
-	struct tnt_tm tm = {.tm_epoch = date->epoch};
-	if (!timezone_tzindex_lookup(date->tzindex, &tm))
+	struct tnt_tm tm = {.tm_epoch = epoch};
+	if (!timezone_tzindex_lookup(tzindex, &tm))
 		return false;
 
 	*gmtoff = tm.tm_gmtoff;
@@ -75,7 +76,8 @@ datetime_isdst(const struct datetime *date)
 	int isdst = 0;
 	long gmtoff = 0;
 
-	return datetime_timezone_lookup(date, &gmtoff, &isdst) && (isdst != 0);
+	epoch_timezone_lookup(date->epoch, date->tzindex, &gmtoff, &isdst);
+	return isdst != 0;
 }
 
 long
@@ -84,8 +86,7 @@ datetime_gmtoff(const struct datetime *date)
 	int isdst = 0;
 	long gmtoff = date->tzoffset * 60;
 
-	datetime_timezone_lookup(date, &gmtoff, &isdst);
-
+	epoch_timezone_lookup(date->epoch, date->tzindex, &gmtoff, &isdst);
 	return gmtoff;
 }
 
@@ -704,6 +705,7 @@ datetime_increment_by(struct datetime *self, int direction,
 	int64_t dt = local_dt(secs);
 	int nsec = self->nsec;
 	int offset = self->tzoffset;
+	int tzindex = self->tzindex;
 
 	bool is_ymd_updated = false;
 	int64_t years = ival->year;
@@ -789,9 +791,15 @@ datetime_increment_by(struct datetime *self, int direction,
 	if (rc != 0)
 		return rc;
 
+	if (tzindex != 0) {
+		int isdst = 0;
+		long gmtoff = offset * 60;
+		epoch_timezone_lookup(secs, tzindex, &gmtoff, &isdst);
+		offset = gmtoff / 60;
+	}
 	self->epoch = utc_secs(secs, offset);
 	self->nsec = nsec;
-	self->tzoffset = datetime_gmtoff(self) / 60;
+	self->tzoffset = offset;
 	return 0;
 }
 
diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua
index a7089fc9e7915366168e103055debd1a2d0962ac..b08b0dd26ae76a76a4dd702e503872f713a0c55b 100755
--- a/test/app-tap/datetime.test.lua
+++ b/test/app-tap/datetime.test.lua
@@ -1395,11 +1395,12 @@ Matrix of addition operands eligibility and their result type
 | table           |          |          |          |
 ]]
 test:test("Matrix of allowed time and interval additions", function(test)
-    test:plan(67)
+    test:plan(79)
 
     -- check arithmetic with leap dates
     local T1970 = date.new{year = 1970, month = 1, day = 1}
-    local T2000 = date.new{year = 2000, month = 1, day = 1}
+    local T2000 = date.new{year = 2000, month = 1, day = 1,
+                           tz = 'Europe/Moscow'}
 
     local I1 = date.interval.new{day = 1}
     local M2 = date.interval.new{month = 2}
@@ -1462,6 +1463,42 @@ test:test("Matrix of allowed time and interval additions", function(test)
     test:is(tostring(Y1 + T1970), "1971-01-01T00:00:00Z", "value: Y + T")
     test:is(tostring(Y5 + Y1), "+6 years", "Y + Y")
 
+    -- Check winter/DST sensitive operations with intervals.
+    -- We use 2000 year here, because then Moscow still were
+    -- switching between winter and summer time.
+    local msk_offset = 180 -- expected winter time offset
+    local msd_offset = 240 -- expected daylight saving time offset
+
+    local res = T2000 + I1
+    test:is(tostring(res), "2000-01-02T00:00:00 Europe/Moscow",
+        "value: 2000 + I")
+    test:is(res.tzoffset, msk_offset, "2000-01-02T00:00:00 - winter")
+
+    res = T2000 + i1
+    test:is(tostring(res), "2000-01-02T00:00:00 Europe/Moscow",
+        "value: 2000 + i")
+    test:is(res.tzoffset, msk_offset, "2000-01-02T00:00:00 - winter")
+
+    res = T2000 + M2
+    test:is(tostring(res), "2000-03-01T00:00:00 Europe/Moscow",
+        "value: 2000 + 2M")
+    test:is(res.tzoffset, msk_offset, "2000-03-01T00:00:00 - winter")
+
+    res = T2000 + M2 + M2 + M2
+    test:is(tostring(res), "2000-07-01T00:00:00 Europe/Moscow",
+        "value: 2000 + 6M")
+    test:is(res.tzoffset, msd_offset, "2000-07-01T00:00:00 - summer")
+
+    res = T2000 + m2
+    test:is(tostring(res), "2000-03-01T00:00:00 Europe/Moscow",
+        "value: 2000 + 2m")
+    test:is(res.tzoffset, msk_offset, "2000-03-01T00:00:00 - winter")
+
+    res = T2000 + m2 + M2 + m2
+    test:is(tostring(res), "2000-07-01T00:00:00 Europe/Moscow",
+        "value: 2000 + 6m")
+    test:is(res.tzoffset, msd_offset, "2000-07-01T00:00:00 - summer")
+
     assert_raises_like(test, expected_interval_but,
                        function() return T1970 + 123 end)
     assert_raises_like(test, expected_interval_but,