Check for a new month using Date in Swift - ios

I would like to notify the user and reset an aspect of the app once a new month begins. This reset needs to repeats every time the month changes.
Using Swift and have used the DateToolsSwift pod.Date Pod
What's the best way to get this to work

func checkIfNewMonth(newDate: Date, oldDate: Date){
var userCalendar = Calendar.current
userCalendar.timeZone = TimeZone(abbreviation: "UTC")!
let oldComponents = userCalendar.dateComponents([.month, .year], from: oldDate)
let newComponents = userCalendar.dateComponents([.month, .year], from: newDate)
guard let oldCompareDate = userCalendar.date(from: oldComponents) else { return }
guard let newCompareDate = userCalendar.date(from: newComponents) else { return }
if newCompareDate > oldCompareDate {
//New date is a new month
} else if newCompareDate < oldCompareDate {
//New date is an previous month
}
}
Thought I'd post a function that does what the op asked. Just feed in the two dates you want to compare. I think UserDefaults would be a great way of storing the old date as well.

The calendar can tell you the range of the current month. The next month begins at the end of the current month:
let startOfNextMonth = Calendar.current.dateInterval(of: .month, for: Date())?.end
let formatter = DateFormatter()
formatter.dateStyle = .long
print(startOfNextMonth.map(formatter.string(from:)) ?? "not a date")
Simply schedule a UNNotificationRequest for this date.

Related

Calendar regardless of settings

In the application, I use the days of the week to open the achievements.
let date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let dayInWeek = dateFormatter.string(from: date)
if dayInWeek == "Monday" {
achiv = true
}
I use English, Russian and Ukrainian languages ​​in the application. The problem is that this condition is valid if the phone is in English language. Help pass this method so that the conditions work regardless of the language on the device.
You can use the weekday property of DateComponents
let now = Date()
let today = Calendar(identifier: .gregorian).component(.weekday, from: now)
if today == 2 {
print("It is Monday")
}
Note that I am using the Gregorian calendar explicitly rather than the current calendar as weekday values can vary in other calendars.
You can check the weekday using DateComponents:
let date = Date()
let weekday = Calendar.current.component(.weekday, from: date)
if weekday == 1 {
print("Sunday")
} else if weekday == 2 {
print("Monday")
}
Note: String checking if weekday == "Monday" is not safe, because different regions have different strings.

Get next 3rd month name from the current month in swift

How can I get The 3rd month names from Current month to Nov. Ex:-if the current month is Nov then I want the month names from Feb. Current month should be the running month.
Question: How to get 3rd month name from the current month?
Can someone please explain to me how to get month name. Any help would be greatly appreciated.
Thanks in advance.
You need to use the Calendar class, e.g.
var now = Date()
var calendar = Calendar.current
if let then = calendar.date(byAdding: .month, value: 3, to: now) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let monthName = dateFormatter.string(from: then)
print ("\(monthName)")
}
Just keep in mind how calenar arithmetics is handled: if you add "3 months" to let's say Nov 30th, 2019, then you'll get Feb-29th, 2020, although someone might expect March-01, 2020.
//set start & end date in correct format
let startDate = "September"
let strEndDate = "December"
//create date formatter
let formatter = DateFormatter()
formatter.dateFormat = "MMMM"
//convert string into date object
guard let startDate = formatter.date(from: startDate) else {
print("invalid start date")
return
}
//convert string into date object
guard let endDate = formatter.date(from: strEndDate) else {
print("invalid end date time")
return
}
//calculate the month from end date and that should not exceed the start date
for month in 1...6 {
if let dt = Calendar.current.date(byAdding: .month, value: -month, to: endDate) {
if dt.compare(startDate) == .orderedAscending {
break
}
print(formatter.string(from: dt!))
}
}

How do I perform +1 or -1 on a date which I select from previous screen?

I want to add / or subtract a day from a selected date. The user selects for example 09-10-2019, when user press a button, the date should be 10-10-2019.
I am not trying to add or reduce a day from Current date.
On the previous screen , I have a variable -> Which is user selected and is not static
selecteddate = "09.10.2019"
I declared it as a global variable so it can be used in several screens, so in this screen , I get this 'selected date'.
I have 3 UIButtons. yesterdaybutton, selecteddatebutton and tomorrowbutton
When user presses yesterday button then 1 day should be reduced from
'selecteddate' variable
When user presses tomorrow button then 1 day should be added to
'selecteddate' variable.
I followed what the answer said. But unfortunately my selecteddate was a string and I needed to sent string to api in its format.
So , I did the following.
let dateFormattera = DateFormatter()
dateFormattera.dateFormat = "dd.MM.yyyy"
let date = dateFormattera.date(from: datetoday)
let newdate = Calendar.current.date(byAdding: .day, value: +1, to: date!)
let dateFormatterb = DateFormatter()
dateFormatterb.dateFormat = "dd.MM.yyyy"
let tomorrowdate = dateFormatterb.string(from: newdate!)
let dateFormatterx = DateFormatter()
let day = dateFormatterx.date(from: datetoday)
let newday = Calendar.current.date(byAdding: .day, value: +1, to: date!)
let dateFormattery = DateFormatter()
dateFormattery.dateFormat = "EEE"
let tomorrowday = dateFormattery.string(from: newdate!)
There might be junk code in this, but this made everything work as it should. This gets the DATE and DAY. I had to use this because converting string to date added +1 day to the date (due to UTC timing 18:00) despite doing -1 or +1
I suggest that you use:
Calendar.current.date(byAdding: .day, value: 1, to: selecteddate)
This will add one day to selecteddate
Then, if you have 2 buttons, as you have explained, you could set a tag to each button:
yesterdaybutton.tag = -1
tomorrowbutton.tag = 1
We will use each button's tag to add or reduce days from selecteddate
So, both buttons will use this:
#IBAction func buttonAction(_ sender: UIButton) {
let newDate = Calendar.current.date(byAdding: .day, value: sender.tag, to: selecteddate)
}
You can create a method like below that calculates the date.
func calculateDate(fromDate date: Date, withUnit value: Int) -> Date? {
return Calendar.current.date(byAdding: .day, value: value, to: date)
}
How to use it?
When yesterday button is selected then you need call method like below...
if let date = calculateDate(fromDate: selecteddate, withUnit: -1) {
print(date)
} else {
print("date is nil. may be due to different formats of dates")
}
When tomorrow button is selected then you need call method like below...
if let date = calculateDate(fromDate: selecteddate, withUnit: 1) {
print(date)
} else {
print("date is nil. may be due to different formats of dates")
}
Note: Please use date format if required. The date format should be same for all dates.
You still need to convert your string to a concrete object which in your case is Date. To it you can add or subtract components as you wish. In your case those are days alone. Check the following:
func addDays(_ days: Int, toDate dateString: String?) throws -> String {
guard let dateString = dateString else { throw NSError(domain: "Add days", code: 400, userInfo: ["dev_message": "String is null"]) }
let formatter = DateFormatter()
formatter.dateFormat = "dd'.'MM'.'yyyy"
// formatter.dateFormat = "MM'.'dd'.'yyyy" // Not sure which
guard let date = formatter.date(from: dateString) else { throw NSError(domain: "Add days", code: 400, userInfo: ["dev_message": "Incorrect date format. Expecting \(formatter.dateFormat!)"]) }
guard let newDate = formatter.calendar.date(byAdding: {
var components: DateComponents = DateComponents()
components.day = days
return components
}(), to: date) else { throw NSError(domain: "Add days", code: 400, userInfo: ["dev_message": "Could not add days for some reason"]) }
return formatter.string(from: newDate)
}
A usage I tried was:
print(try! addDays(1, toDate: "09.10.2019")) // Prints 10.10.2019
print(try! addDays(30, toDate: "09.10.2019")) // Prints 08.11.2019
print(try! addDays(-1, toDate: "09.10.2019")) // Prints 08.10.2019
So in your case you need addDays(1, toDate: "09.10.2019")to get to next day and addDays(-1, toDate: "09.10.2019") for previous day.

iOS Swift - Calculate the difference between two times

I would like to know how to calculate the difference between the login time and log out time, for Example: Login time: 8:00 AM and Logout time: 5:00 PM, and I should get 9 hours as total hours rendered.
This is the format of the time that is saved to Firebase
// Set the time
let timeFormatter = DateFormatter()
timeFormatter.dateStyle = .none
timeFormatter.timeStyle = .short
timeFormatter.amSymbol = "AM"
timeFormatter.pmSymbol = "PM"
let timeString = timeFormatter.string(from: date)
Edit: Found duplicate question
Create Date objects (link to question) from the login (loginTime) and logout (logoutTime) times, and then use them like so:
let components = Calendar.current.dateComponents([.hour, .minute], from: loginTime, to: logoutTime)
// To get the hours
print(components.hour)
// To get the minutes
print(components.minute)
At login...
let loginTime = Date()
UserDefaults.standard.set(loginTime, forKey: "loginTime")
Then at logout...
let loginTime = UserDefaults.standard.object(forKey: "loginTime") as? Date ?? Date()
let loginInterval = -loginTime.timeIntervalSinceNow
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.includesApproximationPhrase = false
formatter.includesTimeRemainingPhrase = false
formatter.allowedUnits = [.hour, .minute]
// Use the configured formatter to generate the string.
let userLoginTimeString = formatter.string(from: loginInterval) ?? ""
print("user was logged in for \(userLoginTimeString)")

Swift - check if a timestamp is yesterday, today, tomorrow, or X days ago

I'm trying to work out how to decide if a given timestamp occurs today, or +1 / -1 days. Essentially, I'd like to do something like this (Pseudocode)
IF days_from_today(timestamp) == -1 RETURN 'Yesterday'
ELSE IF days_from_today(timestamp) == 0 RETURN 'Today'
ELSE IF days_from_today(timestamp) == 1 RETURN 'Tomorrow'
ELSE IF days_from_today(timestamp) < 1 RETURN days_from_today(timestamp) + ' days ago'
ELSE RETURN 'In ' + days_from_today(timestamp) + ' ago'
Crucially though, it needs to be in Swift and I'm struggling with the NSDate / NSCalendar objects. I started with working out the time difference like this:
let calendar = NSCalendar.currentCalendar()
let date = NSDate(timeIntervalSince1970: Double(timestamp))
let timeDifference = calendar.components([.Second,.Minute,.Day,.Hour],
fromDate: date, toDate: NSDate(), options: NSCalendarOptions())
However comparing in this way isn't easy, because the .Day is different depending on the time of day and the timestamp. In PHP I'd just use mktime to create a new date, based on the start of the day (i.e. mktime(0,0,0)), but I'm not sure of the easiest way to do that in Swift.
Does anybody have a good idea on how to approach this? Perhaps an extension to NSDate or something similar would be best?
Swift 3/4/5:
Calendar.current.isDateInToday(yourDate)
Calendar.current.isDateInYesterday(yourDate)
Calendar.current.isDateInTomorrow(yourDate)
Additionally:
Calendar.current.isDateInWeekend(yourDate)
Note that for some countries weekend may be different than Saturday-Sunday, it depends on the calendar.
You can also use autoupdatingCurrent instead of current calendar, which will track user updates. You use it the same way:
Calendar.autoupdatingCurrent.isDateInToday(yourDate)
Calendar is a type alias for the NSCalendar.
Calendar has methods for all three cases
func isDateInYesterday(_ date: Date) -> Bool
func isDateInToday(_ date: Date) -> Bool
func isDateInTomorrow(_ date: Date) -> Bool
To calculate the days earlier than yesterday use
func dateComponents(_ components: Set<Calendar.Component>,
from start: Date,
to end: Date) -> DateComponents
pass [.day] to components and get the day property from the result.
This is a function which considers also is in for earlier and later dates by stripping the time part (Swift 3+).
func dayDifference(from interval : TimeInterval) -> String
{
let calendar = Calendar.current
let date = Date(timeIntervalSince1970: interval)
if calendar.isDateInYesterday(date) { return "Yesterday" }
else if calendar.isDateInToday(date) { return "Today" }
else if calendar.isDateInTomorrow(date) { return "Tomorrow" }
else {
let startOfNow = calendar.startOfDay(for: Date())
let startOfTimeStamp = calendar.startOfDay(for: date)
let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
let day = components.day!
if day < 1 { return "\(-day) days ago" }
else { return "In \(day) days" }
}
}
Alternatively you could use DateFormatter for Yesterday, Today and Tomorrow to get localized strings for free
func dayDifference(from interval : TimeInterval) -> String
{
let calendar = Calendar.current
let date = Date(timeIntervalSince1970: interval)
let startOfNow = calendar.startOfDay(for: Date())
let startOfTimeStamp = calendar.startOfDay(for: date)
let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
let day = components.day!
if abs(day) < 2 {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .none
formatter.doesRelativeDateFormatting = true
return formatter.string(from: date)
} else if day > 1 {
return "In \(day) days"
} else {
return "\(-day) days ago"
}
}
Update:
In macOS 10.15 / iOS 13 RelativeDateTimeFormatter was introduced to return (localized) strings relative to a specific date.
Swift 4 update:
let calendar = Calendar.current
let date = Date()
calendar.isDateInYesterday(date)
calendar.isDateInToday(date)
calendar.isDateInTomorrow(date)
NSCalender has new methods that you can use directly.
NSCalendar.currentCalendar().isDateInTomorrow(NSDate())//Replace NSDate() with your date
NSCalendar.currentCalendar().isDateInYesterday()
NSCalendar.currentCalendar().isDateInTomorrow()
Hope this helps
On Swift 5 and iOS 13 use the RelativeDateTimeFormatter,
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"
formatter.localizedString(from: DateComponents(day: 1)) // "Tomorrow"
formatter.localizedString(from: DateComponents(hour: 2)) // "in 2 hours"
formatter.localizedString(from: DateComponents(minute: 45)) // "in 45 minutes"
1)According to your example you want to receive labels "Yesterday", "Today" and etc. iOS can do this by default:
https://developer.apple.com/documentation/foundation/nsdateformatter/1415848-doesrelativedateformatting?language=objc
2)If you want to compute your custom label when iOS don't add these labels by itself then alternatively you can use 2 DateFormatter objects with both doesRelativeDateFormatting == true and doesRelativeDateFormatting == false and compare if their result date strings are the same or different

Resources