I can get the offset seconds from GMT with this: TimeZone.current.secondsFromGMT().
However, how do I get the format as ±hh:mm?
Some integer arithmetic to obtain the offset in hours and
minutes:
let seconds = TimeZone.current.secondsFromGMT()
let hours = seconds/3600
let minutes = abs(seconds/60) % 60
Formatted printing:
let tz = String(format: "%+.2d:%.2d", hours, minutes)
print(tz) // "+01:00"
%.2d prints an integer with (at least) two decimal digits (and leading
zero if necessary). %+.2d is the same but with a leading + sign for
non-negative numbers.
Here is extension for getting timezone offset Difference and as ±hh:mm (Swift 4 | Swift 5 Code)
extension TimeZone {
func offsetFromUTC() -> String
{
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.timeZone = self
localTimeZoneFormatter.dateFormat = "Z"
return localTimeZoneFormatter.string(from: Date())
}
func offsetInHours() -> String
{
let hours = secondsFromGMT()/3600
let minutes = abs(secondsFromGMT()/60) % 60
let tz_hr = String(format: "%+.2d:%.2d", hours, minutes) // "+hh:mm"
return tz_hr
}
}
Use like this
print(TimeZone.current.offsetFromUTC()) // output is +0530
print(TimeZone.current.offsetInHours()) // output is "+05:30"
If you can use Date()
func getCurrentTimezone() -> String {
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.dateFormat = "ZZZZZ"
return localTimeZoneFormatter.string(from: Date())
}
Will return "+01:00" format
extension TimeZone {
func offsetFromUTC() -> String
{
let localTimeZoneFormatter = DateFormatter()
localTimeZoneFormatter.timeZone = self
localTimeZoneFormatter.dateFormat = "Z"
return localTimeZoneFormatter.string(from: Date())
}
func currentTimezoneOffset() -> String {
let timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = "ZZZZZ"
return timeZoneFormatter.string(from: Date())
}
}
Use like this
print(TimeZone.current.offsetFromUTC()) // output is +0530
print(TimeZone.current.currentTimezoneOffset()) // output is "+05:30"
it working 100% in all countries according to timezone.
Swift 4 and above
extension TimeZone {
func timeZoneOffsetInHours() -> Int {
let seconds = secondsFromGMT()
let hours = seconds/3600
return hours
}
func timeZoneOffsetInMinutes() -> Int {
let seconds = secondsFromGMT()
let minutes = abs(seconds / 60)
return minutes
}
}
The accepted answer does not handle the case "-00:30" correctly since the "+/-" is only being determined from the hours, and not the minutes. I would set the sign based on a check of the initial seconds value. Alternatively you could use DateComponentsFormatter.
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
let interval: TimeInterval = TimeInterval.init(abs(secondsOffset))
let offsetValue: String = formatter.string(from: interval)
Related
On my project I need to work with hours and min.
I can't find to much info online.
here my issue:
let time1 = "22:00"
let time2 = "20:00"
Question 1:
how do I subtract the time1 - time2 ?
I'm expecting result = 02:00 h
i start writing some code, converting this time to date..
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
var time1d = formatter.date(from: time1)!
let time2d = formatter.date(from: time2)!
but now how do I subtract this two hours?
Question 2:
let time1 = "22:00"
let time2 = "20:00"
How do i sum time1 + time2 ? for example should give me result 42:00 hours
thanks for the help
This is not a Date. If you are only gonna work with hours and minutes and the input will always be a string properly formatted you should struct your data.
Create a Time structure
struct Time {
let hour: Int
let minute: Int
}
And a custom initializer. This assumes your string is always properly formatted 00:00:
extension Time {
init?(string: String) {
guard string.count == 5,
Array(string)[2] == ":",
let hour = Int(string.prefix(2)),
let minute = Int(string.suffix(2)),
0...59 ~= minute else {
return nil
}
self.hour = hour
self.minute = minute
}
}
For displaying your Time struct property you can conform it to CustomStringConvertible and provide a custom description
extension Time: CustomStringConvertible {
var description: String {
String(format: "%02d:%02d", hour, minute)
}
}
Regarding adding and subtracting you can make your Time struct conform to AdditiveArithmetic and implement the required operators:
extension Time: AdditiveArithmetic {
static func - (lhs: Time, rhs: Time) -> Time {
let minutes = lhs.minute - rhs.minute + lhs.hour * 60 - rhs.hour * 60
return .init(hour: minutes/60, minute: minutes%60)
}
static func + (lhs: Time, rhs: Time) -> Time {
let minutes = lhs.minute + rhs.minute + lhs.hour * 60 + rhs.hour * 60
return .init(hour: minutes/60, minute: minutes%60)
}
static var zero: Time { .init(hour: 0, minute: 0) }
}
Playground testing:
let time1 = Time(string: "22:00")!
let time2 = Time(string: "20:00")!
let time3 = time1-time2
print(time3)
let time4 = time1+time2
print(time4)
Those will print
02:00
42:00
First we create the calculateDifference function which uses dateComponents function provided by Swift.
let time1 = "22:00"
let time2 = "20:00"
func formattedTime(_ time: String) -> Date? {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "HH:mm"
return formatter.date(from: time)
}
func calculateDifference(_ from: String, _ to: String) -> (hour: Int, minutes: Int) {
guard let fromTime = formattedTime(from),
let toTime = formattedTime(to) else {
return (0,0)
}
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute], from: fromTime, to: toTime)
return (components.hour ?? 0, components.minute ?? 0)
}
Then we create the calculateSum which uses the calculateDifference internally to get the hours by supplying the default hour as 00:00
func calculateSum(_ time1: String, _ time2: String) -> (hour: Int, minutes: Int) {
let defaultTime = "00:00"
let calculatedTime1 = calculateDifference(defaultTime, time1)
let calcaultedTime2 = calculateDifference(defaultTime, time2)
return(calculatedTime1.hour + calcaultedTime2.hour,
calculatedTime1.minutes + calcaultedTime2.minutes)
}
Now if we run the below, we get the required results. Of course, some formatting will be required.
let difference = calculateDifference(time2, time1)
print("\(difference.hour):\(difference.minutes)")
let sum = calculateSum(time1, time2)
print("\(sum.hour):\(sum.minutes)")
I am working on Date and Time where I keep a log of logged in and logged out. For e.g. Logged in time is 12:45 and Logged Out time is 1:45 so need to check the total hour's user was online. I have both time and below is the code where I am trying to check max hour's by adding but I am not getting the output I need.
//Current time
let formatter = DateFormatter()
formatter.timeStyle = .medium
formatter.dateStyle = .none
outTimeString = formatter.string(from: currentDateTime) as NSString
//LoggedIn Time
inTimeString = "12:49:50"
let inFormatter = DateFormatter()
inFormatter.dateFormat = "HH:mm:ss"
let outFormatter = DateFormatter()
outFormatter.dateFormat = "hh:mm"
let inTime = inTimeString
let date = inFormatter.date(from: inTime)!
let inStr = outFormatter.string(from: date)
print(inStr)->12:49:50
let outTime = outTimeString
let outDate = inFormatter.date(from: outTime as String)!
let outStr = outFormatter.string(from: outDate)
print(outStr)->16:10:15
//Adding 4 hour to logged in timw
let calendar = NSCalendar.current
let finaldate = date.addingTimeInterval(-4 * 60.0 * 60.0)
print(finaldate)->2000-01-01 03:19:50 +0000
Problem is I am getting the output wrong when 4 hours are added to the logged in time.
I need the total time logged in or add 4 hours to the original logged in time. Any one will do.
Help is much appreciated.
Calculating total time logged in.
var totalTimeLoggedIn: TimeInterval = 3600 // 1 hour
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm:ss"
guard
let loginTimeDate = dateFormatter.date(from: "26-04-2018 12:00:00"),
let logoutTimeDate = dateFormatter.date(from: "26-04-2018 13:00:00")
else {
return
}
let timeLoggedIn = logoutTimeDate.timeIntervalSince(loginTimeDate)
totalTimeLoggedIn += timeLoggedIn
print("totalTimeLoggedIn: \(totalTimeLoggedIn.stringValue())") // totalTimeLoggedIn: 02:00:00
Extension used for printing total login time in hours:minutes:seconds.
extension TimeInterval {
/// Returns the time interval into a string value in hours:minutes:seconds (e.g. 09:15:34)
func stringValue(removeHoursIfEmpty:Bool = true) -> String {
if(self.isNaN){
return "00:00"
}
var value = self
if value < 0 {
value = 0
}
let hours:Int = Int(value / 3600)
let minutes:Int = Int((value.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds:Int = Int(value.truncatingRemainder(dividingBy: 60))
if (hours > 0 || removeHoursIfEmpty == false){
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
} else {
return String(format: "%02d:%02d", minutes, seconds)
}
}
}
Try the below code, hopefully it will help you.
func findIntervalBtwToDate(startingDate: String, endingDate : String) -> TimeInterval {
var interval = 0.0
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(name: "UTC")! as TimeZone
guard let endDate = dateFormatter.date(from: endingDate), let startDate = dateFormatter.date(from: startingDate) else { return interval }
interval = endDate.timeIntervalSince(startDate)
return interval
}
I am trying to convert the timestamp from server and it is converting also but only month is coming wrong.Like timestamp is 1492747393892 and it convert into 21/03/17 - 12:03PM but it should be 21/04/17 - 12:03PM.
Here is my code
var arriveTimestamp: Int
if let timeStampToDate = arriveTimestamp {
let timeSt = Date(jsonTimeDate:"/Date(\(timeStampToDate))/")
let time = Date().dateTime(date: timeSt!)
valueLbl.text = time
}
init?(jsonTimeDate: String) {
// "/Date(1487058855745)/"
let prefix = "/Date("
let suffix = ")/"
let scanner = Scanner(string: jsonTimeDate)
// Check prefix:
guard scanner.scanString(prefix, into: nil) else { return nil }
// Read milliseconds part:
var milliseconds : Int64 = 0
guard scanner.scanInt64(&milliseconds) else { return nil }
// Milliseconds to seconds:
var timeStamp = TimeInterval(milliseconds)/1000.0
// Read optional timezone part:
var timeZoneOffset : Int = 0
if scanner.scanInt(&timeZoneOffset) {
let hours = timeZoneOffset / 100
let minutes = timeZoneOffset % 100
// Adjust timestamp according to timezone:
timeStamp += TimeInterval(3600 * hours + 60 * minutes)
}
// Check suffix:
guard scanner.scanString(suffix, into: nil) else { return nil }
// Success! Create NSDate and return.
self.init(timeIntervalSince1970: timeStamp)
}
func dateTime(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/mm/yy - hh:mm a"
return dateFormatter.string(from: date as Date)
}
The main error in your code is the wrong date format for the month,
which should be "MM", not "mm" (which is for the minutes).
Apart from that, your approach is far too complicated. All you have to
do is to divide the timestamp (which is in milliseconds) by 1000
and call Date(timeIntervalSince1970:):
let arriveTimestamp = 1492747393892
let date = Date(timeIntervalSince1970: TimeInterval(arriveTimestamp)/1000)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yy - hh:mm a"
let text = dateFormatter.string(from: date)
print(text) // 21/04/17 - 06:03 AM
iOS Date() returns date with at least microsecond precision.
I checked this statement by calling Date().timeIntervalSince1970 which results in 1490891661.074981
Then I need to convert date into string with microsecond precision.
I am using DateFormatter in following way:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZZZZ"
print(formatter.string(from: date))
which results in
"2017-03-30T16:34:21.075000Z"
Now if we compare two results:
1490891661.074981 and "2017-03-30T16:34:21.075000Z"
we can notice that DateFormatter rounds date to millisecond precision while still presenting zeros for microseconds.
Does anybody know how to configure DateFormatter so I can keep microseconds and get correct result: "2017-03-30T16:34:21.074981Z"?
Thanks to #MartinR for solving first half of my problem and to #ForestKunecke for giving me tips how to solve second half of the problem.
Based on their help I created ready to use solution which converts date from string and vice versa with microsecond precision:
public final class MicrosecondPrecisionDateFormatter: DateFormatter {
private let microsecondsPrefix = "."
override public init() {
super.init()
locale = Locale(identifier: "en_US_POSIX")
timeZone = TimeZone(secondsFromGMT: 0)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func string(from date: Date) -> String {
dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let components = calendar.dateComponents(Set([Calendar.Component.nanosecond]), from: date)
let nanosecondsInMicrosecond = Double(1000)
let microseconds = lrint(Double(components.nanosecond!) / nanosecondsInMicrosecond)
// Subtract nanoseconds from date to ensure string(from: Date) doesn't attempt faulty rounding.
let updatedDate = calendar.date(byAdding: .nanosecond, value: -(components.nanosecond!), to: date)!
let dateTimeString = super.string(from: updatedDate)
let string = String(format: "%#.%06ldZ",
dateTimeString,
microseconds)
return string
}
override public func date(from string: String) -> Date? {
dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
guard let microsecondsPrefixRange = string.range(of: microsecondsPrefix) else { return nil }
let microsecondsWithTimeZoneString = String(string.suffix(from: microsecondsPrefixRange.upperBound))
let nonDigitsCharacterSet = CharacterSet.decimalDigits.inverted
guard let timeZoneRangePrefixRange = microsecondsWithTimeZoneString.rangeOfCharacter(from: nonDigitsCharacterSet) else { return nil }
let microsecondsString = String(microsecondsWithTimeZoneString.prefix(upTo: timeZoneRangePrefixRange.lowerBound))
guard let microsecondsCount = Double(microsecondsString) else { return nil }
let dateStringExludingMicroseconds = string
.replacingOccurrences(of: microsecondsString, with: "")
.replacingOccurrences(of: microsecondsPrefix, with: "")
guard let date = super.date(from: dateStringExludingMicroseconds) else { return nil }
let microsecondsInSecond = Double(1000000)
let dateWithMicroseconds = date + microsecondsCount / microsecondsInSecond
return dateWithMicroseconds
}
}
Usage:
let formatter = MicrosecondPrecisionDateFormatter()
let date = Date(timeIntervalSince1970: 1490891661.074981)
let formattedString = formatter.string(from: date) // 2017-03-30T16:34:21.074981Z
The resolution of (NS)DateFormatter is limited to milliseconds, compare
NSDateFormatter milliseconds bug. A possible solution is to retrieve all date components (up to
nanoseconds) as numbers and do a custom string formatting. The date formatter can still be used for the timezone string.
Example:
let date = Date(timeIntervalSince1970: 1490891661.074981)
let formatter = DateFormatter()
formatter.dateFormat = "ZZZZZ"
let tzString = formatter.string(from: date)
let cal = Calendar.current
let comps = cal.dateComponents([.year, .month, .day, .hour, .minute, .second, .nanosecond],
from: date)
let microSeconds = lrint(Double(comps.nanosecond!)/1000) // Divide by 1000 and round
let formatted = String(format: "%04ld-%02ld-%02ldT%02ld:%02ld:%02ld.%06ld",
comps.year!, comps.month!, comps.day!,
comps.hour!, comps.minute!, comps.second!,
microSeconds) + tzString
print(formatted) // 2017-03-30T18:34:21.074981+02:00
Solution by #Vlad Papko has some issue:
For dates like following:
2019-02-01T00:01:54.3684Z
it can make string with extra zero:
2019-02-01T00:01:54.03684Z
Here is fixed solution, it's ugly, but works without issues:
override public func string(from date: Date) -> String {
dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let components = calendar.dateComponents(Set([Calendar.Component.nanosecond]), from: date)
let nanosecondsInMicrosecond = Double(1000)
let microseconds = lrint(Double(components.nanosecond!) / nanosecondsInMicrosecond)
// Subtract nanoseconds from date to ensure string(from: Date) doesn't attempt faulty rounding.
let updatedDate = calendar.date(byAdding: .nanosecond, value: -(components.nanosecond!), to: date)!
let dateTimeString = super.string(from: updatedDate)
let stingWithMicroseconds = "\(date.timeIntervalSinceReferenceDate)"
let dotIndex = stingWithMicroseconds.lastIndex(of: ".")!
let hasZero = stingWithMicroseconds[stingWithMicroseconds.index(after: dotIndex)] == "0"
let format = hasZero ? "%#.%06ldZ" : "%#.%6ldZ"
let string = String(format: format,
dateTimeString,
microseconds)
return string
}
It is a bit of a hack, but not that complex and 100% Swift:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.'MICROS'xx"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
// Get the number of microseconds with a precision of 6 digits
let now = Date()
let dateParts = Calendar.current.dateComponents([.nanosecond], from: now)
let microSeconds = Int((Double(dateParts.nanosecond!) / 1000).rounded(.toNearestOrEven))
let microSecPart = String(microSeconds).padding(toLength: 6, withPad: "0", startingAt: 0)
// Format the date and add in the microseconds
var timestamp = dateFormatter.string(from: now)
timestamp = timestamp.replacingOccurrences(of: "MICROS", with: microSecPart)
I have a String in following format
"/Date(573465600000-0800)/"
How do I convert this to regular NSDate object?
The first part "573465600000" is the time since the Unix epoch
in milliseconds, and the second part "-0800" is a time zone
specification.
Here is a slight modification of Parsing JSON (date) to Swift
which also covers the time zone part:
extension NSDate {
convenience init?(jsonDate: String) {
let prefix = "/Date("
let suffix = ")/"
let scanner = NSScanner(string: jsonDate)
// Check prefix:
if scanner.scanString(prefix, intoString: nil) {
// Read milliseconds part:
var milliseconds : Int64 = 0
if scanner.scanLongLong(&milliseconds) {
// Milliseconds to seconds:
var timeStamp = NSTimeInterval(milliseconds)/1000.0
// Read optional timezone part:
var timeZoneOffset : Int = 0
if scanner.scanInteger(&timeZoneOffset) {
let hours = timeZoneOffset / 100
let minutes = timeZoneOffset % 100
// Adjust timestamp according to timezone:
timeStamp += NSTimeInterval(3600 * hours + 60 * minutes)
}
// Check suffix:
if scanner.scanString(suffix, intoString: nil) {
// Success! Create NSDate and return.
self.init(timeIntervalSince1970: timeStamp)
return
}
}
}
// Wrong format, return nil. (The compiler requires us to
// do an initialization first.)
self.init(timeIntervalSince1970: 0)
return nil
}
}
Example:
if let theDate = NSDate(jsonDate: "/Date(573465600000-0800)/") {
println(theDate)
} else {
println("wrong format")
}
Output:
1988-03-04 00:00:00 +0000
Update for Swift 3 (Xcode 8):
extension Date {
init?(jsonDate: String) {
let prefix = "/Date("
let suffix = ")/"
let scanner = Scanner(string: jsonDate)
// Check prefix:
guard scanner.scanString(prefix, into: nil) else { return nil }
// Read milliseconds part:
var milliseconds : Int64 = 0
guard scanner.scanInt64(&milliseconds) else { return nil }
// Milliseconds to seconds:
var timeStamp = TimeInterval(milliseconds)/1000.0
// Read optional timezone part:
var timeZoneOffset : Int = 0
if scanner.scanInt(&timeZoneOffset) {
let hours = timeZoneOffset / 100
let minutes = timeZoneOffset % 100
// Adjust timestamp according to timezone:
timeStamp += TimeInterval(3600 * hours + 60 * minutes)
}
// Check suffix:
guard scanner.scanString(suffix, into: nil) else { return nil }
// Success! Create NSDate and return.
self.init(timeIntervalSince1970: timeStamp)
}
}
Example:
if let theDate = Date(jsonDate: "/Date(573465600000-0800)/") {
print(theDate)
} else {
print("wrong format")
}
var date:NSDate = NSDate(timeIntervalSince1970: timeInterval)
let myTimeStamp = "1463689800000.0"
let dateTimeStamp = NSDate(timeIntervalSince1970:Double(myTimeStamp)!/1000) //UTC time
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone.localTimeZone() //Edit
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.dateStyle = NSDateFormatterStyle.FullStyle
dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let strDateSelect = dateFormatter.stringFromDate(dateTimeStamp)
print(strDateSelect) //Local time
let dateFormatter2 = NSDateFormatter()
dateFormatter2.timeZone = NSTimeZone.localTimeZone()
dateFormatter2.dateFormat = "yyyy-MM-dd"
let date3 = dateFormatter.dateFromString(strDateSelect)
datepicker.date = date3!
Swift 4
let date = Date(timeIntervalSince1970: TimeInterval(1463689800000.0))