How to work with date/times in Swift when users are in different time zones? - swift

I have a number of blog posts with a publish date set at UTC time. I want to indicate the age of the post (1 hour, 1 day, 1 month, etc) How do I detect the time zone of the user so I can properly convert UTC date/time?
Thanks!
Edit:
I'm using DateHelper Date Extension on GitHub:
https://github.com/melvitax/DateHelper
let sqlDate = post.age
var hoursSincePost = sqlDate.hoursAfterDate(Date())
hoursSincePost = abs(hoursSincePost)
var daysSincePost = sqlDate.daysAfterDate(Date())
daysSincePost = abs(daysSincePost)
if hoursSincePost < 24 {
age.text = String(hoursSincePost) + " hours ago"
} else if daysSincePost < 7 {
age.text = String(daysSincePost) + " days ago"
} else {
age.text = sqlDate.toString(.custom("MMM dd yyyy"))
}

You can use Calendar and DateFormatter to get the user's time zone or a specific time zone. If you don't know how to use them, here's Apple's guide

Related

Convert current time to local time in date format(Without converting it to string) in swift

I want to convert current time(in UTC) to my local time zone. But I don't want it in string I want it in date format itself.
I have written following code:
let currentDate = Date()
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
Now after that, mostly people have done
let dateString = dateFormatter.string(from: now)
Its okay, I am getting correct time in my local timezone but in string format. I want it in date format.
Again, if I do
let currentDateInLocal = dateFormatter.date(from: dateString) //converting string to date
I am getting the date in UTC format. How I can get the date in local timezone in date format?
You said "I want to convert current time(in UTC) to my local time zone. But I don't want it in string I want it in date format itself."
A Date object does not have a time zone. A Date records an instant in time, anywhere in the world. Imagine there is a bright flash in the sky all over the world at the same instant. What time did that happen? It depends on the time zone you are in. However, a Date object could capture that instant. You'd then convert that Date to a specific time zone if you wanted to describe the time of day the event occurred.
So your question doesn't really make sense.
I suggest using the DateFormatter class method localizedString(from:dateStyle:timeStyle:) to display a date in your local time zone:
e.g.
print(DateFormatter.localizedString(
from: Date(),
dateStyle: .medium,
timeStyle: .medium))
That lets you view a Date object in your local time zone without needing to create a DateFormatter.
I know you said you don't want it in a string format, however you can simply convert it over after to a date object.
you're welcome to use this, i created this function based on my own string format, change that to whatever you need and boom. enjoy
//this function will allow you to easily convert your date string into readable current context
func convertDateToNow(createdAt:String) -> String{
//create a date to represent the seconds since the start of the internet
let currentDate = Date().timeIntervalSince1970
//create a dateformatter
let dateFormatter = DateFormatter()
//use the locale on the device to set the the related time
dateFormatter.locale = Locale.current
//set the formate of which our date will be coming in at
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
//create a created at date for the post based off its createdAt date and get the seconds since the start of the internet
let createdDate = dateFormatter.date(from: "\(createdAt)")?.timeIntervalSince1970
//now we are left with two values depending on age of post either a few thousands off or it could be years in seconds. so we minus the current date on the users phone seconds from the created date seconds and voilla
let secondsDifference = currentDate - createdDate!
//use our specially created function to convert the seconds difference into time values
let (d,h,m,s) = secondsToHoursMinutesSeconds(seconds: Int(secondsDifference))
//test the values
// print("d:",d)
// print("h:",h)
// print("m:",m)
// print("s:",s)
//
//this will hold the final output of the string
var finalDateLabel:String!
//set the datelabel on the cell accordingly
//create arithmetic for the date features
if d != 0 {
//check to see if the label is a day old
if d == 1 {
finalDateLabel = "\(d) day ago"
}
}else if h != 0{
//set the date label
finalDateLabel = "\(h) hour ago"
if h == 1 {
finalDateLabel = "\(h) hour ago"
}else if h >= 2 {
finalDateLabel = "\(h) hours ago"
}
}else if m != 0{
//set the date label
finalDateLabel = "\(m) minutes ago"
if m == 1 {
finalDateLabel = "\(m) minute ago"
}
}else if s != 0{
//set the date label
finalDateLabel = "\(s) seconds ago"
if s == 1 {
finalDateLabel = "\(s) second ago"
}
}
return finalDateLabel
}
//to help convert the story
func secondsToHoursMinutesSeconds (seconds : Int) -> (Int ,Int, Int, Int) {
return (seconds / 86400, seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)
}

Time and Date Calculations

Using date format "EEEE:dd:MMM:HH:mm" returns the correct date and time, however, the day of the week is incorrect. For example a 750 nautical mile voyage conducted at 7.5 knots will take 100 hours. If I use my code to calculate the arrival time using a start of, say, Friday 1 Nov at 12:00 it returns Sunday 5 Nov at 16:00. Time and date are correct but day of the week is not. Should be Tuesday.
#IBAction func Calculate(_ sender: UIButton) {
let userCalendar = Calendar.current
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
let distance = (Distance.text! as NSString).floatValue
let speed = (GndSpd.text! as NSString).floatValue
let calcDT = ((distance / speed) * 3600)
if var date = dateMakerFormatter.date(from: (DTG.text!)) {
date = dateMakerFormatter.date(from: (DTG.text!))!
var timeAdj = DateComponents()
timeAdj.second = Int(calcDT)
if var adj = userCalendar.date(byAdding: timeAdj, to: date) {
adj = userCalendar.date(byAdding: timeAdj, to: date)!
CalcDTG.text = dateMakerFormatter.string(from: adj)
}
}
}
You should use d for Day, not D
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
DateFormatter
You can't say the day of the week is incorrect when you're not giving a year.
The date formatter seems to ignore the day of the week when creating a date:
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
let date = dateMakerFormatter.date(from: "Friday:01:Nov:12:00")!
print(date) -> 2000-11-01 12:00:00 +0000
print(dateMakerFormatter.string(from: date)) -> Wednesday:01:Nov:12:00
Hey presto, you're now in the year 2000, where 5 November did fall on a Sunday.
The important takeaway you need is that you should never, ever, ever, use strings to pass around date values in your code. Use Date. If you're getting a date from an API response, change it to a date on ingestion. If you're getting one from user entry, use a date picker or other control. If you're getting one from a string the user is typing in, I'd politely suggest you're making unnecessary work for yourself, but do make sure you fill in all the details the user doesn't give you.

Test whether current time of day is between two TimeIntervals

I have 2 TimeIntervals, which just represent date-agnostic times of day (e.g. 8:00 AM and 5:00 PM). So 0 represents exactly midnight, in this case, and 29,040 represents 8:04 AM. I want to check if the phone's time of day is between the two TimeIntervals.
I found a few similar Stack Overflow questions, but none of them really dealt with TimeIntervals. It seems like just using start <= Date().timeIntervalSinceReferenceDate <= end or something wouldn't work, because it would return a huge value.
What's the best way to handle this type of situation in Swift 3?
Edit: To clarify, I don't need to worry about things like daylight savings. As an example, assume that the user only wants certain things in the app to happen between the hours of X and Y, where X and Y are given to me as TimeInterval values since midnight. So it should be sufficient to check if the phone's TimeInterval since midnight on a typical day is between X and Y before completing the action.
Date().timeIntervalSinceReferenceDate returns the number of seconds since Jan 1, 2000 so no doubt it's a huge number.
It's inadvisable to store time as seconds since midnight due to this naggy little thing called Daylight Saving Time. Every year, different countries do it on different days and on different hours. For example, even though Britain and France change their clock on the same day (March 26, 2017), one makes the shift from 1AM to 2AM, the other goes from 2AM to 3AM. That's very easy to make for a mess!
Use DateComponents instead:
let calendar = Calendar.current
let startTimeComponent = DateComponents(calendar: calendar, hour: 8)
let endTimeComponent = DateComponents(calendar: calendar, hour: 17, minute: 30)
let now = Date()
let startOfToday = calendar.startOfDay(for: now)
let startTime = calendar.date(byAdding: startTimeComponent, to: startOfToday)!
let endTime = calendar.date(byAdding: endTimeComponent, to: startOfToday)!
if startTime <= now && now <= endTime {
print("between 8 AM and 5:30 PM")
} else {
print("not between 8 AM and 5:30 PM")
}
I ended up using DateComponents to calculate a TimeInterval.
let components = Calendar.current.dateComponents(
[.hour, .minute, .second], from: Date())
guard let seconds = components.second,
let minutes = components.minute,
let hours = components.hour else
{
return false
}
let currentTime = Double(seconds + minutes * 60 + hours * 60 * 60)
return startTime <= currentTime && currentTime <= endTime

presenting Unix Timestamps with Swift

Using a current timestamp and a given timestamp, what is the best way to present them on a page? If the given timestamp represents "yesterday" the presentation should say yesterday. if the given timestamp was minutes or hours ago, it should only include how many. etc. is it best to subtract one from the other and evaluate what the difference means or is there a built-in library or framework that can be used? so far I am working with the following playground:
let ts = 1499299978.149724
let current_ts: Double
let date = NSDate(timeIntervalSince1970: ts)
let dayTimePeriodFormatter = DateFormatter()
let totalFormat = "MMM dd YYYY hh:mm a" //just for reference
dayTimePeriodFormatter.dateFormat = "hh:mm a" //add to this as desired
let dateString = dayTimePeriodFormatter.string(from: date as Date)
this should get you started:
let midnight = startOfToday.timeIntervalSince1970
let secondsInADay = 86400.0
if ts > midnight {
print ("today")
code
} else if ts > midnight - secondsInADay {
print ("yesterday")
code
} else if ts > midnight - secondsInADay - secondsInADay {
print ("2 days ago")
code
} else if ts > midnight - secondsInADay * 3 {
print ("3 days ago")
code
}
inside the "today" block you can use a similar process to check for seconds ago, minutes ago, else then subtract the ts from the current ts then divide by 60 accordingly to get the number you need

How to get a list of days or a number of days in a month with GWT?

What is counter part of this code in GWT ?
public int returnAllDaysOf(2012,6){
Calendar calendar = Calendar.getInstance();
calendar.set(2012, Calendar.FEBRUARY, 1);
int daysOfFeb = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
return daysOfFeb;
}
Thanks in advance for your help.
I want to get the number of days of a month in the client side. I searched Google and StackOverFlow but didn't get anything.
for example Feb has 29 days, Match has 31 days and so on ...
I don't know a direct way, but you can calculate this value by adding one month to your date, and then calcualting the difference in days:
final Date myDate = ...;
final Date copyOfDate = CalendarUtil.copyDate(myDate);
CalendarUtil.addMonthsToDate(copyOfDate, 1);
final int daysBetween = CalendarUtil.getDaysBetween(myDate, copyOfDate);
Note: This even works if myDate is something like 2012-01-31. copyOfDate is then 2012-03-02 (because february doesn't have 31 days), and the result is correct again.
"Cheating" way to do it:
int daysInCurrentMonth = new Date(year-1900, month+1, 0).getDate();
I.E.
int daysInJanuary2014 = new Date(114, 1, 0).getDate();
basically set the Date object to the 0th day of the NEXT month, then get the day of the month.
NOTE: Date(int year, int month, int date) expects year=calendarYear-1900 (i.e. 2014=114) and month is 0-based (i.e. January would be month 0)
and yes, I know this constructor is deprecated, but I still use it.
DateField dfMois = new DateField();
Calendar calendar = Calendar.getInstance();
calendar.setTime(dfMois.getValue());
Date date = dfMois.getValue();
Date dateCopy = dateFin;
dateCopy.setDate(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
if(date.getMonth() == Calendar.FEBRUARY + 1){
date.setDate(31 - dateCopy.getDate());
date.setMonth(date.getMonth()-1);
}
else{
date.setDate(dateCopy.getDate());
}
dfMois.setValue(date);
In your code... it work.