티스토리 뷰

컴퓨터/iOS

[iOS] 날짜/시간 치트시트

limorbear 2025. 1. 15. 20:03

 

 

 

Dates and Times | Apple Developer Documentation

Compare dates and times, and perform calendar and time zone calculations.

developer.apple.com

 

체크포인트

날짜/시간 데이터를 서버에서 받아오는 경우

  - 되도록 서버에서 유닉스 타임스탬프 형태로 반환해주거나, 타임존을 포함한 ISO8601 형태로 반환해주고,

  - 애플리케이션에서는 현재 디바이스의 지역과 유저 설정에 맞게 적절한 형식으로 화면에 뿌려주는 식

으로 하면 편리하다.

 

어디서는 수동으로 9시간을 더하고 빼고 ... 이런 작업을 할 필요가 없어진다.

 

Date

Swift의 Foundation에서 Date는 문서에 따르면 시간 상에서 특정한 지점을 가리킨다.

일상적인 의미에서 Date라고 한다면 지역에 따른 타임존과 역법 체계 등에 따라 표현이 달라지겠지만, Swift의 Date는 이러한 표현적인 측면과는 상관 없이 절대적인 개념이다.

import Foundation

/**
 Date는 시간 상의 어떠한 특정 지점을 가리킨다. 특정 시스템이나 지역과는 상관 없는, 절대적 의미의 시각이다.
 */

// MARK: - 선언
print(Date())
print(Date.now)
print(Date(timeIntervalSince1970: 1736930232))

// MARK: - 비교
print(Date.now < Date.distantFuture) // true
print(Date.now == Date()) // true
print(Date.distantPast > Date.distantFuture) // false

let compareResult1: ComparisonResult = Date.now.compare(Date())
print(compareResult1.rawValue) // 0
let compareResult2: ComparisonResult = Date.now.compare(Date.distantFuture)
print(compareResult2.rawValue) // -1
let compareResult3: ComparisonResult = Date.now.compare(Date.distantPast)
print(compareResult3.rawValue) // 1
/*
 @frozen public enum ComparisonResult : Int, @unchecked Sendable {
     case orderedAscending = -1
     case orderedSame = 0
     case orderedDescending = 1
 }
 */

// MARK: - 계산
// MARK: Date와 Date
var addedDate = Date.now.addingTimeInterval(12345)
let distance1: TimeInterval = Date.now.distance(to: addedDate) // public typealias TimeInterval = Double
print(distance1) // 12344.999601006508; A TimeInterval value is always specified in seconds

// MARK: TimeInterval과 TimeInterval
let date1 = Date()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    let date2 = Date()
    let intervalDiff = date2.timeIntervalSince1970 - date1.timeIntervalSince1970
    print("intervalDiff \(intervalDiff)") // 5.249634742736816
}

// MARK: Date와 TimeInterval
addedDate.addTimeInterval(-12345) // mutate
let distance2: TimeInterval = Date.now.distance(to: addedDate)
print(distance2) // -0.0004349946975708008

addedDate += 20.0 // Date +/- TimeInterval
print("current \(Date.now) addedDate \(addedDate)")

// MARK: - 간단한 포매팅
let current = Date.now
print(current.formatted()) // 14/01/2025, 10:27 PM
print(current.formatted(date: .complete, time: .omitted)) // Tuesday, 14 January 2025
print(current.formatted(date: .omitted, time: .complete)) // 10:27:59 PM GMT+9
print(current.formatted(date: .numeric, time: .standard)) // 14/01/2025, 10:29:11 PM
// Date.FormatStyle을 넘겨 더 자세하게 세팅할 수도 있다 see: https://developer.apple.com/documentation/foundation/date/formatstyle
// 아래 DateFormatter 예제 코드 참고


// MARK: - 범위
let range: ClosedRange<Date> = current...current + 10
print(range)
print(range.contains(current + 5)) // true
print(range.contains(current + 20)) // false

 

DateFormatter

Date를 적절한 형식으로 표현해주는데 사용하는 도구

적당히 dateFormat을 지정해두고, Date를 String으로 바꿀 때에는 string(from: Date) 메서드를, String을 Date로 바꿀 때에는 string(from: Date) 메서드를 써주면 된다.

import Foundation

let dateFormatter = DateFormatter()

// MARK: - dateFormat
// see: https://stackoverflow.com/a/52297497
/*
 Wednesday, Sep 12, 2018           --> EEEE, MMM d, yyyy
 09/12/2018                        --> MM/dd/yyyy
 09-12-2018 14:11                  --> MM-dd-yyyy HH:mm
 Sep 12, 2:11 PM                   --> MMM d, h:mm a
 September 2018                    --> MMMM yyyy
 Sep 12, 2018                      --> MMM d, yyyy
 Wed, 12 Sep 2018 14:11:54 +0000   --> E, d MMM yyyy HH:mm:ss Z
 2018-09-12T14:11:54+0000          --> yyyy-MM-dd'T'HH:mm:ssZ
 12.09.18                          --> dd.MM.yy
 10:41:02.112                      --> HH:mm:ss.SSS
 */
dateFormatter.dateFormat = "yyyy-MM-dd a HH:mm:ss"

// MARK: - Date -> String
let dateStringEn = dateFormatter.string(from: Date())
print("Date -> String (en_001) \(dateStringEn)")

// MARK: Locale
print("before to change locale: \(dateFormatter.locale)")
dateFormatter.locale = Locale(identifier: "ko_KR")
print("after to change locale: \(dateFormatter.locale)")
let dateStringKo = dateFormatter.string(from: Date())
print("Date -> String (ko_KR) \(dateStringKo)")

// MARK: - String -> Date
let rawLocalizedString = "2025-01-01 오전 00:00:00"
let convertedDate = dateFormatter.date(from: rawLocalizedString)!
print(convertedDate)

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
let rawTimestamp = "2025-01-15T14:12:25+0900"
let convertedDate2 = dateFormatter.date(from: rawTimestamp)!
print(convertedDate2)

let dummyData = "2025!01!15!!!!14:12:25!0900"
let trial = dateFormatter.date(from: dummyData)
print("suceess: \(trial != nil)")

 

[참고] Date.FormatStyle, Date.ParseStrategy 활용

Date.FormatStyle과 Date.ParseStrategy를 이용하면 보다 선언적인 방식으로 날짜 표현을 다룰 수 있다.

// see: https://developer.apple.com/documentation/foundation/date/formatstyle
// see: https://developer.apple.com/documentation/foundation/date/parsestrategy
let format = Date.FormatStyle()
    .year(.defaultDigits)
    .month(.narrow)
    .day(.defaultDigits)
    .locale(Locale(identifier: "ko_KR"))
let formatted = Date.now.formatted(format)
print(formatted)

let parseStrategy = Date.ParseStrategy(format: "\(year: .defaultDigits)\(month: .narrow)\(day: .defaultDigits)일", locale: Locale(identifier: "ko_KR"), timeZone: TimeZone.current)
if let converted = try? Date("2026년 4월 12일", strategy: parseStrategy) {
    print(converted)
}

 

다만... 개인적으로는 템플릿 방식의 DateFormatter가 익숙하긴 하다... 영미권의 경우 월을 셀 때 별도의 단어를 쓰고 축약하기도 해서 이런 선언적인 방식이 편리하다고 느껴질 수 있겠지만, 우리나라에서는 주로 일자를 표현할 때 아라비아 숫자에 단위만 붙여서 표현하기 때문인지 크게 체감되지는 않았다.

 

Calendar

Date가 시간 상의 절대적인 특정 지점을 추상화한 것이라면,

Calendar는 다양한 지역과 시스템의 달력 상의 단위(연월일시 등)들과 Date의 관계를 정의해둔 것이다.

 

문서 상에는 위와 같이 되어 있는데, 구체적으로

  - Date 객체로부터 연/월/일/시/분/초 등 달력 상 Unit들을 알고 싶을 때

  - Date 객체에 특정한 연/월/일/시/분/초 등 만큼 더하거나 빼고 싶을 때

사용하면 될 것 같다.

import Foundation

// MARK: - 현재 캘린더
let calendar = Calendar.current // 현재 유저 캘린더를 가져온다.
print("identifier: \(calendar.identifier) / locale: \(String(describing: calendar.locale)) / timezone: \(calendar.timeZone)")

// MARK: - 달력 계산
var date = Date()
let components = calendar.dateComponents([.year, .month, .day], from: date)
let month = components.month ?? 0
let day = components.day ?? 0
print("오늘은 \(month)\(day)일")

let nextDayComponents = DateComponents(day: 1)
//nextDayComponents.day = 1
let nextDay = calendar.date(byAdding: nextDayComponents, to: date)!
print("다음 날: \(nextDay)")

let nextMonth = calendar.date(byAdding: .month, value: 1, to: date)! // 이런 식으로도 가능
print("다음 달: \(nextMonth)")

let nextYear = calendar.date(byAdding: .year, value: 1, to: date)!
print("다음 해: \(nextYear)")

var firstDayOfNextMonthComponents = calendar.dateComponents([.year, .month], from: date)
firstDayOfNextMonthComponents.day = 1
var lastDayOfThisMonth = calendar.date(byAdding: .day, value: -1, to: calendar.date(from: firstDayOfNextMonthComponents)!)
print("저번 달 마지막 날: \(String(describing: lastDayOfThisMonth))")

// MARK: - 특정한 날짜로 Date 생성
let certainDateComponents = DateComponents(calendar: Calendar.current, year: 2222, month: 2, day: 22, hour: 22, minute: 22) // calendar를 잘 넘겨야 date가 잘 반환된다
let certainDate = calendar.date(from: certainDateComponents)
print(String(describing: certainDate))

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함