diff --git a/src/plugin/timezone/index.js b/src/plugin/timezone/index.js index bcacf9ab6..b5469c334 100644 --- a/src/plugin/timezone/index.js +++ b/src/plugin/timezone/index.js @@ -132,6 +132,31 @@ export default (o, c, d) => { return startOfWithoutTz.tz(this.$x.$timezone, true) } + const oldAdd = proto.add + proto.add = function (number, units) { + if (!this.$x || !this.$x.$timezone) { + return oldAdd.call(this, number, units) + } + + const unit = this.$utils().p(units) + // For calendar units (day, week, month, year), work in timezone context + // For time units (ms, second, minute, hour), use timestamp-based approach + if (unit === 'd' || unit === 'day' || unit === 'D' || unit === 'date' || + unit === 'w' || unit === 'week' || unit === 'W' || + unit === 'M' || unit === 'month' || + unit === 'y' || unit === 'year' || unit === 'Y' || + unit === 'Q' || unit === 'quarter') { + // Format in timezone, add without timezone, convert back + const formatted = this.format('YYYY-MM-DD HH:mm:ss:SSS') + const withoutTz = d(formatted, { locale: this.$L }) + const addedWithoutTz = oldAdd.call(withoutTz, number, units) + return addedWithoutTz.tz(this.$x.$timezone, true) + } + + // For time units, use the original add method which works with timestamps + return oldAdd.call(this, number, units) + } + d.tz = function (input, arg1, arg2) { const parseFormat = arg2 && arg1 const timezone = arg2 || arg1 || defaultTimezone diff --git a/test/plugin/timezone.test.js b/test/plugin/timezone.test.js index d83a03f8a..a85f9ed9e 100644 --- a/test/plugin/timezone.test.js +++ b/test/plugin/timezone.test.js @@ -352,3 +352,22 @@ describe('UTC timezone', () => { expect(dayjs2.format()).toBe(moment2.format()) }) }) + +describe('Add with timezone', () => { + it('add day preserves timezone correctly', () => { + const time = 1762066800000 + const tz = 'US/Pacific' + + const a = dayjs.tz(time, tz).add(1, 'day') + const b = dayjs.tz(dayjs.tz(time, tz).add(1, 'day').valueOf(), tz) + const c = dayjs.tz(dayjs.tz(time, tz).add(1, 'day').startOf('millisecond').valueOf(), tz) + + // All three should produce the same result + expect(a.valueOf()).toBe(b.valueOf()) + expect(b.valueOf()).toBe(c.valueOf()) + expect(a.format('YYYY-MM-DD HH:mm:ss')).toBe(b.format('YYYY-MM-DD HH:mm:ss')) + expect(b.format('YYYY-MM-DD HH:mm:ss')).toBe(c.format('YYYY-MM-DD HH:mm:ss')) + expect(a.valueOf()).toBe(1762156800000) + expect(a.format('YYYY-MM-DD HH:mm:ss')).toBe('2025-11-03 00:00:00') + }) +})