datecount

日数カウンターを作ろう

Dateオブジェクトは年月日の加算減算ができます。年月の繰り上がり繰り下がりを意識せずともいい塩梅に処理してくれるので、2016年2月1日に30日を足しても2月31日なんていう答えにはならず、うるう年も考慮して2016年3月2日と返してくれます。

3年後の今日って何月何日だっけ?とかは悩みませんが、180日後って具体的に何月何日?となると少し悩みますよね。海外行って買ったSIMのExpiry Dateが180日後で次回渡航までもつかな?とかたまにあるんですよ。

なのでこんな仕様を考えてみました。

  • 今日を起点に指定日数経過前後の日付を得ることができる
  • 過去の日付は-b(before)、未来の日付は-a(after)スイッチで指定する

つまり今日買ったSIMの有効期限が切れる180日後の日付問い合わせは次のようになります。

$ datecount -a 180
180day(s) after is Thr Jul 28 2016

この演算自体はたったの1行、30行目でおこなっているだけです。

#! /usr/bin/swift
import Foundation
var msg: [String] = ["", "date counter ver.0.1 (c)2015 Takeru-chan\nusage: datecount -[a|b] n", "datecount: Specified term is not in range A.D.1100..9999."]
var msg_status: Int32 = 0
let month_odr: [String] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
let week_odr: [String] = ["Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"]
var diff_days:Int = 0
// Date型の引数を受け取り、DateComponents型を返す関数
func getCalComp(date: Date) -> DateComponents {
    let cal: NSCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!
    let components: DateComponents = cal.components([.year, .month, .day, .weekday], from:date)
    return components
}
// 実質プログラムの始まり
let arguments: [String] = CommandLine.arguments
if arguments.count == 3 {
    switch arguments[1] {
    case "-a":
        msg[0] = arguments[2] + "day(s) after is "
        diff_days = Int(arguments[2])!
    case "-b":
        msg[0] = arguments[2] + "day(s) before is "
        diff_days = Int(arguments[2])! * (-1)
    default:
        msg_status = 1
    }
    if msg_status != 1 {
        let now: Date = Date()
        var cal_comp: DateComponents = getCalComp(date:now)
        cal_comp.day = cal_comp.day! + diff_days
        var result_date: Date = Calendar.current.date(from:cal_comp)!
        cal_comp = getCalComp(date:result_date)
        msg[0] = msg[0] + week_odr[cal_comp.weekday! - 1] + " " + month_odr[cal_comp.month! - 1]
        if cal_comp.year! >= 1100 && cal_comp.year! <= 9999 {
            msg[0] = msg[0] + " \(cal_comp.day!) \(cal_comp.year!)"
        } else {
            msg_status = 2
        }
    }
} else {
    msg_status = 1
}
print("\(msg[Int(msg_status)])\n")
exit(msg_status)

プログラムの流れをざっくり追いかけると

  1. 初期化(msg_status=0)
  2. 引数が2個あることの確認(オプションエラーならmsg_status=1)
  3. オプションスイッチが”-a”または”-b”であることのチェック(オプションエラーならmsg_status=1)
  4. 異常終了でなければ日付演算開始(演算エラーならmsg_status=2)
  5. msg_statusの値に応じたメッセージ表示と終了処理を実行

となります。終了時のパターンは次の3パターンです。

  • 問い合わせ内容に応じた日付を返す(正常終了)
  • プログラムクレジットを表示する(オプション指示エラー等の異常終了)
  • 演算範囲を超えてしまった旨の警告表示(不正な結果となるため実行中止)

そこでそれぞれに応じたメッセージを配列にしてmsg_statusの値によってメッセージを切り替えられるようにしておきます。さらにmsg_statusの値を終了コードとしてexitに渡すことで終了処理が共通化できます。

なお、正常終了時だけは計算結果に応じてメッセージ内容が変わるので、都度データ書き換えが発生します。