質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

DataFormatterで文字列から日付型に変更するとnilになってしまう

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,558

score 19

メモ型アプリの作成

簡単なメモ型アプリを作成しています。就活のエントリーシート用のメモです。
提出締め切り日の前日にプッシュ通知ができるように、
datePickerからdateを取得して、1日前のdateをcoredata[Memo]のalertDateというプロパティに保存したいです。

問題部分

しかし、DataFormatterで文字列から日付型に変更するとnilになってしまい、デバックに"baseDateの日付変換に失敗したので本日を使用します"と表示されます。
formatter.date(from: baseDate) がnilにならない方法を教えていただきたいです。

試したこと

いくつかこのエラーに関して調べてた際に
「locale プロパティに “en_US_POSIX” を指定する」という記事をみつけたので
試してみたんですがうまくいきませんでした。

func calcDate(day:Int ,hour:Int ,baseDate:String ) -> Date {
/////////////////該当箇所///////////////////
///////////////////////////////////////////
        let formatter = DateFormatter()
//        formatter.locale = NSLocale(localeIdentifier: "ja_JP") as Locale
     //変更部分
        formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        //
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

        var components = DateComponents()

        components.setValue(day,for: Calendar.Component.day)
        components.setValue(hour,for: Calendar.Component.hour)

        let calendar = Calendar(identifier: Calendar.Identifier.gregorian)

        var base:Date?

        if let _:String = baseDate {
            print(formatter.date(from: baseDate))
            print(baseDate)
            if  formatter.date(from: baseDate) != nil {
                base = formatter.date(from: baseDate)!
            } else {
                print("baseDateの日付変換に失敗したので本日を使用します")
                base = Date()
            }

        } else {
            base = Date()
        }
  /////////////////該当箇所///////////////////
///////////////////////////////////////////      
        return calendar.date(byAdding: components, to: base!)!
    }
}

全体コード

import UIKit
import CoreData

//データを保存する画面
class ViewController: UIViewController, UITextFieldDelegate,UITextViewDelegate {
    //それぞれUI部品を定義
    @IBOutlet var titleField: UITextField!
    @IBOutlet var memoTextView: UITextView!
    @IBOutlet var memoNumLabel: UILabel!
    @IBOutlet var companyField: UITextField!
    @IBOutlet var dateField: UITextField!

    //coreData(エンティティがMemo)
    var memo:Memo?

    //UIDatePickerを定義するための変数
    var datePicker: UIDatePicker = UIDatePicker()
    //resultDateで1日前を日付計算
    var resultDate:Date?
    //MemoTableViewConrtollerから引き渡されたcontext
    var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext


/*
 画面が呼ばれる前
*/
    override func viewDidAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

    }
    //テキストフィールド以外のところをタップするとキーボードが閉じる
    @IBAction func tapScreen(_ sender: Any) {
        self.view.endEditing(true)
    }

/*
 画面が呼ばれた時
*/
    override func viewDidLoad() {
        super.viewDidLoad()
        titleField.delegate = self
        memoTextView.delegate =  self
        companyField.delegate = self
        dateField.delegate = self

        // メモがなければ新規作成
        if let memo = self.memo{
            //メモの値を表示。
            editedMemo(memo)
        }

        // ピッカー設定
        datePicker.datePickerMode = UIDatePicker.Mode.dateAndTime
        datePicker.timeZone = NSTimeZone.local
        datePicker.locale = Locale(identifier: "ja")
        dateField.inputView = datePicker

        // 決定バーの生成
        let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35))
        let spacelItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(ViewController.doneBtn))
        toolbar.setItems([spacelItem, doneItem], animated: true)

        // インプットビュー設定(紐づいているUITextfieldへ代入)
        dateField.inputView = datePicker
        dateField.inputAccessoryView = toolbar
        //キーボードを閉じる
        view.endEditing(true)
    }
/*
 ピッカーのdoneボタン
*/

    // UIDatePickerのDoneを押したら発火
    @objc func doneBtn() {
        dateField.endEditing(true)

        // 日付のフォーマット
        let formatter = DateFormatter()
        //"yyyy年MM月dd日"を"yyyy/MM/dd"したりして出力の仕方を好きに変更できる
        formatter.dateFormat = "yyyy年MM月dd日H時"
        //datePickerで指定した日付が表示される
        dateField.text = "\(formatter.string(from: datePicker.date))"
        let pickerTime = "\(formatter.string(from: datePicker.date))"

        //前日,日本時間を設定
        resultDate = calcDate(day: -1 ,hour: 9 ,baseDate: pickerTime)
        print(resultDate)
    }

    //入力ごとに文字数をカウントする
    func textViewDidChange(_ textView: UITextView) {
        let str = memoTextView.text
        let commentNum = memoTextView.text.count
        //空白と改行を抽出して取り除く
        let newStr = String(str!.unicodeScalars
            .filter(CharacterSet.whitespacesAndNewlines.contains)
            .map(Character.init))
        let numLabel = newStr.count
        memoNumLabel.text = String(commentNum - numLabel)

    }

    func editedMemo(_ memo:Memo){
        //編集用に表示
        titleField.text = memo.title
        companyField.text = memo.company
        memoTextView.text = memo.memoText
        memoNumLabel.text = memo.memoNum
        dateField.text = memo.memoDate

    }

    @IBAction func saveMemo(_ sender: Any) {
        //alertの設定
        let alert: UIAlertController = UIAlertController(title: "メモの登録", message: "この内容で保存しますか?", preferredStyle:  UIAlertController.Style.alert)

        // キャンセルボタン
        let cancelAction: UIAlertAction = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel, handler:{
            // ボタンが押された時の処理を書く(クロージャ実装)
            (action: UIAlertAction!) -> Void in
            print("Cancel")
        })


        // OKボタン押下時のイベント
        let okAction = UIAlertAction(title: "OK", style: .default) { (action) in

            // 編集の場合は詳細画面から渡されたself.memoを変更、
            // 新規の場合は新しいMemoオブジェクトを作り、現在の日時を入れる_
            let memo: Memo = {
                if let memo = self.memo {
                    return memo
                } else {
                    let memo = Memo(context: self.context)
                    memo.createdAt = Date()
                    return memo
                }
            }()

            memo.title = self.titleField.text
            memo.company = self.companyField.text
            memo.memoText = self.memoTextView.text
            memo.memoNum = self.memoNumLabel.text
            memo.memoDate = self.dateField.text
            memo.alertDate = self.resultDate

            // 上で作成したデータをデータベースに保存
            (UIApplication.shared.delegate as! AppDelegate).saveContext()

            self.dismiss(animated: true, completion: nil)

            //入力値をクリアにする
            self.clearData()
        }

        func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }

        alert.dismiss(animated: true, completion: nil)

        // ③ UIAlertControllerにActionを追加
        alert.addAction(cancelAction)
        alert.addAction(okAction)

        // ④ Alertを表示
        present(alert, animated: true, completion: nil)

    }
    // 入力値をクリア
    func clearData()  {
        titleField.text = ""
        companyField.text = ""
        memoTextView.text = ""
        memoNumLabel.text = "0"
        dateField.text = ""
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        //キーボードを隠す
        textField.resignFirstResponder()
        return true
    }

    func applicationWillTerminate(_ application: UIApplication) {
        self.saveContext()
    }
    //データを保存
    func saveContext () {

        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
                print(context)

            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

    func calcDate(day:Int ,hour:Int ,baseDate:String ) -> Date {
        /////////////////該当箇所///////////////////
///////////////////////////////////////////
        let formatter = DateFormatter()
//        formatter.locale = NSLocale(localeIdentifier: "ja_JP") as Locale
        formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

        var components = DateComponents()

        components.setValue(day,for: Calendar.Component.day)
        components.setValue(hour,for: Calendar.Component.hour)

        let calendar = Calendar(identifier: Calendar.Identifier.gregorian)

        var base:Date?

        if let _:String = baseDate {
            print(formatter.date(from: baseDate))
            print(baseDate)
            if  formatter.date(from: baseDate) != nil {
                base = formatter.date(from: baseDate)!
            } else {
                print("baseDateの日付変換に失敗したので本日を使用します")
                base = Date()
            }

        } else {
            base = Date()
        }
  /////////////////該当箇所///////////////////
///////////////////////////////////////////      
        return calendar.date(byAdding: components, to: base!)!
    }
}


アドバイスいただきたいです。よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • MasakiHori

    2019/03/02 14:24

    これはかなり多くの人が勘違いする部分なので仕方ありません。
    Date はユーザの場所や言語にかかわらない日時に対する絶対的指標ととらえておけばいいです。
    表示に関しても、特殊なことをしない限り、システムの提供するものを使っておけばシステムがいいようにしてくれます。
    LocaleやTimezoneを気にしなくてはいけないのは、システム設定を無視して常に日本語で日本時間で表示したいなどの場合です。
    あと、Deteのdescriptionはテストやデバッグ用ですので、これをユーザーに見せてはいけません。

    キャンセル

  • lilybelly

    2019/03/02 16:46

    大変勉強になりました。
    ご指摘いただいた部分を修正してアプリ開発進めていきたいと思います。
    ありがとうございました。今後ともよろしくお願いいたします。

    キャンセル

  • takabosoft

    2019/03/06 15:23

    解決したのでしたら、(ご自分で回答を登録するなりして)質問をクローズしてください。

    キャンセル

回答 1

check解決した方法

0

文字型から日付型に変更させたかったのですが、
そもそも文字列ではなく最初から日付型で保存してしまって構わないとのアドバイスをいただき、
その通りに行ったところ問題なく開発が進みました。
ご協力していただいたみなさんありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る