I generate a NSDate object from string.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let stringToDate = dateFormatter.dateFromString(dateFromService) // 2015-07-20 12:00:43 +0000
I get this string value from webserver. I need to modify for personal device timezone. Want to add hours this stringToDate object but not work
var addHours : Int = 2 // 2 hours will be added
var newDate = stringToDate.dateByAddingTimeInterval(addHours)
Use NSCalendarComponents:
let calendar = NSCalendar.currentCalendar()
let newDate = calendar.dateByAddingUnit(
.CalendarUnitHour, // adding hours
value: 2, // adding two hours
toDate: oldDate,
options: .allZeros
)
Using NSCalendar will account for things like leap seconds, leap hours, etc.
But as Duncan C's answer points out, simply adding hours is definitely the wrong approach. Two time zones won't always be separated by the same amount of time. Again, this is something especially true when we take daylight savings into account. (For example, the United States doesn't start/end daylight savings on the same days as Europe, and Arizona doesn't even do daylight savings).
You're asking the wrong question. This is what's known as an "XY Problem".
You should be asking "How do I display a date string I get from a web server in the user's local time zone."
NSDate represents a date/time in an abstract form that does not contain a time zone. You convert it to a specific time zone for display. Do not try to add/subtract hours to an NSDate to offset for time zones. That is the wrong approach.
The correct answer is simple. Create a second date formatter and don't set it's timezone to GMT. It defaults to the user's local time zone.
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let date = dateFormatter.dateFromString(dateFromService)
let outputDatedateFormatter = NSDateFormatter()
outputDatedateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
//leave the time zone at the default (user's time zone)
let displayString = outputDateFormatter.stringFromDate(date)
println("Date in local time zone = \(displayString)")
For Swift 3 you can use this function:
//get next date by adding hours func
getNewDateAfterAddingHours(hoursToAdd:NSInteger, oldDate:Date) -> Int64 {
let calendar = Calendar.current
let newDate = calendar.date(byAdding: .hour, value: hoursToAdd, to: oldDate)
return Int64((newDate?.timeIntervalSince1970)!)
}
If you are doing it more often, check out library called SwiftMoment (inspired by the same .js library), which allows you to do following (and much more!):
// Create date using moment library
let myDate = moment(myString)
// Add one hour
let dateWithAddedHour = myDate + 1.hours
Moment is a wrapper around NSDate instance, while Duration (which is what you get from Int.hours, Int.minutes etc.) wraps an NSTimeInterval value.
Implementing this should take you just a moment! (Pun intended).
Related
So I have a time that's in string format HH:mm:ss (08:30:00 for example). I want to be able to convert it to 8:30 AM.
Normally it would be easy if I had the year,month,day to go along so it would be simple to do the conversions but in this case I don't need it.
My original plan was to get current Date() then assign the time to it then convert it to the new format of H:mm a but I haven't been able to do this successfully. Meaning I have only been able to convert it to a Date object that has year 1/1/2000 and the time is set in UTC.
Am I overthinking this? Is there an easier way without doing any conversions? I just want to be able to display the time in H:mm a format without altering any timezones.
You need to create a Date with the specified time value. For this, I'd simple create a new instance of Date and then use Calendar to set the time to the desired value
var date = Date()
let cal = Calendar.current
// Please note, this returns an optional, you will need to deal
// with, for demonstration purposes, I've forced unwrapped it
date = cal.date(bySettingHour: 8, minute: 30, second: 0, of: date)!
From there, you make use of a DateFormatter to format the result to a String
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
formatter.string(from: date)
Now, if you're starting with a String value (of HH:mm:ss), you can actually use a DateFormatter to parse it, for example...
let value = "08:30:00"
let parser = DateFormatter()
parser.dateFormat = "HH:mm:ss"
// Again, this is forced unwrapped for demonstration purposes
let parsedDate = parser.date(from: value)!
I have a scenario where dates are being on a server without the day aspect and just the time is needed. Therefore the day element is being stripped away before uploading to the server. Development up to this point (Point 1 in code below) cannot be changed as it is coming from an external source that is already in production.
We are then facing an issue when outputting this time back to the user. The time varies by minutes in different locations, even in the same timezone. Please check the code with comments below.
//1.
//Taking a date with just hours and minutes as I don't need the date.
//The timezone set (Europe/Rome) is different from the machine (Europe/Malta)
//Issue below still occurs if we use the same timezone as the machine.
let date = Date(timeIntervalSinceReferenceDate: 1570168800) //Result: Oct 4, 2050 at 8:00 AM
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "Europe/Rome")!
let comps = calendar.dateComponents([.hour, .minute], from: date)
let strippedDate = calendar.date(from: comps)! //Result: Jan 1, 1 at 8:08 AM
//the date outputted is bypassing the timezone setting and using the machine timezone when stripping out the day
//and keeping hours and minutes alone
//2.
//Outputing the date back to the user in different timezone identifiers
//These timezone identiers are all in the same timezone
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short;
dateFormatter.timeZone = TimeZone(identifier: "Europe/Paris")!
var formattedStart = dateFormatter.string(from: strippedDate) //Result: "7:19 AM"
dateFormatter.timeZone = TimeZone(identifier: "Europe/Rome")!
formattedStart = dateFormatter.string(from: strippedDate) //Result: "8:00 AM"
//This above appears correctly as it's coming from the same timezone that set up the starting date
dateFormatter.timeZone = TimeZone(identifier: "Europe/Malta")!
formattedStart = dateFormatter.string(from: strippedDate) //Result: "8:08 AM"
//The above appears the same as the stripped date which is the same as the machine used to create the stripped date
dateFormatter.timeZone = TimeZone(identifier: "Europe/Madrid")!
formattedStart = dateFormatter.string(from: strippedDate) //Result: "6:55 AM"
Referencing this answer, this issue is happening "Because at noon on November 18, 1883, the US and Canada railway companies began using a new, standard time system". Therefore since the date is being saved before this event, this might be the reason why we're getting varied minutes. However I still cannot find a solution to output the correct time.
In swift, the current time Date() is retrieved from the system time of iPhone. Using DateFormatter() seems to work the same way with a given string. For example, using the following method, we can get the same result as calling Date() right when the iPhone time reaches 2018/04/01 02:00:00
let MY_DATE_STRING = "2018/04/01 02:00:00"
var resultTime: Date {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
formatter.timeZone = TimeZone.current
return formatter.date(from: MY_DATE_STRING)!
}
My question is, let's say, if 2018/04/01 2:00:00 is the end of daylight saving time, so the iPhone will have two 2am on that day -- one is with DST, while the other is not. These two times should have different UTC timestamp though. If currently the iPhone(or my self-defined MY_DATE_STRING) just enters the second 2am, which is a non-DST one, how could I set the TimeZone or other attributes so that the Date() or the given MY_DATE_STRING could generate the timestamp of second 2am instead of the first one?
So I would like that all my users would have the same date and time no matter their location. Even if they have changed date and time settings the date and time in the application would not change.
At the moment I have constructed a string which is based on every user's 'iPhone time'.
let todaysDate: String = {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let result = formatter.string(from: date)
return result
}()
My wish is that every user would have EET (Eastern Europe Time).
Just set the time zone of your dateformatter to EET if you need the time/date to be independent of user location.
formatter.timeZone = TimeZone(abbreviation: "EET")
However, Date() returns the current device time, so if the user changes their date/time setting, you would get wrong results. To always get a correct data, you have to rely on a date fetched from a reliable server.
how can i return a NSDate in a predefined time zone from a string
let responseString = "2015-8-17 GMT+05:30"
var dFormatter = NSDateFormatter()
dFormatter.dateFormat = "yyyy-M-dd ZZZZ"
var serverTime = dFormatter.dateFromString(responseString)
println("NSDate : \(serverTime!)")
the above code returns the time as
2015-08-16 18:30:00 +0000
The date format has to be assigned to the dateFormat property of the date formatter instead.
let date = NSDate.date()
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let str = dateFormatter.stringFromDate(date)
println(str)
This prints the date using the default time zone on the device. Only if you want the output according to a different time zone then you would add for example
Swift 3.*
dateFormatter.timeZone = NSTimeZone(name: "UTC")
Swift 4.*
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
also refer link http://www.brianjcoleman.com/tutorial-nsdate-in-swift/
how can i return a NSDate in a predefined time zone?
You can't.
An instance of NSDate does not carry any information about timezone or calendar. It just simply identifies one point in universal time.
You can interpret this NSDate object in whatever calendar you want. Swift's string interpolation (the last line of your example code) uses an NSDateFormatter that uses UTC (that's the "+0000" in the output).
If you want the NSDate's value as a string in the current user's calendar you have to explicitly set up a date formatter for that.
Swift 4.0
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
If you always have the same time zone for the input string, you can create two date formatters to output the local time zone (or a specified one):
let timeFormatterGet = DateFormatter()
timeFormatterGet.dateFormat = "h:mm a"
timeFormatterGet.timeZone = TimeZone(abbreviation: "PST")
let timeFormatterPrint = DateFormatter()
timeFormatterPrint.dateFormat = "h:mm a"
// timeFormatterPrint.timeZone = TimeZone(abbreviation: "EST") // if you want to specify timezone for output, otherwise leave this line blank and it will default to devices timezone
if let date = timeFormatterGet.date(from: "3:30 PM") {
print(timeFormatterPrint.string(from: date)). // "6:30 PM" if device in EST
} else {
print("There was an error decoding the string")
}
The number 1 means 1 regardless of language. Yet in English it's spelled as one, in Spanish it's una, in Arabic it wahid, etc.
Similarly 123982373 seconds pass 1970 is going to reflect differently in different timezones or calendar formats, but's all still 123982373 seconds passed 1970
The difference between 3 seconds and 7 seconds is 4 seconds. That doesn't require a calendar. Neither you need a calendar/timezone to know the difference in time between these two Epoch times 1585420200 and 1584729000
Dates are just a timeInterval from January 1, 1970 (midnight UTC/GMT). Dates also happen to have a string representation.
Repeating Nikolia's answer, Swift's default string interpolation (2015-08-16 18:30:00 +0000) uses a DateFormatter that uses UTC (that's the "+0000" in the output).
Calendars with the use of timezones give us a contextual representation that is just easier to understand than trying to calculate the difference between two gigantic numbers.
Meaning a single date (think of a single timeInterval since 1970) will have a different string interpretations per calendar. On top of that a calendar will itself vary based on time zones
I highly recommend that you go and play around with this Epoch converter site and see how selecting a different timezone will cause the string representations for the same moment/date/timeInterval to change
I also recommend to see this answer. Mainly this part:
Timezone is just an amendment to the timestamp string, it's not considered by the date formatter.
To consider the time zone you have to set the timeZone of the formatter
dateFormatter.timeZone = TimeZone(secondsFromGMT: -14400)