Set calendar type on whole application - ios

I've an application that is working fine with Gregorian calendar type. All API info for date is in Gregorian calendar type. But if users have Buddhist calendar I have crashes in some places. For example:
func days(from date: Date) -> Int {
let beginningOfDay = date.beginningOfDay ?? date
return Int(timeIntervalSince1970 - beginningOfDay.timeIntervalSince1970) / Int(TimeInterval.day)
}
How can I ignore user settings for calendar and force date to be in Gregorian calendar in whole application? Is it possible?
I've just founded converters, but I don't want to convert, I just want to force Gregorian calendar on whole app.
Thanks

This looks like you've added a lot of extensions on Date that don't belong there. Date is a point in time. In order to talk about "days" you should be calling Calendar and DateComponents methods. This function isn't quite correct anyway. It can be off by a day depending on DST changes. You can't assume that a day is 24 hours long; some are 25 hours, and some are 23 hours.
The code you wanted was:
let calendar = Calendar(identifier: .gregorian)
calendar.dateComponents([.day], from: d1, to: d2).day!
Likely somewhere in your extensions you have Calendar.current. That means "the current user calendar." There's no way to tell the system "even when I explicitly ask for the current user calendar, please give me something else." Look for the code that uses Calendar.current and replace it with Calendar(identifier: .gregorian) if that's what you mean.

Related

Adding time from string into date object swift

I have a time coming from service in string form like so "12:30 PM".
I am able to get this time but I want to add this time in current date. like so
31/3/2021 12:30 PM
The current date is in date object and coming time is in string format.
Please let me know what is a right way to do so? I am right now taking 12, 30,from string and setting it via Calendar. But dont know how to set am pm . Please let me know how to append time with date object. Thanks in advance.
First, if that is the date string you are getting from your service, it is incomplete. It needs a time zone.
Here's what I would do:
Assuming the service always uses the same time zone, find out that time zone.
Create a date formatter for that date string format, including the AM/PM bit.
Set the date formatter to use the time zone from step 1.
Convert the date string to a Date object using your DateFormatter.
Use the current calendar to extract the hours and minutes values into a DateComponents object.
Get the current date, and use the Calendar function date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:) to set the hour and minutes of the current date to the values you got from step 5.
You should search in the Xcode help system for:
Dates and Times (overview)
Calendrical calculations (discussion specific to doing math on dates and times)
DateFormatter. (See this article for info on the characters to use to build your dateFormat string.)
Calendar
DateComponents
Calendar
You can do this by setting up your DateFormatter with the correct timeZone, calendar, defaultDate, and dateFormat. Here's an example:
import Foundation
let parser = DateFormatter()
parser.calendar = Calendar(identifier: .gregorian)
parser.timeZone = TimeZone(identifier: "US/Eastern")!
parser.defaultDate = parser.calendar!.date(
from: DateComponents(
timeZone: parser.timeZone!, era: 1,
year: 2021, month: 3, day: 31,
hour: 12, minute: 0, second: 0))!
parser.dateFormat = "hh:mm a"
print(parser.date(from: "12:30 PM"))
Output:
Optional(2021-03-31 16:30:00 +0000)

Swift: first day of the week in app doesn't change after I change it in Settings

In my app I need to group different items based on their date, so I need a method to get date of the beginning of the week, and also a method to find out if two dates are in the same week. However, I get unexpected problems trying to implement this.
I use this code to get the first day of the week:
return Calendar.current.date(from: Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
I use Calendar.current, but it returns the same day no matter which day is chosen as the beginning of the week in iOS Settings.
To check if two dates are in the same week I use this code:
func isInSameWeek(date: Date) -> Bool { isEqual(to: date, toGranularity: .weekOfYear) }
Still, Sunday and Monday dates are always considered not in the same week no matter which day is chosen as the beginning of the week in iOS Settings.
How do I fix this? Or maybe it is a normal behavior and this code will work correctly for users in different regions?
You are changing the settings of the Calendar App not the iOS current calendar itself. You need to provide your own app settings to be changed by the user.
var calendar = Calendar.current
calendar.firstWeekday = 2
calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: Date()))?.description(with: .current) // "Monday, September 14, 2020 at 12:00:00 AM Brasilia Standard Time"

Comparing timestamps and working with timezones on iOS

Given a unix timestamp, how can my iOS code know if it is before or after 6am (6am = the most recent 6am that occurred)? Are there time zones involved?
Convert the unix timestamp to a Date. Then use Calendar to get the DateComponents from the Date. By default, these components will be interpreted in the user's current timezone. If you wish to interpret the date in a different timezone, set the calendar's timezone before getting the components from the date.
By looking at the desired components you can make your decision about the hour.
let date = Date(timeIntervalSince1970: someUnixTimeStamp)
let components = Calendar.current.dateComponents(in: TimeZone.current, from: date)
// look at hour as needed
There are other Calendar APIs if you just want a single component or just a smaller subset of components instead of all components from the date.

Display Year, Month and Date in sequence on DateTimePicker iOS

Here is my situation, Current Date picker is showing date, month and year in sequence of Date, Month and Year OR Month, Date and Year. But I want to display this sequence as Year first then Month and at last Date.
I searched a lot but same question I found in Javascript but not in iOS, I also searched in Apple Documentation but I didn't find any solution.
How can I achieve this sequence Year, Month and Date? If there any property or method available for DateTime Picker? OR Do I need to use custom picker? if any then which?
Thanks.
The order of year, month and day is depending on the picker's locale.
Eg.
datePicker.locale = NSLocale(localeIdentifier: "zh_TW")
will have different order from
datePicker.locale = NSLocale(localeIdentifier: "en_US")

Why compareDate from NSCalendar seems to need to set an UTC timeZone to work properly?

I create two dates like the following:
let date1 = stringToDate("2015-02-12 12:29:29")!
let date2 = stringToDate("2015-02-11 19:18:49")!
func stringToDate(var dateString: String) -> NSDate? {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
return dateFormatter.dateFromString(dateString)
}
As you can see, the two date are different and are not on the same day.
To test if two dates are on the same day, I use the following method:
func isSameDayThan(date1: NSDate, date2: NSDate) -> Bool {
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone(abbreviation: "GMT+10")!
return calendar.compareDate(date1, toDate: date2, toUnitGranularity: .DayCalendarUnit) == .OrderedSame
}
There I don't precise any timeZone in the calendar. The local TimeZone of my device is set to GMT+10.
In that case, isSameDayThan(date1, date2)-> true
If I change the timeZone to something inferior or equal to GMT+04, then I get isSameDayThan(date1, date2)-> false.
What I don't understand is that the result is different depending on the timeZone, but I am comparing two NSDate() and NSDate() has nothing to do with time zone if I'm not wrong.
The timezone comes into play because you compare the dates with a granularity that is timezone dependent. So you are actually comparing against the local representation of the date. The point in time model that is often used to describe NSDate doesn't know about days and weeks. From a abstract standpoint (i.e. the point in time that is the same everywhere in the universe) it actually doesn't even know about seconds.
Anyway, if you would compare with == you would obviously not need a timezone. That's the only comparison that is truly independent from the local representation. If two points in time are exactly the same they are equal. Easy.
Everything beyond a straight == comparison has to be converted into local units. Not only you have to use the correct calendar, but you have to use the correct timezone as well.
Luckily there are no calendars that have days that are shorter or longer than 24 hours. And there are no timezones that differ in seconds either. Because we know that, you can actually see if dates are within the same minute with an easy calculation. e.g.:
Int(date1.timeIntervalSince1970 / 60) == Int(date2.timeIntervalSince1970 / 60)
No calendar needed because we (currently) don't have calendars that have minutes that are not 60 seconds long. No timezone needed, because we don't have timezones with offsets that differ in the number of seconds.
But we have a few timezones that have offsets that are only fractions of an hour. For example India Time Zone which has an offset of +05:30. So starting with hours the boundaries of the granularity units are timezone dependent.
If you have two NSDates which are set to 9:25 and 9:35 UTC, they are in the same hour if you compare in any timezone that has an offset that does not differ in the number of minutes (e.g. 00 in +x:00). They are in the same hour in UTC, they are in the same hour in UTC+5:00 and UTC-5:00.
But if you compare in India Time Zone these two dates are actually in different hours. Because 9:25 UTC in IST is 2:55, and 9:35 UTC is 3:05 in IST.
In your example you are comparing to the granularity of the day. Which needs to take all timezones into account. But we can still ignore the calendar, because all calendars use days that are 24 hours long.
But if you would compare to the granularity of a week, month or year you would have to take the calendar into account as well. There are calendars that have totally different months. Just because two dates are in the same month in gregorian calendars doesn't mean that they are in the same month in hebrew calendars.
Yes, it's complicated. And that's the reason all date calculation appear so verbose. People often try to hide the complexity behind a fancy helper function. Which often leads to problems. So be aware of creation functions like isSameDay().
Each time you compare a date you have to make the decision what timezone and calendar to use. If you rely on helper functions you will miss the one instance where you should actually compare against UTC instead of the local timezone.
TL;DR: If you compare with granularity you should always set the correct calendar and the correct timezone.
Th two dates are different days in the UTC time zone. But in the GMT+10 time zone they are both the same day - February 12.
2015-02-12 12:29:29 UTC = 2015-02-12 22:29:29 UTC+10
2015-02-11 19:18:49 UTC = 2015-02-12 05:18:49 UTC+10
By Default, the comparison is done in the local time zone but your date objects were specifically created in the UTC time zone.
If you create the NSDate objects from the strings using the default time zone and compare them using the default time zone, then the dates would have two different days.

Resources