ottijp blog

「ソフトウェア設計のトレードオフと誤り」にある日付の演算に伴う注意点をswiftで検証する

  • 2024-06-19

「ソフトウェア設計のトレードオフと誤り」という本を読んでいます.

cf. O’Reilly Japan - ソフトウェア設計のトレードオフと誤り

この本の「7章 日付と時間のデータを効率よく扱う」に日付の演算に関する実装の注意点が書いてあり,swiftだとどのような挙動になるのか,学習のためにコードを書いてみました.

検証用のテストコード

import XCTest
@testable import swift_date_learning

final class swift_date_learningTests: XCTestCase {
  let dateFormatter = {
    let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" return dateFormatter }()

  func test_1_131日の2ヶ月後は331() throws {
    let endOfJanuary = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 31))!
    let endOfJanuaryPlus2Months = Calendar.current.date(byAdding: .month, value: 2, to: endOfJanuary)!
    XCTAssertEqual(dateFormatter.string(from: endOfJanuaryPlus2Months), "2024-03-31")
  }

  func test_2_131日の1ヶ月後の1ヶ月後は329() throws {
    let endOfJanuary = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 31))!
    let endOfJanuaryPlus1Month = Calendar.current.date(byAdding: .month, value: 1, to: endOfJanuary)!
    let endOfJanuaryPlus1MonthPlus1Month = Calendar.current.date(byAdding: .month, value: 1, to: endOfJanuaryPlus1Month)!
    XCTAssertEqual(dateFormatter.string(from: endOfJanuaryPlus1MonthPlus1Month), "2024-03-29")
  }

  func test_3_131日の1ヶ月後は229() throws {
    let endOfJanuary = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 31))!
    let endOfJanuaryPlus1Month = Calendar.current.date(byAdding: .month, value: 1, to: endOfJanuary)!
    XCTAssertEqual(dateFormatter.string(from: endOfJanuaryPlus1Month), "2024-02-29")
  }

  func test_4_229日の1ヶ月前は129() throws {
    let endOfFebrary = Calendar.current.date(from: DateComponents(year: 2024, month: 2, day: 29))!
    let endOfFebraryMinus1Month = Calendar.current.date(byAdding: .month, value: -1, to: endOfFebrary)!
    XCTAssertEqual(dateFormatter.string(from: endOfFebraryMinus1Month), "2024-01-29")
  }
}

テスト実行結果

$ swift test 2>/dev/null | xcpretty
All tests
Test Suite swift-date-learningPackageTests.xctest started
swift_date_learningTests
    ✓ test_1_1月31日の2ヶ月後は3月31日 (0.004 seconds)
    ✓ test_2_1月31日の1ヶ月後の1ヶ月後は3月29日 (0.000 seconds)
    ✓ test_3_1月31日の1ヶ月後は2月29日 (0.000 seconds)
    ✓ test_4_2月29日の1ヶ月前は1月29日 (0.000 seconds)


         Executed 4 tests, with 0 failures (0 unexpected) in 0.004 (0.005) seconds

2ヶ月を加算した日付と,1ヶ月を加算した日付にさらに1ヶ月を加算した日付は異なる場合がある

1月31日の2ヶ月後は3月31日になります. しかし,1月31日に1ヶ月を加算した日付(今年は閏年なので2月29日)にさらに1ヶ月を加算した場合,3月29日になります. これは1,2つ目のテストコードです.

1ヶ月を加算した日付から,1ヶ月を減算しても元の日付にはならない場合がある

1月31日の1ヶ月後は,今年は閏年なので2月29日になります. しかし,2月29日の1ヶ月前は1月29日になります. これは3,4つ目のテストコードです.


ottijp
都内でアプリケーションエンジニアをしています
© 2024, ottijp