Qtにおける日時・時間・日付の基本と使い方

はい、承知いたしました。Qtにおける日時・時間・日付の基本と使い方について、詳細な説明を含む記事を作成します。約5000語を目指し、各クラスの基本的な使い方から応用、注意点までを網羅します。


Qtにおける日時・時間・日付の基本と使い方:徹底解説

ソフトウェア開発において、日時や時間の扱いは非常に一般的かつ重要なタスクです。ログの記録、イベントのスケジュール設定、データのエントリー時刻の記録、経過時間の計算、ユーザーインターフェースへの表示など、多くの場面で正確かつ信頼性の高い日時処理が求められます。しかし、タイムゾーン、夏時間(DST)、うるう年、異なる日付形式など、日時を取り巻く様々な複雑さから、その扱いは時に開発者にとって挑戦となります。

Qtフレームワークは、これらの課題に対応するために、強力かつ使いやすい日時関連のクラスを提供しています。主に以下の3つのクラスが中心となります。

  1. QDate: 日付(年、月、日)を扱います。
  2. QTime: 時間(時、分、秒、ミリ秒)を扱います。
  3. QDateTime: 日付と時間、そしてタイムゾーン情報を組み合わせて扱います。

これらの基本クラスに加えて、タイムゾーンをより詳細に扱うための QTimeZone クラスや、ローカライズされた表示・解析のための QLocale との連携も重要になります。

この記事では、これらのQtのクラスについて、その基本的な使い方から、生成、要素へのアクセス、算術演算、比較、文字列への変換、タイムゾーンの扱い、そして実践的な応用例まで、詳細に解説します。

1. QDateクラス: 日付の操作

QDate クラスは、暦上の日付、すなわち「年」「月」「日」の情報をカプセル化します。時刻やタイムゾーンの情報は含みません。カレンダー上の特定の日を表現するのに使用されます。

1.1 QDateオブジェクトの生成

QDate オブジェクトを生成する主な方法はいくつかあります。

  • 現在の日付を取得する:
    静的関数 QDate::currentDate() を使用すると、システムのローカルタイムゾーンにおける現在の日付を取得できます。

    “`cpp

    include

    include

    int main() {
    QDate today = QDate::currentDate();
    qDebug() << “今日の日付:” << today.toString(Qt::ISODate); // ISO 8601形式で出力
    return 0;
    }
    “`

  • 特定の日付を指定して生成する:
    コンストラクタに年、月、日を指定して生成します。指定した日付が無効な場合(例: 2月30日)、生成される QDate オブジェクトは無効な状態になります。

    “`cpp

    include

    include

    int main() {
    QDate specificDate(2023, 10, 27); // 2023年10月27日
    qDebug() << “特定の日付:” << specificDate.toString();

    QDate invalidDate(2023, 2, 30); // 無効な日付
    qDebug() << "無効な日付:" << invalidDate.isValid(); // false
    return 0;
    

    }
    “`

  • 無効なQDateオブジェクトを生成する:
    デフォルトコンストラクタや、無効な引数で生成すると、無効な QDate オブジェクトになります。isValid() メソッドで有効性を確認できます。

    “`cpp

    include

    include

    int main() {
    QDate invalid; // デフォルトコンストラクタで無効
    qDebug() << “無効な日付オブジェクト:” << invalid.isValid(); // false
    return 0;
    }
    “`

1.2 日付の構成要素へのアクセス

QDate オブジェクトから、年、月、日、曜日、年内の日数などの構成要素を取得できます。

“`cpp

include

include

int main() {
QDate date(2023, 10, 27);

qDebug() << "年:" << date.year();       // 2023
qDebug() << "月:" << date.month();      // 10
qDebug() << "日:" << date.day();        // 27
qDebug() << "曜日 (ISO):" << date.dayOfWeek(); // 1 (月曜日) ... 7 (日曜日)
qDebug() << "曜日 (Qt):" << date.dayOfWeek(); // 1 (月曜日) ... 7 (日曜日), Qt::Monday to Qt::Sunday
qDebug() << "年内の日数:" << date.dayOfYear(); // 300 (2023年10月27日は300日目)
qDebug() << "月内の日数:" << date.daysInMonth(); // 31 (10月は31日まで)
qDebug() << "年内の日数:" << date.daysInYear();  // 365 (2023年は平年)

QDate leapYear(2024, 1, 1);
qDebug() << "うるう年の日数:" << leapYear.daysInYear(); // 366 (2024年はうるう年)

return 0;

}
``dayOfWeek()はISO 8601規格に従い、月曜日を1、日曜日を7として返します。また、Qt::DayOfWeek列挙型の値(Qt::Monday,Qt::Tuesday, ... ,Qt::Sunday`)としても解釈できます。

1.3 日付の有効性の確認

isValid() メソッドで、QDate オブジェクトが有効な日付を表しているかを確認します。これは、ユーザー入力の検証などにおいて重要です。

“`cpp

include

include

int main() {
QDate validDate(2023, 12, 31);
QDate invalidDate(2023, 13, 1);

qDebug() << "2023/12/31 は有効か?" << validDate.isValid();   // true
qDebug() << "2023/13/1 は有効か?" << invalidDate.isValid(); // false
return 0;

}
“`

1.4 日付の算術演算

QDate は、日付に日数、月数、年数を加減算するメソッドを提供します。これらのメソッドは、元のオブジェクトを変更せず、新しい QDate オブジェクトを返します。

  • 日数を加減算する: addDays(int ndays)
    “`cpp
    #include
    #include

    int main() {
    QDate date(2023, 10, 27);
    QDate nextWeek = date.addDays(7); // 7日後
    QDate yesterday = date.addDays(-1); // 1日前

    qDebug() << "今日:" << date.toString(Qt::ISODate);
    qDebug() << "1週間後:" << nextWeek.toString(Qt::ISODate);
    qDebug() << "昨日:" << yesterday.toString(Qt::ISODate);
    
    QDate endOfMonth(2023, 10, 31);
    QDate nextDay = endOfMonth.addDays(1); // 月をまたぐ
    qDebug() << "10/31 の次の日:" << nextDay.toString(Qt::ISODate); // 2023-11-01
    
    QDate endOfYear(2023, 12, 31);
    QDate nextYear = endOfYear.addDays(1); // 年をまたぐ
    qDebug() << "12/31 の次の日:" << nextYear.toString(Qt::ISODate); // 2024-01-01
    return 0;
    

    }
    ``addDays()` は、月の終わりや年の終わりを正確に処理します。

  • 月数を加減算する: addMonths(int nmonths)
    月数を加減算します。結果の日付は、可能な限り元の日の値を維持しようとします。例えば、1月31日に1ヶ月を加えると、2月は31日までないので、2月末(2月28日または29日)になります。

    “`cpp

    include

    include

    int main() {
    QDate date(2023, 10, 27);
    QDate nextMonth = date.addMonths(1); // 1ヶ月後
    QDate prevMonth = date.addMonths(-1); // 1ヶ月前

    qDebug() << "今日:" << date.toString(Qt::ISODate);
    qDebug() << "1ヶ月後:" << nextMonth.toString(Qt::ISODate); // 2023-11-27
    qDebug() << "1ヶ月前:" << prevMonth.toString(Qt::ISODate); // 2023-09-27
    
    QDate jan31(2023, 1, 31);
    QDate febPlus1Month = jan31.addMonths(1); // 1月31日 + 1ヶ月
    qDebug() << "1/31 + 1ヶ月:" << febPlus1Month.toString(Qt::ISODate); // 2023-02-28
    
    QDate jan31Leap(2024, 1, 31); // 2024年はうるう年
    QDate febPlus1MonthLeap = jan31Leap.addMonths(1);
    qDebug() << "1/31 (うるう年) + 1ヶ月:" << febPlus1MonthLeap.toString(Qt::ISODate); // 2024-02-29
    return 0;
    

    }
    “`

  • 年数を加減算する: addYears(int nyears)
    年数を加減算します。うるう年の2月29日の場合、加算後の年がうるう年であれば2月29日のまま、平年であれば2月28日になります。

    “`cpp

    include

    include

    int main() {
    QDate date(2023, 10, 27);
    QDate nextYear = date.addYears(1); // 1年後
    QDate prevYear = date.addYears(-1); // 1年前

    qDebug() << "今日:" << date.toString(Qt::ISODate);
    qDebug() << "1年後:" << nextYear.toString(Qt::ISODate); // 2024-10-27
    qDebug() << "1年前:" << prevYear.toString(Qt::ISODate); // 2022-10-27
    
    QDate leapDay(2020, 2, 29); // 2020年はうるう年
    QDate plus1Year = leapDay.addYears(1); // 2021年は平年
    QDate plus4Years = leapDay.addYears(4); // 2024年はうるう年
    
    qDebug() << "2020/02/29 + 1年:" << plus1Year.toString(Qt::ISODate); // 2021-02-28
    qDebug() << "2020/02/29 + 4年:" << plus4Years.toString(Qt::ISODate); // 2024-02-29
    return 0;
    

    }
    “`

1.5 日付間の日数計算

2つの QDate オブジェクト間の日数を計算するには、daysTo(const QDate &other) メソッドを使用します。これは、other の日付がこの日付より後の場合は正の値、前の場合は負の値を返します。

“`cpp

include

include

int main() {
QDate startDate(2023, 1, 1);
QDate endDate(2023, 10, 27);

int days = startDate.daysTo(endDate);
qDebug() << startDate.toString(Qt::ISODate) << "から" << endDate.toString(Qt::ISODate) << "までの日数:" << days; // 300

int reverseDays = endDate.daysTo(startDate);
qDebug() << endDate.toString(Qt::ISODate) << "から" << startDate.toString(Qt::ISODate) << "までの日数:" << reverseDays; // -300
return 0;

}
“`

1.6 日付の比較

QDate オブジェクトは、標準的な比較演算子(<, <=, ==, !=, >=, >)を使用して比較できます。

“`cpp

include

include

int main() {
QDate date1(2023, 10, 27);
QDate date2(2023, 10, 28);
QDate date3(2023, 10, 27);

qDebug() << "date1 == date2:" << (date1 == date2); // false
qDebug() << "date1 != date2:" << (date1 != date2); // true
qDebug() << "date1 < date2:" << (date1 < date2);   // true
qDebug() << "date1 == date3:" << (date1 == date3); // true
return 0;

}
“`

1.7 文字列との相互変換

QDate を文字列に変換するには toString() メソッド、文字列から QDate を解析するには fromString() 静的メソッドを使用します。

  • toString():
    フォーマット指定子を使用したり、Qt::DateFormat 列挙型(Qt::ISODate, Qt::SystemLocaleDate, Qt::TextDate, Qt::RFC2822Date など)を使用したりできます。詳細なフォーマット指定子については、後の「フォーマットとパース」セクションで解説します。

    “`cpp

    include

    include

    int main() {
    QDate date(2023, 10, 27);

    qDebug() << "ISO形式:" << date.toString(Qt::ISODate);             // "2023-10-27"
    qDebug() << "システムロケール形式:" << date.toString(Qt::SystemLocaleDate); // システム設定に依存 (例: "2023/10/27")
    qDebug() << "テキスト形式:" << date.toString(Qt::TextDate);         // "Fri Oct 27 2023"
    qDebug() << "カスタム形式:" << date.toString("yyyy/MM/dd");      // "2023/10/27"
    qDebug() << "カスタム形式 (略式):" << date.toString("yy-M-d");   // "23-10-27"
    return 0;
    

    }
    “`

  • fromString():
    文字列と、その文字列のフォーマット指定子を引数に取ります。解析に成功した場合、有効な QDate オブジェクトを返します。失敗した場合は無効な QDate オブジェクトを返します。

    “`cpp

    include

    include

    int main() {
    QString dateString = “2023/10/27”;
    QDate parsedDate = QDate::fromString(dateString, “yyyy/MM/dd”);

    if (parsedDate.isValid()) {
        qDebug() << "解析成功:" << parsedDate.toString(Qt::ISODate);
    } else {
        qDebug() << "解析失敗";
    }
    
    QString invalidDateString = "October 27, 2023"; // フォーマットが違う
    QDate failedParse = QDate::fromString(invalidDateString, "yyyy/MM/dd");
    if (!failedParse.isValid()) {
        qDebug() << "別の解析失敗 (フォーマット違い)";
    }
    
    QString anotherString = "2023-10-27";
     QDate parsedISO = QDate::fromString(anotherString, Qt::ISODate); // 定義済みのフォーマットも使える
     if (parsedISO.isValid()) {
         qDebug() << "ISO形式からの解析成功:" << parsedISO.toString();
     }
    return 0;
    

    }
    “`

1.8 その他の便利なメソッド

  • うるう年判定: isLeapYear(int year) 静的メソッド
    cpp
    qDebug() << "2023年はうるう年か?" << QDate::isLeapYear(2023); // false
    qDebug() << "2024年はうるう年か?" << QDate::isLeapYear(2024); // true
  • ユリウス日との相互変換: toJulianDay(), fromJulianDay(qint64 jd) 静的メソッド
    ユリウス日は、紀元前4713年1月1日正午からの日数で、天文学などで日付計算に用いられる連続した数値です。異なる暦(グレゴリオ暦、ユリウス暦など)を跨いだ計算を簡単に行うのに便利です。

    “`cpp

    include

    include

    int main() {
    QDate date(2000, 1, 1);
    qint64 jd = date.toJulianDay();
    qDebug() << date.toString(Qt::ISODate) << “のユリウス日:” << jd; // 2451545

    QDate dateFromJulian = QDate::fromJulianDay(jd);
    qDebug() << jd << "から変換した日付:" << dateFromJulian.toString(Qt::ISODate); // 2000-01-01
    return 0;
    

    }
    “`

2. QTimeクラス: 時間の操作

QTime クラスは、1日のうちの特定の時刻(時、分、秒、ミリ秒)をカプセル化します。日付やタイムゾーンの情報は含みません。通常、午前0時(00:00:00.000)を基準とします。

2.1 QTimeオブジェクトの生成

  • 現在の時刻を取得する:
    静的関数 QTime::currentTime() を使用します。システムのローカルタイムゾーンにおける現在の時刻を取得します。

    “`cpp

    include

    include

    int main() {
    QTime now = QTime::currentTime();
    qDebug() << “現在の時刻:” << now.toString(Qt::ISODate); // ISO 8601形式 (HH:mm:ss.zzz)
    return 0;
    }
    “`

  • 特定の時刻を指定して生成する:
    コンストラクタに時、分、秒、ミリ秒(省略可能、デフォルトは0)を指定します。指定した時刻が無効な場合(例: 25時、70分)、無効な QTime オブジェクトになります。

    “`cpp

    include

    include

    int main() {
    QTime specificTime(12, 30, 45, 123); // 12時30分45秒123ミリ秒
    qDebug() << “特定の時刻:” << specificTime.toString();

    QTime invalidTime(25, 0, 0); // 無効な時刻
    qDebug() << "無効な時刻:" << invalidTime.isValid(); // false
    return 0;
    

    }
    “`

  • 無効なQTimeオブジェクトを生成する:
    デフォルトコンストラクタや、無効な引数で生成すると、無効な QTime オブジェクトになります。isValid() メソッドで有効性を確認できます。

    “`cpp

    include

    include

    int main() {
    QTime invalid; // デフォルトコンストラクタで無効
    qDebug() << “無効な時刻オブジェクト:” << invalid.isValid(); // false
    return 0;
    }
    “`

  • 午前0時から経過したミリ秒を指定して生成する:
    静的メソッド QTime::fromMSecsSinceStartOfDay(int msecs) を使用します。これは、00:00:00.000からの経過ミリ秒数から QTime オブジェクトを生成します。24時間(86,400,000ミリ秒)を超える値は、24で割った余りで処理されます(つまり、次の日にラップアラウンドします)。

    “`cpp

    include

    include

    int main() {
    QTime time1 = QTime::fromMSecsSinceStartOfDay(12 * 60 * 60 * 1000 + 30 * 60 * 1000); // 12時30分
    qDebug() << “午前0時から経過ミリ秒で生成:” << time1.toString(Qt::ISODate); // 12:30:00.000

    // 24時間を超える値 (25時間) はラップアラウンド
    QTime time2 = QTime::fromMSecsSinceStartOfDay(25 * 60 * 60 * 1000);
    qDebug() << "25時間後の時刻:" << time2.toString(Qt::ISODate); // 01:00:00.000
    return 0;
    

    }
    “`

2.2 時刻の構成要素へのアクセス

QTime オブジェクトから、時、分、秒、ミリ秒を取得できます。

“`cpp

include

include

int main() {
QTime time(14, 5, 8, 50); // 14時05分08秒050ミリ秒

qDebug() << "時:" << time.hour();     // 14
qDebug() << "分:" << time.minute();   // 5
qDebug() << "秒:" << time.second();   // 8
qDebug() << "ミリ秒:" << time.msec(); // 50

// 午前0時からの経過ミリ秒
qDebug() << "午前0時からの経過ミリ秒:" << time.msecsSinceStartOfDay(); // 50708050
return 0;

}
“`

2.3 時刻の有効性の確認

isValid() メソッドで、QTime オブジェクトが有効な時刻を表しているかを確認します。

“`cpp

include

include

int main() {
QTime validTime(23, 59, 59, 999);
QTime invalidTime(24, 0, 0);

qDebug() << "23:59:59.999 は有効か?" << validTime.isValid();   // true
qDebug() << "24:00:00.000 は有効か?" << invalidTime.isValid(); // false
return 0;

}
“`

2.4 時刻の算術演算

QTime は、時刻に秒数やミリ秒数を加減算するメソッドを提供します。日付を跨ぐ場合は、時刻がラップアラウンドします。

  • 秒数を加減算する: addSecs(int nsecs)
    “`cpp
    #include
    #include

    int main() {
    QTime time(12, 30, 0);
    QTime later = time.addSecs(90); // 90秒後 (1分30秒後)
    QTime earlier = time.addSecs(-120); // 120秒前 (2分前)

    qDebug() << "元の時刻:" << time.toString(Qt::ISODate); // 12:30:00
    qDebug() << "90秒後:" << later.toString(Qt::ISODate);   // 12:31:30
    qDebug() << "120秒前:" << earlier.toString(Qt::ISODate); // 12:28:00
    
    QTime endOfDay(23, 59, 50);
    QTime nextDay = endOfDay.addSecs(20); // 20秒後 (日付を跨ぐ)
    qDebug() << "23:59:50 の20秒後:" << nextDay.toString(Qt::ISODate); // 00:00:10
    return 0;
    

    }
    ``addSecs()` は、日付の境界を越えると時刻がラップアラウンドしますが、日付の変更は扱いません。

  • ミリ秒数を加減算する: addMSecs(int nms)
    addSecs と同様に、ミリ秒数を加減算します。

    “`cpp

    include

    include

    int main() {
    QTime time(12, 30, 0, 0);
    QTime later = time.addMSecs(1500); // 1500ミリ秒後 (1.5秒後)

    qDebug() << "元の時刻:" << time.toString(Qt::ISODate); // 12:30:00.000
    qDebug() << "1500ミリ秒後:" << later.toString(Qt::ISODate); // 12:30:01.500
    
    QTime almostMidnight(23, 59, 59, 500);
    QTime pastMidnight = almostMidnight.addMSecs(600); // 500 + 600 = 1100 ms (日付を跨ぐ)
    qDebug() << "23:59:59.500 の600ミリ秒後:" << pastMidnight.toString(Qt::ISODate); // 00:00:00.100
    return 0;
    

    }
    “`

2.5 時刻間の差分計算

2つの QTime オブジェクト間の秒数やミリ秒数の差分を計算するには、secsTo(const QTime &other)msecsTo(const QTime &other) メソッドを使用します。これらのメソッドは、午前0時からの経過時間に基づいて差分を計算します。other の時刻がこの時刻より後の場合は正の値、前の場合は負の値を返します。計算結果は、24時間を超える差分も考慮します(ラップアラウンドを含む)。

“`cpp

include

include

int main() {
QTime time1(10, 30, 0);
QTime time2(12, 0, 0);

int secs = time1.secsTo(time2);
qDebug() << time1.toString(Qt::ISODate) << "から" << time2.toString(Qt::ISODate) << "までの秒数:" << secs; // 5400 (1時間30分 = 90分 = 5400秒)

int reverseSecs = time2.secsTo(time1);
qDebug() << time2.toString(Qt::ISODate) << "から" << time1.toString(Qt::ISODate) << "までの秒数:" << reverseSecs; // -5400

QTime timeA(23, 0, 0);
QTime timeB(1, 0, 0); // 次の日の1時

// AからBへの差分は、Aから深夜までの時間 + 深夜からBまでの時間
// 23:00 から 00:00 まで 1時間 (3600秒)
// 00:00 から 01:00 まで 1時間 (3600秒)
// 合計 2時間 = 7200秒
int secsWrapAround = timeA.secsTo(timeB);
qDebug() << timeA.toString(Qt::ISODate) << "から" << timeB.toString(Qt::ISODate) << "までの秒数:" << secsWrapAround; // 7200

int msecsWrapAround = timeA.msecsTo(timeB);
qDebug() << timeA.toString(Qt::ISODate) << "から" << timeB.toString(Qt::ISODate) << "までのミリ秒数:" << msecsWrapAround; // 7200000

return 0;

}
``secsTo()およびmsecsTo()は、計算結果をintで返すため、約±68年の範囲に制限があります(msecsToの場合は約±24日)。それ以上の時間差を正確に計算するには、QDateTime` を使用する必要があります。

2.6 時刻の比較

QTime オブジェクトは、標準的な比較演算子を使用して比較できます。

“`cpp

include

include

int main() {
QTime time1(10, 30, 0);
QTime time2(12, 0, 0);
QTime time3(10, 30, 0, 0);

qDebug() << "time1 == time2:" << (time1 == time2); // false
qDebug() << "time1 != time2:" << (time1 != time2); // true
qDebug() << "time1 < time2:" << (time1 < time2);   // true
qDebug() << "time1 == time3:" << (time1 == time3); // true (ミリ秒まで同じ)
return 0;

}
“`

2.7 文字列との相互変換

QTime を文字列に変換するには toString() メソッド、文字列から QTime を解析するには fromString() 静的メソッドを使用します。フォーマット指定子については、後のセクションでまとめて解説します。

“`cpp

include

include

int main() {
QTime time(14, 5, 8, 50);

qDebug() << "ISO形式:" << time.toString(Qt::ISODate);      // "14:05:08.050"
qDebug() << "システムロケール形式:" << time.toString(Qt::SystemLocaleDate); // システム設定に依存
qDebug() << "カスタム形式:" << time.toString("HH:mm:ss"); // "14:05:08"
qDebug() << "カスタム形式 (午前/午後):" << time.toString("h:m:s ap"); // 例: "2:5:8 PM"

QString timeString = "14:05:08";
QTime parsedTime = QTime::fromString(timeString, "HH:mm:ss");

if (parsedTime.isValid()) {
    qDebug() << "解析成功:" << parsedTime.toString(Qt::ISODate); // 14:05:08.000
} else {
    qDebug() << "解析失敗";
}
return 0;

}
“`

3. QDateTimeクラス: 日付と時間の組み合わせ操作

QDateTime クラスは、日付 (QDate) と時間 (QTime) の情報を組み合わせて、特定の時点を表現します。さらに、タイムゾーンの情報も保持できます。通常、日時を扱う際には最も頻繁に使用されるクラスです。

3.1 QDateTimeオブジェクトの生成

  • 現在の日時を取得する:
    QDateTime::currentDateTime() はシステムのローカルタイムゾーンでの現在日時を、QDateTime::currentDateTimeUtc() はUTC(協定世界時)での現在日時を取得します。

    “`cpp

    include

    include

    int main() {
    QDateTime nowLocal = QDateTime::currentDateTime();
    QDateTime nowUtc = QDateTime::currentDateTimeUtc();

    qDebug() << "現在のローカル日時:" << nowLocal.toString(Qt::ISODate); // 例: 2023-10-27T10:30:00
    qDebug() << "現在のUTC日時:" << nowUtc.toString(Qt::ISODate);     // 例: 2023-10-27T01:30:00Z (日本時間+9時間)
    return 0;
    

    }
    ``Qt::ISODate` 形式では、末尾の ‘Z’ はUTCを表します。

  • 特定の日付と時間を指定して生成する:
    コンストラクタに QDateQTime を指定します。タイムゾーン仕様 (Qt::TimeSpec) も指定できます。デフォルトは Qt::LocalTime です。

    “`cpp

    include

    include

    int main() {
    QDate date(2023, 10, 27);
    QTime time(14, 30, 0);
    QDateTime dateTime(date, time); // デフォルトはローカルタイム

    qDebug() << "指定した日時 (ローカル):" << dateTime.toString(Qt::ISODate); // 2023-10-27T14:30:00
    
    // UTCとして解釈して生成
    QDateTime dateTimeUtc(date, time, Qt::UTC);
    qDebug() << "指定した日時 (UTCとして):" << dateTimeUtc.toString(Qt::ISODate); // 2023-10-27T14:30:00Z
    
    return 0;
    

    }
    ``Qt::LocalTimeはシステムのローカルタイムゾーンを、Qt::UTCはUTCを表します。Qt 5.2以降では、より柔軟なQTimeZoneを使用することを推奨します。Qt::TimeSpec` は下位互換性のために残されています。

  • Unix epoch (Unix時間) から生成する:
    Unix epochは、協定世界時(UTC)の1970年1月1日00:00:00からの経過秒数です。QDateTime::fromSecsSinceEpoch(qint64 secs) またはミリ秒版の QDateTime::fromMSecsSinceEpoch(qint64 msecs) を使用します。デフォルトではUTCとして解釈されますが、2つ目の引数でタイムゾーンを指定できます。

    “`cpp

    include

    include

    int main() {
    qint64 unixTime = 1698386400; // 2023-10-27 02:00:00 UTC

    QDateTime dateTimeUtc = QDateTime::fromSecsSinceEpoch(unixTime);
    qDebug() << "Unix時間から (UTC):" << dateTimeUtc.toString(Qt::ISODate); // 2023-10-27T02:00:00Z
    
    // ローカルタイムゾーンとして解釈したい場合
    QDateTime dateTimeLocal = QDateTime::fromSecsSinceEpoch(unixTime, QTimeZone::systemTimeZone());
    qDebug() << "Unix時間から (ローカル):" << dateTimeLocal.toString(Qt::ISODate); // 日本時間の場合 2023-10-27T11:00:00+09:00
    return 0;
    

    }
    “`

  • 文字列から生成する:
    QDateTime::fromString(const QString &s, const QString &format) または QDateTime::fromString(const QString &s, Qt::DateFormat format) を使用します。文字列と、その文字列のフォーマットを指定します。解析に成功すると有効な QDateTime を返します。

    “`cpp

    include

    include

    int main() {
    QString dateTimeString = “2023-10-27T14:30:00”;
    QDateTime parsedDateTime = QDateTime::fromString(dateTimeString, Qt::ISODate);

    if (parsedDateTime.isValid()) {
        qDebug() << "解析成功:" << parsedDateTime.toString(Qt::ISODate); // 2023-10-27T14:30:00
        // デフォルトではQt::LocalTimeとして解釈されます。
        qDebug() << "解析された日時のタイムゾーン:" << parsedDateTime.timeZoneAbbreviation(); // 例: "JST"
    } else {
        qDebug() << "解析失敗";
    }
    
    // タイムゾーン情報を含む文字列を解析する場合
    QString dateTimeStringWithZone = "2023-10-27T14:30:00+09:00";
    QDateTime parsedWithZone = QDateTime::fromString(dateTimeStringWithZone, Qt::ISODateWithMs); // ISO 8601拡張形式はQt::ISODateWithMsで解析可能
     if (parsedWithZone.isValid()) {
         qDebug() << "タイムゾーン情報付きの解析成功:" << parsedWithZone.toString(Qt::ISODateWithMs); // 2023-10-27T14:30:00.000+09:00
         qDebug() << "タイムゾーン:" << parsedWithZone.timeZoneAbbreviation(); // "JST"
         // 内部的にはUTCに変換されて保持されます
         qDebug() << "内部UTC:" << parsedWithZone.toUTC().toString(Qt::ISODate); // 2023-10-27T05:30:00Z
     } else {
         qDebug() << "タイムゾーン情報付きの解析失敗";
     }
    return 0;
    

    }
    ``fromStringは、指定されたフォーマット文字列と一致する場合にのみ成功します。また、解析されたタイムゾーン情報はQDateTimeオブジェクトに設定されます。タイムゾーン情報が含まれない文字列を解析した場合、デフォルトではQt::LocalTime` と解釈されます。

  • 無効なQDateTimeオブジェクトを生成する:
    無効な QDate または QTime を指定した場合、またはデフォルトコンストラクタで生成した場合、無効な QDateTime オブジェクトになります。

    “`cpp

    include

    include

    int main() {
    QDateTime invalid; // デフォルトコンストラクタで無効
    qDebug() << “無効な日時オブジェクト:” << invalid.isValid(); // false

    QDate invalidDate(2023, 13, 1);
    QTime validTime(12, 0, 0);
    QDateTime invalidDateTime(invalidDate, validTime); // 無効な日付を含む
    qDebug() << "無効な日付を含む日時:" << invalidDateTime.isValid(); // false
    return 0;
    

    }
    “`

3.2 日時・時刻・タイムゾーンへのアクセス

QDateTime オブジェクトから、日付 (QDate)、時間 (QTime)、タイムゾーン情報 (QTimeZone または Qt::TimeSpec) を取得できます。

“`cpp

include

include

include

int main() {
QDateTime dateTime = QDateTime::currentDateTime(); // ローカルタイム

QDate datePart = dateTime.date();
QTime timePart = dateTime.time();

qDebug() << "日付部分:" << datePart.toString(Qt::ISODate);
qDebug() << "時間部分:" << timePart.toString(Qt::ISODate);

// タイムゾーン情報
qDebug() << "タイムゾーン仕様:" << dateTime.timeSpec(); // Qt::LocalTime または Qt::UTC

// Qt 5.2以降で推奨されるQTimeZone
QTimeZone timeZone = dateTime.timeZone();
qDebug() << "タイムゾーンID:" << timeZone.id(); // 例: "Asia/Tokyo"
qDebug() << "タイムゾーン略称:" << timeZone.abbreviation(dateTime); // 例: "JST"
qDebug() << "UTCからのオフセット (秒):" << timeZone.offsetFromUtc(dateTime); // 例: 32400 (日本は+9時間 = 9*60*60)

return 0;

}
``timeSpec()は古いAPIであり、QTimeZone` を使用してタイムゾーン情報を取得・設定することが推奨されます。

3.3 日時の有効性の確認

isValid() メソッドで、QDateTime オブジェクトが有効な日時を表しているかを確認します。

“`cpp

include

include

int main() {
QDateTime validDateTime(QDate(2023, 10, 27), QTime(14, 30, 0));
QDateTime invalidDateTime(QDate(2023, 2, 30), QTime(12, 0, 0)); // 無効な日付

qDebug() << "有効な日時の isValid:" << validDateTime.isValid();   // true
qDebug() << "無効な日時の isValid:" << invalidDateTime.isValid(); // false
return 0;

}
“`

3.4 タイムゾーンの扱いと変換

QDateTime はタイムゾーン情報を内部的に保持しており、異なるタイムゾーン間での変換が可能です。Qt 5.2以降では QTimeZone クラスを使用します。

  • タイムゾーンの設定: setTimeZone(const QTimeZone &timeZone)
    既存の QDateTime オブジェクトのタイムゾーンを設定します。これにより、日時の内部表現が調整される可能性があります(通常、内部的にはUTCで保持されます)。

    “`cpp

    include

    include

    include

    int main() {
    QDateTime dtUtc = QDateTime::currentDateTimeUtc();
    qDebug() << “現在のUTC日時:” << dtUtc.toString(Qt::ISODate);

    // 東京タイムゾーン (JST, +09:00)
    QTimeZone tokyo("Asia/Tokyo");
    if (tokyo.isValid()) {
        QDateTime dtTokyo = dtUtc; // UTCの値をコピー
        dtTokyo.setTimeZone(tokyo); // 東京タイムゾーンに設定
        qDebug() << "東京タイムゾーンでの日時:" << dtTokyo.toString(Qt::ISODateWithMs); // 例: 2023-10-27T11:00:00.000+09:00
        qDebug() << "タイムゾーン:" << dtTokyo.timeZoneAbbreviation();
    } else {
         qDebug() << "Tokyoタイムゾーンが見つかりませんでした。";
    }
    
    // ニューヨークタイムゾーン (EST, -05:00 or EDT, -04:00 depending on DST)
    QTimeZone newYork("America/New_York");
     if (newYork.isValid()) {
        QDateTime dtNewYork = dtUtc;
        dtNewYork.setTimeZone(newYork);
        qDebug() << "ニューヨークタイムゾーンでの日時:" << dtNewYork.toString(Qt::ISODateWithMs); // 例: 2023-10-27T06:00:00.000-05:00
         qDebug() << "タイムゾーン:" << dtNewYork.timeZoneAbbreviation(); // EST
     } else {
         qDebug() << "New Yorkタイムゾーンが見つかりませんでした。";
     }
    
    return 0;
    

    }
    ``setTimeZone()` は、オブジェクトが表す 瞬間の時点 は変更せず、その瞬間の時刻を新しいタイムゾーンでどのように表現するかを設定します。内部的にはUTCに変換して保持されることが多いです。

  • タイムゾーン間の変換: toTimeZone(const QTimeZone &targetTimeZone)
    指定したタイムゾーンにおける日時を返す新しい QDateTime オブジェクトを生成します。

    “`cpp

    include

    include

    include

    int main() {
    QDateTime dtTokyo(QDate(2023, 10, 27), QTime(14, 30, 0), QTimeZone(“Asia/Tokyo”)); // 東京時間14:30
    qDebug() << “東京時間:” << dtTokyo.toString(Qt::ISODateWithMs); // 2023-10-27T14:30:00.000+09:00

    // UTCに変換
    QDateTime dtUtc = dtTokyo.toUTC(); // toUTC() は toTimeZone(QTimeZone::utc()) と同じ
    qDebug() << "UTCに変換:" << dtUtc.toString(Qt::ISODateWithMs); // 2023-10-27T05:30:00.000Z
    
    // ニューヨークタイムゾーンに変換
    QTimeZone newYork("America/New_York");
    if (newYork.isValid()) {
         QDateTime dtNewYork = dtTokyo.toTimeZone(newYork);
         qDebug() << "ニューヨーク時間に変換:" << dtNewYork.toString(Qt::ISODateWithMs); // 2023-10-27T06:30:00.000-04:00 (注意: 10月末はDST期間の可能性あり)
    }
    
    return 0;
    

    }
    ``toTimeZone()` は非常に便利で、異なるタイムゾーンのユーザーに日時を表示する際に役立ちます。

3.5 日時の算術演算

QDateTime は、日時、秒数、ミリ秒数を加減算するメソッドを提供します。これらの演算はタイムゾーンを考慮して行われます。

  • 日数を加減算する: addDays(qint64 ndays)
    QDate と同様に日数を加減算します。日付を跨ぐ際の時刻の変化も正確に処理します。

    “`cpp

    include

    include

    int main() {
    QDateTime dt(QDate(2023, 10, 27), QTime(14, 30, 0)); // ローカルタイム
    QDateTime nextDay = dt.addDays(1); // 1日後
    QDateTime prevDay = dt.addDays(-1); // 1日前

    qDebug() << "元の日時:" << dt.toString(Qt::ISODate); // 2023-10-27T14:30:00
    qDebug() << "1日後:" << nextDay.toString(Qt::ISODate); // 2023-10-28T14:30:00
    qDebug() << "1日前:" << prevDay.toString(Qt::ISODate); // 2023-10-26T14:30:00
    return 0;
    

    }
    “`

  • 秒数を加減算する: addSecs(qint64 nsecs)
    秒数を加減算します。日付や時間を跨ぐ場合も正確に処理します。

    “`cpp

    include

    include

    int main() {
    QDateTime dt(QDate(2023, 10, 27), QTime(23, 59, 50), Qt::UTC); // UTC 2023-10-27 23:59:50
    QDateTime later = dt.addSecs(20); // 20秒後 (UTC 2023-10-28 00:00:10)

    qDebug() << "元の日時 (UTC):" << dt.toString(Qt::ISODate); // 2023-10-27T23:59:50Z
    qDebug() << "20秒後 (UTC):" << later.toString(Qt::ISODate); // 2023-10-28T00:00:10Z
    
    // ローカルタイムで見てみる (日本時間 +9時間)
    QTimeZone tokyo("Asia/Tokyo");
    if (tokyo.isValid()) {
        qDebug() << "元の日時 (東京時間):" << dt.toTimeZone(tokyo).toString(Qt::ISODateWithMs); // 2023-10-28T08:59:50.000+09:00
        qDebug() << "20秒後 (東京時間):" << later.toTimeZone(tokyo).toString(Qt::ISODateWithMs); // 2023-10-28T09:00:10.000+09:00
    }
    return 0;
    

    }
    ``addSecs()` は内部的にUTC時刻に変換して計算を行うため、タイムゾーンやDSTの境界を正確に処理します。

  • ミリ秒数を加減算する: addMSecs(qint64 nms)
    addSecs と同様に、ミリ秒数を加減算します。

3.6 日時間の差分計算

2つの QDateTime オブジェクト間の秒数やミリ秒数の差分を計算するには、secsTo(const QDateTime &other)msecsTo(const QDateTime &other) メソッドを使用します。これらのメソッドは、両方の QDateTime オブジェクトをUTCに変換してから差分を計算するため、タイムゾーンやDSTに関わらず正確な物理的な時間差を得られます。

“`cpp

include

include

include

int main() {
QDateTime dtTokyo(QDate(2023, 10, 27), QTime(14, 30, 0), QTimeZone(“Asia/Tokyo”)); // 東京時間14:30
QDateTime dtNewYork(QDate(2023, 10, 27), QTime(0, 30, 0), QTimeZone(“America/New_York”)); // ニューヨーク時間0:30

// UTCに変換して比較される
// 東京 14:30 JST (+9h) -> UTC 05:30
// NY    0:30 EST (-5h) -> UTC 05:30 (この日はまだDSTではないと仮定)
// この例では同じ瞬間を指している
qint64 secs = dtTokyo.secsTo(dtNewYork);
qint64 msecs = dtTokyo.msecsTo(dtNewYork);

qDebug() << "東京 14:30 から NY 0:30 までの秒数:" << secs; // 0 (同じ瞬間を指しているため)
qDebug() << "ミリ秒数:" << msecs; // 0

// 時間が違う例
QDateTime dtLaterTokyo = dtTokyo.addSecs(3600); // 東京時間で1時間後 (15:30)
// UTCでは 05:30 + 1時間 = 06:30

secs = dtTokyo.secsTo(dtLaterTokyo);
qDebug() << "東京 14:30 から 東京 15:30 までの秒数:" << secs; // 3600

QDateTime dtYesterdayTokyo = dtTokyo.addDays(-1); // 東京時間で1日前 (2023-10-26 14:30)

secs = dtYesterdayTokyo.secsTo(dtTokyo);
qDebug() << "1日前 東京 14:30 から 今日 東京 14:30 までの秒数:" << secs; // 86400 (24時間)
return 0;

}
``secsTo()msecsTo()` は、期間ではなく、2つの時点間の物理的な時間を計算するのに適しています。

daysTo(const QDateTime &other) メソッドも存在しますが、これは日付部分のみを比較し、時間部分を無視するため、注意が必要です。例えば、10月27日 23:00 から 10月28日 01:00 までの daysTo は1日となりますが、実際の物理的な時間は2時間です。日付間の日数を計算する場合は QDate::daysTo を、物理的な時間差を計算する場合は QDateTime::secsTo または msecsTo を使用するのが適切です。

3.7 日時の比較

QDateTime オブジェクトは、標準的な比較演算子を使用して比較できます。比較は内部的にUTC時刻に変換して行われるため、異なるタイムゾーンの QDateTime オブジェクトでも正確に比較できます。

“`cpp

include

include

include

int main() {
QDateTime dtTokyo(QDate(2023, 10, 27), QTime(14, 30, 0), QTimeZone(“Asia/Tokyo”)); // 東京時間14:30
QDateTime dtNewYork(QDate(2023, 10, 27), QTime(0, 30, 0), QTimeZone(“America/New_York”)); // ニューヨーク時間0:30

// 東京 14:30 JST (+9h) -> UTC 05:30
// NY    0:30 EST (-5h) -> UTC 05:30
// 同じ瞬間を指しているため等しい
qDebug() << "dtTokyo == dtNewYork:" << (dtTokyo == dtNewYork); // true

QDateTime dtLaterTokyo = dtTokyo.addSecs(1); // 東京時間で1秒後
qDebug() << "dtTokyo < dtLaterTokyo:" << (dtTokyo < dtLaterTokyo); // true

return 0;

}
``QDateTime` の比較演算子がタイムゾーンを考慮してくれるのは非常に便利です。

3.8 文字列との相互変換

QDateTime を文字列に変換するには toString() メソッド、文字列から QDateTime を解析するには fromString() 静的メソッドを使用します。フォーマット指定子については次で詳しく解説します。

タイムゾーン情報を含む文字列との相互変換には特に注意が必要です。fromString でタイムゾーン情報を読み込むには、適切なフォーマット指定子(例: ZK)を使用するか、Qt::ISODateWithMs のような定義済みフォーマットを使用します。toString では、ZK などの指定子を使ってタイムゾーン情報を出力できます。

4. フォーマットとパース (文字列変換)

QDate, QTime, QDateTimetoString() メソッドと fromString() 静的メソッドは、日付や時刻を文字列として表現したり、文字列から解釈したりする際に、柔軟なフォーマット指定子をサポートしています。

4.1 toString() のフォーマット指定子

toString(const QString &format) メソッドに渡すフォーマット文字列は、プレースホルダーとして機能する特殊な文字の組み合わせです。これらの文字は、対応する日付/時刻の要素に置き換えられます。

よく使われるフォーマット指定子の例:

指定子 説明 例 (2023-10-27 14:05:08.050)
d 月内の日 (1桁または2桁) 27
dd 月内の日 (常に2桁、ゼロ埋め) 27
M 月 (1桁または2桁) 10
MM 月 (常に2桁、ゼロ埋め) 10
MMM 月の省略名 (例: Oct) Oct
MMMM 月の正式名 (例: October) October
y 年 (2桁) 23
yy 年 (2桁) 23
yyyy 年 (4桁) 2023
h 時 (12時間形式、1桁または2桁) 2
hh 時 (12時間形式、常に2桁) 02
H 時 (24時間形式、1桁または2桁) 14
HH 時 (24時間形式、常に2桁) 14
m 分 (1桁または2桁) 5
mm 分 (常に2桁、ゼロ埋め) 05
s 秒 (1桁または2桁) 8
ss 秒 (常に2桁、ゼロ埋め) 08
z ミリ秒 (最大3桁) 50
zzz ミリ秒 (常に3桁、ゼロ埋め) 050
AP 午前/午後 (AM/PM) PM
ap 午前/午後 (am/pm) pm
t タイムゾーン略称 (ローカル) JST (またはDSTなら JDT)
tt タイムゾーン略称 (ローカル) JST
Z UTCからのオフセット (+HH:mm) +09:00 (UTCの場合は Z)
K UTCからのオフセット (+HHmm) +0900 (UTCの場合は +0000)
'text' リテラル文字列 '年'

注意: タイムゾーン関連の指定子 (t, tt, Z, K) は QDateTimetoString() でのみ有効です。

例:
“`cpp

include

include

include

int main() {
QDateTime dt(QDate(2023, 10, 27), QTime(14, 5, 8, 50), QTimeZone(“Asia/Tokyo”)); // 東京時間 2023/10/27 14:05:08.050 JST

qDebug() << dt.toString("yyyy-MM-dd HH:mm:ss.zzz");     // "2023-10-27 14:05:08.050"
qDebug() << dt.toString("dd/MM/yyyy h:mm:ss ap");       // "27/10/2023 2:05:08 PM"
qDebug() << dt.toString("MMMM d, yyyy");              // "October 27, 2023"
qDebug() << dt.toString("dddd, MMMM d, yyyy");        // "Friday, October 27, 2023" (ddddは曜日正式名)
qDebug() << dt.toString("HH'時'mm'分'ss'秒'");         // "14時05分08秒"
qDebug() << dt.toString("yyyy-MM-ddTHH:mm:ssZ");        // "2023-10-27T14:05:08+09:00"
qDebug() << dt.toString("yyyy-MM-ddTHH:mm:ssK");        // "2023-10-27T14:05:08+0900"
qDebug() << dt.toString("yyyy-MM-dd HH:mm:ss tt");      // "2023-10-27 14:05:08 JST"

QDateTime dtUtc = dt.toUTC();
qDebug() << dtUtc.toString("yyyy-MM-dd HH:mm:ss.zzz Z"); // "2023-10-27 05:05:08.050 Z"
qDebug() << dtUtc.toString("yyyy-MM-dd HH:mm:ss.zzz K"); // "2023-10-27 05:05:08.050 +0000"

return 0;

}
“`

  • 定義済みフォーマット:
    Qt::DateFormat 列挙型には、よく使われるフォーマットが定義されています。

    • Qt::TextDate (例: “Fri Oct 27 14:05:08 2023”)
    • Qt::ISODate (例: “2023-10-27T14:05:08”)
    • Qt::ISODateWithMs (例: “2023-10-27T14:05:08.050+09:00” または “2023-10-27T05:05:08.050Z”)
    • Qt::SystemLocaleDate, Qt::SystemLocaleTime, Qt::SystemLocaleDateTime (システムのロケール設定に依存)
    • Qt::LocaleDate, Qt::LocaleTime, Qt::LocaleDateTime (QLocale::system().toString() と同じ)
    • Qt::RFC2822Date (例: “Fri, 27 Oct 2023 14:05:08 +0900”)
    • Qt::ISODateWithTimeZone (Qt 6以降)

    “`cpp

    include

    include

    int main() {
    QDateTime dt = QDateTime::currentDateTime();
    qDebug() << “TextDate:” << dt.toString(Qt::TextDate);
    qDebug() << “ISODate:” << dt.toString(Qt::ISODate);
    qDebug() << “ISODateWithMs:” << dt.toString(Qt::ISODateWithMs);
    qDebug() << “SystemLocaleDateTime:” << dt.toString(Qt::SystemLocaleDateTime);
    qDebug() << “RFC2822Date:” << dt.toString(Qt::RFC2822Date);
    return 0;
    }
    “`

  • ロケールによるフォーマット:
    QLocale クラスを使用すると、特定の言語や国/地域の慣習に従ったフォーマットで文字列を生成できます。QLocale::toString() を使用します。

    “`cpp

    include

    include

    include

    int main() {
    QDateTime dt = QDateTime::currentDateTime(); // 例: 2023-10-27 14:05 JST

    // 日本語ロケール
    QLocale japanese(QLocale::Japanese);
    qDebug() << "日本語ロケール (ショート):" << japanese.toString(dt, QLocale::ShortFormat); // 例: "2023/10/27 14:05"
    qDebug() << "日本語ロケール (ロング):" << japanese.toString(dt, QLocale::LongFormat);  // 例: "2023年10月27日 14時05分08秒"
    
    // ドイツ語ロケール
    QLocale german(QLocale::German);
    qDebug() << "ドイツ語ロケール (ショート):" << german.toString(dt, QLocale::ShortFormat); // 例: "27.10.23, 14:05"
    
    // カスタムフォーマットとロケールの組み合わせ
    qDebug() << "日本語カスタム:" << japanese.toString(dt, "yyyy年M月d日(dddd) HH時mm分"); // 例: "2023年10月27日(金曜日) 14時05分"
    return 0;
    

    }
    ``QLocale` を使用すると、月の名前や曜日の名前などが自動的にローカライズされます。

4.2 fromString() のフォーマット指定子と注意点

fromString(const QString &s, const QString &format) は、入力文字列 s を指定された format に従って解析します。文字列とフォーマットが厳密に一致する必要があります。一致しない場合や、日付/時刻として無効な値が含まれる場合は、無効なオブジェクトを返します。

例:
“`cpp

include

include

int main() {
QString s1 = “2023-10-27 14:05:08”;
QDateTime dt1 = QDateTime::fromString(s1, “yyyy-MM-dd HH:mm:ss”); // 一致
qDebug() << “解析1 isValid:” << dt1.isValid(); // true

QString s2 = "2023/10/27 14:05:08"; // 区切り文字が違う
QDateTime dt2 = QDateTime::fromString(s2, "yyyy-MM-dd HH:mm:ss");
qDebug() << "解析2 isValid:" << dt2.isValid(); // false

QString s3 = "October 27, 2023 2:05:08 PM";
QDateTime dt3 = QDateTime::fromString(s3, "MMMM d, yyyy h:mm:ss AP"); // 月名、AM/PMを含むフォーマット
qDebug() << "解析3 isValid:" << dt3.isValid(); // true
if (dt3.isValid()) {
     qDebug() << "解析3 結果:" << dt3.toString(Qt::ISODate);
}

// タイムゾーン情報付きの文字列解析 (Z/K指定子)
QString s4 = "2023-10-27T14:05:08+09:00";
QDateTime dt4 = QDateTime::fromString(s4, "yyyy-MM-ddTHH:mm:ssZ"); // Z指定子を使う
qDebug() << "解析4 isValid:" << dt4.isValid(); // true
 if (dt4.isValid()) {
     qDebug() << "解析4 結果 (UTC):" << dt4.toUTC().toString(Qt::ISODateWithMs); // 2023-10-27T05:05:08.000Z
 }

// 定義済みフォーマットによる解析
QString s5 = "2023-10-27T14:05:08.123+09:00";
QDateTime dt5 = QDateTime::fromString(s5, Qt::ISODateWithMs); // ISO 8601拡張形式を解析
qDebug() << "解析5 isValid:" << dt5.isValid(); // true
if (dt5.isValid()) {
    qDebug() << "解析5 結果 (UTC):" << dt5.toUTC().toString(Qt::ISODateWithMs); // 2023-10-27T05:05:08.123Z
}

return 0;

}
“`

fromString は解析に失敗しても例外を投げません。代わりに無効な QDate/QTime/QDateTime オブジェクトを返します。したがって、常に isValid() を呼び出して解析の成否を確認する必要があります。

また、QLocale を使用してローカライズされた文字列を解析することも可能です。QLocale::fromString() を使用します。

“`cpp

include

include

include

int main() {
QString japaneseString = “2023年10月27日(金曜日) 14時05分”;
QLocale japanese(QLocale::Japanese);

QDateTime dt = japanese.fromString(japaneseString, "yyyy年M月d日(dddd) H時m分"); // H時m分はHH時mm分でも解析可能

if (dt.isValid()) {
    qDebug() << "日本語文字列の解析成功:" << dt.toString(Qt::ISODate); // 2023-10-27T14:05:00
} else {
    qDebug() << "日本語文字列の解析失敗";
}
return 0;

}
“`

5. QTimeZoneクラス: タイムゾーンの詳細な操作

Qt 5.2以降では、QTimeZone クラスがタイムゾーンの扱いの中心となります。夏時間(DST)やタイムゾーンルールの変更履歴など、複雑なタイムゾーン情報に対応しています。

5.1 QTimeZoneオブジェクトの生成

  • タイムゾーンIDを指定して生成: QTimeZone(const QByteArray &id)
    “Asia/Tokyo”, “America/New_York”, “Europe/London” のような標準的なOlsonタイムゾーンデータベースID(IANAタイムゾーンデータベースIDとも呼ばれる)を指定して生成します。

    “`cpp

    include

    include

    int main() {
    QTimeZone tokyo(“Asia/Tokyo”);
    if (tokyo.isValid()) {
    qDebug() << “東京タイムゾーンID:” << tokyo.id();
    } else {
    qDebug() << “無効なタイムゾーンID”;
    }

    QTimeZone invalid("NonExistent/Zone");
    qDebug() << "無効なタイムゾーン isValid:" << invalid.isValid(); // false
    return 0;
    

    }
    ``
    利用可能なタイムゾーンIDのリストは
    QTimeZone::availableTimeZoneIds()` 静的メソッドで取得できます。

  • システム標準のタイムゾーンを取得: QTimeZone::systemTimeZone()
    プログラムを実行しているシステムのタイムゾーンを取得します。

    “`cpp

    include

    include

    int main() {
    QTimeZone systemZone = QTimeZone::systemTimeZone();
    qDebug() << “システムタイムゾーンID:” << systemZone.id(); // 例: “Asia/Tokyo”
    }
    “`

  • UTCを取得: QTimeZone::utc()
    UTCタイムゾーンを表すオブジェクトを取得します。

    “`cpp

    include

    include

    int main() {
    QTimeZone utcZone = QTimeZone::utc();
    qDebug() << “UTCタイムゾーンID:” << utcZone.id(); // “UTC”
    }
    “`

  • UTCからのオフセットを指定して生成: QTimeZone(int offsetSeconds)
    UTCからの静的なオフセット(秒単位)を指定してタイムゾーンを生成します。これは、特定のオフセットを持つタイムゾーン(例: “GMT+9″)を扱う場合に便利ですが、DSTには対応しません。

    “`cpp

    include

    include

    int main() {
    // UTC+9時間を表すタイムゾーン
    QTimeZone offsetZone(9 * 60 * 60);
    qDebug() << “UTC+9時間のオフセットID:” << offsetZone.id(); // 例: “UTC+09:00”
    }
    “`

5.2 QTimeZoneとQDateTimeの連携

前述のように、QDateTime::setTimeZone()QDateTime::toTimeZone() を使用して QTimeZoneQDateTime と連携させます。

5.3 夏時間 (Daylight Saving Time, DST) の扱い

QTimeZone はDSTを自動的に処理します。特定の時点がDST期間中であるか、その時点でのUTCからのオフセットはいくらか、などを取得できます。

  • DST期間中か確認: isDaylightTime(const QDateTime &atDateTime)
    指定した日時 atDateTime がこのタイムゾーンでDST期間中であれば true を返します。atDateTime のタイムゾーンは考慮されず、タイムゾーン情報は this オブジェクトから取得されます。

    “`cpp

    include

    include

    include

    int main() {
    QTimeZone newYork(“America/New_York”); // 通常 EST (-5h), DST期間 EDT (-4h)

    // 仮に10月27日をDST期間中、11月10日をDST期間外とする
    QDateTime dtOct(QDate(2023, 10, 27), QTime(12,0,0), Qt::UTC); // 時刻はUTCで指定
    QDateTime dtNov(QDate(2023, 11, 10), QTime(12,0,0), Qt::UTC);
    
    // New Yorkタイムゾーンでのそれぞれの瞬間がDST期間中か確認
    qDebug() << "Oct 27 is DST in New York?" << newYork.isDaylightTime(dtOct); // true (北米のDSTは11月頭まで)
    qDebug() << "Nov 10 is DST in New York?" << newYork.isDaylightTime(dtNov); // false
    return 0;
    

    }
    “`

  • UTCからのオフセットを取得: offsetFromUtc(const QDateTime &atDateTime)
    指定した日時 atDateTime における、このタイムゾーンのUTCからのオフセット(秒単位)を返します。DSTが考慮されます。

    “`cpp

    include

    include

    include

    int main() {
    QTimeZone newYork(“America/New_York”);

    QDateTime dtOct(QDate(2023, 10, 27), QTime(12,0,0), Qt::UTC);
    QDateTime dtNov(QDate(2023, 11, 10), QTime(12,0,0), Qt::UTC);
    
    // DST期間中のオフセット (-4時間 = -4*3600秒)
    qDebug() << "Oct 27 offset:" << newYork.offsetFromUtc(dtOct); // -14400
    
    // DST期間外のオフセット (-5時間 = -5*3600秒)
    qDebug() << "Nov 10 offset:" << newYork.offsetFromUtc(dtNov); // -18000
    
    return 0;
    

    }
    “`

  • タイムゾーンイベント:
    QTimeZone は、タイムゾーンルール(DSTの開始/終了時刻、オフセットの変更など)のイベント履歴を持っています。timeZoneEvents() メソッドで取得できます。これは高度な用途向けで、特定の期間のルール変更を調べる際に役立ちます。

5.4 曖昧な時刻と無効な時刻

DSTの開始や終了時には、時間がスキップされたり、同じローカル時刻が2回出現したりすることがあります。これらの「無効な時刻」や「曖昧な時刻」は、特定のタイムゾーンにおいて有効な瞬間と一対一に対応しないローカル時刻です。

例えば、DSTが午前2時に始まり、時計が午前3時に進む場合、午前2時から午前3時までの間の時間は存在しません(無効な時刻)。逆に、DSTが午前3時に終わり、時計が午前2時に戻る場合、午前2時台が2回出現します(曖昧な時刻)。

QTimeZone::isValidDateTime()QTimeZone::mapDateTime() メソッドでこれらを検出・処理できます。

“`cpp

include

include

include

int main() {
QTimeZone cet(“Europe/Berlin”); // 中央ヨーロッパ時間 (CET, +1h), 夏時間 (CEST, +2h)
// 2023年の夏時間終了は10月29日 03:00 CEST -> 02:00 CET

// 夏時間終了時の曖昧な時間帯 (02:00-03:00 CEST -> 02:00-03:00 CET)
// 例えば、2023-10-29 02:30 というローカル時刻は2回出現する

QDateTime ambiguousLocalTime(QDate(2023, 10, 29), QTime(2, 30, 0)); // ローカル時刻として指定

// このローカル時刻が有効か?
qDebug() << "2023-10-29 02:30 in Berlin is valid?" << cet.isValidDateTime(ambiguousLocalTime, QTimeZone::AmbiguousTime); // true (曖昧だが有効なローカル時刻)
qDebug() << "2023-10-29 02:30 in Berlin is valid without ambiguity?" << cet.isValidDateTime(ambiguousLocalTime, QTimeZone::ValidTime); // false (曖昧さがないという意味では無効)

// このローカル時刻が指す瞬間 (UTC) は複数あるか?
QList<QDateTime> mappedTimes = cet.mapDateTime(ambiguousLocalTime);
qDebug() << "2023-10-29 02:30 in Berlin maps to:" << mappedTimes.size() << "UTC instants."; // 2

if (mappedTimes.size() == 2) {
    // 1回目の2:30 CEST (+2h) -> UTC 00:30
    // 2回目の2:30 CET (+1h) -> UTC 01:30
    qDebug() << "Mapping 1 (CEST):" << mappedTimes.first().toUTC().toString(Qt::ISODateWithMs);
    qDebug() << "Mapping 2 (CET):" << mappedTimes.last().toUTC().toString(Qt::ISODateWithMs);
}

// 夏時間開始時の無効な時間帯
// 2024年の夏時間開始は3月31日 02:00 CET -> 03:00 CEST
// 2024-03-31 02:30 は存在しないローカル時刻

QDateTime invalidLocalTime(QDate(2024, 3, 31), QTime(2, 30, 0));
qDebug() << "2024-03-31 02:30 in Berlin is valid?" << cet.isValidDateTime(invalidLocalTime); // false
qDebug() << "2024-03-31 02:30 in Berlin maps to:" << cet.mapDateTime(invalidLocalTime).size() << "UTC instants."; // 0
return 0;

}
``
デフォルトでは、
fromStringQDateTime(QDate, QTime, QTimeZone)で曖昧な時刻を指定した場合、より早い時刻(DSTが終了して時間が戻る前)が選択されます。無効な時刻を指定した場合は、無効なQDateTimeオブジェクトになります。mapDateTime` を使用することで、曖昧な時刻のすべての可能性を取得できます。

6. 実践的な応用例とベストプラクティス

6.1 現在の日時を使ったシンプルなデジタル時計

QTimer を使って定期的に現在日時を取得し、表示を更新するシンプルなデジタル時計です。

“`cpp
// mainwindow.h (例)

include

include

include

include

include

include

include

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

private slots:
void updateTime();

private:
QLabel timeLabel;
QTimer
timer;
QLocale locale;
};

// mainwindow.cpp (例)

include “mainwindow.h”

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
locale(QLocale::system()) // システムのロケールを使用
{
timeLabel = new QLabel(“Waiting for time…”, this);
timeLabel->setAlignment(Qt::AlignCenter);
timeLabel->setStyleSheet(“font-size: 24pt;”);

QWidget *centralWidget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(centralWidget);
layout->addWidget(timeLabel);
setCentralWidget(centralWidget);

timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MainWindow::updateTime);
timer->start(1000); // 1秒ごとに更新

// 初期表示
updateTime();

setWindowTitle("デジタル時計");
resize(300, 150);

}

MainWindow::~MainWindow()
{
}

void MainWindow::updateTime()
{
QDateTime now = QDateTime::currentDateTime(); // 現在のローカル日時

// ロケールに合わせたフォーマットで表示
QString formattedTime = locale.toString(now, QLocale::LongFormat); // または "yyyy年MM月dd日 HH:mm:ss"

timeLabel->setText(formattedTime);

}

// main.cpp (例)

include

include “mainwindow.h”

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
“`

6.2 イベントまでの残り時間計算

特定の未来の日時までの残り時間(秒数など)を計算する例です。

“`cpp

include

include

include

int main() {
// 未来のイベント日時 (例: 2024年1月1日 00:00:00 JST)
QDate eventDate(2024, 1, 1);
QTime eventTime(0, 0, 0);
QTimeZone tokyo(“Asia/Tokyo”);
QDateTime eventDateTime(eventDate, eventTime, tokyo);

qDebug() << "イベント日時 (東京時間):" << eventDateTime.toString(Qt::ISODateWithMs);

// 現在日時 (ローカルタイム)
QDateTime currentDateTime = QDateTime::currentDateTime();
qDebug() << "現在のローカル日時:" << currentDateTime.toString(Qt::ISODateWithMs);

// イベント日時までの秒数を計算
// QDateTime::secsTo は内部的にUTC変換してから計算するため、タイムゾーンの違いを吸収する
qint64 secondsToEvent = currentDateTime.secsTo(eventDateTime);

if (secondsToEvent > 0) {
    qDebug() << "イベントまで残り" << secondsToEvent << "秒です。";
    // より分かりやすい表示に変換 (日数、時間、分、秒)
    qint64 days = secondsToEvent / 86400;
    qint64 remainingSecs = secondsToEvent % 86400;
    qint64 hours = remainingSecs / 3600;
    remainingSecs %= 3600;
    qint64 minutes = remainingSecs / 60;
    qint64 seconds = remainingSecs % 60;

    qDebug() << QString("イベントまで残り %1日 %2時間 %3分 %4秒").arg(days).arg(hours).arg(minutes).arg(seconds);

} else if (secondsToEvent == 0) {
    qDebug() << "イベントはちょうど今です!";
} else {
    qDebug() << "イベントは過去です。";
    qDebug() << "イベントから" << -secondsToEvent << "秒経過しました。";
}

return 0;

}
``
この例では、
QDateTime::secsTo` を使用して正確な時間差を計算しています。その結果を日、時、分、秒に変換して表示しています。

6.3 日時の保存と復元 (Unix時間)

データベースやファイルに日時を保存する場合、タイムゾーンに依存しない形で保存するのが一般的です。Unix時間(UTCの1970年1月1日00:00:00からの経過秒数)はその目的に適しています。

“`cpp

include

include

include

int main() {
// 保存したい日時 (例えば、システムのローカル日時)
QDateTime originalDateTime = QDateTime::currentDateTime();
qDebug() << “元の日時 (ローカル):” << originalDateTime.toString(Qt::ISODateWithMs);

// Unix時間に変換して保存
// toSecsSinceEpoch() / toMSecsSinceEpoch() は内部的にUTC変換を行う
qint64 unixTimestamp = originalDateTime.toSecsSinceEpoch();
qDebug() << "Unixタイムスタンプ (秒):" << unixTimestamp;

// 保存されたUnix時間からQDateTimeを復元
// fromSecsSinceEpoch() はデフォルトでUTCとして解釈される
QDateTime restoredDateTimeUtc = QDateTime::fromSecsSinceEpoch(unixTimestamp);
qDebug() << "復元された日時 (UTC):" << restoredDateTimeUtc.toString(Qt::ISODateWithMs);

// 必要であれば、ローカルタイムゾーンや他のタイムゾーンに変換して表示
QTimeZone systemZone = QTimeZone::systemTimeZone();
QDateTime restoredDateTimeLocal = restoredDateTimeUtc.toTimeZone(systemZone);
 qDebug() << "復元された日時 (ローカル変換):" << restoredDateTimeLocal.toString(Qt::ISODateWithMs);

// originalDateTime と restoredDateTimeLocal はほぼ同じ日時を指すはず

return 0;

}
“`
Unix時間で保存することで、異なるシステムや異なるタイムゾーン設定の環境でも、元の正確な時点を復元できます。表示時には、ユーザーのタイムゾーンに合わせて変換するのが一般的です。

6.4 ベストプラクティス

  • 内部的にはUTCを使用する: 日時データを保存したり、内部的な計算を行ったりする際には、可能な限りUTC (QDateTime::currentDateTimeUtc(), QDateTime::fromSecsSinceEpoch, QDateTime::toUTC()) を使用することをお勧めします。これにより、異なるタイムゾーンや夏時間の影響を受けない、普遍的な時点を扱うことができます。
  • 表示やユーザー入力にはQTimeZoneを使用する: ユーザーに日時を表示したり、ユーザーから日時を入力として受け取る際には、QTimeZoneQLocale を使用して、ユーザーのタイムゾーンやロケールに合わせた形式で処理します。QDateTime::toTimeZone(), QTimeZone::systemTimeZone(), QLocale::toString(), QLocale::fromString() が役立ちます。
  • isValid() で検証する: fromString() など、外部からの入力や解析を行う際には、必ず isValid() メソッドで結果の有効性を確認してください。
  • QDateTime::secsTo()QDateTime::msecsTo() を使用する: 2つの時点間の物理的な時間差を計算する場合は、これらのメソッドを使用してください。QDate::daysTo()QTime::secsTo() は、特定の用途に限定して使用するか、挙動をよく理解して使用してください。
  • DSTと曖昧/無効な時刻に注意: 夏時間の開始・終了付近の日時を扱う場合は、時刻がスキップされたり重複したりする可能性があることを意識し、必要であれば QTimeZone::mapDateTime() などで適切に処理してください。
  • うるう年に注意: QDateQDateTime はうるう年を自動的に処理しますが、2月29日を含む日付の加減算(特に年単位や月単位)の挙動は、意図した通りか確認してください。
  • 精度を考慮する: 必要に応じてミリ秒精度 (QTime::msec(), QDateTime::addMSecs(), QDateTime::msecsTo(), QDateTime::fromMSecsSinceEpoch(), QDateTime::toMSecsSinceEpoch()) を使用してください。

7. まとめ

Qtは、QDate, QTime, QDateTime という3つの中心的なクラスと、タイムゾーンを扱う QTimeZone、そしてローカライズされた表示/解析を可能にする QLocale との連携により、強力かつ柔軟な日時処理機能を提供しています。

  • QDate は日付のみ、QTime は時間のみを扱います。
  • QDateTime は日付と時間を組み合わせ、タイムゾーン情報も扱えます。内部的にUTCで表現されることが多く、異なるタイムゾーン間の変換や正確な物理的時間差の計算に優れています。
  • QTimeZone は、タイムゾーンID、UTCオフセット、夏時間ルールなど、タイムゾーンに関する詳細情報を提供します。
  • toString()fromString() で文字列との相互変換が可能で、柔軟なフォーマット指定子や QLocale によるローカライズをサポートします。
  • 特に重要なベストプラクティスは、「内部処理にはUTCを使用し、ユーザーとのやり取りにはユーザーのタイムゾーンとロケールに合わせる」という点です。

これらのクラスを理解し適切に使用することで、様々なアプリケーションにおいて、正確で信頼性の高い日時処理を実装することができます。公式ドキュメントには、さらに詳細な情報やメソッドが記載されていますので、必要に応じて参照することをお勧めします。

これらの知識を活かし、Qtアプリケーション開発における日時・時間処理をマスターしてください。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール