please help me to get local date and the start of the day, I mean the midnight. For getting local date I'm using code below, but I dont think it is right
var calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
calendar!.timeZone = NSTimeZone.localTimeZone()
let components = NSDateComponents()
components.second = NSTimeZone.localTimeZone().secondsFromGMT
let today = calendar!.dateByAddingComponents(components, toDate: NSDate(), options: nil)
But I can't get the midnight of the day, it keeps returning time 21.00
var comps = calendar!.components(NSCalendarUnit.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: today!)
comps.hour = 0
comps.minute = 0
comps.second = 0
let startToday = calendar!.dateFromComponents(comps)!
even this return 21.00
calendar!.startOfDayForDate(today)
NSDate does not have an attached timezone. For some head-scratching reason, Apple decided to always display the date in GMT when you print it. After enough hair loss on this, I decided to write my own description method see the date in my local time zone:
extension NSDate {
public var localTimeString : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
formatter.timeZone = NSTimeZone.localTimeZone()
return formatter.stringFromDate(self)
}
}
let now = NSDate()
let flags : NSCalendarUnit = [.Day, .Month, .Year]
let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = gregorian.components(flags, fromDate: now)
let today12AM = gregorian.dateFromComponents(components)!
print(today12AM)
print(today12AM.localTimeString)
Edit:
You can construct a point in time manually:
let x = gregorian.dateWithEra(1, year: 2015, month: 8, day: 15, hour: 0, minute: 0, second: 0, nanosecond: 0)!
print(x)
print(x.localTimeString)
print(x.timeIntervalSince1970) // seconds since midnight Jan 1, 1970
print(today12AM.timeIntervalSince1970)
x is no different than today12AM. As I said, NSDate has no built-in timezone. When you use print, it converts your date into GMT. That's why it appears superficially different from the same point in time when expressed in your own timezone.
Related
I am trying to format a time interval. I want a result that looks like this:
10:45 - 12:00 AM
I can get very close to this using DateInvervalFormatter:
let cal = Calendar.current
let formatter = DateIntervalFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
let start = Date()
let end = cal.date(byAdding: .hour, value: 1, to: start)
formatter.string(from: DateInterval(start: start, end: end!))
The above (in the en_US locale) will produce an output such as:
5:27 – 6:27 PM
Looks good right? However, this does not work if the two dates in the interval are on different days. For example:
let formatter = DateIntervalFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
let startComponents = DateComponents(year: 2020, month: 1, day: 1, hour: 23, minute: 45)
let start = cal.date(from: startComponents)
let end = cal.date(byAdding: .hour, value: 1, to: start!)
formatter.string(from: DateInterval(start: start!, end: end!))
Despite setting dateStyle to .none, the string produced in the above example (in the en_US locale) is:
1/1/2020, 11:45 PM – 1/2/2020, 12:45 AM
What I want is:
11:45 PM – 12:45 AM
How can I get this? I know I could use a DateFormatter to format each date (start and end) into just a time, and then append the two strings together with a hyphen (-) in the middle, but this is not necessarily localization-friendly.
What I ended up with is:
extension Date {
func formatTimeInterval(
to: Date,
timeZone: TimeZone = .autoupdatingCurrent
) -> String {
let formatter = DateIntervalFormatter()
formatter.timeZone = timeZone
formatter.timeStyle = .short
formatter.dateStyle = .none
// we need to manually strip any "date" metadata, because for some locales,
// even if we set `formatter.dateStyle = .none`, if the dates are in two different days
// we will still get the date information in the end result (e.g. for the US locale)
let calendar = Calendar.current
let fromComponents = calendar.dateComponents([.hour, .minute, .second], from: self)
let toComponents = calendar.dateComponents([.hour, .minute, .second], from: to)
let fromDate = calendar.date(from: fromComponents)!
let toDate = calendar.date(from: toComponents)!
return formatter.string(from: fromDate, to: toDate)
}
}
However, if the interval dates are in different months (say 31st August - 1 September), it adds a date starting at year 1, something like
1/1/1, 11:45 PM – 1/2/1, 12:45 AM
No clean solution to this still...
I want to get the first day and the last day of the week. But my results do not match the documentation from apple:
https://developer.apple.com/documentation/foundation/nsdatecomponents/1410442-weekday
This is my function:
func startAndEndDateOfWeek(weekOfYearWithYear: (week: Int,year: Int)) -> (start: Date, end: Date) {
var output = (start: Date.init(), end: Date.init())
let calendar = Calendar(identifier: .gregorian)
var firstDayComponents = DateComponents()
firstDayComponents.weekOfYear = weekOfYearWithYear.week
firstDayComponents.yearForWeekOfYear = weekOfYearWithYear.year
firstDayComponents.weekday = 1
let firstDay = calendar.date(from: firstDayComponents)
var lastDayComponents = DateComponents()
lastDayComponents.weekOfYear = weekOfYearWithYear.week
lastDayComponents.yearForWeekOfYear = weekOfYearWithYear.year
lastDayComponents.weekday = 2
let lastDay = calendar.date(from: lastDayComponents)
output = (start: firstDay!, end: lastDay!)
return output
}
.weekday = 2 -> leads to the sunday and not 0.
I also want to have the entire day and not 16:00.
A couple of observations:
In the Gregorian calendar, weekday = 1 means Sunday; weekday = 2 means Monday; etc. You can look at calendar.maximumRange(of: .weekday) to get the range of valid values, and you can look at calendar.weekdaySymbols to see what these weekDay values mean (e.g. “Sun”, “Mon”, “Tue”, “Wed”, “Thu”, “Fri”, and “Sat”).
You said:
I also want to have the entire day and not 16:00.
A Date object references a moment in time. So it can’t represent an “entire day”. But it can represent midnight (and midnight in your time zone is likely 4pm in GMT/UTC/Zulu).
You can, alternatively, return a DateInterval, which does represent a range of time.
func interval(ofWeek week: Int, in year: Int) -> DateInterval {
let calendar = Calendar.current
let date = DateComponents(calendar: calendar, weekOfYear: week, yearForWeekOfYear: year).date!
return calendar.dateInterval(of: .weekOfYear, for: date)!
}
And then
let formatter = DateIntervalFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
let year = Calendar.current.component(.year, from: Date())
let dateInterval = interval(ofWeek: 2, in: year)
print(formatter.string(from: dateInterval))
In a US locale, the interval starts on January 6th:
1/6/19, 12:00 AM – 1/13/19, 12:00 AM
Whereas in a German locale, the interval starts on the 7th:
07.01.19, 00:00 – 14.01.19, 00:00
If you want the start of the first day of the week and the last day of the week, you can do:
func startAndEndDate(ofWeek week: Int, in year: Int) -> (Date, Date) {
let date = DateComponents(calendar: calendar, weekOfYear: week, yearForWeekOfYear: year).date!
let lastDate = calendar.date(byAdding: .day, value: 6, to: date)!
return (date, lastDate)
}
And then
let formatter = DateFormatter()
formatter.dateStyle = .short
let year = Calendar.current.component(.year, from: Date())
let (start, end) = startAndEndDate(ofWeek: 2, in: year)
print(formatter.string(from: start), "-", formatter.string(from: end))
I have the following code to convert a String with a date like April 22, 2017 and a second String with the time 4:30 PM and I need to convert these 2 Strings into one complete NSDate object in the Eastern Time Zone.
//convert scheduled date and time from String to NSDate
let scheduledServiceDateStr = orderReview.serviceDate //current format example: April 21, 2017
let scheduledServiceTimeStr = orderReview.serviceTime //current format example: 04:30 PM
print("scheduledServiceDateStr: \(scheduledServiceDateStr)")
print("scheduledServiceTimeStr: \(scheduledServiceTimeStr)")
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MMMM d, yyyy"
dateFormatter.timeZone = NSTimeZone(name: "EST")
let scheduledServiceDate = dateFormatter.dateFromString(scheduledServiceDateStr)
print("scheduledServiceDate: \(scheduledServiceDate)")
let timeFormatter = NSDateFormatter()
timeFormatter.dateFormat = "hh:mm a"
timeFormatter.timeZone = NSTimeZone(name: "EST")
let scheduledServiceTime = timeFormatter.dateFromString(scheduledServiceTimeStr)
print("scheduledServiceTime: \(scheduledServiceTime)")
let timeComponents = NSCalendar.currentCalendar().components([.Hour, .Minute, .Second], fromDate: scheduledServiceTime!)
let dateComponents = NSCalendar.currentCalendar().components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: scheduledServiceDate!)
// append the time to the date
dateComponents.hour = timeComponents.hour
dateComponents.minute = timeComponents.minute
dateComponents.second = timeComponents.second
let dateAndTime = NSCalendar.currentCalendar().dateFromComponents(dateComponents)
print("dateAndTime: \(dateAndTime)")
This is what is printed in the Console:
scheduledServiceDateStr: April 22, 2017
scheduledServiceTimeStr: 04:30 PM
scheduledServiceDate: Optional(2017-04-22 05:00:00 +0000)
scheduledServiceTime: Optional(2000-01-01 21:30:00 +0000)
dateAndTime: Optional(2017-04-21 20:30:00 +0000)
The issue I am having is that the ending result, the dateAndTime NSDate value displayed in the console at the end is 2017-04-21 20:30:00 +0000 but, the time 20:30:00 I believe is equivalent to 8:30 PM Eastern Time when the time originally selected was 4:30 PM. I understand that NSDate does not take Time Zones into account and that it is just a representation of of seconds since January 1, 2001. As you can see I am setting the Time Zone to EST before converting each String to an NSDate object. What am I doing wrong?
I don't think there's anything wrong with the time. EST is -04:00 so 16:30 EST = 20:30 GMT. And you wrote way more code that needed to be:
let scheduledServiceDateStr = "April 21, 2017"
let scheduledServiceTimeStr = "04:30 PM"
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd, yyyy hh:mm a"
formatter.timeZone = TimeZone(identifier: "EST")
if let date = formatter.date(from: scheduledServiceDateStr + " " + scheduledServiceTimeStr) {
print(date)
}
I'm trying to write a loop for every 10 minutes of a given 24 hour day, starting at midnight and ending at ten minutes before midnight. So I tried this...
let x = Calendar.current.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
let june = dateFormatter.date(from: "21-06-" + String(x))
The result for june is "2017-06-21 04:00:00 UTC". Now technically this is correct, my local day will be 4 AM UTZ, but the code I'm passing this into, from the Astronomical Almanac, already handles local/global conversion.
So then I tried this:
var UTZCal = Calendar.current
UTZCal.timeZone = TimeZone(abbreviation: "GMT")!
let x = UTZCal.component(.year, from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat="dd-MM-yyyy"
dateFormatter.calendar = UTZCal
let june = dateFormatter.date(from: "21-06-" + String(x))
This produced the exact same result. What am I missing?
It seems that the date formatter does not use the timezone of the
assigned calendar, and adding
dateFormatter.timeZone = UTZCal.timeZone
to your code makes it produce the expected result. But note that you
can simplify the calculation to
var utzCal = Calendar(identifier: .gregorian)
utzCal.timeZone = TimeZone(secondsFromGMT: 0)!
let year = utzCal.component(.year, from: Date())
let june = DateComponents(calendar: utzCal, year: year, month: 6, day: 21).date!
print(june) // 2017-06-21 00:00:00 +0000
I am trying to put the dates of the beginning of the week in a app.
However in 2017, this results in un-expected behavior, as this year has the week '1' 2 times in it,
starting Sunday Jan 1st, and Sun Dec 31.
My program finds the latter. Giving me the result of
'01-01-2018' where is should be, '02-01-2017'
How can i make sure my program finds the first date available?
this is my code:
getFirstDay(Int(weekNummer)!, year: yearWrap)
func getFirstDay(weekNumber:Int, year:Int)->String?{
let Calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let dayComponent = NSDateComponents()
dayComponent.weekOfYear = weekNumber
dayComponent.weekday = 2
dayComponent.year = year
var date = Calendar.dateFromComponents(dayComponent)
if ((weekNumber == 1 || weekNumber == 52 || weekNumber == 53) && Calendar.components(.Month, fromDate: date!).month != 1 ){
dayComponent.year = year - 1
date = Calendar.dateFromComponents(dayComponent)
}
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
return String(dateFormatter.stringFromDate(date!))
}
Let do NSCalendar the complete date math
func firstDayOfWeek(week: Int, inYear year : Int) -> NSString {
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let components = NSDateComponents()
components.weekOfYear = week
components.yearForWeekOfYear = year
components.weekday = 2 // First weekday of the week (Sunday = 1)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
let date = calendar.dateFromComponents(components)!
return dateFormatter.stringFromDate(date)
}
firstDayOfWeek(1, inYear : 2017) // 02-01-2017
firstDayOfWeek(1, inYear : 2018) // 01-01.2018