TimeZone là một lớp Foundation trong iOS và macOS trừu tượng hóa thông tin múi giờ để chuyển đổi thời gian chính xác giữa các khu vực địa lý. Theo Apple Developer Documentation, 2024, TimeZone cung cấp các phương thức để làm việc với các định danh múi giờ (IANA Time Zone Database), độ lệch so với UTC và quy tắc chuyển sang giờ mùa hè. Lớp này được tích hợp với DateFormatter và Calendar, đảm bảo áp dụng tự động múi giờ chính xác khi định dạng ngày. Không giống như tính toán độ lệch thủ công, TimeZone tự động cập nhật dữ liệu khi múi giờ thiết bị thay đổi.
Điểm chính
TimeZone là một kiểu giá trị trong Swift cung cấp thông tin về múi giờ địa lý: độ lệch UTC, tên, tên viết tắt và quy tắc giờ mùa hè. Trong Objective-C, lớp này được gọi là NSTimeZone. Cả hai lớp đều dựa trên IANA Time Zone Database (còn được gọi là cơ sở dữ liệu Olson), chứa lịch sử thay đổi múi giờ từ năm 1970.
Mỗi phiên bản TimeZone lưu trữ một định danh múi giờ (ví dụ: Europe/Moscow), độ lệch hiện tại tính bằng giây so với UTC, cờ isDaylightSavingTime và ngày chuyển đổi tiếp theo. Định danh là khóa chính: khi khởi tạo TimeZone(identifier:), hệ thống tải bản ghi tương ứng từ cơ sở dữ liệu múi giờ của thiết bị.
Theo IANA (2024), cơ sở dữ liệu chứa hơn 600 định danh múi giờ duy nhất. Apple cung cấp ảnh chụp cơ sở dữ liệu này với mỗi bản phát hành iOS và macOS, đảm bảo tính toán nhất quán trên tất cả thiết bị mà không cần yêu cầu mạng.
Kiến trúc TimeZone trong Foundation được xây dựng trên hệ thống hai cấp: định danh múi giờ (tên có thể đọc được) và biểu diễn số của nó (độ lệch UTC). Hệ thống tự động chọn múi giờ hiện tại từ cài đặt thiết bị, nhưng nhà phát triển có thể ghi đè cho các thao tác định dạng cụ thể.
TimeZone liên kết chặt chẽ với Calendar và DateFormatter. Khi định dạng ngày, DateFormatter sử dụng thuộc tính timeZone của phiên bản TimeZone để chuyển đổi một thời điểm tuyệt đối (Date) thành biểu diễn chuỗi trong múi giờ mong muốn. Nếu timeZone không được đặt, múi giờ hệ thống mặc định được sử dụng — TimeZone.current.
| Loại | Khởi tạo | Đặc điểm |
|---|---|---|
| Hiện tại | TimeZone.current | Tự động cập nhật khi thay đổi khu vực trong cài đặt, theo dõi giờ mùa hè |
| Cố định | TimeZone(identifier:) | Độc lập với khu vực thiết bị. Áp dụng nhất quán định danh đã chọn |
| UTC | TimeZone(secondsFromGMT: 0) | Múi giờ không hiệu chỉnh. Định danh: GMT |
| Độ lệch tùy ý | TimeZone(secondsFromGMT: 10800) | Độ lệch cố định tính bằng giây. Không tính đến giờ mùa hè |
Lưu ý quan trọng: TimeZone(identifier:) trả về nil cho các định danh không xác định. Đây là nguyên nhân phổ biến gây ra sự cố ứng dụng — các nhà phát triển quên xử lý giá trị tùy chọn khi truyền định danh không hợp lệ từ đầu vào của người dùng. Đối với định danh IANA, chữ hoa/chữ thường rất quan trọng: Europe/Moscow hợp lệ, europe/moscow trả về nil.
IANA Time Zone Database sử dụng định dạng “Khu vực/Thành phố” (Châu lục/Thành phố), trong đó khu vực là một trong các châu lục (Africa, America, Asia, Atlantic, Australia, Europe, Indian, Pacific) hoặc một đại dương, và thành phố là địa phương đông dân nhất trong vùng phủ sóng của múi giờ. Định dạng này đảm bảo tính duy nhất và dễ đọc của định danh.
Ngoài định dạng chính, TimeZone hỗ trợ ba phương pháp nhận dạng bổ sung: tên viết tắt (MSK, EST, PST), mã múi giờ ba chữ cái (GMT, UTC) và độ lệch số (+0300, -0500). Tuy nhiên, các tên viết tắt không rõ ràng: EST có thể có nghĩa là Eastern Standard Time (GMT-5) hoặc Eastern Summer Time (GMT+10) ở Úc. Apple khuyên chỉ nên sử dụng định danh IANA.
import Foundation
// Lấy tất cả các định danh múi giờ đã biết
let allIdentifiers: [String] = TimeZone.knownTimeZoneIdentifiers
print("Tổng số múi giờ: \(allIdentifiers.count)")
// Lọc theo khu vực
let europeZones = allIdentifiers.filter { $0.hasPrefix("Europe/") }
print("Múi giờ châu Âu: \(europeZones)")
// Tên viết tắt (không khuyến nghị cho sản xuất)
if let moscowTimeZone = TimeZone(abbreviation: "MSK") {
print("Giây MSK từ GMT: \(moscowTimeZone.secondsFromGMT())")
}
// Tìm định danh theo độ lệch
let utcPlus3 = TimeZone(secondsFromGMT: 10800)
print("Định danh: \(utcPlus3.identifier)")
Tên viết tắt trong TimeZone.abbreviationDictionary chứa các tên viết tắt cho tất cả các múi giờ đã biết, nhưng từ điển này không đảm bảo tính duy nhất: khóa PST có thể tương ứng với America/Los_Angeles hoặc Pacific/Pago_Pago. Đối với mã sản xuất, luôn sử dụng định danh IANA.
TimeZone tự động tính đến việc chuyển sang giờ mùa hè (DST) cho tất cả các khu vực áp dụng. Hệ thống sử dụng dữ liệu lịch sử từ IANA Time Zone Database, bao gồm ngày chuyển đổi chính xác cho mỗi múi giờ. Thuộc tính isDaylightSavingTime trả về true nếu múi giờ hiện đang ở giờ mùa hè.
Phương thức nextDaylightSavingTimeTransition cho phép biết ngày chuyển đổi tiếp theo, hữu ích cho việc lên kế hoạch các sự kiện trong tương lai. Chức năng này đặc biệt quan trọng đối với các khu vực có thay đổi quy tắc DST thường xuyên, như Brazil hoặc Morocco — cho đến năm 2024, Brazil thay đổi ngày chuyển đổi hàng năm và tính toán thủ công dẫn đến lỗi trong các ứng dụng.
Theo Apple WWDC 2023, thư viện ICU (International Components for Unicode), nền tảng của Foundation, cập nhật dữ liệu DST với mỗi bản cập nhật iOS. Các ứng dụng không nên lưu vùng đệm dữ liệu giờ mùa hè lâu hơn một ngày sau khi cập nhật hệ thống — cơ sở dữ liệu IANA có thể thay đổi ngay cả khi không có bản cập nhật phiên bản OS thông qua các điều chỉnh múi giờ.
import Foundation
// Kiểm tra DST cho Europe/Moscow
let moscow = TimeZone(identifier: "Europe/Moscow")!
let now = Date()
let isMoscowDST = moscow.isDaylightSavingTime(for: now)
print("Moscow hiện đang trong DST: \(isMoscowDST)")
// Lấy ngày chuyển đổi DST tiếp theo
if let nextTransition = moscow.nextDaylightSavingTimeTransition(
after: now
) {
let dstOffset = moscow.daylightSavingTimeOffset(
for: nextTransition
)
print("Chuyển đổi tiếp theo: \(nextTransition), độ lệch DST: \(dstOffset)s")
}
// Chuyển đổi an toàn với nhận thức DST
let newYork = TimeZone(identifier: "America/New_York")!
let offsetNY = newYork.secondsFromGMT(for: now)
print("Độ lệch hiện tại của NY: \(offsetNY / 3600)h")
Sắc thái quan trọng: secondsFromGMT(for:) tính đến DST cho ngày đã chỉ định, trong khi secondsFromGMT() chỉ áp dụng cho thời gian hiện tại. Khi định dạng ngày lịch sử, luôn sử dụng phiên bản có tham số Date: secondsFromGMT(for: someHistoricalDate). Sự khác biệt có thể là 1–2 giờ, điều này rất quan trọng đối với nhật ký hoặc dữ liệu lịch sử.
Định dạng ngày với múi giờ cụ thể là tác vụ phổ biến nhất khi làm việc với TimeZone. DateFormatter sử dụng thuộc tính timeZone để chuyển đổi Date thành chuỗi. Nếu timeZone không được đặt rõ ràng, trình định dạng sử dụng TimeZone.current — múi giờ được đặt trên thiết bị của người dùng, có thể dẫn đến kết quả không mong đợi cho dữ liệu máy chủ.
import Foundation
// Định dạng ngày trong múi giờ cụ thể
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let tokyo = TimeZone(identifier: "Asia/Tokyo")!
formatter.timeZone = tokyo
let tokyoTime = formatter.string(from: Date())
print("Giờ Tokyo: \(tokyoTime)")
// Các định danh có sẵn để người dùng chọn
let displayNames: [(String, String)] = TimeZone.knownTimeZoneIdentifiers
.prefix(20)
.map { ($0, TimeZone(identifier: $0)!.localizedName(
for: .generic, locale: .current
)) }
// So sánh hai múi giờ
let london = TimeZone(identifier: "Europe/London")!
let difference = tokyo.secondsFromGMT(for: Date())
- london.secondsFromGMT(for: Date())
print("Chênh lệch Tokyo-London: \(difference / 3600)h")
// Làm việc với từ điển tên viết tắt
let knownAbbrevs = TimeZone.abbreviationDictionary
for (abbr, ident) in knownAbbrevs.sorted(by: { $0.key < $1.key }).prefix(5) {
print("\(abbr) -> \(ident)")
}
Tên đã định vị của múi giờ thông qua localizedName(for:locale:) trả về tên có thể đọc được bằng ngôn ngữ đã chỉ định. Ví dụ, đối với Europe/Moscow với ngôn ngữ Nga, phương thức trả về tên tiếng Nga “Moskva”, và với ngôn ngữ tiếng Anh — “Moscow Time”. Các kiểu có sẵn: .standard (tên tiêu chuẩn), .daylightSaving (giờ mùa hè) và .shortGeneric (ngắn).
import Foundation
let paris = TimeZone(identifier: "Europe/Paris")!
let nameRU = paris.localizedName(
for: .standard,
locale: Locale(identifier: "ru_RU")
)
print("Tên tiếng Nga: \(nameRU)")
// Kiểm tra xem khu vực có cùng ngày không
let isSameDay = Calendar.current.isDate(
Date(),
equalTo: Date(),
toGranularity: .day
)
print("Cùng ngày qua các múi giờ: \(isSameDay)")
Tuần tự hóa định danh múi giờ là phương pháp tốt nhất để lưu trữ TimeZone trong cơ sở dữ liệu hoặc UserDefaults. Lưu định danh (một chuỗi như Europe/Moscow), không phải độ lệch tính bằng giây hoặc tên viết tắt. Độ lệch có thể thay đổi theo sự thay đổi DST và tên viết tắt không rõ ràng. Khôi phục: TimeZone(identifier: savedString).
Sử dụng độ lệch cố định thay vì định danh múi giờ là lỗi phổ biến nhất. TimeZone(secondsFromGMT: 10800) không tính đến DST, vì vậy đối với Europe/Moscow vào mùa hè, cấu trúc này cho độ lệch sai 1 giờ. Luôn sử dụng định danh IANA cho các khu vực có giờ mùa hè.
Quên xử lý nil khi khởi tạo TimeZone(identifier:) là lỗi phổ biến thứ hai. Nếu người dùng nhập định danh không chính xác (ví dụ: “moscow” thay vì “Europe/Moscow”), hàm tạo trả về nil. Nếu không xử lý giá trị tùy chọn, ứng dụng sẽ bị sự cố với lỗi runtime. Sử dụng guard let hoặc TimeZone(identifier:) với phương án dự phòng đã biết.
Bỏ qua DST khi làm việc với các ngày trong tương lai. TimeZone.secondsFromGMT(for:) là cách duy nhất chính xác để lấy độ lệch cho một ngày cụ thể. Sử dụng secondsFromGMT() mà không có tham số cho các ngày lịch sử hoặc tương lai sẽ cho độ lệch tại thời điểm hiện tại, có thể không khớp với độ lệch thực tế vào ngày đã chỉ định, đặc biệt đối với các khu vực đã bãi bỏ hoặc áp dụng DST.
Theo Stack Overflow (2024), khoảng 15% câu hỏi về DateFormatter liên quan đến cấu hình timeZone không chính xác. Một kịch bản điển hình: máy chủ gửi ngày theo UTC, nhà phát triển định dạng mà không đặt timeZone của trình định dạng, và ngày được hiển thị theo múi giờ thiết bị, gây nhầm lẫn cho người dùng từ các khu vực khác nhau. Quy tắc: luôn đặt rõ ràng timeZone của trình định dạng cho dữ liệu máy chủ.
Câu hỏi thường gặp
TimeZone là một lớp Foundation để làm việc với múi giờ trong iOS và macOS. Nó cung cấp thông tin về độ lệch UTC, quy tắc giờ mùa hè và các định danh múi giờ dựa trên IANA Time Zone Database.
Ba định dạng: định danh IANA (Europe/Moscow), tên viết tắt (MSK, EST) và độ lệch số (+0300). Apple khuyên sử dụng định danh IANA là định dạng rõ ràng duy nhất cho mã sản xuất.
Tự động thông qua các phương thức secondsFromGMT(for:) và isDaylightSavingTime(for:). TimeZone sử dụng dữ liệu IANA lịch sử, được cập nhật với mỗi bản phát hành iOS, đảm bảo chuyển đổi DST chính xác cho bất kỳ ngày nào.
TimeZone.current trả về múi giờ được người dùng chọn trong cài đặt (có thể khác với múi giờ địa lý). TimeZone.system trả về múi giờ thiết bị, được xác định tự động bằng định vị và không thể bị ghi đè bởi người dùng.
TimeZone.current trả về múi giờ hiện tại của thiết bị. Để lấy định danh, sử dụng thuộc tính identifier: TimeZone.current.identifier. Để có tên đã định vị, hãy gọi localizedName(for:locale:).
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm