batoulapps / adhan-swift

High precision Islamic prayer time library for Swift
MIT License
182 stars 42 forks source link

Unable to display prayer times in a different timezone #84

Closed Muhammed9991 closed 1 year ago

Muhammed9991 commented 1 year ago

As per your README:

Once the PrayerTimes struct has been initialized it will contain members for all five prayer times and the time for sunrise. The prayer times will be instances of NSDate and as such will refer to a fixed point in universal time. To display these times for the local timezone you will need to create a date formatter and set the appropriate timezone.

I seem to be getting the initial prayer wrong but my conversion to another timezone is correct for the initial wrong prayer time. Here's what I did

var cal = Calendar(identifier: Calendar.Identifier.gregorian)
let timeZone =  TimeZone(identifier: "America/New_York")!
cal.timeZone = timeZone
let currentDateComponents = DateComponents( // I was using this for Unit tests
            calendar: calendar,
            year: 2023,
            month: 1,
            day: 1,
            hour: 1,
            minute: 0
        )

let date =  cal.dateComponents([.year, .month, .day], from: currentDateComponents)

let coordinates = Coordinates(latitude: 40.7660, longitude: 73.9470)

var params = CalculationMethod.muslimWorldLeague.params
let prayerTime = PrayerTimes(coordinates: coordinates, date: date, calculationParameters: params) 

let prayerTimes = [
            prayerTime?.fajr,
            prayerTime?.dhuhr,
            prayerTime?.asr,
            prayerTime?.maghrib,
            prayerTime?.isha
        ]

// Converting all prayer time using the timezone
let convertedPrayerTimes  = prayerTimes.compactMap { prayerTime in
   let secondsFromGMT = timeZone.secondsFromGMT(for: prayerTime!)
   let secondsFromGMTAsDouble = Double(secondsFromGMT)
   let adjustedTime = prayerTime!.addingTimeInterval(secondsFromGMTAsDouble)
    return adjustedTime
}

Not sure where I am going wrong but the prayerTimes is returning before the conversion.

fajr = 2023-01-01 00:50:00 UTC
sunrise = 2023-01-01 02:28:00 UTC
dhuhr = 2023-01-01 07:09:00 UTC
asr = 2023-01-01 09:29:00 UTC
maghrib = 2023-01-01 11:47:00 UTC
isha = 2023-01-01 13:20:00 UTC

Not sure where I am going wrong but I am getting the initial prayer time as wrong.

z3bi commented 1 year ago

The problem is here

// Converting all prayer time using the timezone
let convertedPrayerTimes  = prayerTimes.compactMap { prayerTime in
   let secondsFromGMT = timeZone.secondsFromGMT(for: prayerTime!)
   let secondsFromGMTAsDouble = Double(secondsFromGMT)
   let adjustedTime = prayerTime!.addingTimeInterval(secondsFromGMTAsDouble)
    return adjustedTime
}

You don't want to add time for the timezone, you just want to use the timezone in the formatter. It's helpful to remember that the time you get back is already the exact moment, timezones just allow us to display it correctly according to someone who lives in a particular location.

2023-01-01 09:29:00 UTC is exactly the same moment in time as 2023-01-01 10:29:00 GMT+1, it's just formatted differently and we tend to not show the UTC offset when we display times to people.

If you look at the README you will see the formatter being used in the following manner.

    let formatter = DateFormatter()
    formatter.timeStyle = .medium
    formatter.timeZone = TimeZone(identifier: "America/New_York")!

    print("fajr \(formatter.string(from: prayers.fajr))")
    print("sunrise \(formatter.string(from: prayers.sunrise))")
    print("dhuhr \(formatter.string(from: prayers.dhuhr))")
    print("asr \(formatter.string(from: prayers.asr))")
    print("maghrib \(formatter.string(from: prayers.maghrib))")
    print("isha \(formatter.string(from: prayers.isha))")

I hope that helps!

Muhammed9991 commented 1 year ago

Thanks. I might be a bit late in replying so please don't close this.

That does make kind of sense but the using the calculation before I did that wrong off-set calculation. I was getting Fajr to be 2023-01-01 00:50:00 UTC. If I follow the README suggested approach with a slight variation:

The initial datetime object is showing as Jan 1, 2023 at 12:50. So when I apply timezone using DateFormatter(). I get the wrong time for Fajr.

let formatter = DateFormatter()
formatter.timeStyle = .medium
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss zzz"
let fajrDate = formatter.date(from: "2023-01-01 00:50:00 UTC") // "Jan 1, 2023 at 12:50"

formatter.timeZone = TimeZone(identifier: "America/New_York")!
formatter.string(from: fajrDate!) // 2022-12-31 19:50:00 EST
z3bi commented 1 year ago

@Muhammed9991 so the main issue is the coordinates you are using are in Kyrgyzstan, this means you need to use TimeZone(identifier: "Asia/Bishkek") as your TimeZone (not New York).

The date you are getting Jan 1, 2023 at 12:50 UTC is correct, because it is the exact same moment in time as Jan 1, 2023 at 6:50:00 AM GMT+6 (which is how it looks when formatted for Kyrgyzstan time).

Here is a complete working example

        var cal = Calendar(identifier: Calendar.Identifier.gregorian)
        let timeZone =  TimeZone(identifier: "Asia/Bishkek")!
        cal.timeZone = timeZone
        let currentDateComponents = DateComponents(
                    calendar: cal,
                    year: 2023,
                    month: 1,
                    day: 1,
                    hour: 1,
                    minute: 0
                )

        let coordinates = Coordinates(latitude: 40.7660, longitude: 73.9470)

        var params = CalculationMethod.muslimWorldLeague.params
        let prayerTime = PrayerTimes(coordinates: coordinates, date: currentDateComponents, calculationParameters: params)
        print(prayerTime!.fajr)

        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        formatter.timeZone = TimeZone(identifier: "Asia/Bishkek")!
        print("\(formatter.string(from: prayerTime!.fajr))")
Muhammed9991 commented 1 year ago

Wow. Thats embarrassing. Google failed me when I tried to find coordinates for somewhere in NewYork.

Screenshot 2023-03-20 at 17 58 39

That makes sense now. Thanks for the help. I'll close this.

For people reading this in future. Don't trust the first search result on google. 😂

z3bi commented 1 year ago

@Muhammed9991 Google is correct but longitude values going west should be represented as a negative number