Swift Dateオブジェクトをどのように作成しますか?


156

Swift Xcodeの日付から日付オブジェクトをどのように作成しますか?

たとえば、JavaScriptでは次のようにします。 var day = new Date('2014-05-20');


10
NSDateObjective-Cと同じように使用しますか?
rmaddy 2014

回答:


263

Swiftには独自のDateタイプがあります。使用する必要はありませんNSDate

Swiftで日付と時刻を作成する

Swiftでは、日付と時刻は、2001年1月1日の基準日00:00:00 UTCからの秒数を測定する64ビット浮動小数点数に格納されます。これはDate構造で表されます。次のようにすると、現在の日付と時刻がわかります。

let currentDateTime = Date()

他の日時を作成するには、次のいずれかの方法を使用できます。

方法1

2001年の基準日の前後の秒数がわかっている場合は、それを使用できます。

let someDateTime = Date(timeIntervalSinceReferenceDate: -123456789.0) // Feb 2, 1997, 10:26 AM

方法2

もちろん、(相対的な秒数ではなく)年、月、日、時間などを使用してを作成する方が簡単Dateです。これを使用DateComponentsして、コンポーネントを指定しCalendar、日付を作成できます。Calendar与えDateコンテキストを。それ以外の場合、どのタイムゾーンまたはカレンダーでそれを表現するかをどのようにして知るのでしょうか?

// Specify date components
var dateComponents = DateComponents()
dateComponents.year = 1980
dateComponents.month = 7
dateComponents.day = 11
dateComponents.timeZone = TimeZone(abbreviation: "JST") // Japan Standard Time
dateComponents.hour = 8
dateComponents.minute = 34

// Create date from components
let userCalendar = Calendar.current // user calendar
let someDateTime = userCalendar.date(from: dateComponents)

他のタイムゾーンの省略形はここにあります。空白のままにすると、デフォルトではユーザーのタイムゾーンが使用されます。

方法3

最も簡潔な方法(ただし、必ずしも最良とは限りません)は、を使用することDateFormatterです。

let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
let someDateTime = formatter.date(from: "2016/10/08 22:31")

Unicodeの技術基準は、他のフォーマットを示しているDateFormatterサポートします。

ノート

日付と時刻を読みやすい形式で表示する方法については、私の完全な回答を参照しください。これらの優れた記事も読んでください:


118

これは、既存の拡張機能を使用して行うのが最適です。 NSDateクラスのです。

次の拡張機能は、指定した形式の日付文字列を使用して現在のロケールで日付を作成する新しい初期化子を追加します。

extension NSDate
{
    convenience
      init(dateString:String) {
      let dateStringFormatter = NSDateFormatter()
      dateStringFormatter.dateFormat = "yyyy-MM-dd"
      dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
      let d = dateStringFormatter.dateFromString(dateString)!
      self.init(timeInterval:0, sinceDate:d)
    }
 }

これで、次のようにするだけでSwiftからNSDateを作成できます。

NSDate(dateString:"2014-06-06")

この実装はNSDateFormatterをキャッシュしないことに注意してください。これは、パフォーマンス上の理由から、 NSDate。この方法でのを。

また、NSDate正しく解析できない文字列を渡して初期化しようとすると、この実装は単にクラッシュすることにも注意してください。これは、によって返されるオプションの値が強制的にアンラップされるためですdateFromStringnil不正な解析でを返したい場合は、理想的には失敗する初期化子を使用します。しかし、Swift 1.2の制限により、今はそれを行うことはできません(2015年6月)。そのため、クラスファクトリーメソッドを使用するのが次善の策です。

両方の問題に対処するより複雑な例は、https//gist.github.com/algal/09b08515460b7bd229faです。


Swift 5のアップデート

extension Date {
    init(_ dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        let date = dateStringFormatter.date(from: dateString)!
        self.init(timeInterval:0, since:date)
    }
}

9
では、「便利」という言葉は何をしているのでしょうか。
ジェームズバーンズ2014

18
これは、拡張機能が、既存の指定された初期化子(この場合はNSDate.init(timeInterval:、sinceDate :)初期化子)に初期化を実際に委譲する初期化子を提供していることを示しています。これは、Swiftプログラミング言語の本の275ページで説明されています。
藻類2014年

1
上記のコードで私が見た欠点の1つはNSDateFormatter、関数がアクセスされるたびに再作成されることです。日付の書式ガイドは述べて安く操作ではありませんフォーマッタ日付を作成します。また、このクラスはiOS7以降スレッドセーフなので、すべてのNSDate拡張機能で安全に使用できます。
Yevhen Dubinin 2014年

@eugenedubinin正解です。そのため、「この実装はNSDateFormatterをキャッシュしないので、パフォーマンス上の理由からこれを実行することができます」と述べました。
藻類2014年

1
Swift 1.2の制限は何ですか?「失敗可能なイニシャライザ」機能は、Swift 1.1以降で使用できます
フランクリンユー

48

Swiftには独自の日付型はありませんが、既存のCocoa NSDate型を使用する必要があります。例:

class Date {

    class func from(year: Int, month: Int, day: Int) -> Date {
        let gregorianCalendar = NSCalendar(calendarIdentifier: .gregorian)!

        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day

        let date = gregorianCalendar.date(from: dateComponents)!
        return date
    }

    class func parse(_ string: String, format: String = "yyyy-MM-dd") -> Date {
        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = NSTimeZone.default
        dateFormatter.dateFormat = format

        let date = dateFormatter.date(from: string)!
        return date
    }
}

次のように使用できます:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)

var date = gregorian?.dateFromComponents(c)という行を変更する必要がありましたが、理由がわかりません。また、解析の戻り値dateFmt.dateFromStringも問題があります。手がかりはありますか?ああ、関数の戻り値の型をNSDateからNSDateに変更しましたか?コンパイルできます。しかし、なぜnull可能にする必要があるのか​​はわかりません。
セネター

1
@TheSenator NSCalendar initはオプション(?)を返すため、グレゴリアンはオプション(?)です。オプションの必要性へのアクセスは、それをアクセスするためのオプションの連鎖であるgregorian?.dateFromCoomponentsは、オプションのグレゴリアン(NSCalendar)から値をアンラップするためのオプションの連鎖です。オプションの連鎖の詳細はこちら
iluvatar_GR

NSGregorianCalendarはiOS 8で非推奨となったため、代わりにNSCalendarIdentifierGregorianを使用してください
Adam Knights

2
@Senator Complains coz SwiftにはDate構造体があり、このコードはDateをクラスとして再宣言します。だから
Zaporozhchenko Oleksandr

日付に戻った解析文字列に関するメモ。以下のための「YYYY-MM-DD」と文字列「1979年7月1日」NSDateFormatterの方法date(from:)。戻りますnil。この問題は、fabric.ioクラッシュログで見つかりました。
AechoLiu 2018年

12

これがSwift 4.2での方法です。

extension Date {

    /// Create a date from specified parameters
    ///
    /// - Parameters:
    ///   - year: The desired year
    ///   - month: The desired month
    ///   - day: The desired day
    /// - Returns: A `Date` object
    static func from(year: Int, month: Int, day: Int) -> Date? {
        let calendar = Calendar(identifier: .gregorian)
        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day
        return calendar.date(from: dateComponents) ?? nil
    }
}

使用法:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)

私は「15」を1日として渡していますが、返された日付は14日です。考え?
ルークザンミット

@LukeZammitにタイムゾーンを追加dateComponents
Adrian

運なしでこれを試しました-dateComponents.timeZone = TimeZone.current、考えてください?
ルークザミット


4

Swift 3.0では、この方法で日付オブジェクトを設定しています。

extension Date
{
    init(dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = Locale(identifier: "en_US_POSIX")
        let d = dateStringFormatter.date(from: dateString)!
        self(timeInterval:0, since:d)
    }
}

@ Zonker.in。ジュネーブはどこでも好きです。別のファイルを作成し、「Extension」、idkと呼びます。
Zaporozhchenko Oleksandr

2

個人的には、失敗する可能性のあるイニシャライザであるべきだと思います。

extension Date {

    init?(dateString: String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        if let d = dateStringFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since: d)
        } else {
            return nil
        }
    }
}

それ以外の場合、無効な形式の文字列は例外を発生させます。


2

@mythzの回答によると、私はswift3構文を使用して彼の拡張機能の更新版を投稿することにしました。

extension Date {
    static func from(_ year: Int, _ month: Int, _ day: Int) -> Date?
    {
        let gregorianCalendar = Calendar(identifier: .gregorian)
        let dateComponents = DateComponents(calendar: gregorianCalendar, year: year, month: month, day: day)
        return gregorianCalendar.date(from: dateComponents)
    }
}

私はparseメソッドを使用しませんが、必要に応じてこの投稿を更新します。


1

ある場所の日付値を別の場所の時間値と組み合わせる必要があることがよくあります。これを実現するためのヘルパー関数を書きました。

let startDateTimeComponents = NSDateComponents()
startDateTimeComponents.year = NSCalendar.currentCalendar().components(NSCalendarUnit.Year, fromDate: date).year
startDateTimeComponents.month = NSCalendar.currentCalendar().components(NSCalendarUnit.Month, fromDate: date).month
startDateTimeComponents.day = NSCalendar.currentCalendar().components(NSCalendarUnit.Day, fromDate: date).day
startDateTimeComponents.hour = NSCalendar.currentCalendar().components(NSCalendarUnit.Hour, fromDate: time).hour
startDateTimeComponents.minute = NSCalendar.currentCalendar().components(NSCalendarUnit.Minute, fromDate: time).minute
let startDateCalendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
combinedDateTime = startDateCalendar!.dateFromComponents(startDateTimeComponents)!

0

Appleのデータフォーマットガイドによると

日付フォーマッターの作成は、安価な操作ではありません。フォーマッターを頻繁に使用する可能性がある場合、通常、複数のインスタンスを作成して破棄するよりも、単一のインスタンスをキャッシュする方が効率的です。1つのアプローチは、静的変数を使用することです

そして、これが失敗可能なイニシャライザであることに@Leonに同意しますが、シードデータを入力すると、失敗しないものがある可能性があります(のようにUIImage(imageLiteralResourceName:))。

だからここに私のアプローチがあります:

extension DateFormatter {
  static let yyyyMMdd: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    formatter.calendar = Calendar(identifier: .iso8601)
    formatter.timeZone = TimeZone(secondsFromGMT: 0)
    formatter.locale = Locale(identifier: "en_US_POSIX")
    return formatter
  }()
}

extension Date {
    init?(yyyyMMdd: String) {
        guard let date = DateFormatter.yyyyMMdd.date(from: yyyyMMdd) else { return nil }
        self.init(timeInterval: 0, since: date)
    }

    init(dateLiteralString yyyyMMdd: String) {
        let date = DateFormatter.yyyyMMdd.date(from: yyyyMMdd)!
        self.init(timeInterval: 0, since: date)
    }
}

そして今単に呼び出すことを楽しんでください:

// For seed data
Date(dateLiteralString: "2020-03-30")

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.