Compare a string with current time - ios

I have a string with a specific hour-minut timestamp like this:
let timeToday = "14:00"
and I want to compare this time to the current time of the current day. I'm having problems with NSDate and the fact that it uses a UTC timestamp. I havn't used NSDate, NSCalendar or NSDateFormatter before, and I'm a bit frustrated over how dificult it is to solve this seemingly simple task...
So I was trying to set both the same time from the string and the current time, into two NSDate variables, and then use some sort of compare method, but i can't seem to get them on the same basis.
How can get the two timestamps on the same basic?
so the I have a NSDate with:
NSDate: timeToday // "2015-12-05 14:00:00 +0000"
NSDate: currentTime // "2015-12-05 22:46:54 +0000"

So I ended up reading through the documentation to understands the conpects of the datatypes. NSDate represents a point in time, which will be displayed differently depending on where you are on the globe. So using the UTC as the basis for both the time string and the current time is totally fine.
I solved the problem like this, using :
let calender = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
calender!.locale = NSLocale.currentLocale()
let timeToday = "14:00"
let timeArray = timeToday.componentsSeparatedByString(":")
let timeTodayHours = Int(timeArray[0])
let timeTodayMin = Int(timeArray[1])
let timeTodayDate = calender!.dateBySettingHour(timeTodayHours!, minute: timeTodayMin!, second: 00, ofDate: NSDate(), options: NSCalendarOptions())
let now = NSDate()
if now.compare(timeTodayDate!) == .OrderedDescending {
print(" 'timeToday' has passed!!! ")
}

Related

Convert UTC time to PST in SWIFT

I need to convert the UTC time to PST
From backed, I get UTC dates like "2021-06-25T07:00:00Z"
I need to show the dates in Hstack from Provided UTC date to the current date.
I write the following code.
Anyone help to me.
func datesRange(from:Date, to:Date)->[Date]{
if from > to {return [Date]()}
var tmpdate = from
var array:[Date] = []
while tmpdate <= to {
array.append(tmpdate)
tmpdate = Calendar.current.date(byAdding: .day,value: 1, to: tmpdate)!
}
return array
}
extension Date{
func convertTimezone(timezone:String)-> Date{
if let targettimeZone = TimeZone(abbreviation: timezone){
let delta = TimeInterval(targettimeZone.secondsFromGMT(for: self) - TimeZone.current.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}else{
return self
}
}
}
I used as follows
func getrangeDays(){
let startday = "2021-06-25T07:00:00Z"
let dateformater = DateFormatter()
dateformater.locale = Locale(identifier: "en_US_POSIX")
dateformater.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
if let date = dateformater.date(from: startday){
let rangedays = datesRange(from:date.convertTimezone(timezone: "PST") , to: Date().convertTimezone(timezone: "PST"))
print(rangedays)
}
}
Your convertTimezone() function does not make sense. It is trying to convert a Date to a different time zone. A Date object does not have a time zone. It is an instant in time, anywhere on the planet. Time zones only make sense when you want to display a Date, or do time zone specific date calculations. (And in that case you want to create a Calendar object and set its time zone to the desired time zone, then use that Calendar for your date calculations.)
Get rid of that function.
Convert your input date string to a Date as you are doing now (although you might want to use an ISO8601DateFormatter rather than a regular date formatter, since those are specifically intended for handling ISO8601 dates.)
Build your date range using your datesRange() function.
Then use a second DateFormatter to display your dates in PST. (Not convert Dates to PST. That doesn't make sense.)

How to get NSDate from string without converting it to UTC

I have following problem. Let's suppose that I receive from server some string containing date time in following format:
yyyy-MM-dd'T'HH:mm:ssxxxx
This is a server date time and I would like to display it in application as it is. So if I receive date time:
2017-04-07T10:29:23+ 02:00
it means that on the server is 10:29:23 (08:29:23 UTC)
and I would like to display it in application as:
04.07.2017 10:29:23
but whenever I try to convert the string to NSDate I get the date converted to UTC which is not desired because I lose information about server time zone. I use following code right now:
let formatter = NSDateFormatter()
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxx"
let date = formatter.dateFromString(dateString)
How can I achieve that in Swift (2.3)?
Thanks in advance.
NSDate does not have a built-in time zone. If you print an NSDate then it'll print in GMT because it has to print in something. But the date itself does not contain a time zone, and can't. They're time-zone independent.
To print a date in a particular time zone, configure an NSDateFormatter with the format and time zone that you want, then use .stringFromDate. Or don't set a time zone and it'll print in your device's time zone and locale, likely being what your user expects (e.g. your server says "event happened at 00:23+02:00" so to your CET user you'll display "event happened at 23:23", and to your EST user you display "event happened at 6:23PM").
Frustratingly, you have only one problem left: the date formatter understands the time zone in your original string (the +02:00), but can't communicate it to you by any means. So if you want to take an NSDate and print it in the same time zone as your server then you're going to have to determine the time zone on your own, communicate it via another channel, or hard-code it.
Ok, so finally I've managed to resolve the problem using your hints.
Following code meets my expectations:
let formatter = NSDateFormatter()
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxx"
let timeZonePattern = "([+-]([01][0-9]):[0-5][0-9])$" // e.g. "+02:00"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let dateStringWithoutTimeZone = regex.stringByReplacingMatchesInString(dateString, options: [], range: NSMakeRange(0, dateString.characters.count), withTemplate: "Z")
let date = formatter.dateFromString(dateStringWithoutTimeZone)
Thank you all!

Get tomorrow's date with Swift 2

I have this code which gives today's date in this formate M/dd/yy
let dateFormater = NSDateFormatter()
dateFormater.dateFormat = "M/dd/yy"
let todayDate = dateFormater.stringFromDate(NSDate())
How can I get the same thing but with next day's date please?
First, you get a NSDate for the day you need, in this example (one day from now is tomorrow):
var oneDayfromNow: Date? {
Calendar.current.date(byAdding: .day, value: 1, to: Date())
}
print(oneDayfromNow)
Then you convert it to your format as string (your case M/dd/yy):
if let oneDayfromNow {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "M/dd/yy"
let str = dateFormatter.string(from: oneDayfromNow)
print(str)
}
It's a bit complicated, but it's all things that you need to know anyway.
Why it's difficult: You would think that you could just take NSDate (timeIntervalSinceNow:24 * 60 * 60), adding one day to now. But when you turn on daylight savings time, then 11:30pm plus 24 hours is 00:30am two days later. When daylight savings time is turned off, then 00:30am plus 24 hours can be 11:30pm on the same day.
So you need to create an NSCalendar object, convert NSDate () into components, add one day to the components, convert back to an NSDate (all that gives you the same time on the next day, handling all special cases), and then format the result as you did now.
I finally used this code to fix it :
let dateFormater = NSDateFormatter()
dateFormater.dateFormat = "M/dd/yy"
let todayDate = dateFormater.stringFromDate(NSDate().dateByAddingTimeInterval(24 * 60 * 60))

Combining time and date in ISO8601 format string using Swift

I have two texfields representing date and time from the date picker, but when I parse the string only the date is displayed. I would like to combine date and time into a string in ISO8601 format (e.g. 2015-06-11T00:00:00.000Z) and send it to the server.
You can do it using NSCalendar method dateBySettingHour as follow:
Xcode 8.2.1 • Swift 3.0.2
let df = DateFormatter()
df.calendar = Calendar(identifier: .iso8601)
df.locale = Locale(identifier: "en_US_POSIX")
df.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z"
if let date = df.date(from:"2015-06-11T00:00:00.000Z") {
let hour = 4
if let dateWithTime = Calendar.current.date(bySettingHour: hour, minute: 0, second: 0, of: date) {
let resultString = df.string(from: dateWithTime)
print(resultString) // "2015-06-11T04:00:00.000Z"
}
}
Strings are textual representations of dates and often include an explicit or implicit timezone. NSDate represents a date in UTC.
If you want to add an hour, you convert the string to NSDate, add an hour, convert it to a string. The conversion will take care for example of daylight savings time, where one hour after 2:30am might be 4:30am. Or 2:30am.

Search CoreData for Matching NSDate - Swift

I want to search through all existing objects to see if there are any matching date objects in CoreData:
Dates are currently saved in my CoreData via the start_date attribute with the following format: 2013-08-29 14:27:47 +0000.
I am then letting a user select a date from a UIDatePicker and assigning .date() to variable date.
e.g. My Selected date = 2013-08-29 17:34:23 +0000.
Below is how i search CoreData, using a predicate.
let predicate = NSPredicate(format: "start_date contains[search] %#", date)
let request:NSFetchRequest = NSFetchRequest(entityName: "Project")
let sortDescriptor:NSSortDescriptor = NSSortDescriptor(key: "number", ascending: true)
let sortDescriptorsAry:NSArray = [sortDescriptor]
request.sortDescriptors = sortDescriptorsAry
request.predicate = predicate
return request
However i get no results. I assume this because both attributes don't match because of the time:
start_date = 2013-08-29 14:27:47 +0000
date = 2013-08-29 17:34:23 +0000
How can i some how tell CoreData to ignore the "177:34:23 +0000" bit, or is there a better way?
Edit:
I do have the option to change the way in which the date format is stored initially:
I have tried this:
var now:NSDate = self.startDatePicker.date
var calendar:NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
var components:NSDateComponents = calendar.components(NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay, fromDate: now)
components.hour = 00
components.minute = 00
components.second = 00
var newDate:NSDate = calendar.dateFromComponents(components)!
However on some days, my time is being set to a day before.
For Example:
Selecting the 30th August, after transform I get 2014-08-29 23:00:00 +0000
Before to save start_date attributes in Core Data, you need to be sure that their time is set to 12:00 AM:
//Get "Aug 29, 2014, 12:00 AM" from "Aug 29, 2014, 10:07 PM"
let newDate = NSDate() //or any other date
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.systemTimeZone()
var startDate: NSDate?
var duration: NSTimeInterval = 0
calendar.rangeOfUnit(.DayCalendarUnit, startDate: &startDate, interval: &duration, forDate: newDate)
//Create, set and save a new managedObject
//Records, here, is the name of your NSManagedObject subclass
let record = NSEntityDescription.insertNewObjectForEntityForName("Records", inManagedObjectContext: managedObjectContext) as Records
record.start_date = startDate
/* set other attributes here */
var error: NSError?
if !managedObjectContext.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
println("Unresolved error \(error), \(error!.userInfo)")
abort()
}
Then, you will be able to fetch a date in Core Data with the predicate you want this way:
//Set your datePicker date to 12:00 AM
let pickerDate = dateFromMyDatePicker //as NSDate
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.systemTimeZone()
var predicateDate: NSDate?
var duration: NSTimeInterval = 0
calendar.rangeOfUnit(.DayCalendarUnit, startDate: &predicateDate, interval: &duration, forDate: pickerDate)
//Create your fetchRequest
/* ... */
//Set your predicate
let predicate = NSPredicate(format: "start_date == %#", predicateDate)
Edit
As an alternative to rangeOfUnit:startDate:interval:forDate:, you can use the code you provide. But in both cases, don't forget to add the following line:
calendar.timeZone = NSTimeZone.systemTimeZone()
If you still have to option of changing the way you store data, I would store it on your project-entity as three attributes: an Integer for year, an Integer for month and an Integer for day. It will be easier to code (you can create a predicate like:
NSInteger year = ....
NSInteger month = ....
NSInteger day = ....
[NSPredicate predicateWithFormat: #"day == %# AND month == %# and year == %#", #(day), #(month), #(year)];
) and this probably work quicker compared to using a string. If you want to go with the string (beware extraneous spaces or other interpunction!), you can in code construct a string with the format 'yyyy-mm-dd' (use NSString stringWithFormat:) and then use an NSPredicate like:
NSInteger year = ....
NSInteger month = ....
NSInteger day = ....
NSString * dateString = [NSStringWithFormat: #"%4i-%2i-%2i", year, month, day];
[NSPredicate predicateWithFormat: #"dateString contains %#", dateString];
However, this will only work if YOU constructed the string that you stored, not the NSDate.description(). If you store actual NSDate, see my first original answer :).
But really, if you still have an option, don't store it as a string. It wil really be problematic, if not now, then later (I can tell from experience).
<edit>
Your second way of doing things seems to me like a good way to go. Store the integer values for year, month and day, and create a predicate with that. Your usage of NSDateComponents and calendar is great, and so this should be the easiest way to go. Very important: don't use for the time of 00, as midnight is a tricky time (there is daylight savings times in parts of the world, there is the question of whether exactly midnight is part of the previous of the next day). Just enter 12 as the time, to be as far away as you can from midnight.
Also, i would advice you to watch the WWDC from 2011 'Performing Calendar Calculations, Session 117. It talks among other very interesting things about why midnight is tricky (if not, then that is explained in the 2013 session :) ).
</edit>
NSDate has sub-second accuracy. It's description method (that gets called when you NSLog an NSDate) only displays second-accuracy.
So to use an NSDate in an NSPredicate, always specify 'larger/smaller then or equal to' operators.
For example:
NSDate * lastWeek = // ... created using NSCalendar and NSDateComponents.
NSDate * now = [NSDate date];
NSPredicate * entitiesFromAfterLastWeekAndBeforeNow = [NSPredicate predicateWithFormat: #"start_date >= %# AND start_date <= %#", lastWeek, now];
I would advice against storing a data in something else the a native NSDate, as you might store dates that are incorrect due to conversions. Also, storing dates as string will make queries a lot slower (as strings have to be parsed, and NSDate is simply a number). But that is another discussion, and you might want to ship, so you have to do what you think is best.
Edit: I'm sorry, only now noticed that you are writing Swift. But I think my example is clear enough that it is convertible to Swift, right?

Resources