I have searched Parse blog to see if I can get the solution to this, but didn't get any satisfactory answer. So I though I will clearly ask the question here with all the details so anyone else stuck at similar situation will find it easy to work.
Need:
I have a departure and return text field which are updated using a single UIDatePicker. I want to save the selected dates in my Parse.com database. To which I can query and filter data. I also want to store local timezone in parse if possible. I am working with checking number of days but it was not relevant to this question so not including the code for that.
Success & Problem:
I am able to save correct date and time in String format , but when I try to save in NSDate format using the code below, I get wrong time.
For ex: my date selection and stored result in parse are as below:
Departure date:
Date Picker selection: 01/May/2015 01:00 AM +0530
Stored date in Parse: Apr 30, 2015, 19:30
Return Date:
Date Picker selection: 02/May/2015 01:00 AM +0530
Stored date in Parse: May 01, 2015, 19:30
//My code is as below:
#IBOutlet var dOfTravelText: UITextField!
#IBOutlet var returnDateText: UITextField!
lazy var dateFormatter: NSDateFormatter = {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd/MMM/yyyy hh:mm a Z"
// dateFormatter.dateStyle = .MediumStyle
// dateFormatter.timeStyle = .ShortStyle
return dateFormatter
}()
#IBAction func travelDatePicker(sender: UITextField) {
datePickerView.minimumDate = NSDate()
datePickerView.datePickerMode = UIDatePickerMode.DateAndTime
sender.inputView = datePickerView
timeSelected = sender
datePickerView.addTarget(self, action: "handleDatePicker:", forControlEvents: UIControlEvents.AllEvents)
}
// Date Picker target - Displaying date in textfield
func handleDatePicker(sender: UIDatePicker) {
//var dateFormatter = NSDateFormatter()
//dateFormatter.dateFormat = "mm/dd/yyyy"
timeSelected.text = dateFormatter.stringFromDate(sender.date)
println("From Date \(dOfTravelText.text!)")
println("To Date \(returnDateText.text!)")
}
// Submitting the dates to parse backend
#IBAction func postBtn(sender: AnyObject) {
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute, fromDate: date)
let dateMakerFormatter = NSDateFormatter()
dateMakerFormatter.dateFormat = "dd/MMM/yyyy hh:mm a Z"
let dD = dateMakerFormatter.dateFromString("\(dOfTravelText.text!)")!
let departureD = NSDateFormatter.localizedStringFromDate(dD, dateStyle: .MediumStyle, timeStyle: .ShortStyle)
println("From-------...\(departureD)")
let rD = dateMakerFormatter.dateFromString("\(returnDateText.text!)")!
let returnD = NSDateFormatter.localizedStringFromDate(rD, dateStyle: .MediumStyle, timeStyle: .ShortStyle)
println("To-------\(returnD)")
var userPost = PFObject(className:"UserPost")
userPost["departureDate"] = dD // Works but has Wrong time
userPost["rDate"] = rD // Works but Wrong time
userPost["travelDate"] = dOfTravelText.text // Works but it is stored in String format
userPost["returnDate"] = returnDateText.text // Works but it is stored in string format
userPost.saveInBackgroundWithBlock {
(success, error: NSError?) -> Void in
if (success) {
// The object has been saved.
println("Saved")
} else {
// There was a problem, check error.description
println("Error")
}
}
}
}
// Parse database column and type
travelDate & returnDate are defined as of type "String"
departureDate & rDate are defined as Of type "Date"
Just for information: I am using
Platform - iOS Swift (xcode 6.3, swift 1.2)
Database backend = Parse.com
Since Parse stores the dates referenced to GMT, when you check them on parse or retrieve them, you may find this difference to your local timezone. In general dates are stored in GMT.
You can add an extra field in your Parse database and store the local timezone there. When retrieving data you can then use that information to interpret the date in the zone it is referenced in.
Depending on the type of data you store it might be okay to always interpret the date in the users local timezone, even when this has changed. You also could ask the user for a resolution if saved timezone and user timezone are different (so the user has moved).
Setting up date pickers:
#IBOutlet weak var dDDatePicker: UIDatePicker!
#IBOutlet weak var rdDatePicker: UIDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
setUpDatePickers()
}
func setUpDatePickers() {
// Set start date
dDDatePicker.date = Date()
rdDatePicker.date = Date()
// Set min and max date if you want
dDDatePicker.maximumDate = minDepartDate
rdDatePicker.maximumDate = minReturnDate
dDDatePicker.minimumDate = maxDepartDate
rdDatePicker.minimumDate = maxReturnDate
}
Saving dates to Parse:
func saveUserPost() {
let dD = dDDatePicker.date
let rd = rdDatePicker.date
let userPost = PFObject(className: "UserPost")
userPost["departureDate"] = dD
userPost["rDate"] = rD
userPost.saveInBackgroundWithBlock {
(success, error) -> Void in
if (success) {
// The object has been saved.
print("Saved")
} else {
// There was a problem, check error.description
print("Error")
}
}
}
As mentioned date is saved in UTC I would use DateFormatter() to get it back to string in the correct time zone:
if let dD = userPost["departureDate"] as? Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let str = dateFormatter.string(from: dD)
print("Depart date \(str)")
}
if let rD = userPost["rDate"] as? Date {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let str = dateFormatter.string(from: rD)
print("Return date \(str)")
}
Use this link to set 'dateFormat' however you want:
https://nsdateformatter.com/
From my understanding dateFormatter will format to the devices current timeZone - Honestly I never tested it in different time zones but it works for EST - you can test and if not as mentioned you can also save the user time zone and then specify in date formatter like so
dateFormatter.timeZone = TimeZone(abbreviation: "\(usertimeZone)")
Hope this helps!
Related
I'm trying to make an attendance app and I am really confused about date and time in iOS and Firebase.
I use date as Key, this is the structure of my Firebase database.
--Employees
--Unique_ID
--Details
Name: John
--Attendance
--dateToday
Timein: 8:00 AM
Timeout: 5:00 PM
BreakStart: 12:00 PM
BreakFinish: 1:00 PM
This is my code to get the date timestamp I used as Key
override func viewDidLoad() {
super.viewDidLoad()
let now = NSDate()
let nowTimeStamp = self.getCurrentTimeStampWOMiliseconds(dateToConvert: now)
// I save this dateToday as Key in Firebase
dateToday = nowTimeStamp
}
func getCurrentTimeStampWOMiliseconds(dateToConvert: NSDate) -> String {
let objDateformat: DateFormatter = DateFormatter()
objDateformat.dateFormat = "yyyy-MM-dd"
let strTime: String = objDateformat.string(from: dateToConvert as Date)
let objUTCDate: NSDate = objDateformat.date(from: strTime)! as NSDate
let milliseconds: Int64 = Int64(objUTCDate.timeIntervalSince1970)
let strTimeStamp: String = "\(milliseconds)"
return strTimeStamp
}
But when I convert it back to date I get 2017-09-22 16:00:00 +0000, which is wrong because it is 23rd of September in my location.
What is the right code to use so that I can get the correct date timestamp and time timestamp?
For saving Current time to firebase database I use Unic Epoch Conversation:
let timestamp = NSDate().timeIntervalSince1970
and For Decoding Unix Epoch time to Date().
let myTimeInterval = TimeInterval(timestamp)
let time = NSDate(timeIntervalSince1970: TimeInterval(myTimeInterval))
If you just want the unix timestamp, create an extension:
extension Date {
func currentTimeMillis() -> Int64 {
return Int64(self.timeIntervalSince1970 * 1000)
}
}
Then you can use it just like in other programming languages:
let timestamp = Date().currentTimeMillis()
First I would recommend you to store your timestamp as a NSNumber in your Firebase Database, instead of storing it as a String.
Another thing worth mentioning here, is that if you want to manipulate dates with Swift, you'd better use Date instead of NSDate, except if you're interacting with some Obj-C code in your app.
You can of course use both, but the Documentation states:
Date bridges to the NSDate class. You can use these interchangeably in
code that interacts with Objective-C APIs.
Now to answer your question, I think the problem here is because of the timezone.
For example if you print(Date()), as for now, you would get:
2017-09-23 06:59:34 +0000
This is the Greenwich Mean Time (GMT).
So depending on where you are located (or where your users are located) you need to adjust the timezone before (or after, when you try to access the data for example) storing your Date:
let now = Date()
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let dateString = formatter.string(from: now)
Then you have your properly formatted String, reflecting the current time at your location, and you're free to do whatever you want with it :) (convert it to a Date / NSNumber, or store it directly as a String in the database..)
in Swift 5
extension Date {
static var currentTimeStamp: Int64{
return Int64(Date().timeIntervalSince1970 * 1000)
}
}
call like this:
let timeStamp = Date.currentTimeStamp
print(timeStamp)
Thanks #lenooh
The simple way to create Current TimeStamp. like below,
func generateCurrentTimeStamp () -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy_MM_dd_hh_mm_ss"
return (formatter.string(from: Date()) as NSString) as String
}
you can call like this:
let timeStmp = generateCurrentTimeStamp()
print("time stamp: \(timeStmp)")
If you code for iOS 13.0 or later and want a timestamp, then you can use:
let currentDate = NSDate.now
On expanding #MacacoAzul's answer here is my current working example :
import SwiftUI
struct TimestampDemo: View {
var body: some View {
Text(getActualTimeStamp(1))
.padding(10)
Text(getActualTimeStamp(2))
.padding(10)
Text(getActualTimeStamp(3))
.padding(10)
Text(getActualTimeStamp(4))
.padding(10)
}
func getActualTimeStamp(_ tipo:Int) -> String {
let date = Date()
let formatter = DateFormatter()
if tipo == 1{
formatter.dateFormat = "dd/MM/yyyy"
} else if tipo == 2{
formatter.dateFormat = "yyyy-MM-dd HH:mm"
}else if tipo == 3{
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
else if tipo == 4 {
formatter.dateFormat = "dd-MM-yyyy"
}
return formatter.string(from: date)
}
}
struct TimestampDemo_Previews: PreviewProvider {
static var previews: some View {
TimestampDemo()
}
}
Swift Language Version : 5
When we convert a UTC timestamp (2017-11-06 20:15:33 -08:00) into a Date object, the time zone is zeroed out to GMT. For calculating time intervals, this isn't an issue, but it can be for rendering times in the UI.
I favor the RFC3339 format (2017-11-06T20:15:33-08:00) for its universality. The date format in Swift is yyyy-MM-dd'T'HH:mm:ssXXXXX but RFC3339 allows us to take advantage of the ISO8601DateFormatter:
func getDateFromUTC(RFC3339: String) -> Date? {
let formatter = ISO8601DateFormatter()
return formatter.date(from: RFC3339)
}
RFC3339 also makes time-zone extraction simple:
func getTimeZoneFromUTC(RFC3339: String) -> TimeZone? {
switch RFC3339.suffix(6) {
case "+05:30":
return TimeZone(identifier: "Asia/Kolkata")
case "+05:45":
return TimeZone(identifier: "Asia/Kathmandu")
default:
return nil
}
}
There are 37 or so other time zones we'd have to account for and it's up to you to determine which ones, because there is no definitive list. Some standards count fewer time zones, some more. Most time zones break on the hour, some on the half hour, some on 0:45, some on 0:15.
We can combine the two methods above into something like this:
func getFormattedDateFromUTC(RFC3339: String) -> String? {
guard let date = getDateFromUTC(RFC3339: RFC3339),
let timeZone = getTimeZoneFromUTC(RFC3339: RFC3339) else {
return nil
}
let formatter = DateFormatter()
formatter.dateFormat = "h:mma EEE, MMM d yyyy"
formatter.amSymbol = "AM"
formatter.pmSymbol = "PM"
formatter.timeZone = timeZone // preserve local time zone
return formatter.string(from: date)
}
And so the string "2018-11-06T17:00:00+05:45", which represents 5:00PM somewhere in Kathmandu, will print 5:00PM Tue, Nov 6 2018, displaying the local time, regardless of where the machine is.
As an aside, I recommend storing dates as strings remotely (including Firestore which has a native date object) because, I think, remote data should agnostic to create as little friction between servers and clients as possible.
you can even create a function to return different time stamps depending on your necessity:
func dataatual(_ tipo:Int) -> String {
let date = Date()
let formatter = DateFormatter()
if tipo == 1{
formatter.dateFormat = "dd/MM/yyyy"
} else if tipo == 2{
formatter.dateFormat = "yyyy-MM-dd HH:mm"
} else {
formatter.dateFormat = "dd-MM-yyyy"
}
return formatter.string(from: date)
}
My code below creates a date picker and has it select a date and time. All I want to do is when the date picker's date and time match the user's phone's date and time is to print the line "cool". That's it. I commented the line that is causing me problems.
import UIKit
var dateFormatter : DateFormatter!
let datePicker2 = UIDatePicker();
let date = Date()
class ViewController: UIViewController {
#IBOutlet var dateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let datePicker : UIDatePicker = UIDatePicker(frame: CGRect(x: 0,y: 330,width: self.view.frame.size.width,height: 220))
datePicker.datePickerMode = UIDatePickerMode.dateAndTime
self.view.addSubview(datePicker)
datePicker.addTarget(self, action: #selector(ViewController.change(_:)), for: UIControlEvents.valueChanged)
dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd hh:mm"
}
#IBAction func change(_ sender : UIDatePicker)
{
dateLabel.text = dateFormatter.string(from: sender.date)
///print cool line; what I have does not work
if dateLabel.text == String(describing: date){
print("cool")
}
}
}
Your primary issue is how you compare the two dates. You should be using the same date formatter to convert both Date instances to strings in the same format. Then you can compare the two strings.
#IBAction func change(_ sender : UIDatePicker)
{
let pickerString = dateFormatter.string(from: sender.date)
let nowString = dateFormatter.string(from: Date())
dateLabel.text = pickerString
if pickerString == nowString {
print("cool")
}
}
You are also using the wrong format. You need "yyyy-MM-dd HH:mm". YYYY is slightly different. You always want yyyy unless you have a clearly understood and specific need to use YYYY. And for the hour you want HH instead of hh. HH is a 24-hour hour while hh is a 12-hour hour. Only use hh is you also use a (for AM/PM).
And your properties should be inside the class, not outside.
Move datePicker2 inside the class.
date is now obsolete based on my answer so you can remove it completely.
dateFormatter should also be moved inside the class.
Basically my challenge is :
I got an array of objects . Each object has a date property .
I want to get the array of objects which matches today's date.
So, I want to group the array based on date.
I want to show in history list by grouping objects from today, yesterday, last 7 days ,.....
I can sort array using sort function:
historylist.sort(by: {$0.createdDate! as Date > $1.createdDate! as Date })
But I need a filter function based on date.
I want all the data in historicist which are created today only.
Any quick pseudo code .
Thanks.
let date1 = NSDate()
let date2 = NSDate()
let dates = [date1, date2]
let calendar = NSCalendar.currentCalendar()
let todayDates = dates.filter({calendar.isDateInToday($0)})
Demo of how this works with filtering date objects:
let d1 = Date().addingTimeInterval(22000000)
let d2 = Date().addingTimeInterval(23000000)
let d3 = Date().addingTimeInterval(30000000)
let dates = [d1, d2, d3, Date(), Date()]
let calendar = Calendar.current
let todayDates = dates.filter({calendar.isDateInToday($0 as Date)})
print(todayDates) // [2017-06-03 15:50:11 +0000, 2017-06-03 15:50:11 +0000]
So in your case you should do it like this:
let todayDates = historylist.filter({calendar.isDateInToday($0.createdDate as Date)})
Update:
To convert a string to a Date use the following:
extension String {
var toDate: Date {
return Date.Formatter.customDate.date(from: self)!
}
}
extension Date {
struct Formatter {
static let customDate: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MM-dd-yyyy hh:mm:ss a"
return formatter
}()
}
}
let strDate = "06-01-2017 09:32:48 PM"
let date = strDate.toDate
What I am trying to accomplish:
I am trying to make an enter button that checks the date in the UITextField to see if it is before the current date(any date that is today) and if the date in the UITextField is in the past, I want to throw an error/alert to tell the user to enter a date in the future--possibly a week or month from the current date.
A little background:
I made a viewController with a textfield that a user is going to enter a
date into and the way this works is the user presses on the textField and
a datePicker pops up allowing them to update the textField with the datePicker.
The problem I am running into:
This all works fine until I want to compare the date in the textField with the current date. This is because the date in the textField is a string and not a date that NSDate can recognize.
My question basically is
How do I convert the string date in the TextField into a format that NSDate can recognize?
Here is my code for my enterButton function:
#IBAction func enterButton(sender: AnyObject) {
let dateFormatter = NSDateFormatter()
var raceDate = raceDateTextField.text
let currentDate = NSDate()
raceDate = dateFormatter.stringFromDate(sender.date)
if currentDate.compare(raceDate) == NSComparisonResult.OrderedDescending {
print("Race Date is earlier than Current Date")
}
}
I'm getting the error
cannot convert value of type 'String?' to expected argument type 'NSDate' # the line below
if currentDate.compare(raceDate) == NSComparisonResult.OrderedDescending {
print("Race Date is earlier than Current Date")
}
Here is the my code for updating my textField using a datePicker just for reference:
#IBAction func textFieldEditing(sender: UITextField) {
let datePicker:UIDatePicker = UIDatePicker()
datePicker.datePickerMode = UIDatePickerMode.Date
sender.inputView = datePicker
datePicker.addTarget(self, action: #selector(SecondViewController.datePickerValueChanged), forControlEvents: UIControlEvents.ValueChanged)
}
func datePickerValueChanged(sender:UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateFormatter.timeStyle = NSDateFormatterStyle.NoStyle
raceDateTextField.text = dateFormatter.stringFromDate(sender.date)
}
Am I going about this wrong logically?
Any help with this will be greatly appreciated! :)
EDIT/UPDATE!
I made the changes like you guys advised and I am getting the same error at the same line.
Here is my updated code:
#IBAction func enterButton(sender: AnyObject) {
let dateFormatter = NSDateFormatter()
var raceDate = raceDateTextField.text
dateFormatter.dateFormat = "dd/MM/yyyy"
var minimumDate: NSDate? = NSDate()
raceDate = dateFormatter.stringFromDate(sender.date)
if minimumDate!.compare(raceDate) == NSComparisonResult.OrderedDescending {
print("Race Date is earlier than Current Date")
}
}
I think the (sender.date) parameter is wrong # line:
raceDate = dateFormatter.stringFromDate(sender.date)
Right now I'm thinking I entered in the wrong parameter(sender.date) because raceDate still shows up as a string data type in the if statement.
Swift 3:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM, dd, yyyy"
let raceDate = raceDateTextField.text
let date = dateFormatter.date(from: raceDate!)
let minimumDate = NSDate()
if minimumDate.compare(date!) == ComparisonResult.orderedDescending {
}
The text that is currently in your textField is a String. You need to run that through a dateFormatter and get the date (NSDate). Afterwards you can compare it to the current date.
It's basically the same thing as when you set the text in your textField, but the other way around.
see: https://developer.apple.com/reference/foundation/nsiso8601dateformatter/1643127-datefromstring
You said that you want to compare the date in the textField with the current date.
So just create and set a minimum date for the date picker to today. var minimumDate: NSDate?
For example:
if minimumDate.compare(raceDate) == NSComparisonResult.OrderedDescending {
print("Race Date is earlier than Current Date. Are you a time traveler?")
}
#IBAction func enterButton(sender: AnyObject) {
let myDate = raceDateTextField.text
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date = dateFormatter.dateFromString(myDate)!
dateFormatter.dateFormat = "dd/MM/yyyy"
let dateString = dateFormatter.stringFromDate(date)
}
Got it working correctly, thanks to #Amit Jagesha, #tymac, #Andrei Filip, and #Leo Dabus
Here is the end product in case anybody else has a similar problem and for future reference.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMM, dd, yyyy"
let raceDate = raceDateTextField.text
let date = dateFormatter.dateFromString(raceDate!)
let minimumDate = NSDate()
if minimumDate.compare(date!) == NSComparisonResult.OrderedDescending {
}
I am teaching myself swift and I am still very new but I decided to make a simple app that prints the current time when you press a button. the code from the viewcontroller file is as follows:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var LblTime: UILabel!
#IBAction func BtnCalltime(sender: AnyObject) {
var time = NSDate()
var formatter = NSDateFormatter()
formatter.dateFormat = "dd-MM"
var formatteddate = formatter.stringFromDate(time)
LblTime.text = time
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I am having an issue with the line:
LblTime.text = time
I keep getting the error:
Cannot assign a value of type 'NSDate' to a value of type 'String?'
I have tried using:
lblTime.text = time as! string?
And:
lblTime.text = time as! string
but it does still not work, I would be very appreciative of some help.
Thanks
You need use a value from formatter.
#IBAction func BtnCalltime(sender: AnyObject) {
var time = NSDate()
var formatter = NSDateFormatter()
formatter.dateFormat = "dd-MM"
var formatteddate = formatter.stringFromDate(time)
LblTime.text = formatteddate
}
You made the string from an NSDate already, you just aren't using it.
lblTime.text = formatteddate
Date is now preferred over NSDate. It is an overlay class meaning both will work, but Date but has a lot of advantages, this answer lists some of those.
Here is how to format a date to a string using Date instead of NSDate.
var time = Date()
var formatter = DateFormatter()
formatter.dateFormat = "MMM d yyyy, h:mm:ss a"
let formattedDateInString = formatter.string(from: time)
dateLabel.text = formattedDateInString
A great site to get the formatter strings is http://nsdateformatter.com/
I had no idea that "MMM d yyyy, h:mm:ss a" would equal Mar 1, 7:02:35 AM but the site makes it easy.