How to post data to Firestore as Timestamp data type - swift - swift

I wonder how can I post data as String to Firestore as Timestamp. I am creating a sample app which store data on tableView and data are going to sort base on Timestamp.
So, in programming I have tried to get current time and date as String but I don't know how to set data to Firestore as field is Timestamp. When i query data from Firestore i want to sort data base on Timestamp.
let currentDateTime = Date()
// initialize the date formatter and set the style
let formatter = DateFormatter()
formatter.timeStyle = .long
formatter.dateStyle = .long
// get the date time String from the date object
formatter.string(from: currentDateTime)
Timestamp in Firestore contain date and time.
How can I post data as String to Firestore as Timestamp?

If I understand correctly, you want to store a file under a time stamp or date in Firestore?
You seem to be doing a Time stamp literally as a date, a much simpler way would be storing it as a Time stamp this way as it will just be a number that will be ordered in Firestore.
let timestamp = Int(NSDate.timeIntervalSinceReferenceDate*1000).description
Image Link : This will be the end result

Why do you want to post it as String? Here's my approach with an Int instead
let timestamp = Int(Date().timeIntervalSince1970) // gives you an Int like 1534840591
Then when you parse it from Firebase, pass it to a func like something like this to convert that Int timestamp into a date:
func timestampIntToString(integerTime: Int, timestampLabel: UILabel) {
let timestampDate = Date(timeIntervalSince1970: Double(integerTime))
let now = Date()
let components = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfMonth])
let difference = Calendar.current.dateComponents(components, from: timestampDate, to: now)
var timeText = ""
if difference.second! <= 0 {
timeText = "now"
}
if difference.second! > 0 && difference.minute! == 0 {
timeText = "\(difference.second!) sec ago"
}
if difference.minute! > 0 && difference.hour! == 0 {
timeText = "\(difference.minute!) min"
}
if difference.hour! > 0 && difference.day! == 0 {
timeText = "\(difference.hour!)h"
}
if difference.day! > 0 && difference.weekOfMonth! == 0 {
timeText = (difference.day == 1) ? "\(difference.day!)day" : "\(difference.day!) days ago"
}
if difference.weekOfMonth! > 0 {
timeText = (difference.weekOfMonth == 1) ? "\(difference.weekOfMonth!) w" : "\(difference.weekOfMonth!)w"
}
timestampLabel.text = timeText
}

Related

sorting dates to closest by a given time in swift

I have a list of date strings in which I want to sort the list closest to the given time. If two times are clashing then earlier date priority would be considered.
var givenTIme = "10:00AM"
var strDates = ["2021-04-30 10:00AM", "2021-04-16 10:00AM", "2021-04-26 12:00AM", "2021-04-28 09:00AM"]
var output = ["2021-04-16 10:00AM", "2021-04-30 10:00AM", "2021-04-28 09:00AM", "2021-04-26 12:00AM"]
in here we have to find sort the array dates close 10:00AM
If anybody knows the solution please help me out.
Not 100% sure what you need but something like this could be a start?
The strategy is to transform the data into things that can be easily compared to give the sort order that we want.
Working with dates and times is always tricky because of calendars and locale issues.
var givenTIme = "10:00AM"
var calendar = Calendar.current
let strDates = ["2021-04-30 10:00AM", "2021-04-16 10:00AM", "2021-04-26 12:00AM", "2021-04-28 09:00AM"]
let formatter = DateFormatter()
formatter.dateFormat = "y-M-d hh:mma"// hh:mma"
let dates = strDates.compactMap(formatter.date(from:))
struct CompareHelper {
let date: Date
let deltaT: Int
}
let hhmmformatter = DateFormatter()
hhmmformatter.dateFormat = "hh:mma"
let target = hhmmformatter.date(from: givenTIme)!
let dts = dates.map { date -> CompareHelper in
let minute = calendar.component(.minute, from: date)
let hour = calendar.component(.hour, from: date)
let targetMinute = calendar.component(.minute, from: target)
let targetHour = calendar.component(.hour, from: target)
let deltaT = (targetMinute + targetHour * 60) - (minute + hour * 60)
return CompareHelper(date: date, deltaT: deltaT)
}
let sorted = dts.sorted { (lhs: CompareHelper, rhs:CompareHelper) -> Bool in
if lhs.deltaT == rhs.deltaT {
return lhs.date < rhs.date
}
else {
return lhs.deltaT < rhs.deltaT
}
}

Get All Hours of The Day (in date format) from 9am tp 11pm and Save within an array in Swift

I need to get all the remaining hours of the day starting from whatever the current hour is until 11pm and save it in an array in a date format. Here is what I have written so far:
var sortedTime: [Date] = {
let today = Date()
var sortedTime: [Date] = []
(0..<14).forEach {
sortedTime.append(Calendar.current.date(byAdding: .hour, value: $0, to: today)!)
}
return sortedTime
}()
In the code I have: (0..<14).forEach { This is giving me the next 14 hours but thats not what I need; I need to make sure the hours I get are between 9am and 11pm. How would I be able to set this limit?
When you access .hour for any calender its in a 24h format. so you need to something like this:
let today = Date()
var sortedTime: [Date] = []
var calender = Calendar(identifier: .iso8601)
calender.locale = Locale(identifier: "en_US_POSIX")
let currentHour = calender.component(.hour, from: today)
(0...(24-currentHour)).forEach {
guard let newDate = calender.date(byAdding: .hour, value: $0, to: today) && calender.component(.hour, from: newDate) != 0,
calender.component(.hour, from: newDate) <= 23 else {
return
}
//convert date into desired format
sortedTime.append(newDate)
}

Create TimeZone object from timeZoneOffset string?

What would be a clean way to initialise a Swift TimeZone object from timeZoneOffset string of the form: "+HH:MM".
I am looking for something of the form:
extension TimeZone {
init?(UTCOffsetString ofs: String) {
let signIndex = ofs.firstIndex(of: "+") ?? ofs.firstIndex(of: "-")
let sign = ofs[signIndex!]
let separatorIndex = ofs.firstIndex(of: ":")!
let hhRange = ofs.index(signIndex!, offsetBy: 1)..<separatorIndex
let hh = ofs[hhRange]
let mmRange = ofs.index(separatorIndex, offsetBy: 1)..<ofs.index(separatorIndex, offsetBy: 3)
let mm = ofs[mmRange]
var offsetInMin = (Int(String(hh))! * 60) + Int(String(mm))!
if sign == "-" {
offsetInMin.negate()
}
let offsetInSec = offsetInMin * 60
// Convert string to TimeZone, eg.
self.init(secondsFromGMT: offsetInSec)
}
}
let tz = TimeZone.init(UTCOffsetString: "-07:30")
print(tz?.identifier ?? "unknown")
The above code block is a correct solution and prints:
GMT-0730
However I am looking for a cleaner solution where I don't need to extract substrings in order to compute the offset.
My suggestion is to use DateFormatter which is able to parse the time zone string format. refZoneString is the reference to UTC in the current time zone.
extension TimeZone {
init?(UTCOffsetString ofs: String) {
let refZoneString = "+0000"
let formatter = DateFormatter()
formatter.dateFormat = "Z"
guard let refDate = formatter.date(from: refZoneString),
let date = formatter.date(from: ofs) else { return nil }
self.init(secondsFromGMT: Calendar.current.dateComponents([.second], from: date, to: refDate).second!)
}
}
let tz = TimeZone.init(UTCOffsetString: "-07:30")
print(tz?.identifier ?? "unknown")
I don't know what you mean by a cleaner but you can combine collection methods suffix and prefix to avoid the need to use String index to access the desired values:
let time = "-02:00"
let hours = Int(time.suffix(5).prefix(2)) ?? 0
let minutes = Int(time.suffix(2)) ?? 0
var offset = hours * 3600 + minutes * 60
if time.first == "-" { offset = -offset }
print(offset) // -7200

Swift - Retrieve timezone from ISO8601 date string

I have dates saved in a database in this format - "yyyy-MM-dd'T'HH:mm:ssZ". For example, "2018-05-17T11:15:00+0330". The time zone varies, it is in whatever user's local time zone is.
I want to retrieve and display the date like "May 17, 2018 11.15AM", keeping the date/time in the same time zone as it was saved. How can I do that?
I tried this.
let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
//formatter.timeZone = ???
print(formatter.date(from: dateString))
But I get the Date object in local timezone and converting that to date string displays the date in my local timezone as well.
Is there anything like this, https://stackoverflow.com/a/46216251/1373592, in Swift?
Thanks.
What you are really trying to do, is to ignore the time zone.
You can just strip off the last 5 characters from the date string, parse it in one format, and format it in another format:
let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = formatter.date(from: String(dateString.dropLast(5)))! // note the stripping off here
formatter.dateFormat = "MMMM d, yyyy hh.mma"
print(formatter.string(from: date))
Note that you can't compare this date with other dates because you've stripped its time zone off.
Also, SwiftDate is a similar library to JodaTime.
Here's a little extension to TimeZone I just whipped up. You can use it to create a TimeZone from an ISO8601 date string. It will handle timezones in the string in the following formats:
Z (for UTC time)
+/-HH
+/-HHmm
+/-HH:mm
Here is the extension:
extension TimeZone {
init?(iso8601: String) {
let tz = iso8601.dropFirst(19) // remove yyyy-MM-ddTHH:mm:ss part
if tz == "Z" {
self.init(secondsFromGMT: 0)
} else if tz.count == 3 { // assume +/-HH
if let hour = Int(tz) {
self.init(secondsFromGMT: hour * 3600)
return
}
} else if tz.count == 5 { // assume +/-HHMM
if let hour = Int(tz.dropLast(2)), let min = Int(tz.dropFirst(3)) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
} else if tz.count == 6 { // assime +/-HH:MM
let parts = tz.components(separatedBy: ":")
if parts.count == 2 {
if let hour = Int(parts[0]), let min = Int(parts[1]) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
}
}
return nil
}
}
And a test:
print(TimeZone(iso8601: "2018-05-17T11:15:00+0330"))
You can combine this parsing and formatting so the final result is in the original timezone.
let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
formatter.locale = Locale(identifier: "en_US_POSIX")
if let date = formatter.date(from: dateString), let tz = TimeZone(iso8601: dateString) {
let newFmt = DateFormatter()
newFmt.dateStyle = .medium
newFmt.timeStyle = .short
newFmt.timeZone = tz
let newString = newFmt.string(from: date)
print(newString)
}
Quick fix to #rmaddy answer as his answer has a problem with negative offset specially minute component as #jjoelson mentioned
extension TimeZone {
init?(iso8601: String) {
let tz = iso8601.dropFirst(19) // remove yyyy-MM-ddTHH:mm:ss part
if tz == "Z" {
self.init(secondsFromGMT: 0)
} else if tz.count == 3 { // assume +/-HH
if let hour = Int(tz) {
self.init(secondsFromGMT: hour * 3600)
return
}
} else if tz.count == 5 { // assume +/-HHMM
if let hour = Int(tz.dropLast(2)), var min = Int(tz.dropFirst(3)) {
min = (hour < 0) ? -1 * min : min
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
} else if tz.count == 6 { // assime +/-HH:MM
let parts = tz.components(separatedBy: ":")
if parts.count == 2 {
if let hour = Int(parts[0]), var min = Int(parts[1]) {
min = (hour < 0) ? -1 * min : min
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
}
}
return nil
}
}

How can I apply custom sort to this array of objects?

I have a collection of objects where I want to sort the object by SortDate where SortDate is sorted by date newest to current date then from future date to past date. E.g. If my Events array contains objects with SortDate equal to June 4, June 8 and June 20. I want to sort it so that June 8 is shown first then June 20 then June 4. Where June 8 is closest to today's date June 6. How can I do that?
Here's my attempt:
self.eventsArray = Array(self.realm.objects(Event.self).filter("EventType == \"Event\"").sorted(byKeyPath: "SortDate", ascending: false))
let dateObjectsFiltered = self.eventsArray.filter ({ ($0.SortDate?.toDate)! > Date() })
self.eventsArray = dateObjectsFiltered.sorted { return $0.SortDate! < $1.SortDate! }
you can use this, assuming that all your date optionals are not nil.
func days(fromDate: Date, toDate: Date) -> Int {
return Calendar.current.dateComponents(Set<Calendar.Component>([.day]), from: fromDate, to: toDate).day ?? 0
}
let today = Date()
self.eventsArray.sort {
let first = days(fromDate: today, toDate: $0.SortDate!.toDate!)
let second = days(fromDate: today, toDate: $1.SortDate!.toDate!)
return (first >= 0 && second < 0) ? true : ((first < 0 && second >= 0) ? false : (first < second))
}
You can custom sort the array using Array's sort(by:) function.
Here is a sample:
import Foundation
struct event {
var SortDate: Date
}
//Create the array
var unsortedArray = [event]()
unsortedArray.append(event(SortDate: Date(timeIntervalSince1970: 1528070400)))
unsortedArray.append(event(SortDate: Date(timeIntervalSince1970: 1528416000)))
unsortedArray.append(event(SortDate: Date(timeIntervalSince1970: 1529452800)))
//Determine the closest date to the current date
let currentDate = Date(timeIntervalSinceNow: 0)
var lowestDiff = -1
var closestDate: Date?
var component:Set<Calendar.Component> = Set<Calendar.Component>()
component.insert(.second)
//Loop through the dates, keep track of the current closest date
for element in unsortedArray {
let dateComponents = Calendar.current.dateComponents(component, from: currentDate, to: element.SortDate)
if (lowestDiff == -1 || (abs(dateComponents.second!) < lowestDiff)) {
lowestDiff = abs(dateComponents.second!)
closestDate = element.SortDate
}
}
//Sort the array
unsortedArray = unsortedArray.sorted(by:
{
//If the closest date is in the comparison, return the closest date as greater.
if (closestDate != nil) {
if ($0.SortDate == closestDate) {
print($0.SortDate)
return true
}
else if ($1.SortDate == closestDate){
print($1.SortDate)
return false
}
}
//Otherwise, compare the dates normally
return $0.SortDate > $1.SortDate
}
)