iyOmSd/Title: Swift

[Swift] TimeZone과 Locale

냄수 2024. 1. 30. 20:50
반응형

여러나라에 배포할 서비스를 개발하면서 헷갈렸던 개념을 정리해보려고해요

 

 

TimeZone

특정 지정학적 지역과 관련된 표준 시간법에 대한 정보.

지정학이란 지리적 환경과 정치현상의 관계를 연구하는 학문이라고하네요

 

다른나라를 여행갈 때 흔히 겪는 시차를 계산할 때와 같아요

국가별로 고유한 TimeZone을 사용하고있어요

 

문서를 보면..

TimeZone은 시간대의 동작을 정의합니다

TimeZone값은 지정학적 지역을 나타내고 이러한 값에는 해당 지역에대한 이름이 존재합니다.

TimeZone값은 그리니치 표준시(Greenwich Mean Time)라고 부르는 GMT에서 더하거나 뺀 시간적 차이 입니다.

swift TimeZone타입은 current와 autoupdatingCurrent 2가지 함수를 제공합니다

 

current: 현재 사용하고 있는 시스템 표준시간대

autoupdatingCurrent: 현재 사용하고 있는 시스템 표준시간대로, 사용자의 현재 기본설정으로 자동업데이트됨

 

America/Los_Angeles 같은 표준시간대 데이터베이스 항목은 이름이 아니라 ID라는 점에 유의해야합니다

표준시간대 이름의 예는 태평양표준시(Pacific Daylight Time) PDT입니다.

많은 시간대 함수에 "name"이라는 단어가 포함돼있지만 이는 ID를 가르킵니다.

 

서울은 GMT+9 라고 표현해요

아이폰에선 설정에 들어가서 날짜 및 시간 항목에서

사용자가 설정할 수도 있어요

 

 

abbreviationDictionary 타입 프로퍼티를 통해서 축약형을 확인해볼 수 있어요

TimeZone.abbreviationDictionary

/*
["PHT": "Asia/Manila",
"HKT": "Asia/Hong_Kong",
"AKDT": "America/Juneau",
"PET": "America/Lima",
"CST": "America/Chicago",
"NZST": "Pacific/Auckland",
"ART": "America/Argentina/Buenos_Aires", 
"WEST": "Europe/Lisbon",
"BDT": "Asia/Dhaka",
"KST": "Asia/Seoul",
"WAT": "Africa/Lagos",
"EEST": "Europe/Athens",
"MSK": "Europe/Moscow",
"CAT": "Africa/Harare",
"MDT": "America/Denver",
"BRST": "America/Sao_Paulo",
"ADT": "America/Halifax", 
"MST": "America/Phoenix",
"EST": "America/New_York",
"PST": "America/Los_Angeles",
"CEST": "Europe/Paris", 
"SGT": "Asia/Singapore",
"NST": "America/St_Johns",
"GST": "Asia/Dubai", 
"AST": "America/Halifax",
"GMT": "GMT", "IRST": "Asia/Tehran",
"PKT": "Asia/Karachi", 
"TRT": "Europe/Istanbul",
"PDT": "America/Los_Angeles",
"WET": "Europe/Lisbon",
"NDT": "America/St_Johns",
"WIT": "Asia/Jakarta",
"CET": "Europe/Paris",
"MSD": "Europe/Moscow", 
"UTC": "UTC", "EET": "Europe/Athens", "IST": "Asia/Kolkata",
"EDT": "America/New_York",
"COT": "America/Bogota",
"ICT": "Asia/Bangkok",
"CDT": "America/Chicago",
"JST": "Asia/Tokyo",
"AKST": "America/Juneau", 
"CLT": "America/Santiago",
"CLST": "America/Santiago",
"EAT": "Africa/Addis_Ababa",
"BST": "Europe/London",
"HST": "Pacific/Honolulu",
"BRT": "America/Sao_Paulo",
"NZDT": "Pacific/Auckland"]
*/

 

 

또한 TimeZone에 어떤 identifier가 있는지 알고싶다면

TimeZone.knownTimeZoneIdentifiers.forEach {
    print($0)
}

/*
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Bamako
Africa/Bangui
Africa/Banjul
Africa/Bissau
Africa/Blantyre
Africa/Brazzaville
Africa/Bujumbura
Africa/Cairo
Africa/Casablanca
Africa/Ceuta
Africa/Conakry
Africa/Dakar
Africa/Dar_es_Salaam
Africa/Djibouti
Africa/Douala
America/New_York
Asia/Seoul
Asia/Shanghai
Europe/Paris
*/

을 통해 모든 identifier를 확인할 수 있어요

 

 

Locale

데이터 서식을 지정할 때 사용하는 언어적, 문화적, 기술적 규칙에 대한 정보

 

Locale에 의해 캡슐화된 정보의 예로는 숫자의 소수점 구분기호, 날짜, 시간의 서식지정 규칙 등이 있습니다.

사용자에게 보여질 출력형태를 정의해요

print(Locale.current.identifier)
print(Locale.current.region)

// ko_KR
// Optional(KR)



let locale = Locale(languageCode: .korean, script: .japanese, languageRegion: .southKorea)
print(locale)

// ko-Jpan_KR (fixed)

 

보통은 ko_KR 같은 형식이 익숙할거에요

Locale의 구성은

(언어코드)-(스크립트코드)_(지역코드)

로 구성돼있어요(스크립트코드는 없을 수 도 있어요)

 

스크립트코드는

중국어에 간체, 번체와 같은 언어내의 분류를 나타내요

Latn, Hant 같은 것들이 포함돼있어요

 

새로만들어보면

한국언어, 스크립트는 일본, 지역은 대한민국으로 Locale을 생성하면

ko-Jpan_KR이란 출력을 볼 수 있어요

 

 

이렇게 설정에있는 언어 및 지역에서 변경할 수 있죠

ISO8601은 그레고리력을 사용하고 날짜와 시간에 대한 표쥰 규격이에요

개발할떄 자주보는 yyyy-MM 이런 형식이죠

 

Locale.preferredLanguages를 사용해서 언어설정에 등록된 언어도 불러올 수 있어요

지금 스크린샷기준으로는 ["ko-KR", "en-KR"] 이렇게 배열로 들어와요

이중 first가 현재 사용중인 언어에요 

 

해당 언어코드 (위기준으로 ko, en)를 UserDefaults에 넣어서 언어를 변경 시킬 수 있어요

UserDefaults.standard.set([languageCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

 

하지만 이때 주의할 점으로

Localization(다국어)을 지원하지않는 언어라면

예를들어

영어만 다국어를 지원하는데 일본어를 UserDefaults값으로 Set해서 저장했다면

Locale.preferredLanguages을 조회할때 빈배열이 출력돼요

 

(변경이 안된상태인 오류)

Locale.preferredLanguages -> []

Locale.current.language.languageCode -> ko

하지만 UserDefaults를 조회하면 값은 일본어로 존재해요

 

(변경이 성공시)

Locale.preferredLanguages -> ["ja"]

Locale.current.language.languageCode -> ja

다국어를 지원하는 언어라면 정상적으로 배열에 ["ja"] 이렇게 값이 들어오고

Locale.current.language.languageCode값도 ja로 변경돼요

 

 

UTC? GMT?

시간 기준을 보다보면 누구는 UTC를 쓰고 누구는 GMT를 쓰기도하는 것같아요

둘이 거의 같은것이지만

GMT에서 윤초보정을 한게 UTC로 소수점 단위의 차이가 아주 살짝 있어요

기술적인 표기에는 UTC를 사용한다고해요

서울은 GMT+9 이자 UTC+9 에 속하는거죠

 

 

Date

Date()로 현재시간을 생성하는데

이때 생성시 값은

달력시스템이나 표준시간대(TimeZone)과 무관하게 단일 시점을 캡슐화 해요

날짜값은 절대 기준일을 기준으로한 시간간격을 나타내요

 

print(TimeZone.current)
print(Date())

America/New_York (fixed)
2024-01-29 14:32:18 +0000

Asia/Seoul (fixed)
2024-01-29 14:32:48 +0000

TimeZone을 바꿔서 각각 테스트했는데

TimeZone과 무관하게 Date 시간은 일정하게 생성돼요

 

 

이렇게 TimeZone을 변경해서 시차에 맞게 변경할 수 있어요

let seoul = Date()
print(seoul)
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "Asia/Seoul")!

// 시차 계산 - Seoul
let timeDifferenceSeoul = calendar.timeZone.secondsFromGMT(for: seoul)
print("시차:", timeDifferenceSeoul / 60 / 60, "시간")

// 날짜 계산 - Seoul
let seoulTime = calendar.dateComponents([.month, .day, .hour], from: seoul)
print("seoulTime:", seoulTime)

// 시차 계산 - New York
calendar.timeZone = TimeZone(identifier: "America/New_York")!
let timeDifferenceNewYork = calendar.timeZone.secondsFromGMT(for: seoul)
print("시차:", timeDifferenceNewYork / 60 / 60, "시간")

// 날짜 계산 - New York
let NewYorkTime = calendar.dateComponents([.month, .day, .hour], from: seoul)
print("NewYorkTime:", NewYorkTime)


/* print
기기 타임존: Asia/Seoul (fixed)
2024-01-30 11:35:02 +0000

시차: 9 시간
seoulTime: month: 1 day: 30 hour: 20 isLeapMonth: false 

시차: -5 시간
NewYorkTime: month: 1 day: 30 hour: 6 isLeapMonth: false 
*/

 

secondsFromGMT을 이용하여 시차를 알수있고

또는

DateFormatter의 TimeZone을 변경해서 원하는 출력으로도 변경할 수 있어요

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd 'T' HH:mm:ss ZZZ"
// 해당 TimeZone으로 시간대변경
dateFormatter.timeZone = TimeZone(identifier: "America/New_York")

// Date -> String
let dateString = dateFormatter.string(from: localeDate)
print("new time string:", dateString)

// String -> Date
let date = dateFormatter.date(from: dateString)!
print("new time date:", date)


/* print
Asia/Seoul (fixed)
new time string: 2024-01-30 T 06:37:56 -0500
new time date: 2024-01-30 11:37:56 +0000
*/

이때 주의해야할점으론

String에는 시차가 적용돼서 06:37:56 -0500 (시차 -5시간이 적용된 시간) 을 표현하고

이걸 다시 Date로 바꾸면

앞에서 말했듯 단일 시점으로 절대기준으로한 시간간격을 나타내기때문에

+5를해서

11:37:56 +0000 으로 되는걸 알 수 있어요

 

 

마지막으로 정리하면!

TimeZone은 우리가 인지하고있는 개념으로는 로컬시간, 국가별 시간차! 를 나타내는 개념

표준시간대 정보를 알 수 있어요

 

Locale은 우리가 시간, 날짜를 보여주기위한 형식 개념

언어코드 및 지역코드 정보를 알 수 있어요

 

여러나라 배포를 위해 시차를 고려하다보니 헷갈리는 점이 많았네요! 

저 처럼 헷갈려하는 누군가에게 도움이 될 수 있기를...💪

 

 

반응형