Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calendar nextDate/enumerateDates provides wrong dates when using backward search direction #348

Open
alpennec opened this issue Dec 12, 2023 · 0 comments
Assignees

Comments

@alpennec
Copy link

Feedback: FB13462533
Post on Swift Forums: https://forums.swift.org/t/calendar-nextdate-enumeratedates-provides-wrong-dates/68943


In my app, I need to get previous dates that matches specific months in the year (think of it like recurring events, and I want to find the last occurence of the recurrence based on the current date and time: every month on the 13th day, or every month on the 3rd Friday -> what is the previous date that matches these components).

It mainly works but for an unknown reason, I get wrong dates under certains circumstances.

It does not work as expected for the 9nth month in the Gregorian calendar (which is September). When the after date is Date.now (currently in December), I expect the previous September month to be in 2023. But the first date returned is in 1995.

var calendar: Calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone.autoupdatingCurrent

let matchingDateComponents: DateComponents = DateComponents(month: 09)
                        
let date: Date? = calendar.nextDate(
    after: Date.now,
    matching: matchingDateComponents,
    matchingPolicy: .nextTime,
    direction: .backward
)

I tested with other time zones: for some, the result are corrects, for others, it's not.
I used the following code to print the result for all time zones:

for zone in TimeZone.knownTimeZoneIdentifiers {
    var calendar: Calendar = Calendar(identifier: .gregorian)
    calendar.timeZone = TimeZone(identifier: zone) ?? .autoupdatingCurrent
                
    let matchingDateComponents: DateComponents = DateComponents(month: 9)
                
    let date: Date? = calendar.nextDate(
        after: Date.now,
        matching: matchingDateComponents,
        matchingPolicy: .nextTime,
        direction: .backward
    )
                
    print(date, zone)
}
  • 1970-08-31 23:00:00 +0000 for Europe/London
  • 1995-08-31 23:00:00 +0000 for Europe/Paris
  • 2002-08-31 22:00:00 +0000 for Europe/Vilnius
  • 2023-09-01 07:00:00 +0000 for America/Los_Angeles

I also tested with other date components to see if the dates provided by these methods are correct when searching backward. I tested using a .nextTime and .strict matchingPolicy.

If we assume that these methods provide the first day of the month for a forward and backward search when we only use a month component (see additional notes below regarding this), here are some results:

DateComponents(month: 1)
Correct years, wrong days. I mainly get dates around January the 5th. But the years seem to be correct. In this case, only the America/Los_Angeles date seems to be correct. Even GMT is wrong.

  • 2023-01-05 00:00:00 +0000 for Europe/London
  • 2023-01-04 23:00:00 +0000 for Europe/Paris
  • 2023-01-04 22:00:00 +0000 for Europe/Vilnius
  • 2023-01-01 08:00:00 +0000 for America/Los_Angeles
  • 2023-01-05 00:00:00 +0000 for GMT

DateComponents(month: 2)
Correct years, correct days

DateComponents(month: 3)
Correct years, wrong days

DateComponents(month: 4)
Correct years, correct days

DateComponents(month: 5)
Correct years, wrong days. I mainly get dates around May the 3rd.

  • 2023-05-02 23:00:00 +0000 for Europe/London
  • 2023-05-02 22:00:00 +0000 for Europe/Paris
  • 2023-05-02 21:00:00 +0000 for Europe/Vilnius
  • 2023-05-03 07:00:00 +0000 for America/Los_Angeles
  • 2023-05-03 00:00:00 +0000 for GMT

DateComponents(month: 6)
Correct years, correct days

DateComponents(month: 7)
Correct years, wrong days

DateComponents(month: 8)
Correct years, wrong days

DateComponents(month: 9)
Wrong years, correct days

DateComponents(month: 10)
Correct years, wrong days

DateComponents(month: 11)
Correct years, correct days

DateComponents(month: 12)
Correct years, correct days if we expect to get the start of the December from the previous year when we already are in December (and not the start of the current month).

If I additionally specify a day in the date components like DateComponents(month: 1, day: 1) or DateComponents(month: 4, day: 20), I get the correct results for all time zones, except for September (still wrong years).


Should we avoid using these methods with a backward direction?


Additional notes

When searching .forward, and only specifying a month in the date components, we expect to get the next start of the month. So if I request the nextDate that matches December after December the 12th in 2023, I expect to get December the 1st in 2024. This is the actual result provided by the nextDate method.

It seems that when we search .backward, we also get the start of the month when we only specify the month. I was expecting to get the last day of the specified month, not the first one. To me, the next date that matches only a month when searching backward is the last day (first day encountering the specified month if we go back day by day).

@parkera parkera self-assigned this Dec 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants