날짜 다루기

iOS에서 날짜를 다루는 방법에 대해 알아보자.
Swift에서 날짜를 다루는 타입으로는 Date, DateFormatter, Calendar, DateComponent 등이 있다.

Date

시간의 특정 시점을 나타내는 구조체로 달력이나 타임 존에 독립적이다. 날짜 비교, 날짜 간격 계산, 날짜 간격으로 새 날짜 생성 등 다양한 메서드를 지원한다. 참고로 Objective-C에서는 NSDate라는 클래스를 이용했는데 iOS 7 이후 Swift에서는 Date 구조체로 이와 같은 기능을 제공한다.

Date 타입의 초기화는 다음과 같이 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. 현재 시간
let date1 = Date()
// 2. 지금으로부터 300초 지난 시점
let date2 = Date(timeIntervalSinceNow: 300)
// 3. date2로부터 300초 지난 시점
let date3 = Date(timeInterval: 300, since: date2)
// 4. 유닉스 시간인 1970년 1월 1일 00:00:00 UTC로부터 300초 지난 시점
let date4 = Date(timeIntervalSince1970: 300)
// 5. 2001 1월 1일 00:00:00 UTC로부터 300초 지난 시점
let date5 = Date(timeIntervalSinceReferenceDate: 300)

print(date1) // 2018-02-23 06:15:51 +0000
print(date2) // 2018-02-23 06:20:51 +0000
print(date3) // 2018-02-23 06:25:51 +0000
print(date4) // 1970-01-01 00:05:00 +0000
print(date5) // 2001-01-01 00:05:00 +0000

위의 초기화에서 timeInterval은 TimeInterval 타입을 말하며 이는 Double 타입의 typealias로 시간의 초를 나타낸다. 특정 시점을 나타내는 Date 타입에 TimeInterval을 더하고 뺌으로써 새로운 Date를 얻을 수 있다. 또한 Date 구조체는 날짜 간 비교를 지원한다.

1
2
3
4
5
6
7
8
9
10
11
var date1 = Date()
date1.addTimeInterval(300)

let date2 = date1.addingTimeInterval(300)

print(date1) // 2018-02-23 06:48:37 +0000
print(date2) // 2018-02-23 06:53:37 +0000
print(date1 == date2) // false
print(date1 != date2) // true
print(date1 > date2) // false
print(date1 < date2) // true

DateFormatter

Date 타입은 날짜의 한 지점을 나타내지만 이것을 텍스트로 표현하고 싶을 때는 DateFormatter를 사용한다.

DateFormatter를 인스턴스화 시켜 사용하며 dateStyle, timeStyle, locale, 그리고 dateFormat을 통해 다양한 방법으로 Date를 문자화한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var date = Date()

let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .medium

dateFormatter.locale = Locale(identifier: "en_US")
print(dateFormatter.string(from: date)) // Feb 23, 2018 at 4:34:31 PM

dateFormatter.locale = Locale(identifier: "fr_FR")
print(dateFormatter.string(from: date)) // 23 févr. 2018 à 16:34:31

dateFormatter.locale = Locale(identifier: "ko_KR")
print(dateFormatter.string(from: date)) // 2018. 2. 23. 오후 4:34:31

dateFormatter.dateFormat = "YY년 M월 d일 h시 mm분 ss초"
print(dateFormatter.string(from: date)) // 18년 2월 23일 4시 34분 31초

DateFormatter를 사용하면 String 타입으로부터 Date를 얻는 것도 가능해진다. 이 때 StringDateFormatter의 형식과 맞지 않을 경우에는 nil을 리턴해야하므로 반환형이 Optional이다. Date FormatApple의 Date Formatting Guide를 참고하면 된다.

1
2
3
4
5
6
7
8
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YY년 M월 d일"

let string1 = "18년 2월 1일"
let string2 = "18.02.23"

print(dateFormatter.date(from: string1)) // Optional(2018-01-31 15:00:00 +0000)
print(dateFormatter.date(from: string2)) // nil

Calendar

달력과 관련된 정보를 담는 구조체로 달력과 관련된 계산과 각 달력의 요소인 DateComponent를 얻을 수 있다.

Calendar의 생성은 Calendar.Identifier 열거형을 init 메소드에 전달함으로써 이루지는데 이는 나라 또는 종교 별로 달력이 다른 것을 반영한 것이다.

인스턴스로 만든 뒤에는 달력의 요소를 나타내는 DateComponent 구조체에 관한 다양한 메서드를 사용할 수 있다. Date에서 DateComponent를 뽑아낼 수도 있고 그 반대도 가능하다.

1
2
3
4
5
6
7
8
let date = Date()

let calendar = Calendar(identifier: .gregorian)
print(calendar.dateComponents([.day, .month], from: date)) // month: 2 day: 23 isLeapMonth: false

var dateComponents = DateComponents()
dateComponents.year = 1998
print(calendar.date(from: dateComponents)) // Optional(1997-12-31 15:00:00 +0000)

그 외에도 AM/PM, Weekday, Month, Quarter, Era 등 locale 설정 별로 달라질 수 있는 Symbols의 배열을 얻을 수 있다.

1
2
3
4
5
6
7
var calendar = Calendar(identifier: .gregorian)
calendar.locale = Locale(identifier: "fr_FR")

print(calendar.amSymbol) // AM
print(calendar.eraSymbols) // ["av. J.-C.", "ap. J.-C."]
print(calendar.weekdaySymbols) // ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
print(calendar.quarterSymbols) // ["1er trimestre", "2e trimestre", "3e trimestre", "4e trimestre"]

Reference