Skip to content

eachYearOfInterval may returns duplicate years when interval uses TZDate in the zone for America/São Paulo #72

@gpbl

Description

@gpbl

Bug description

When eachYearOfInterval runs on an interval built with TZDate from @date-fns/tz in certain historical DST time zones, the helper yields duplicate years. In America/Sao_Paulo, 1913 is returned twice and later entries land on December 31st, which breaks year dropdowns that rely on unique years.

To reproduce

import { TZDate } from "@date-fns/tz";
import { eachYearOfInterval, endOfYear, startOfYear } from "date-fns";

const timeZone = "America/Sao_Paulo";

const interval = {
  start: startOfYear(new TZDate(1910, 1, 1, timeZone)),
  end: endOfYear(new TZDate(1915, 1, 1, timeZone)),
};

const years = eachYearOfInterval(interval);

console.log(years.map((d) => d.toISOString()));
console.log(years.map((d) => d.getFullYear()));

Output

[
  '1910-01-01T00:00:28.000-03:06',
  '1911-01-01T00:00:56.000-03:06',
  '1912-01-01T00:01:24.000-03:06',
  '1913-01-01T00:01:52.000-03:06',
  '1913-12-31T23:56:20.000-03:06',
  '1914-12-31T23:56:48.000-03:00',
  '1915-12-31T23:56:48.000-03:00'
]
[
  1910, 1911,
  1912, 1913,
  1913, 1914,
  1915
]

Expected behavior

The helper should return one date per calendar year.

Actual behavior

After 1913, the function emits another entry for 1913 pointing at December 31st, and subsequent entries are also at year-end.

Investigations

You can see that entry in the official tzdb southamerica file here: https://data.iana.org/time-zones/tzdb/southamerica (look for the Zone America/Sao_Paulo block).

It shows the switch from local mean time (-3:06:28) to the standard −03:00 offset on 1914‑01‑01.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions