Swift DateFormatter не конвертирует дату в соответствии с предустановленным форматом даты - PullRequest
0 голосов
/ 14 июля 2020

Я столкнулся с проблемой, когда выполнял онлайн-упражнение для Swfit на Exercism . Однако написанный мной код не смог пройти тестовый набор, предоставленный веб-сайтом. Проблема, кажется, l ie с форматом даты результирующего объекта даты, заключенного в необязательный.

Мне удалось получить формат входящего dateString в соответствии с форматом даты набора тестов. Однако мне не удалось заставить destinationDate соответствовать формату даты набора тестов.

Я пытался использовать ISO8601DateFormatter, но компилятор на моем старом Ma c не поддерживает это класс. Я пробовал свой код на онлайн-компиляторах Swift, но результаты тоже пока неудовлетворительны.

Упражнение описывается следующим образом:

Вычислите момент, когда кто-то прожил 10 ^ 9 секунд.

Гигасекунда равна 10 ^ 9 (1000000000) секунд.

Я написал следующий код:

    import Foundation
    
    func Gigasecond(from dateString: String) -> Date? {
        let GIGASECOND: Double = 1_000_000_000
        let RFC3339DateFormatter = DateFormatter()
    
        RFC3339DateFormatter.locale = Locale(identifier: "en_US_POSIX")
        RFC3339DateFormatter.dateFormat = "yyyy-MM-dd'T'hh:mm:ss"
    
        let sourceDate = RFC3339DateFormatter.date(from: dateString)
        var destinationDate: Date? = Date(timeInterval: GIGASECOND, since: sourceDate ?? Date())
        let destDateString = RFC3339DateFormatter.string(from: destinationDate ?? Date())
        destinationDate = RFC3339DateFormatter.date(from: destDateString)
      
        return destinationDate
    }

Тест набор для этого упражнения, предоставляемый веб-сайтом, выглядит следующим образом:

//GigasecondTests.swift

import XCTest
@testable import Gigasecond

class GigasecondTests: XCTestCase {

    func test1 () {
        let gs = Gigasecond(from: "2011-04-25T00:00:00")?.description
        XCTAssertEqual("2043-01-01T01:46:40", gs)
    }

    func test2 () {
        let gs = Gigasecond(from: "1977-06-13T00:00:00")?.description
        XCTAssertEqual("2009-02-19T01:46:40", gs)
    }

    func test3 () {
        let gs = Gigasecond(from: "1959-07-19T00:00:00")?.description
        XCTAssertEqual("1991-03-27T01:46:40", gs)
    }

    func testTimeWithSeconds () {
        let gs = Gigasecond(from: "1959-07-19T23:59:59")?.description
        XCTAssertEqual("1991-03-28T01:46:39", gs)
    }

    func testFullTimeSpecified () {
        let gs = Gigasecond(from: "2015-01-24T22:00:00")?.description
        XCTAssertEqual("2046-10-02T23:46:40", gs)
    }

    func testFullTimeWithDayRollOver () {
        let gs = Gigasecond(from: "2015-01-24T23:59:59")?.description
        XCTAssertEqual("2046-10-03T01:46:39", gs)
    }

    static var allTests: [(String, (GigasecondTests) -> () throws -> Void)] {
        return [
            ("test1 ", test1 ),
            ("test2 ", test2 ),
            ("test3 ", test3 ),
            ("testTimeWithSeconds ", testTimeWithSeconds ),
            ("testFullTimeSpecified ", testFullTimeSpecified ),
            ("testFullTimeWithDayRollOver ", testFullTimeWithDayRollOver ),
        ]
    }
} 

// LinuxMain.swift
import XCTest
@testable import GigasecondTests

XCTMain([
    testCase(GigasecondTests.allTests),
    ])

Пожалуйста, помогите узнать, в чем проблема с моим кодом. Спасибо большое!

Ответы [ 3 ]

3 голосов
/ 14 июля 2020

Вы используете неправильный формат даты hh для 01-12 часов. Вам нужно HH 00-23 часа. Ваш dateFormat должен быть "yyyy-MM-dd'T'HH:mm:ss". Обратите внимание, что описание даты вернет описание даты UT C (а не текущий часовой пояс). Вам нужно будет использовать тот же форматировщик даты, чтобы сгенерировать строку из проанализированной даты для проверки равенства:

extension Formatter {
    static let rfc3339: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
        return dateFormatter
    }()
}

Ваш метод гигасекунды можно упростить как:

func gigasecond(from dateString: String) -> Date? {
    return Formatter.rfc3339.date(from: dateString)?
        .addingTimeInterval(1_000_000_000)
}

Тестирование:

if let gs = gigasecond(from: "2011-04-25T00:00:00") {
    "2043-01-01T01:46:40" == Formatter.rfc3339.string(from: gs)  // true
}
0 голосов
/ 15 июля 2020

Вот альтернативное решение этого вопроса, которое подходит для более широкого контекста приложения. Это решение основано на предложениях моего наставника на веб-сайте Exercism .

Также выражаю благодарность go Лео Дабусу, который предложил добавить информацию о часовом поясе в средство форматирования даты, чтобы избежать влияние неявно принятого по умолчанию местного часового пояса.

Код выглядит следующим образом:

import Foundation

struct Gigasecond {
    public let gigasecond : Double = 1_000_000_000
    private let rfc3339DateFormatter : DateFormatter = { 
            let myFormatter = DateFormatter()
            myFormatter.timeZone = TimeZone(identifier: "UTC")
            myFormatter.locale = Locale(identifier: "en_US_POSIX")
            myFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"

            return myFormatter 
    }()
    let description : String

    init?(from dateString: String) {

        let sourceDate = rfc3339DateFormatter.date(from: dateString)
        guard let srcDate = sourceDate else { 
            description = "" 
            return 
        }
        let destinationDate = Date(timeInterval: gigasecond, since: srcDate)
        description = rfc3339DateFormatter.string(from: destinationDate)
    }
}

Пожалуйста, дайте мне знать, могу ли я что-нибудь сделать, чтобы улучшить приведенный выше код. Большое спасибо.

0 голосов
/ 14 июля 2020

Я внес некоторые изменения в свой код, и мне повезло, что он прошел тестирование. Мой модифицированный код выглядит следующим образом:

import Foundation

func Gigasecond(from dateString: String) -> String? {
    let GIGASECOND: Double = 1_000_000_000
    let RFC3339DateFormatter = DateFormatter()

    RFC3339DateFormatter.locale = Locale(identifier: "en_US_POSIX")
    RFC3339DateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
    RFC3339DateFormatter.timeZone = TimeZone(secondsFromGMT: 0)

    let sourceDate = RFC3339DateFormatter.date(from: dateString)
    var destinationDate: Date? = Date(timeInterval: GIGASECOND, since: sourceDate ?? Date())
    var destDateString = RFC3339DateFormatter.string(from: destinationDate ?? Date())
    destinationDate = RFC3339DateFormatter.date(from: destDateString)
    destDateString = RFC3339DateFormatter.string(from: destinationDate ?? Date())
  
    return destDateString
}

Оказалось, что полученный destinationDate нужно переформатировать в формате даты "yyyy-MM-dd'T'HH:mm:ss", чтобы получить требуемую строку даты.

My Еще раз сердечное спасибо go всем тем, кто участвовал в обсуждении решений этого вопроса. Без их совета я бы не стал искать решение.

Я ожидаю дальнейшего обсуждения более эффективных способов решения вопроса. Спасибо.

...