날짜 시간 소인을 작성하고 ISO 8601, RFC 3339, UTC 시간대로 형식을 지정하는 방법은 무엇입니까?
ISO 8601 및 RFC 3339 의 형식 표준을 사용하여 날짜 시간 소인을 생성하는 방법은 무엇입니까?
목표는 다음과 같은 문자열입니다.
"2015-01-01T00:00:00.000Z"
체재:
- 년, 월, 일, "XXXX-XX-XX"
- 구분 기호로 문자 "T"
- "XX : XX : XX.XXX"와 같은 시간, 분, 초, 밀리 초
- 영점 오프셋, 일명 UTC, GMT, 줄 루어 시간의 영역 지정자 인 문자 "Z".
가장 좋은 경우 :
- 간단하고 짧고 간단한 스위프트 소스 코드.
- 추가 프레임 워크, 하위 프로젝트, cocoapod, C 코드 등을 사용할 필요가 없습니다.
StackOverflow, Google, Apple 등을 검색했지만 이에 대한 Swift 답변을 찾지 못했습니다.
가장 유망한 보인다 클래스는 NSDate
, NSDateFormatter
, NSTimeZone
.
관련 Q & A : iOS에서 ISO 8601 날짜는 어떻게 얻습니까?
지금까지 내가 찾은 최고는 다음과 같습니다.
var now = NSDate()
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
println(formatter.stringFromDate(now))
스위프트 4 • iOS 11.2.1 이상
extension ISO8601DateFormatter {
convenience init(_ formatOptions: Options, timeZone: TimeZone = TimeZone(secondsFromGMT: 0)!) {
self.init()
self.formatOptions = formatOptions
self.timeZone = timeZone
}
}
extension Formatter {
static let iso8601 = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
}
extension Date {
var iso8601: String {
return Formatter.iso8601.string(from: self)
}
}
extension String {
var iso8601: Date? {
return Formatter.iso8601.date(from: self)
}
}
용법:
Date().description(with: .current) // Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
let dateString = Date().iso8601 // "2019-02-06T00:35:01.746Z"
if let date = dateString.iso8601 {
date.description(with: .current) // "Tuesday, February 5, 2019 at 10:35:01 PM Brasilia Summer Time"
print(date.iso8601) // "2019-02-06T00:35:01.746Z\n"
}
iOS 9 • 스위프트 3 이상
extension Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}()
}
코딩 가능한 프로토콜
코딩 가능한 프로토콜로 작업 할 때이 형식을 인코딩 및 디코딩해야하는 경우 고유 한 사용자 지정 날짜 인코딩 / 디코딩 전략을 만들 수 있습니다.
extension JSONDecoder.DateDecodingStrategy {
static let iso8601withFractionalSeconds = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
guard let date = Formatter.iso8601.date(from: string) else {
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Invalid date: " + string)
}
return date
}
}
인코딩 전략
extension JSONEncoder.DateEncodingStrategy {
static let iso8601withFractionalSeconds = custom {
var container = $1.singleValueContainer()
try container.encode(Formatter.iso8601.string(from: $0))
}
}
운동장 테스트
let dates = [Date()] // ["Feb 8, 2019 at 9:48 PM"]
부호화
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601withFractionalSeconds
let data = try! encoder.encode(dates)
print(String(data: data, encoding: .utf8)!)
디코딩
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
let decodedDates = try! decoder.decode([Date].self, from: data) // ["Feb 8, 2019 at 9:48 PM"]
기술 Q & A1480에en_US_POSIX
설명 된대로 로케일을 설정해야합니다 . 스위프트 3에서 :
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
print(formatter.string(from: date))
문제는 그레고리력이 아닌 달력을 사용하는 장치를 사용하는 locale
경우 timeZone
및 dateFormat
문자열 을 지정하지 않으면 연도가 RFC3339 / ISO8601을 준수하지 않는다는 것 입니다.
또는 당신 ISO8601DateFormatter
은 설정의 잡초 locale
와 timeZone
자신 에서 당신을 얻을 수 있습니다 :
let date = Date()
let formatter = ISO8601DateFormatter()
formatter.formatOptions.insert(.withFractionalSeconds) // this is only available effective iOS 11 and macOS 10.13
print(formatter.string(from: date))
Swift 2 변환에 대해서는 이 답변의 이전 개정판을 참조하십시오 .
ISO8601DateFormatter()
Rails 4+ JSON 피드의 날짜와 함께 날짜 를 사용하려면 (물론 밀리 초가 필요하지 않음) 포맷터에서 몇 가지 옵션을 설정해야 작동합니다. 그렇지 않으면 date(from: string)
함수가 nil을 반환합니다. 내가 사용하는 것은 다음과 같습니다.
extension Date {
init(dateString:String) {
self = Date.iso8601Formatter.date(from: dateString)!
}
static let iso8601Formatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withFullDate,
.withTime,
.withDashSeparatorInDate,
.withColonSeparatorInTime]
return formatter
}()
}
놀이터 스크린 샷이 아닌 옵션 구절을 사용한 결과는 다음과 같습니다.
Andrés Torres Marroquín과 Leo Dabus를 더 칭찬하기 위해 분수 초를 유지하는 버전이 있습니다. 어디서나 문서화 할 수는 없지만 Apple은 입력 및 출력 모두에서 소수 초를 마이크로 초 (정밀도 3 자릿수)로 자릅니다 ( 유니 코드 tr35-31 과 달리 SSSSSSS를 사용하여 지정하더라도 ).
나는 이것이 대부분의 유스 케이스에 필요하지 않을 것이라고 강조해야한다 . 온라인 날짜는 일반적으로 밀리 초 정밀도가 필요하지 않으며, 필요할 때 다른 데이터 형식을 사용하는 것이 좋습니다. 그러나 때로는 기존 방식과 특정 방식으로 상호 운용해야합니다.
Xcode 8/9 및 Swift 3.0-3.2
extension Date {
struct Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXXXX"
return formatter
}()
}
var iso8601: String {
// create base Date format
var formatted = DateFormatter.iso8601.string(from: self)
// Apple returns millisecond precision. find the range of the decimal portion
if let fractionStart = formatted.range(of: "."),
let fractionEnd = formatted.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: formatted.endIndex) {
let fractionRange = fractionStart.lowerBound..<fractionEnd
// replace the decimal range with our own 6 digit fraction output
let microseconds = self.timeIntervalSince1970 - floor(self.timeIntervalSince1970)
var microsecondsStr = String(format: "%.06f", microseconds)
microsecondsStr.remove(at: microsecondsStr.startIndex)
formatted.replaceSubrange(fractionRange, with: microsecondsStr)
}
return formatted
}
}
extension String {
var dateFromISO8601: Date? {
guard let parsedDate = Date.Formatter.iso8601.date(from: self) else {
return nil
}
var preliminaryDate = Date(timeIntervalSinceReferenceDate: floor(parsedDate.timeIntervalSinceReferenceDate))
if let fractionStart = self.range(of: "."),
let fractionEnd = self.index(fractionStart.lowerBound, offsetBy: 7, limitedBy: self.endIndex) {
let fractionRange = fractionStart.lowerBound..<fractionEnd
let fractionStr = self.substring(with: fractionRange)
if var fraction = Double(fractionStr) {
fraction = Double(floor(1000000*fraction)/1000000)
preliminaryDate.addTimeInterval(fraction)
}
}
return preliminaryDate
}
}
필자의 경우 DynamoDB-lastUpdated 열 (Unix Timestamp)을 표준시로 변환해야합니다.
lastUpdated의 초기 값은 다음과 같습니다. 1460650607601-2016-04-14 16:16:47 +0000으로 변환 :
if let lastUpdated : String = userObject.lastUpdated {
let epocTime = NSTimeInterval(lastUpdated)! / 1000 // convert it from milliseconds dividing it by 1000
let unixTimestamp = NSDate(timeIntervalSince1970: epocTime) //convert unix timestamp to Date
let dateFormatter = NSDateFormatter()
dateFormatter.timeZone = NSTimeZone()
dateFormatter.locale = NSLocale.currentLocale() // NSLocale(localeIdentifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
dateFormatter.dateFromString(String(unixTimestamp))
let updatedTimeStamp = unixTimestamp
print(updatedTimeStamp)
}
앞으로 형식이 변경되어야 할 수도 있습니다. 날짜는 date.dateFromISO8601을 사용하여 앱의 모든 곳에서 호출 할 수 있습니다. 클래스와 프로토콜을 사용하여 구현을 래핑하십시오. 한 장소에서 날짜 시간 형식 호출을 변경하는 것이 더 간단합니다. 가능하면보다 완벽한 RFC3339를 사용하십시오. DateFormatProtocol 및 DateFormat은 종속성 주입에 적합합니다.
class AppDelegate: UIResponder, UIApplicationDelegate {
internal static let rfc3339DateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
internal static let localeEnUsPosix = "en_US_POSIX"
}
import Foundation
protocol DateFormatProtocol {
func format(date: NSDate) -> String
func parse(date: String) -> NSDate?
}
import Foundation
class DateFormat: DateFormatProtocol {
func format(date: NSDate) -> String {
return date.rfc3339
}
func parse(date: String) -> NSDate? {
return date.rfc3339
}
}
extension NSDate {
struct Formatter {
static let rfc3339: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)
formatter.locale = NSLocale(localeIdentifier: AppDelegate.localeEnUsPosix)
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
formatter.dateFormat = rfc3339DateFormat
return formatter
}()
}
var rfc3339: String { return Formatter.rfc3339.stringFromDate(self) }
}
extension String {
var rfc3339: NSDate? {
return NSDate.Formatter.rfc3339.dateFromString(self)
}
}
class DependencyService: DependencyServiceProtocol {
private var dateFormat: DateFormatProtocol?
func setDateFormat(dateFormat: DateFormatProtocol) {
self.dateFormat = dateFormat
}
func getDateFormat() -> DateFormatProtocol {
if let dateFormatObject = dateFormat {
return dateFormatObject
} else {
let dateFormatObject = DateFormat()
dateFormat = dateFormatObject
return dateFormatObject
}
}
}
ISO8601DateFormatter
단 한 줄로 문자열을 만들 수 있는 새로운 클래스가 있습니다. 이전 버전과의 호환성을 위해 이전 C 라이브러리를 사용했습니다. 나는 이것이 누군가에게 유용하기를 바랍니다.
스위프트 3.0
extension Date {
var iso8601: String {
if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
return ISO8601DateFormatter.string(from: self, timeZone: TimeZone.current, formatOptions: .withInternetDateTime)
} else {
var buffer = [CChar](repeating: 0, count: 25)
var time = time_t(self.timeIntervalSince1970)
strftime_l(&buffer, buffer.count, "%FT%T%z", localtime(&time), nil)
return String(cString: buffer)
}
}
}
ISO8601DateFormatter
iOS10 이상에서 사용합니다 .
DateFormatter
iOS9 이상에서 사용합니다 .
스위프트 4
protocol DateFormatterProtocol {
func string(from date: Date) -> String
func date(from string: String) -> Date?
}
extension DateFormatter: DateFormatterProtocol {}
@available(iOS 10.0, *)
extension ISO8601DateFormatter: DateFormatterProtocol {}
struct DateFormatterShared {
static let iso8601: DateFormatterProtocol = {
if #available(iOS 10, *) {
return ISO8601DateFormatter()
} else {
// iOS 9
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}
}()
}
Leo Dabus 버전을 보완하기 위해 Swift 및 Objective-C로 작성된 프로젝트에 대한 지원을 추가하고 밀리 초 (옵션)에 대한 지원도 추가했습니다.
Xcode 8 및 스위프트 3
extension Date {
struct Formatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"
return formatter
}()
}
var iso8601: String {
return Formatter.iso8601.string(from: self)
}
}
extension String {
var dateFromISO8601: Date? {
var data = self
if self.range(of: ".") == nil {
// Case where the string doesn't contain the optional milliseconds
data = data.replacingOccurrences(of: "Z", with: ".000000Z")
}
return Date.Formatter.iso8601.date(from: data)
}
}
extension NSString {
var dateFromISO8601: Date? {
return (self as String).dateFromISO8601
}
}
일부 수동 문자열 마스크 또는 TimeFormatter가없는 경우
import Foundation
struct DateISO: Codable {
var date: Date
}
extension Date{
var isoString: String {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
guard let data = try? encoder.encode(DateISO(date: self)),
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: String]
else { return "" }
return json?.first?.value ?? ""
}
}
let dateString = Date().isoString
'IT' 카테고리의 다른 글
ASP.NET MVC :이 개체에 대해 매개 변수가없는 생성자가 정의되어 있지 않습니다 (0) | 2020.05.29 |
---|---|
백업을 복원 할 때 모든 활성 연결을 해제하려면 어떻게합니까? (0) | 2020.05.29 |
컴퓨터에서 .NET Framework 버전을 반환하는 PowerShell 스크립트? (0) | 2020.05.29 |
런타임에 Maven 아티팩트 버전 가져 오기 (0) | 2020.05.28 |
'SubSonic.Schema .DatabaseColumn'유형의 오브젝트를 직렬화하는 중에 순환 참조가 발견되었습니다. (0) | 2020.05.28 |