時間データ処理#

この章では、時間データの処理に焦点を当て、時間データについての計算、時間ベースのグループ化や集計、時間ウィンドウの操作方法について説明します。

import polars as pl
from helper.jupyter import row

時間のデータ型#

時間データを効率的に扱うために、Polarsでは日付や時刻、時間間隔に関連する4つのデータ型を提供しています。それぞれのデータ型には異なる特徴と用途があり、時間ベースのデータ処理において重要な役割を果たします。

データ型

概要

最小単位

ゼロ点

用途

Date

日付を表すデータ型、時刻情報なし

Unix epoch (1970-01-01)

日付のみが重要なデータの管理や処理

2024-07-15

Time

1日の中の時刻を表すデータ型

ナノ秒(ns)

00:00:00(1日の開始時刻)

1日の中の時刻や時間間隔を扱う

12:30:45.123456789

Datetime

日付と時刻を組み合わせたデータ型

ミリ秒(ms)・マイクロ秒(us)・ナノ秒(ns)

Unix epoch (1970-01-01 00:00:00)

時系列データやタイムスタンプの処理

2024-07-15 12:30:45.123456

Duration

時間の長さや間隔を表すデータ型

ミリ秒(ms)・マイクロ秒(us)・ナノ秒(ns)

-

2つの時点間の差分や時間間隔の計算

2 days, 3 hours, 45 minutes

次の演算式関数で、複数の整数列から時間列に変換することができます。

  • pl.date()は、年、月、日を指定してDate型を作成します。これにより、yearmonthdayの列を使用して日付データを作成します。

  • pl.time()は、時、分、秒などを指定してTime型を作成します。

  • pl.datetime()は、年、月、日、時、分、秒などを指定してDatetime型を作成します。

  • pl.duration()は、時間の長さをDuration型として表現します。

df_numbers = pl.DataFrame(
    dict(
        year=[2022, 2023, 2024],
        month=[11, 10, 12],
        day=[4, 31, 2],
        hour=[1, 6, 16],
        minute=[10, 50, 34],
        second=[12.2, 20.5, 21],
    )
)

df_numbers = df_numbers.with_columns(
    total_seconds=pl.col('hour') * 3600 + pl.col('minute') * 60 + pl.col('second')
)

次のコードでは、複数の数値列を持つデータフレームに対して、pl.date()pl.time()pl.datetime()pl.duration()を使用し、整数列から時間列への変換を行っています。それぞれの関数は対応する時間データ型の列を生成します。

DateTimeDatetimeの場合、各フィールドには値の範囲があるため、上限を超えないよう注意する必要があります。また、各フィールドが整数でない場合、整数部分のみを使用します。さらに、❶ 秒のデータが浮動小数点で表現されている場合は、小数部分を取り出してマイクロ秒に換算する必要があります。

❷ 一方、Durationの場合は各フィールドに上限がないため、一番小さい単位であるmicroseconds引数を利用して列を作成するのが便利です。

df_times = df_numbers.select(
    date=pl.date('year', 'month', 'day'),
    time=pl.time('hour', 'minute', 'second', microsecond=pl.col('second').mod(1).mul(1e6).round()), #❶
    datetime=pl.datetime('year', 'month', 'day', 'hour', 'minute', 'second', 
                         microsecond=pl.col('second').mod(1).mul(1e6).round()),
    duration=pl.duration(microseconds=pl.col('total_seconds').mul(1e6)), #❷
)

row(df_numbers, df_times)
shape: (3, 7)
yearmonthdayhourminutesecondtotal_seconds
i64i64i64i64i64f64f64
202211411012.24212.2
2023103165020.524620.5
2024122163421.059661.0
shape: (3, 4)
datetimedatetimeduration
datetimedatetime[μs]duration[μs]
2022-11-0401:10:12.2002022-11-04 01:10:12.2001h 10m 12s 200ms
2023-10-3106:50:20.5002023-10-31 06:50:20.5006h 50m 20s 500ms
2024-12-0216:34:212024-12-02 16:34:2116h 34m 21s

Datetime型およびDuration型では、時間データの最小単位(例えば、ナノ秒やミリ秒)を変更することができます。dt.cast_time_unit()関数を使用することで、時間単位を任意の精度にキャストすることが可能です。大きい単位に変換する場合、端数は切り捨てられます。たとえば、ナノ秒(ns)からミリ秒(ms)へ変換する際、小数点以下の部分は失われます。

df_times.select(
    'datetime',
    pl.col('datetime').dt.cast_time_unit('ms').alias('datetime_ms'),
    pl.col('datetime').dt.cast_time_unit('ns').alias('datetime_ns'),
    'duration',
    pl.col('duration').dt.cast_time_unit('ms').alias('duration_ms'),
    pl.col('duration').dt.cast_time_unit('ns').alias('duration_ns'),    
)
shape: (3, 6)
datetimedatetime_msdatetime_nsdurationduration_msduration_ns
datetime[μs]datetime[ms]datetime[ns]duration[μs]duration[ms]duration[ns]
2022-11-04 01:10:12.2002022-11-04 01:10:12.2002022-11-04 01:10:12.2001h 10m 12s 200ms1h 10m 12s 200ms1h 10m 12s 200ms
2023-10-31 06:50:20.5002023-10-31 06:50:20.5002023-10-31 06:50:20.5006h 50m 20s 500ms6h 50m 20s 500ms6h 50m 20s 500ms
2024-12-02 16:34:212024-12-02 16:34:212024-12-02 16:34:2116h 34m 21s16h 34m 21s16h 34m 21s

時間型の演算式#

時間型同士の変換#

次のコードでは、異なる時間型を変換および操作する例です。以下に各部分の説明を示します。

dt.combine()date列とtime列を結合して、Datetime型の列を作成します。
dt.date()datetime列から日付部分だけを抽出し、Date型の列を作成します。
dt.time()datetime列から時刻部分だけを抽出し、Time型の列を作成します。
cast(pl.Duration)time列をDuration型にキャストします。
Duration型を直接Time型に変換する関数がないため、一旦列dt.cast_time_unit('ns')を使用してをナノ秒単位のDuration型に変換し、to_physical()Int64型に変換後、cast(pl.Time)Time型に変換します。

df_times.select(
    datetime=pl.col('date').dt.combine(pl.col('time')), #❶
    date=pl.col('datetime').dt.date(), #❷
    time=pl.col('datetime').dt.time(), #❸
    duration=pl.col('time').cast(pl.Duration), #❹
    time2=pl.col('duration').dt.cast_time_unit('ns').to_physical().cast(pl.Time), #❺
)
shape: (3, 5)
datetimedatetimedurationtime2
datetime[μs]datetimeduration[μs]time
2022-11-04 01:10:12.2002022-11-0401:10:12.2001h 10m 12s 200ms01:10:12.200
2023-10-31 06:50:20.5002023-10-3106:50:20.5006h 50m 20s 500ms06:50:20.500
2024-12-02 16:34:212024-12-0216:34:2116h 34m 21s16:34:21

時間のフィールド#

次のコードは、ネームスペースdtの下にある関数を使用して、Datetime型の列から日付や時間の各フィールドを取り出します。また、Date型やTime型の列に対しても、同様の関数を使用することができます。

df_times.select(
    year=pl.col('datetime').dt.year(),
    month=pl.col('datetime').dt.month(),
    day=pl.col('datetime').dt.day(),
    hour=pl.col('datetime').dt.hour(),
    minute=pl.col('datetime').dt.minute(),
    second=pl.col('datetime').dt.second(),
    microsecond=pl.col('datetime').dt.microsecond(),
    week=pl.col('datetime').dt.week(),
    weekday=pl.col('datetime').dt.weekday()    
)
shape: (3, 9)
yearmonthdayhourminutesecondmicrosecondweekweekday
i32i8i8i8i8i8i32i8i8
202211411012200000445
2023103165020500000442
20241221634210491

次のコードは、Duration型の列から総日数、総時間数などを整数型に変換します。このとき、注意点として、結果の値は整数であり、小数部分は切り捨てられます。損失なく正確に数値を変換したい場合は、一番小さい単位(例えば、マイクロ秒)に変換して使用する必要があります。

df_times.select(
    pl.col('duration'),
    days=pl.col('duration').dt.total_days(),
    hours=pl.col('duration').dt.total_hours(),
    minutes=pl.col('duration').dt.total_minutes(),
    seconds=pl.col('duration').dt.total_seconds(),
    microseconds=pl.col('duration').dt.total_microseconds()
)
shape: (3, 6)
durationdayshoursminutessecondsmicroseconds
duration[μs]i64i64i64i64i64
1h 10m 12s 200ms017042124212200000
6h 50m 20s 500ms064102462024620500000
16h 34m 21s0169945966159661000000

時間の移動#

時間データの処理では、特定の単位に基づいて日付や時間を移動、丸め、または切り捨てる操作が頻繁に必要になります。このセクションでは、Polarsの便利な時間操作関数を活用し、Datetime型列の操作方法を紹介します。

次のコードは、dt.month_start()dt.month_end()で、指定された日時が含まれる月の開始日終了日を取得します。これにより、特定の月の範囲を効率よく計算できます。

df_times.select(    
    'datetime',
    month_start=pl.col('datetime').dt.month_start(),  # 月初の日時
    month_end=pl.col('datetime').dt.month_end(),      # 月末の日時
)
shape: (3, 3)
datetimemonth_startmonth_end
datetime[μs]datetime[μs]datetime[μs]
2022-11-04 01:10:12.2002022-11-01 01:10:12.2002022-11-30 01:10:12.200
2023-10-31 06:50:20.5002023-10-01 06:50:20.5002023-10-31 06:50:20.500
2024-12-02 16:34:212024-12-01 16:34:212024-12-31 16:34:21

次のコードは、dt.truncate()で指定された単位(例: 秒、分、時間、日、月)で日時を切り捨てます。これは、データを集計や分析のために時間軸で正規化したい場合に役立ちます。

df_times.select(    
    'datetime', 
    t_1s=pl.col('datetime').dt.truncate('1s'),  # 1秒単位に切り捨て
    t_30s=pl.col('datetime').dt.truncate('30s'), # 30秒単位に切り捨て
    t_1h=pl.col('datetime').dt.truncate('1h'),  # 1時間単位に切り捨て
    t_1d=pl.col('datetime').dt.truncate('1d'),  # 1日単位に切り捨て
    t_1mo=pl.col('datetime').dt.truncate('1mo') # 1ヶ月単位に切り捨て
)
shape: (3, 6)
datetimet_1st_30st_1ht_1dt_1mo
datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]
2022-11-04 01:10:12.2002022-11-04 01:10:122022-11-04 01:10:002022-11-04 01:00:002022-11-04 00:00:002022-11-01 00:00:00
2023-10-31 06:50:20.5002023-10-31 06:50:202023-10-31 06:50:002023-10-31 06:00:002023-10-31 00:00:002023-10-01 00:00:00
2024-12-02 16:34:212024-12-02 16:34:212024-12-02 16:34:002024-12-02 16:00:002024-12-02 00:00:002024-12-01 00:00:00

次のコードは、dt.round()で指定された単位で日時を切り上げまたは切り下げます。この操作は、切り捨てよりも柔軟で、データを最も近い時間単位に揃えたい場合に適しています。

df_times.select(
    'datetime',
    r_1s=pl.col('datetime').dt.round('1s'),
    r_30s=pl.col('datetime').dt.round('30s'),
    r_1h=pl.col('datetime').dt.round('1h'),
    r_1d=pl.col('datetime').dt.round('1d'),
    r_1mo=pl.col('datetime').dt.round('1mo'),
)
shape: (3, 6)
datetimer_1sr_30sr_1hr_1dr_1mo
datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]
2022-11-04 01:10:12.2002022-11-04 01:10:122022-11-04 01:10:002022-11-04 01:00:002022-11-04 00:00:002022-11-01 00:00:00
2023-10-31 06:50:20.5002023-10-31 06:50:212023-10-31 06:50:302023-10-31 07:00:002023-10-31 00:00:002023-11-01 00:00:00
2024-12-02 16:34:212024-12-02 16:34:212024-12-02 16:34:302024-12-02 17:00:002024-12-03 00:00:002024-12-01 00:00:00

次のコードは、dt.offset_by()で指定された単位分だけ日時を移動します。正の値を指定すると将来の日時に、負の値を指定すると過去の日時に移動します。

df_times.select(
    'datetime',
    o_1s=pl.col('datetime').dt.offset_by('1s'),
    o_30s=pl.col('datetime').dt.offset_by('30s'),
    o_1h=pl.col('datetime').dt.offset_by('1h'),
    o_1d=pl.col('datetime').dt.offset_by('1d'),
    o_1mo=pl.col('datetime').dt.offset_by('1mo'),
)
shape: (3, 6)
datetimeo_1so_30so_1ho_1do_1mo
datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]datetime[μs]
2022-11-04 01:10:12.2002022-11-04 01:10:13.2002022-11-04 01:10:42.2002022-11-04 02:10:12.2002022-11-05 01:10:12.2002022-12-04 01:10:12.200
2023-10-31 06:50:20.5002023-10-31 06:50:21.5002023-10-31 06:50:50.5002023-10-31 07:50:20.5002023-11-01 06:50:20.5002023-11-30 06:50:20.500
2024-12-02 16:34:212024-12-02 16:34:222024-12-02 16:34:512024-12-02 17:34:212024-12-03 16:34:212025-01-02 16:34:21

リサンプリング#

リサンプリングとは、時系列データの時間間隔を変更する処理のことです。

  • ダウンサンプリング: より長い時間間隔にデータを集約すること(例:1分ごとのデータを1時間ごとに要約)。

  • アップサンプリング: より短い時間間隔にデータを補間または再配置すること(例:1時間ごとのデータを1分ごとに拡張)。

DataFrame.group_by_dynamic() を使用すると、時系列データをダウンサンプリングできます。次のコードでは、air_quality_long.csv ファイルを読み込みます。このファイルには、複数の場所 (location)、複数のパラメータ (parameter) の時間データが含まれています。場所とパラメータごとにダウンサンプリングを行うために、以下の手順を実行します。

データの並び替え: まず、locationparameterdate.utc の順にデータを並び替えます。これにより、各グループのデータが時間の昇順に整列されます。

group_by_dynamic()を適用: date.utc 列を基準に group_by_dynamic() を使用してダウンサンプリングします。この例では、8時間ごとにグループを作成します。group_by 引数で、時間列のグループ列名を指定します。

集計処理: 各グループ内で、value 列の最小値、平均値、および最大値を計算します。

このように、group_by_dynamic() を活用することで、時系列データを効率的に集約できます。

df = pl.read_csv('data/air_quality_long.csv', try_parse_dates=True).drop('city', 'country', 'unit')
df = df.sort('location', 'parameter', 'date.utc') #❶
df_down = df.group_by_dynamic('date.utc', every='8h', group_by=['location', 'parameter']).agg( #❷
    pl.col('value').min().alias('value_min'),  #❸
    pl.col('value').mean().alias('value_mean'),#❸
    pl.col('value').max().alias('value_max')   #❸
)
row(df, df_down)
shape: (5_272, 4)
date.utclocationparametervalue
datetime[μs, UTC]strstrf64
2019-04-09 01:00:00 UTC"BETR801""no2"22.5
2019-04-09 02:00:00 UTC"BETR801""no2"53.5
2019-04-09 03:00:00 UTC"BETR801""no2"54.5
2019-04-09 04:00:00 UTC"BETR801""no2"34.5
2019-04-09 05:00:00 UTC"BETR801""no2"46.5
2019-06-20 20:00:00 UTC"London Westminster""pm25"8.0
2019-06-20 21:00:00 UTC"London Westminster""pm25"8.0
2019-06-20 22:00:00 UTC"London Westminster""pm25"7.0
2019-06-20 23:00:00 UTC"London Westminster""pm25"7.0
2019-06-21 00:00:00 UTC"London Westminster""pm25"7.0
shape: (780, 6)
locationparameterdate.utcvalue_minvalue_meanvalue_max
strstrdatetime[μs, UTC]f64f64f64
"BETR801""no2"2019-04-09 00:00:00 UTC22.542.85714354.5
"BETR801""no2"2019-04-09 08:00:00 UTC27.532.2539.0
"BETR801""no2"2019-04-10 00:00:00 UTC11.512.513.5
"BETR801""no2"2019-04-11 00:00:00 UTC13.513.7514.0
"BETR801""no2"2019-04-12 00:00:00 UTC22.523.7525.0
"London Westminster""pm25"2019-06-19 00:00:00 UTC11.011.011.0
"London Westminster""pm25"2019-06-19 08:00:00 UTC12.012.012.0
"London Westminster""pm25"2019-06-20 08:00:00 UTC8.08.08.0
"London Westminster""pm25"2019-06-20 16:00:00 UTC7.07.758.0
"London Westminster""pm25"2019-06-21 00:00:00 UTC7.07.07.0

DataFrame.upsample() を使用すると、時系列データをアップサンプリングできます。引数は group_by_dynamic() と同じですが、出力はアップサンプリングされたデータフレームとなり、もともと存在しない時間のデータはすべて NULL で埋められます。

一般的には、forward_fill() を適用して、直前のデータで NULL を埋めることで、データの連続性を保ちます。

df_up = df.upsample('date.utc', every='20m', group_by=['location', 'parameter'])
df_up_fill = df_up.with_columns(pl.col('location', 'parameter', 'value').forward_fill())
row(df_up, df_up_fill)
shape: (25_547, 4)
date.utclocationparametervalue
datetime[μs, UTC]strstrf64
2019-04-09 01:00:00 UTC"BETR801""pm25"76.0
2019-04-09 01:20:00 UTCnullnullnull
2019-04-09 01:40:00 UTCnullnullnull
2019-04-09 02:00:00 UTC"BETR801""pm25"91.5
2019-04-09 02:20:00 UTCnullnullnull
2019-06-20 22:40:00 UTCnullnullnull
2019-06-20 23:00:00 UTC"FR04014""no2"21.8
2019-06-20 23:20:00 UTCnullnullnull
2019-06-20 23:40:00 UTCnullnullnull
2019-06-21 00:00:00 UTC"FR04014""no2"20.0
shape: (25_547, 4)
date.utclocationparametervalue
datetime[μs, UTC]strstrf64
2019-04-09 01:00:00 UTC"BETR801""pm25"76.0
2019-04-09 01:20:00 UTC"BETR801""pm25"76.0
2019-04-09 01:40:00 UTC"BETR801""pm25"76.0
2019-04-09 02:00:00 UTC"BETR801""pm25"91.5
2019-04-09 02:20:00 UTC"BETR801""pm25"91.5
2019-06-20 22:40:00 UTC"FR04014""no2"26.5
2019-06-20 23:00:00 UTC"FR04014""no2"21.8
2019-06-20 23:20:00 UTC"FR04014""no2"21.8
2019-06-20 23:40:00 UTC"FR04014""no2"21.8
2019-06-21 00:00:00 UTC"FR04014""no2"20.0

他のデータとの変換#

文字列との変換#

時間データの処理では、文字列と日時型を相互に変換する操作が非常に重要です。Polarsでは、Datetime型やDate型、Time型と文字列を効率的に変換するための多くの関数が用意されています。以下に、代表的な方法と例を示します。

文字列から時間型へ#

pl.read_csv()関数のtry_parse_dates=Trueオプションを指定すると、日時として解釈可能な列を自動的にDatetime型に変換します。

例: 次のCSVデータでは、start列は自動的に日時型に変換されます。

%%writefile data/test_time.csv
start,end,category
2024-12-10 10:32:14.200,20241210_103715.1,B
2024-12-11 02:40:02.340,20241211_024628.2,A
2024-12-11 15:40:30.550,20241211_154317.6,A
2024-12-12 05:48:19.000,20241212_054905.9,B
2024-12-12 13:42:35.990,20241212_135252.98,B
Overwriting data/test_time.csv
df = pl.read_csv('data/test_time.csv', try_parse_dates=True)
df
shape: (5, 3)
startendcategory
datetime[μs]strstr
2024-12-10 10:32:14.200"20241210_103715.1""B"
2024-12-11 02:40:02.340"20241211_024628.2""A"
2024-12-11 15:40:30.550"20241211_154317.6""A"
2024-12-12 05:48:19"20241212_054905.9""B"
2024-12-12 13:42:35.990"20241212_135252.98""B"

end列のような特殊な形式の文字列の場合、str.to_datetime()を使用して、文字列をDatetime型に変換できます。フォーマット指定子(例: %Y%m%d_%H%M%S)を指定することで、適切に変換が行えます。各個フィールドのフォーマットを指定する方法は次のURLに参考してください。

https://docs.rs/chrono/latest/chrono/format/strftime/index.html

df2 = df.with_columns(
    end=pl.col('end').str.to_datetime('%Y%m%d_%H%M%S%.f')
)
row(df.select('end'), df2.select('end'))
shape: (5, 1)
end
str
"20241210_103715.1"
"20241211_024628.2"
"20241211_154317.6"
"20241212_054905.9"
"20241212_135252.98"
shape: (5, 1)
end
datetime[μs]
2024-12-10 10:37:15.100
2024-12-11 02:46:28.200
2024-12-11 15:43:17.600
2024-12-12 05:49:05.900
2024-12-12 13:52:52.980

他には、str.to_date()str.to_time()を使用して文字列をDate型またはTime型に変換することもできます。Duration型に変換する場合は、一旦Time型に変換してから、Duration型にキャストすることができます。

時間型から文字列へ#

時間型データを特定のフォーマットで文字列に変換したい場合は、dt.strftime()を使用します。フォーマット指定子を指定することで、必要な形式の文字列を生成できます。

df3 = df2.select(
    formatted_start=pl.col('start').dt.strftime('%Y/%m/%d %H:%M:%S')
)

row(df2.select('start'), df3)
shape: (5, 1)
start
datetime[μs]
2024-12-10 10:32:14.200
2024-12-11 02:40:02.340
2024-12-11 15:40:30.550
2024-12-12 05:48:19
2024-12-12 13:42:35.990
shape: (5, 1)
formatted_start
str
"2024/12/10 10:32:14"
"2024/12/11 02:40:02"
"2024/12/11 15:40:30"
"2024/12/12 05:48:19"
"2024/12/12 13:42:35"

Duration型を文字列に変換する際には、最小単位を利用して値を計算し、その後pl.format()を使って必要な形式の文字列に整形します。この方法は、カスタムフォーマットを作成したい場合や、単位を変更して表示したい場合に便利です。

以下のコードでは、最小単位の値を適切に計算するために、cast()メソッドを使って十進数型(Decimal)にキャストします。これにより、より精度の高い計算が可能になります。

(
df2
.select(duration=pl.col('end') - pl.col('start'))
.select(
    'duration',
    pl.format('{} secs', 
              pl.col('duration')
              .dt.total_microseconds()
              .cast(pl.Decimal) / 1e6)
      .alias('duration_str')
)
)
shape: (5, 2)
durationduration_str
duration[μs]str
5m 900ms"300.9000 secs"
6m 25s 860ms"385.8600 secs"
2m 47s 50ms"167.0500 secs"
46s 900ms"46.9000 secs"
10m 16s 990ms"616.9900 secs"

Pythonの時間との変換#

Epochとの変換#

Polarsでは、Datetime型とエポック(1970年1月1日00:00:00 UTCからの経過時間)との相互変換を簡単に行うことができます。エポック時間は秒、ミリ秒、マイクロ秒といった単位で表されることが多く、データ処理や統計解析において広く使用されています。

  1. dt.epoch()
    Datetime型の列をエポック時間に変換します。単位(例: 秒's'、ナノ秒'ns')を指定可能です。

  2. pl.from_epoch()
    エポック時間の列をDatetime型に変換します。単位を指定して、対応する時間を生成します。

以下のコードは、エポック時間との変換を実行します。

df2 = df.select(
    epoch_s=pl.col('start').dt.epoch('s'),
    epoch_ns=pl.col('start').dt.epoch('ns'),
)
df3 = df2.select(
    start_s = pl.from_epoch('epoch_s', 's'),
    start_ns = pl.from_epoch('epoch_ns', 'ns')
)
row(df.select('start'), df2, df3)
shape: (5, 1)
start
datetime[μs]
2024-12-10 10:32:14.200
2024-12-11 02:40:02.340
2024-12-11 15:40:30.550
2024-12-12 05:48:19
2024-12-12 13:42:35.990
shape: (5, 2)
epoch_sepoch_ns
i64i64
17338267341733826734200000000
17338848021733884802340000000
17339316301733931630550000000
17339824991733982499000000000
17340109551734010955990000000
shape: (5, 2)
start_sstart_ns
datetime[μs]datetime[ns]
2024-12-10 10:32:142024-12-10 10:32:14.200
2024-12-11 02:40:022024-12-11 02:40:02.340
2024-12-11 15:40:302024-12-11 15:40:30.550
2024-12-12 05:48:192024-12-12 05:48:19
2024-12-12 13:42:352024-12-12 13:42:35.990