I am trying to use yearly quarters in my iOS application but have found that the .quarter component does nothing. There is also no documentation I could find on it.
Calling print(Calendar.current.date(byAdding: .quarter, value: 1, to: Date())) just prints the current date. I would have expected it to add 3 months on to it?
You'd think that wouldn't you — but no, it appears something is broken.
A quick test in a Playground shows it all works until you use a .quarter:
//: Playground - noun: a place where people can play
import UIKit
let result = Calendar.current.date(byAdding: .quarter, value: 1, to: Date())
print (result)
let calendar1 = Calendar.current
let now1 = Date()
let newDate1 = calendar1.date(byAdding: .minute, value: 1, to: now1)
let newDate2 = calendar1.date(byAdding: .hour, value: 1, to: now1)
let newDate3 = calendar1.date(byAdding: .day, value: 1, to: now1)
let newDate4 = calendar1.date(byAdding: .month, value: 1, to: now1)
let newDate5 = calendar1.date(byAdding: .month, value: 3, to: now1)
let newDate6 = calendar1.date(byAdding: .quarter, value: 1, to: now1)
Results in:
"Sep 27, 2016, 3:30 PM"
"Optional(2016-09-27 05:30:03 +0000)\n"
gregorian (current)
"Sep 27, 2016, 3:30 PM"
"Sep 27, 2016, 3:31 PM"
"Sep 27, 2016, 4:30 PM"
"Sep 28, 2016, 3:30 PM"
"Oct 27, 2016, 3:30 PM"
"Dec 27, 2016, 3:30 PM"
"Sep 27, 2016, 3:30 PM"
It looks like as it stands in Xcode 8.0 (8A218a) you will have use months and a value of 3 as shown in the newDate5 line above.
Related
Usually, during end of day light saving, we will be gaining extra 1 hours.
Take Tehran timezone as an example.
During 22 September 2021, Tehran will backward by 1 hour from 00:00
AM, to 11:00 PM.
I wrote the following code to demonstrate such.
import UIKit
func date(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int) -> Date {
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.second = second
let date = Calendar.current.date(from: dateComponents)!
return date
}
// During 22 September 2021, Tehran will backward by 1 hour from 00:00 AM, to 11:00 PM.
let tehranTimeZone = TimeZone(identifier: "Asia/Tehran")!
let oldDefault = NSTimeZone.default
NSTimeZone.default = tehranTimeZone
defer {
NSTimeZone.default = oldDefault
}
let date1 = date(year: 2021, month: 09, day: 21, hour: 23, minute: 59, second: 59)
let date2 = date(year: 2021, month: 09, day: 22, hour: 00, minute: 00, second: 00)
let date3 = date(year: 2021, month: 09, day: 22, hour: 00, minute: 00, second: 01)
// STEP 1: 2021 Sep 21 23:59:59 => 1632252599.0, Tuesday, September 21, 2021 at 11:59:59 PM Iran Daylight Time
print("STEP 1: 2021 Sep 21 23:59:59 => \(date1.timeIntervalSince1970), \(date1.description(with: .current))")
// STEP 2: 2021 Sep 22 00:00:00 => 1632256200.0, Wednesday, September 22, 2021 at 12:00:00 AM Iran Standard Time
print("STEP 2: 2021 Sep 22 00:00:00 => \(date2.timeIntervalSince1970), \(date2.description(with: .current))")
// STEP 3: 2021 Sep 22 00:00:01 => 1632256201.0, Wednesday, September 22, 2021 at 12:00:01 AM Iran Standard Time
print("STEP 3: 2021 Sep 22 00:00:01 => \(date3.timeIntervalSince1970), \(date3.description(with: .current))")
From STEP 1 transits to STEP 2, instead for their timeIntervalSince1970 different by +1 seconds, their difference are +3601 seconds, due to the extra 1 hour gain.
Now, my question is, how can we use DateComponents to represent the extra 1 hour period at the end of day light saving?
In another, how can I use DateComponents to generate a Date which is capable to print the following?
2021 Sep 21 23:00:00 => 1632252600.0, Tuesday, September 21, 2021 at 11:00:00 PM Iran Standard Time
Now, we understand that, in Tehran, during 2021 Sept 21, there are 2 type of 23:00:00 time
23:00:00 Iran Daylight time (Epoch is 1632249000)
23:00:00 Iran Standard time (Epoch is 1632252600)
23:00:00 Iran Daylight time (Epoch is 1632249000)
I can represent the above using
let date = date(year: 2021, month: 09, day: 21, hour: 23, minute: 00, second: 00)
23:00:00 Iran Standard time (Epoch is 1632252600)
I have no idea how to represent the above. As, I do not find a way in DateComponents, to enable us to specific whether the local time is belong to standard time, or daylight time.
DateComponents do not have a time zone. The time zone comes into it when you convert DateComponents to a Date, using the call Calendar.date(from:). It's the calendar's time zone that determines how those DateComponents are converted to a Date.
Instead of using Calendar.current, create a custom calendar and set it to the IRST time zone. (I couldn't figure out the time zone for Iran Daylight time. I would have expected it to have the abbreviation "IRDT", but that doesn't work.)
Let's say we have a calendar irstCalendar that's set to Iran Standard Time ("IRST").
If you use irstCalendar.date(from: dateComponents) you'll always get the Date based on standard time.
Consider this code:
func date(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, calendar: Calendar = Calendar.current) -> Date {
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.second = second
let date = calendar.date(from: dateComponents)!
return date
}
guard let tehranStandardTimeZone = TimeZone(abbreviation: "IRST") else {
fatalError("Can't create time zones")
}
var tehranSTCalendar = Calendar(identifier: .gregorian)
tehranSTCalendar.timeZone = tehranStandardTimeZone
let tehranDateFormatter = DateFormatter()
tehranDateFormatter.dateStyle = .medium
tehranDateFormatter.timeStyle = .medium
tehranDateFormatter.timeZone = tehranStandardTimeZone
let date1 = date(year: 2021, month: 09, day: 21, hour: 23, minute: 59, second: 59, calendar: tehranSTCalendar)
let date2 = date(year: 2021, month: 09, day: 22, hour: 00, minute: 00, second: 00, calendar: tehranSTCalendar)
let date3 = date(year: 2021, month: 09, day: 22, hour: 00, minute: 00, second: 01, calendar: tehranSTCalendar)
print("STEP 1: 2021 Sep 21 23:59:59 => \(date1.timeIntervalSince1970), \(tehranDateFormatter.string(from:date1))")
print("STEP 2: 2021 Sep 22 00:00:00 => \(date2.timeIntervalSince1970), \(tehranDateFormatter.string(from:date2))")
print("STEP 3: 2021 Sep 22 00:00:01 => \(date3.timeIntervalSince1970), \(tehranDateFormatter.string(from:date3))")
That outputs:
STEP 1: 2021 Sep 21 23:59:59 => 1632252599.0, Sep 21, 2021 at 11:59:59 PM
STEP 2: 2021 Sep 22 00:00:00 => 1632256200.0, Sep 22, 2021 at 12:00:00 AM
STEP 3: 2021 Sep 22 00:00:01 => 1632256201.0, Sep 22, 2021 at 12:00:01 AM
I am trying to fill an array of type Date with 15 values, today, the 7 days prior, and the next 7 days. I'm having a hard time figuring out how to get the date. I tried implementing it like this:
func setupDates(){
//get date of 7 days prior to right now
for i in 1...15 {
//add a day per iteration
//append date to date array
}
}
I would suggest having a look at Calendar
It can be a little cumbersome, but generally has a lot of power to it
So you might do something like...
let anchor = Date()
let calendar = Calendar.current
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .long
for dayOffset in -7...7 {
if let date = calendar.date(byAdding: .day, value: dayOffset, to: anchor) {
print(formatter.string(from: date))
}
}
nb: This was a simple playground test
Which will print something like...
October 1, 2021 at 10:22:05 AM GMT+10
October 2, 2021 at 10:22:05 AM GMT+10
October 3, 2021 at 10:22:05 AM GMT+11
October 4, 2021 at 10:22:05 AM GMT+11
October 5, 2021 at 10:22:05 AM GMT+11
October 6, 2021 at 10:22:05 AM GMT+11
October 7, 2021 at 10:22:05 AM GMT+11
October 8, 2021 at 10:22:05 AM GMT+11
October 9, 2021 at 10:22:05 AM GMT+11
October 10, 2021 at 10:22:05 AM GMT+11
October 11, 2021 at 10:22:05 AM GMT+11
October 12, 2021 at 10:22:05 AM GMT+11
October 13, 2021 at 10:22:05 AM GMT+11
October 14, 2021 at 10:22:05 AM GMT+11
October 15, 2021 at 10:22:05 AM GMT+11
nb: Today is the 8th
nbb: Your question title says "date format", but you're just trying to fill an array with Dates. The basic concept will work either way
So this is interesting:
po Calendar.default.date(from: DateComponents(year: 2022, month: 1, hour: 16, minute: 1, second: 1, weekday: 1, weekOfMonth: 1))
▿ Optional<Date>
▿ some : 2021-12-26 23:01:01 +0000
- timeIntervalSinceReferenceDate : 662252461.0
I'm expecting January 1st 2022, but I'm getting December 26th 2021? Why is it doing this? Am I doing something wrong?
It's because the date components you are providing are contradictory. You have weekDay 1 (which will probably be a Sunday or Monday, depending on your locale) but the 1 jan 2022 is a Saturday.
(you also used Calendar.default when I think you meant Calendar.current?)
If you take out the weekDay term you will get the correct answer:
Calendar.current.date(from: DateComponents(year: 2022, month: 1, hour: 16, minute: 1, second: 1, weekOfMonth: 1))
// "Jan 1, 2022 at 4:01 PM"
You could also remove the weekOfMonth term as it is superfluous when you are specifying the actual date.
I am trying to get every 10 minutes of the last hour.
For example, now is 15:46:41
I want [15:40:00, 15:30:00, 15:20:00, 15:10:00, 15:00:00, 14:50:00, 14:40:00, 14:30:00, 14:20:00, 14:10:00, 14:00:00, 13:50:00, 13:40:00]
let calendar = Calendar.current
let now = Date()
var components = DateComponents()
components.hour = -2
if let early = calendar.date(byAdding: components, to: now) {
let nowMin = calendar.component(.minute, from: early)
let diff = 10 - (nowMin % 10)
components.minute = diff
var minutes: [Int] = []
for _ in 0...13 {
// I cant figure out what should I do next.
}
print(minutes)
}
You can get now's minute, get the remainder of this value divided by ten and subtract it from that value. This way you get the last tenth hour minute, then you just need to set it with the same hour component to now to find out the first element of your array. Next you can fill the rest of dates subtracting 10 minutes times the element position from the start date. Try like this:
Xcode 11 • Swift 5.1 (for older versions just add the return statement as usual)
extension Date {
var hour: Int { Calendar.current.component(.hour, from: self) }
var minute: Int { Calendar.current.component(.minute, from: self) }
var previousHourTenth: Date { Calendar.current.date(bySettingHour: hour, minute: minute - minute % 10, second: 0, of: self)! }
func lastNthHourTenth(n: Int) -> [Date] { (0..<n).map { Calendar.current.date(byAdding: .minute, value: -10*$0, to: previousHourTenth)! } }
}
Playground testing
Date() // "Sep 25, 2019 at 10:19 AM"
Date().previousHourTenth // "Sep 25, 2019 at 10:10 AM"
Date().lastNthHourTenth(n: 13) // "Sep 25, 2019 at 10:10 AM", "Sep 25, 2019 at 10:00 AM", "Sep 25, 2019 at 9:50 AM", "Sep 25, 2019 at 9:40 AM", "Sep 25, 2019 at 9:30 AM", "Sep 25, 2019 at 9:20 AM", "Sep 25, 2019 at 9:10 AM", "Sep 25, 2019 at 9:00 AM", "Sep 25, 2019 at 8:50 AM", "Sep 25, 2019 at 8:40 AM", "Sep 25, 2019 at 8:30 AM", "Sep 25, 2019 at 8:20 AM", "Sep 25, 2019 at 8:10 AM"]
Now you just need to use DateFormatter to display those dates as needed to the user.
I cannot find a list of the format specifiers allowed in the template argument of DateFormatter.dateFormat(fromTemplate:options:locale:).
dateFormat directs me to...
Date and Time Programming Guide. None of those pages lists the format specifiers. So I checked the suggested related documentation...
Date and Time Programming Guide for Core Foundation doesn't have a list.
Data Formatting Guide has a section title "Use Format Strings to Specify Custom Formats" but it just points me back to (1) above. None of the other pages in Data Formatting Guide have the format specifiers.
Does anyone know where Apple documents these specifiers?
I'll expand on the answer in the comment with some examples. For iOS 7 and later the format codes are here: http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns. The table is too big to copy it all here but here's a few that helped me get started. See that link for all of the formats and explanations.
era: G (AD), GGGG (Anno Domini)
year: y (1984), yy (84), yyyy (1984)
month: M, MM, MMM, MMMM, MMMMM. Also: L
day of month: d, dd
day name of week: E, EEEE, EEEEE, EEEEEE
Here's a playground fragment that I found helpful to explore these.
import Foundation
let components = DateComponents(
calendar: Calendar(identifier: .gregorian),
timeZone: nil,
era: 1,
year: 1984,
month: 1,
day: 2,
hour: nil, minute: nil, second: nil,
nanosecond: nil, weekday: nil,
weekdayOrdinal: nil, quarter: nil,
weekOfMonth: nil, weekOfYear: nil,
yearForWeekOfYear: nil)
let aDate = Calendar(identifier: .gregorian).date(from: components)!
let en_US = Locale(identifier: "en_US")
var df = DateFormatter()
func formatdate(_ template: String) -> String {
let custom = DateFormatter.dateFormat(fromTemplate: template, options: 0, locale: en_US)
df.dateFormat = custom
return df.string(from: aDate)
}
formatdate("Mdyyyy") // "1/2/1984"
formatdate("yyyyMMdd") // "01/02/1984"
formatdate("yyyyMMMdd") // "Jan 02, 1984"
formatdate("yyyyMMMMdd") // "January 02, 1984"
formatdate("yyyyMMMMMdd") // "J 02, 1984"
formatdate("yyyyG") // "1984 AD"
formatdate("yyyyGGGG") // "1984 Anno Domini"
formatdate("yyyyMMMddE") // "Mon, Jan 02, 1984"
formatdate("yyyyMMMddEEEE") // "Monday, Jan 02, 1984"
formatdate("yyyyMMMddEEEEE") // "M, Jan 02, 1984"
formatdate("MdYYYY") // "1/2/1984"
formatdate("YYYYMMdd") // "01/02/1984"
formatdate("YYYYMMMdd") // "Jan 02, 1984"
formatdate("YYYYMMMMdd") // "January 02, 1984"
formatdate("YYYYMMMMMdd") // "J 02, 1984"
formatdate("YYYYG") // "1984 AD"
formatdate("YYYYGGGG") // "1984 Anno Domini"
formatdate("YYYYMMMddE") // "Mon, Jan 02, 1984"
formatdate("YYYYMMMddEEEE") // "Monday, Jan 02, 1984"
formatdate("YYYYMMMddEEEEE") // "M, Jan 02, 1984"