トラック
/
Julia
Julia
/
演習
/
美容の予約
美容の予約

美容の予約

学習演習

はじめに

「日付と時刻は、小さい子どもにも教えられるものです。いったい何が難しいのでしょう?」

多くのプログラマーがこの過ちを犯してきました。そして、その後に味わう経験は、健康や幸福に悪い影響を与えがちです。

日付と時刻を扱う、ちょっとした以上のプログラミングをするなら、少なくとも起こりうる問題を理解し、和らげる用意はしておくべきです。

この文書では、Juliaが日付と時刻をどのように扱うかの概要を説明します。ただし、詳しい内容についてはマニュアルを参照する必要があります。 Wikibookもあり、説明がよりわかりやすいと感じる読者もいるでしょう。型階層を表した便利な図も含まれています。

Datesモジュール

基本的な機能のほとんどはDatesにあります。Datesはデフォルトのインストールに含まれていますが、標準ライブラリの一部ではありません。

このモジュールを使うには、2つの方法があります。

  1. import Datesを追加し、すべての関数呼び出しの先頭にDates.を付けます。
  2. using Datesを追加します。これでモジュール全体が現在の名前空間に取り込まれます(いくつかの問題のある名前は除きます)。
# option 1
julia> import Dates

julia> Dates.now()
2025-06-21T16:02:39.080

julia> now()  # wrong: need to include `Dates.` after an import
ERROR: UndefVarError: `now` not defined in `Main`


# option 2
julia> using Dates

julia> now()  # functions are now in the namespace
2025-06-21T16:03:04.161

Datesモジュールは大きくて複雑ですが、主に4種類の機能を提供します。

  • Dates.Date:日・月・年を含みますが、時刻は含みません。
  • Dates.Time:時刻を含みますが、日付は含みません。
  • Dates.DateTime:日付と時刻を組み合わせたものです。
  • Dates.Period:時間や日付の間隔を扱います。

DateTimeには通常、タイムゾーンの情報が含まれ_ません_(Pythonではこれを「naive」なdatetimeと呼びます)。 例外はnow()で、システムクロックからローカル時刻を取得し、必要であればUTCに変換できます。

おそらく最もよくあるニーズは次のとおりです。

  • 適切な入力形式を解析して、Datesの型を構築します。
  • Datesの型から、必要な数値形式または文字列形式を取得します。
  • Date、Time、DateTimeの値にオフセットを適用します。
  • そのような2つの値の間隔を計算します。
  • 現在の日付(today())や時刻(now())を取得します。
Note

視覚的な邪魔を減らすため、この文書の残りのコード例のほとんどはusing Datesを前提とし、関数呼び出しにモジュール名Dates.を付けません。

日付と時刻の形式

日付や時刻の書き方には多くの流儀があり、文化によって異なることが少なくありません。 「7/6/23」のような数字だけの日付は曖昧で紛らわしく、多国籍企業で数多くの高くつく間違いを生んできました(これは著者が苦い実体験から述べています)。

国際規格はISO 8601で定められており、主に2つの利点があります。

  • 解析が速く、曖昧さがありません。
  • 日時を通常のテキストとして扱い、アルファベット順に並べ替えられるため、並べ替えが簡単です。

前の例で、Date.now()が2025-06-21T16:03:04.161を返すのを見ました。

これはさまざまな部分から組み立てられています。

  • 日付の部分はYYYY-MM-DDです。
  • 時刻の部分はhh:mm:ssです。
  • 小数点以下には、任意でミリ秒(またはさらに小さい単位)を付けます。

その他の文字列形式については、後の節で説明します。

日付

Dates.Dateは比較的単純な日付だけの型で、時刻やタイムゾーンについては何も知りません。

# constructor with year, month, day arguments
julia> date1 = Date(2025, 06, 23)
2025-06-23

# constructor with ISO 8601 string
julia> date2 = Date("2025-06-23")
2025-06-23

julia> date1 == date2
true

julia> typeof(date1)
Date

日付の個々の部分を取り出すアクセサ関数があり、これらは大文字と小文字で区別されるペアになっています。

# lowercase returns an integer
julia> month(date1)
6

julia> typeof(month(date1))
Int64

# Propercase returns a Period type
julia> Month(date1)
6 months

julia> typeof(Month(date1))
Month

クエリ関数

カレンダーをある程度理解している関数がいろいろと用意されています。 次の例では、その一部だけを紹介します。

julia> date1
2025-06-23

julia> dayofweek(date1) # 1 = Monday, 7 = Sunday
1

julia> dayname(date1)
"Monday"

julia> daysinmonth(date1)
30

julia> isleapyear(date1)
false

julia> lastdayofmonth(date1)
2025-06-30

時刻

Dates.Timeは時刻だけを扱う基本の型です。 日付については何も知りません。真夜中を過ぎると、時刻は自動的にTime(0, 0, 0)に戻ります。

コンストラクタの完全な形式はTime(hour, min, sec, millisec, microsec)、またはこれに相当するISO 8601の形式です。

すべての引数は省略可能で、デフォルトは0です。

julia> Time(14, 57, 25, 142)
14:57:25.142

julia> Time("14:57:25.142")
14:57:25.142

julia> Time(14, 57, 25, 142, 32)
14:57:25.142032

Dateと同様に、各部分はアクセサ関数で取得できます。

julia> minute(t) # => integer
57

julia> Minute(t) # => Period
57 minutes

日時

Dates.DateTimeは、Date型とTime型の機能のほとんどを組み合わせたものです。

julia> dt1 = DateTime(2025, 6, 23, 14, 57, 25, 142)
2025-06-23T14:57:25.142

# date1 and t were defined in previous examples
julia> dt2 = DateTime(date1, t)
2025-06-23T14:57:25.142

年・月・日の引数のデフォルトは1、時刻の引数のデフォルトはすべて0です。

これらの引数をすべて正しく把握するのは大変なので、入力にはISO形式を使うほうがよいでしょう。

あるいは、上の例のように、DateとTimeからDateTimeを構築することもできます。

期間

日付・時刻の各要素には、YearからNanosecondまで、それぞれPeriod型があり、時間の間隔を表します。 これらは特に計算に役立ちます。

julia> dt1
2025-06-23T14:57:25.142

julia> Year(21)
21 years

julia> Second(61)
61 seconds

julia> dt1 + Year(21) - Second(61)
2046-06-23T14:56:24.142

julia> Date(2025, 07, 03) - Date(2025, 04, 25)
69 days

Period型を扱うとき、Juliaは月の長さのようなカレンダーの不規則さを賢く処理しようとし、必要に応じて丸めます。

# February has fewer days than January
julia> Date(2025, 1, 31) + Month(1) 
2025-02-28

文字列表現

先にISO 8601規格に触れました。これは、Juliaが文字列をフォーマットしたり解析したりするときの、日付・時刻のデフォルトの表現です。

もちろん、他にも多くの形式がよく使われており、しばしば文化ごとに異なります。 これらに対応するため、JuliaにはDateFormat型があり、任意の複雑さのフォーマット文字列から構築できます。

# dateformat"" string syntax
julia> dtfmt = dateformat"d/m/y"
dateformat"d/m/y"

julia> typeof(dtfmt)
DateFormat{Symbol("d/m/y"), Tuple{Dates.DatePart{'d'}, Dates.Delim{Char, 1}, Dates.DatePart{'m'}, Dates.Delim{Char, 1}, Dates.DatePart{'y'}}}

# DateFormat constructor
julia> DateFormat("d/m/y")
dateformat"d/m/y"

上の例はごく単純で、ヨーロッパでよく使われる日/月/年の形式の日付にすぎません。 2種類の構文を紹介しましたが、結果は同じです。

DateFormatは、適した文字列を解析するときに使えます。

julia> DateTime("14/7/2023", dtfmt)
2023-07-14T00:00:00

DateFormatの作成には大きなオーバーヘッドがかかるので、多くの日付・時刻文字列をループで処理するときは、この手順を一度だけ行う(ここでのdtfmtのように)と性能の助けになります。

形式はこれよりずっと複雑にできるので、詳しくはマニュアルを参照してください。

同じDateFormatは、Dates.format()関数を使って、日付・時刻を任意の文字列形式で出力するのにも使えます。

残念ながら、formatとは名前空間が衝突するようです(少なくともJulia 1.11.5の時点では)。そのため、using Datesの後でもDates.の接頭辞を付けるのがよいでしょう。

julia> dt1
2025-06-23T14:57:25.142

julia> Dates.format(dt1, dateformat"e, u d")
"Mon, Jun 23"

ドキュメントにあるように、"e, u d"は曜日と月の省略されたテキスト表現を与えます。

テキスト表現のデフォルトは英語です。 他の言語も追加できますが、現時点でのサポートは十分とはいえません。

説明

この演習では、2012年9月15日にオープンしたニューヨークの美容サロン向けの、予約スケジューラーに取り組みます。

タスクは5つあり、どれも予約の日付を扱います。 時刻は24時間表記です(AM/PMは使いません)。

1. 数字だけの予約日付を解析する

schedule_appointment()関数を実装して、テキストで書かれた予約日付を対応するDateTime形式に解析しましょう。

入力はアメリカ式の月/日/年の順であることに注意してください。

julia> schedule_appointment("07/25/2023 13:45:00")
2023-07-25T13:45:00

2. 予約がすでに過ぎているかどうかを確認する

has_passed()関数を実装しましょう。この関数は予約日付を受け取り、その予約が過去のものかどうかを確認します。

julia> has_passed(DateTime(1999, 12, 31, 9, 0, 0))
true

3. 予約が午後かどうかを確認する

is_afternoon_appointment()関数を実装しましょう。この関数は予約日付を受け取り、その予約が午後(12:00以降、18:00より前)かどうかを確認します。

julia> is_afternoon_appointment(DateTime(2023, 3, 29, 15, 0, 0))
true

4. 予約の日時を説明する

description()関数を実装しましょう。この関数は予約日付を受け取り、その日時の説明を返します。

julia> describe(DateTime(2023, 3, 29, 15, 0, 0))
"You have an appointment on Wednesday, March 29, 2023 at 15:00"

5. 記念日を返す

anniversary_date()関数を実装しましょう。この関数は今年の記念日、つまり9月15日を返します。

julia> anniversary_date()  # run during 2025
2025-09-15
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Julia Exercism

美容の予約を始める準備はできましたか?

Exercismに登録すれば、35個のコンセプト128個の演習、そして本物の人間によるメンタリングとともに、Juliaを学んでマスターできます。すべて無料です。