Skip to content

time_utils

Time utils are used to manage time dependencies in the application.

convert_datetime_to_zulu_string(datetime_utc)

Converts a UTC datetime object to a Zulu time string.

This function takes a datetime object in UTC and converts it to a string in Zulu time format (ISO 8601).

Parameters:

Name Type Description Default
datetime_utc datetime

A datetime object in UTC.

required

Returns:

Name Type Description
str str

A string representing the datetime in Zulu time format.

Examples:

from datetime import datetime
from zoneinfo import ZoneInfo

# Convert UTC datetime to Zulu string
dt = datetime(2025, 1, 1, 12, 0, tzinfo=ZoneInfo("UTC"))
zulu_time = convert_datetime_to_zulu_string(dt)
print(zulu_time)  # Output: '2025-01-01T12:00:00Z'
Source code in physical_operations_utils/time_utils.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def convert_datetime_to_zulu_string(datetime_utc: datetime) -> str:
    """
    Converts a UTC datetime object to a Zulu time string.

    This function takes a datetime object in UTC and converts it to a string
    in Zulu time format (ISO 8601).

    Args:
        datetime_utc (datetime): A datetime object in UTC.

    Returns:
        str: A string representing the datetime in Zulu time format.

    Examples:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        # Convert UTC datetime to Zulu string
        dt = datetime(2025, 1, 1, 12, 0, tzinfo=ZoneInfo("UTC"))
        zulu_time = convert_datetime_to_zulu_string(dt)
        print(zulu_time)  # Output: '2025-01-01T12:00:00Z'
        ```
    """
    validate_datetime_in_utc(datetime_utc)
    zulu_time = datetime_utc.strftime(ZULU_STRING_FORMAT)
    return zulu_time

get_nordpool_half_hour_from_utc_datetime(dt)

Determines the Nord Pool half hour corresponding to a given UTC datetime.

This function: 1. Validates that the provided datetime is in UTC. 2. Converts the datetime to the local Stockholm timezone. 3. Determines the Nord Pool half hour, which is the local half hour +1. 4. Handles fall DST changes, distinguishing the repeated half hour (e.g., 05 and 05X).

Parameters:

Name Type Description Default
dt datetime

A UTC datetime object.

required

Returns:

Name Type Description
str str

The Nord Pool half hour as a zero-padded string. - "05" for the first occurrence of 03:00 during fall DST change. - "05X" for the second occurrence of 03:00 during fall DST change. - "01" to "48" for regular cases.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
half_hour_3a = get_nordpool_half_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "06"

dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
half_hour_3b = get_nordpool_half_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "06X"
Source code in physical_operations_utils/time_utils.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def get_nordpool_half_hour_from_utc_datetime(dt: datetime) -> str:
    """
    Determines the Nord Pool half hour corresponding to a given UTC datetime.

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Converts the datetime to the local Stockholm timezone.
    3. Determines the Nord Pool half hour, which is the local half hour +1.
    4. Handles fall DST changes, distinguishing the repeated half hour (e.g., 05 and 05X).

    Parameters:
        dt (datetime): A UTC datetime object.

    Returns:
        str: The Nord Pool half hour as a zero-padded string.
             - `"05"` for the first occurrence of 03:00 during fall DST change.
             - `"05X"` for the second occurrence of 03:00 during fall DST change.
             - `"01"` to `"48"` for regular cases.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
        half_hour_3a = get_nordpool_half_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "06"

        dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
        half_hour_3b = get_nordpool_half_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "06X"
        ```
    """
    validate_datetime_in_utc(dt)
    dt_stockholm = dt.astimezone(tz=ZoneInfo(LOCAL_TIMEZONE_LITERAL))
    half_hour = str(dt_stockholm.minute // 30 + dt_stockholm.hour * 2 + 1).zfill(2)
    if is_fall_dst_change_day(dt) and dt_stockholm.hour == 2 and dt.hour == 1:
        return half_hour + "X"
    else:
        return half_hour

get_nordpool_power_hour_from_utc_datetime(dt)

Determines the Nord Pool power hour corresponding to a given UTC datetime.

This function: 1. Validates that the provided datetime is in UTC. 2. Converts the datetime to the local Stockholm timezone. 3. Determines the Nord Pool power hour, which is the local hour +1. 4. Handles fall DST changes, distinguishing the repeated hour (03 and 03X).

Parameters:

Name Type Description Default
dt datetime

A UTC datetime object.

required

Returns:

Name Type Description
str str

The Nord Pool power hour as a zero-padded string. - "03" for the first occurrence of 03:00 during fall DST change. - "03X" for the second occurrence of 03:00 during fall DST change. - "01" to "24" for regular cases.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt = datetime(2025, 3, 30, 12, 0, tzinfo=ZoneInfo("UTC"))
power_hour = get_nordpool_power_hour_from_utc_datetime(dt)  # Returns "14"

dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
power_hour_3a = get_nordpool_power_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "03"

dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
power_hour_3b = get_nordpool_power_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "03X"
Source code in physical_operations_utils/time_utils.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def get_nordpool_power_hour_from_utc_datetime(dt: datetime) -> str:
    """
    Determines the Nord Pool power hour corresponding to a given UTC datetime.

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Converts the datetime to the local Stockholm timezone.
    3. Determines the Nord Pool power hour, which is the local hour +1.
    4. Handles fall DST changes, distinguishing the repeated hour (03 and 03X).

    Parameters:
        dt (datetime): A UTC datetime object.

    Returns:
        str: The Nord Pool power hour as a zero-padded string.
             - `"03"` for the first occurrence of 03:00 during fall DST change.
             - `"03X"` for the second occurrence of 03:00 during fall DST change.
             - `"01"` to `"24"` for regular cases.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt = datetime(2025, 3, 30, 12, 0, tzinfo=ZoneInfo("UTC"))
        power_hour = get_nordpool_power_hour_from_utc_datetime(dt)  # Returns "14"

        dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
        power_hour_3a = get_nordpool_power_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "03"

        dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
        power_hour_3b = get_nordpool_power_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "03X"
        ```
    """
    validate_datetime_in_utc(dt)
    dt_stockholm = dt.astimezone(tz=ZoneInfo(LOCAL_TIMEZONE_LITERAL))
    power_hour = str(dt_stockholm.hour + 1).zfill(2)
    if is_fall_dst_change_day(dt) and dt_stockholm.hour == 2 and dt.hour == 1:
        return power_hour + "X"
    else:
        return power_hour

get_nordpool_quarter_hour_from_utc_datetime(dt)

Determines the Nord Pool quarter hour corresponding to a given UTC datetime.

This function: 1. Validates that the provided datetime is in UTC. 2. Converts the datetime to the local Stockholm timezone. 3. Determines the Nord Pool quarter hour, which is the local quarter +1. 4. Handles fall DST changes, distinguishing the repeated quarter (e.g. 09 and 09X).

Parameters:

Name Type Description Default
dt datetime

A UTC datetime object.

required

Returns:

Name Type Description
str str

The Nord Pool quarter hour as a zero-padded string. - "09" for the first occurrence of 03:00 during fall DST change. - "09X" for the second occurrence of 03:00 during fall DST change. - "01" to "96" for regular cases.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt = datetime(2025, 3, 29, 23, 0, tzinfo=ZoneInfo("UTC"))
quarter_hour = get_nordpool_quarter_hour_from_utc_datetime(dt)  # Returns "05"

dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
quarter_hour_3a = get_nordpool_quarter_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "10"

dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
quarter_hour_3b = get_nordpool_quarter_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "10X"
Source code in physical_operations_utils/time_utils.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def get_nordpool_quarter_hour_from_utc_datetime(dt: datetime) -> str:
    """
    Determines the Nord Pool quarter hour corresponding to a given UTC datetime.

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Converts the datetime to the local Stockholm timezone.
    3. Determines the Nord Pool quarter hour, which is the local quarter +1.
    4. Handles fall DST changes, distinguishing the repeated quarter (e.g. 09 and 09X).

    Parameters:
        dt (datetime): A UTC datetime object.

    Returns:
        str: The Nord Pool quarter hour as a zero-padded string.
             - `"09"` for the first occurrence of 03:00 during fall DST change.
             - `"09X"` for the second occurrence of 03:00 during fall DST change.
             - `"01"` to `"96"` for regular cases.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt = datetime(2025, 3, 29, 23, 0, tzinfo=ZoneInfo("UTC"))
        quarter_hour = get_nordpool_quarter_hour_from_utc_datetime(dt)  # Returns "05"

        dt_fall_dst_3a = datetime(2025, 10, 26, 0, 30, tzinfo=ZoneInfo("UTC"))
        quarter_hour_3a = get_nordpool_quarter_hour_from_utc_datetime(dt_fall_dst_3a)  # Returns "10"

        dt_fall_dst_3b = datetime(2025, 10, 26, 1, 30, tzinfo=ZoneInfo("UTC"))
        quarter_hour_3b = get_nordpool_quarter_hour_from_utc_datetime(dt_fall_dst_3b)  # Returns "10X"
        ```
    """
    validate_datetime_in_utc(dt)
    dt_stockholm = dt.astimezone(tz=ZoneInfo(LOCAL_TIMEZONE_LITERAL))
    quarter = str(dt_stockholm.minute // 15 + dt_stockholm.hour * 4 + 1).zfill(2)
    if is_fall_dst_change_day(dt) and dt_stockholm.hour == 2 and dt.hour == 1:
        return quarter + "X"
    else:
        return quarter

get_start_current_stockholm_hour_utc()

Returns the current local hour (Stockholm time) as a UTC datetime with minutes, seconds, and microseconds set to zero.

This function: 1. Retrieves the current datetime in Stockholm timezone. 2. Resets the minute, second, and microsecond to zero. 3. Converts the result to UTC.

Returns:

Name Type Description
datetime datetime

The start of the current local hour in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_utc = get_start_current_local_hour_utc()
print(dt_utc)  # Example output: 2025-02-01 13:00:00+00:00
Source code in physical_operations_utils/time_utils.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def get_start_current_stockholm_hour_utc() -> datetime:
    """
    Returns the current local hour (Stockholm time) as a UTC datetime
    with minutes, seconds, and microseconds set to zero.

    This function:
    1. Retrieves the current datetime in Stockholm timezone.
    2. Resets the minute, second, and microsecond to zero.
    3. Converts the result to UTC.

    Returns:
        datetime: The start of the current local hour in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_utc = get_start_current_local_hour_utc()
        print(dt_utc)  # Example output: 2025-02-01 13:00:00+00:00
        ```
    """
    return (
        datetime.now(ZoneInfo(LOCAL_TIMEZONE_LITERAL))
        .replace(minute=0, second=0, microsecond=0)
        .astimezone(tz=ZoneInfo(UTC_LITERAL))
    )

get_start_current_stockholm_quarter_utc()

Returns the current local quarter (Stockholm time) as a UTC datetime with seconds, and microseconds set to zero.

Returns:

Name Type Description
datetime datetime

The start of the current local quarter in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_utc = get_start_current_stockholm_quarter_utc()
print(dt_utc)  # Example output: 2025-02-01 13:30:00+00:00
Source code in physical_operations_utils/time_utils.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
def get_start_current_stockholm_quarter_utc() -> datetime:
    """
    Returns the current local quarter (Stockholm time) as a UTC datetime
    with seconds, and microseconds set to zero.

    Returns:
        datetime: The start of the current local quarter in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_utc = get_start_current_stockholm_quarter_utc()
        print(dt_utc)  # Example output: 2025-02-01 13:30:00+00:00
        ```
    """
    now_utc_with_minutes = (
        datetime.now(ZoneInfo(LOCAL_TIMEZONE_LITERAL))
        .replace(second=0, microsecond=0)
        .astimezone(tz=ZoneInfo(UTC_LITERAL))
    )
    first_minute_of_current_quarter = 15 * (now_utc_with_minutes.minute // 15)
    return now_utc_with_minutes.replace(minute=first_minute_of_current_quarter)

get_start_first_hour_stockholm_date_utc(date_str)

Converts a given date string (YYYY-MM-DD) representing a date in Stockholm timezone to UTC.

This function: 1. Validates the input format using a regex (YYYY-MM-DD). 2. Creates a datetime object at 00:00 in Stockholm timezone. 3. Converts the datetime to UTC.

Parameters:

Name Type Description Default
date_str str

A string representing the date in YYYY-MM-DD format.

required

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 00:00 Stockholm time for the given date.

Raises:

Type Description
ValueError

If the input format is incorrect.

Example
from datetime import datetime

dt_utc = get_start_first_hour_stockholm_date_utc("2025-02-01")
print(dt_utc)  # Example output: 2025-01-31 23:00:00+00:00
Source code in physical_operations_utils/time_utils.py
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
def get_start_first_hour_stockholm_date_utc(date_str: str) -> datetime:
    """
    Converts a given date string (YYYY-MM-DD) representing  a date in
    Stockholm timezone to UTC.

    This function:
    1. Validates the input format using a regex (`YYYY-MM-DD`).
    2. Creates a `datetime` object at `00:00` in Stockholm timezone.
    3. Converts the `datetime` to UTC.

    Parameters:
        date_str (str): A string representing the date in `YYYY-MM-DD` format.

    Returns:
        datetime: The UTC equivalent of `00:00` Stockholm time for the given date.

    Raises:
        ValueError: If the input format is incorrect.

    Example:
        ```python
        from datetime import datetime

        dt_utc = get_start_first_hour_stockholm_date_utc("2025-02-01")
        print(dt_utc)  # Example output: 2025-01-31 23:00:00+00:00
        ```
    """
    if not re.match(r"\d{4}-\d{2}-\d{2}", date_str):
        raise ValueError("Date string must be in the format 'YYYY-MM-DD'")
    datetime_stockholm = datetime.strptime(date_str, "%Y-%m-%d").replace(
        hour=0,
        minute=0,
        second=0,
        microsecond=0,
        tzinfo=ZoneInfo(LOCAL_TIMEZONE_LITERAL),
    )
    return datetime_stockholm.astimezone(tz=ZoneInfo(UTC_LITERAL))

get_start_first_hour_stockholm_day_utc(scope, day_delta=0)

Returns the start of the first hour of day relative to the current day (Stockholm/Europe) in UTC.

This function: 1. Validates the scope ("today", "future", "past"). 2. Validates that day_delta is a non-negative integer. 3. Retrieves the current Stockholm midnight (00:00). 4. Adjusts the date forward or backward based on scope. 5. Converts the resulting datetime to UTC.

Parameters:

Name Type Description Default
scope str

"today", "future", or "past".

required
day_delta int

Number of days to shift in the given scope. Defaults to 0.

0

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 00:00 Stockholm time for the given scope.

Raises:

Type Description
ValueError

If scope is invalid.

ValueError

If day_delta is not a non-negative integer.

Example
from datetime import datetime

dt_utc_today = get_start_first_hour_stockholm_day_utc("today")
print(dt_utc_today)  # Example output: 2025-02-01 23:00:00+00:00

dt_utc_future = get_start_first_hour_stockholm_day_utc("future", 3)
print(dt_utc_future)  # Example output: 2025-02-04 23:00:00+00:00

dt_utc_past = get_start_first_hour_stockholm_day_utc("past", 5)
print(dt_utc_past)  # Example output: 2025-01-27 23:00:00+00:00
Source code in physical_operations_utils/time_utils.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
def get_start_first_hour_stockholm_day_utc(scope: str, day_delta: int = 0) -> datetime:
    """
    Returns the start of the first hour of day relative to the current day (Stockholm/Europe) in UTC.

    This function:
    1. Validates the scope (`"today"`, `"future"`, `"past"`).
    2. Validates that `day_delta` is a non-negative integer.
    3. Retrieves the current Stockholm midnight (`00:00`).
    4. Adjusts the date forward or backward based on `scope`.
    5. Converts the resulting datetime to UTC.

    Parameters:
        scope (str): `"today"`, `"future"`, or `"past"`.
        day_delta (int, optional): Number of days to shift in the given scope. Defaults to 0.

    Returns:
        datetime: The UTC equivalent of `00:00` Stockholm time for the given scope.

    Raises:
        ValueError: If `scope` is invalid.
        ValueError: If `day_delta` is not a non-negative integer.

    Example:
        ```python
        from datetime import datetime

        dt_utc_today = get_start_first_hour_stockholm_day_utc("today")
        print(dt_utc_today)  # Example output: 2025-02-01 23:00:00+00:00

        dt_utc_future = get_start_first_hour_stockholm_day_utc("future", 3)
        print(dt_utc_future)  # Example output: 2025-02-04 23:00:00+00:00

        dt_utc_past = get_start_first_hour_stockholm_day_utc("past", 5)
        print(dt_utc_past)  # Example output: 2025-01-27 23:00:00+00:00
        ```
    """
    scope = scope.lower()
    if scope not in ["today", "future", "past"]:
        raise ValueError("Scope must be 'today', 'future' or 'past'.")
    if not isinstance(day_delta, int) or day_delta < 0:
        raise ValueError("Day delta must be a positive integer or zero.")
    first_hour_local = datetime.now(ZoneInfo(LOCAL_TIMEZONE_LITERAL)).replace(
        hour=0, minute=0, second=0, microsecond=0
    )
    if scope == "future":
        first_hour_local += timedelta(days=day_delta)
    elif scope == "past":
        first_hour_local -= timedelta(days=day_delta)
    return first_hour_local.astimezone(tz=ZoneInfo(UTC_LITERAL))

get_start_first_quarter_stockholm_date_utc(date_str)

Converts a given date string (YYYY-MM-DD) representing the first quarter-hour (00:00) in Stockholm timezone to UTC.

This function: 1. Calls get_start_first_hour_stockholm_date_utc to get the equivalent UTC datetime for 00:00.

Parameters:

Name Type Description Default
date_str str

A string representing the date in YYYY-MM-DD format.

required

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 00:00 Stockholm time for the given date.

Raises:

Type Description
ValueError

If the input format is incorrect.

Example
from datetime import datetime

dt_utc = get_start_first_quarter_stockholm_date_utc("2025-02-01")
print(dt_utc)  # Example output: 2025-01-31 23:00:00+00:00
Source code in physical_operations_utils/time_utils.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
def get_start_first_quarter_stockholm_date_utc(
    date_str: str,
) -> datetime:
    """
    Converts a given date string (YYYY-MM-DD) representing the first quarter-hour (`00:00`)
    in Stockholm timezone to UTC.

    This function:
    1. Calls `get_start_first_hour_stockholm_date_utc` to get the equivalent UTC datetime for `00:00`.

    Parameters:
        date_str (str): A string representing the date in `YYYY-MM-DD` format.

    Returns:
        datetime: The UTC equivalent of `00:00` Stockholm time for the given date.

    Raises:
        ValueError: If the input format is incorrect.

    Example:
        ```python
        from datetime import datetime

        dt_utc = get_start_first_quarter_stockholm_date_utc("2025-02-01")
        print(dt_utc)  # Example output: 2025-01-31 23:00:00+00:00
        ```
    """
    return get_start_first_hour_stockholm_date_utc(date_str)

get_start_first_quarter_stockholm_day_utc(scope, day_delta=0)

Returns the start of the first quarter-hour of the day relative to the current day (Stockholm/Europe) in UTC.

This function: 1. Calls get_start_first_hour_stockholm_day_utc to get the start of the first hour (00:00). 2. The result represents 00:00 in Stockholm time converted to UTC.

Parameters:

Name Type Description Default
scope str

"today", "future", or "past".

required
day_delta int

Number of days to shift in the given scope. Defaults to 0.

0

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 00:00 Stockholm time for the given scope.

Raises:

Type Description
ValueError

If scope is invalid.

ValueError

If day_delta is not a non-negative integer.

Example
from datetime import datetime

dt_utc_today = get_start_first_quarter_stockholm_day_utc("today")
print(dt_utc_today)  # Example output: 2025-02-01 23:00:00+00:00

dt_utc_future = get_start_first_quarter_stockholm_day_utc("future", 3)
print(dt_utc_future)  # Example output: 2025-02-04 23:00:00+00:00

dt_utc_past = get_start_first_quarter_stockholm_day_utc("past", 5)
print(dt_utc_past)  # Example output: 2025-01-27 23:00:00+00:00
Source code in physical_operations_utils/time_utils.py
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def get_start_first_quarter_stockholm_day_utc(
    scope: str, day_delta: int = 0
) -> datetime:
    """
    Returns the start of the first quarter-hour of the day relative to the current day (Stockholm/Europe) in UTC.

    This function:
    1. Calls `get_start_first_hour_stockholm_day_utc` to get the start of the first hour (`00:00`).
    2. The result represents `00:00` in Stockholm time converted to UTC.

    Parameters:
        scope (str): `"today"`, `"future"`, or `"past"`.
        day_delta (int, optional): Number of days to shift in the given scope. Defaults to 0.

    Returns:
        datetime: The UTC equivalent of `00:00` Stockholm time for the given scope.

    Raises:
        ValueError: If `scope` is invalid.
        ValueError: If `day_delta` is not a non-negative integer.

    Example:
        ```python
        from datetime import datetime

        dt_utc_today = get_start_first_quarter_stockholm_day_utc("today")
        print(dt_utc_today)  # Example output: 2025-02-01 23:00:00+00:00

        dt_utc_future = get_start_first_quarter_stockholm_day_utc("future", 3)
        print(dt_utc_future)  # Example output: 2025-02-04 23:00:00+00:00

        dt_utc_past = get_start_first_quarter_stockholm_day_utc("past", 5)
        print(dt_utc_past)  # Example output: 2025-01-27 23:00:00+00:00
        ```
    """
    return get_start_first_hour_stockholm_day_utc(scope, day_delta)

get_start_last_hour_stockholm_date_utc(date_str)

Converts a given date string (YYYY-MM-DD) representing a date in Stockholm timezone to UTC.

This function: 1. Validates the input format using a regex (YYYY-MM-DD). 2. Creates a datetime object at 23:00 in Stockholm timezone. 3. Converts the datetime to UTC.

Parameters:

Name Type Description Default
date_str str

A string representing the date in YYYY-MM-DD format.

required

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 23:00 Stockholm time for the given date.

Raises:

Type Description
ValueError

If the input format is incorrect.

Example
from datetime import datetime

dt_utc = get_start_last_hour_stockholm_date_utc("2025-02-01")
print(dt_utc)  # Example output: 2025-02-01 22:00:00+00:00
Source code in physical_operations_utils/time_utils.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
def get_start_last_hour_stockholm_date_utc(date_str: str) -> datetime:
    """
    Converts a given date string (YYYY-MM-DD) representing a date
    in Stockholm timezone to UTC.

    This function:
    1. Validates the input format using a regex (`YYYY-MM-DD`).
    2. Creates a `datetime` object at `23:00` in Stockholm timezone.
    3. Converts the `datetime` to UTC.

    Parameters:
        date_str (str): A string representing the date in `YYYY-MM-DD` format.

    Returns:
        datetime: The UTC equivalent of `23:00` Stockholm time for the given date.

    Raises:
        ValueError: If the input format is incorrect.

    Example:
        ```python
        from datetime import datetime

        dt_utc = get_start_last_hour_stockholm_date_utc("2025-02-01")
        print(dt_utc)  # Example output: 2025-02-01 22:00:00+00:00
        ```
    """
    if not re.match(r"\d{4}-\d{2}-\d{2}", date_str):
        raise ValueError("Date string must be in the format 'YYYY-MM-DD'")
    datetime_stockholm = datetime.strptime(date_str, "%Y-%m-%d").replace(
        hour=23,
        minute=0,
        second=0,
        microsecond=0,
        tzinfo=ZoneInfo(LOCAL_TIMEZONE_LITERAL),
    )
    return datetime_stockholm.astimezone(tz=ZoneInfo(UTC_LITERAL))

get_start_last_hour_stockholm_day_utc(scope, day_delta=0)

Returns the start of the last hour of day relative to the current day (Stockholm/Europe) in UTC.

This function: 1. Validates the scope ("today", "future", "past"). 2. Validates that day_delta is a non-negative integer. 3. Retrieves the current Stockholm time at 23:00. 4. Adjusts the date forward or backward based on scope. 5. Converts the resulting datetime to UTC.

Parameters:

Name Type Description Default
scope str

"today", "future", or "past".

required
day_delta int

Number of days to shift in the given scope. Defaults to 0.

0

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 23:00 Stockholm time for the given scope.

Raises:

Type Description
ValueError

If scope is invalid.

ValueError

If day_delta is not a non-negative integer.

Example
from datetime import datetime

dt_utc_today = get_start_last_hour_stockholm_day_utc("today")
print(dt_utc_today)  # Example output: 2025-02-01 22:00:00+00:00

dt_utc_future = get_start_last_hour_stockholm_day_utc("future", 3)
print(dt_utc_future)  # Example output: 2025-02-04 22:00:00+00:00

dt_utc_past = get_start_last_hour_stockholm_day_utc("past", 5)
print(dt_utc_past)  # Example output: 2025-01-27 22:00:00+00:00
Source code in physical_operations_utils/time_utils.py
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
def get_start_last_hour_stockholm_day_utc(scope: str, day_delta: int = 0) -> datetime:
    """
    Returns the start of the last hour of day relative to the current day (Stockholm/Europe) in UTC.

    This function:
    1. Validates the scope (`"today"`, `"future"`, `"past"`).
    2. Validates that `day_delta` is a non-negative integer.
    3. Retrieves the current Stockholm time at 23:00.
    4. Adjusts the date forward or backward based on `scope`.
    5. Converts the resulting datetime to UTC.

    Parameters:
        scope (str): `"today"`, `"future"`, or `"past"`.
        day_delta (int, optional): Number of days to shift in the given scope. Defaults to 0.

    Returns:
        datetime: The UTC equivalent of `23:00` Stockholm time for the given scope.

    Raises:
        ValueError: If `scope` is invalid.
        ValueError: If `day_delta` is not a non-negative integer.

    Example:
        ```python
        from datetime import datetime

        dt_utc_today = get_start_last_hour_stockholm_day_utc("today")
        print(dt_utc_today)  # Example output: 2025-02-01 22:00:00+00:00

        dt_utc_future = get_start_last_hour_stockholm_day_utc("future", 3)
        print(dt_utc_future)  # Example output: 2025-02-04 22:00:00+00:00

        dt_utc_past = get_start_last_hour_stockholm_day_utc("past", 5)
        print(dt_utc_past)  # Example output: 2025-01-27 22:00:00+00:00
        ```
    """
    scope = scope.lower()
    if scope not in ["today", "future", "past"]:
        raise ValueError("Scope must be 'today', 'future' or 'past'.")
    if not isinstance(day_delta, int) or day_delta < 0:
        raise ValueError("Day delta must be a positive integer or zero.")
    last_hour_today_local = datetime.now(ZoneInfo(LOCAL_TIMEZONE_LITERAL)).replace(
        hour=23, minute=0, second=0, microsecond=0
    )
    if scope == "future":
        last_hour_today_local += timedelta(days=day_delta)
    elif scope == "past":
        last_hour_today_local -= timedelta(days=day_delta)
    return last_hour_today_local.astimezone(tz=ZoneInfo(UTC_LITERAL))

get_start_last_quarter_stockholm_date_utc(date_str)

Converts a given date string (YYYY-MM-DD) representing the last quarter-hour (23:45) in Stockholm timezone to UTC.

This function: 1. Calls get_start_last_hour_stockholm_date_utc to get the UTC equivalent of 23:00. 2. Adds 45 minutes to get the last quarter-hour (23:45 Stockholm time). 3. The resulting datetime is converted to UTC.

Parameters:

Name Type Description Default
date_str str

A string representing the date in YYYY-MM-DD format.

required

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 23:45 Stockholm time for the given date.

Raises:

Type Description
ValueError

If the input format is incorrect.

Example
from datetime import datetime

dt_utc = get_start_last_quarter_stockholm_date_utc("2025-02-01")
print(dt_utc)  # Example output: 2025-02-01 22:45:00+00:00
Source code in physical_operations_utils/time_utils.py
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
def get_start_last_quarter_stockholm_date_utc(
    date_str: str,
) -> datetime:
    """
    Converts a given date string (YYYY-MM-DD) representing the last quarter-hour (`23:45`)
    in Stockholm timezone to UTC.

    This function:
    1. Calls `get_start_last_hour_stockholm_date_utc` to get the UTC equivalent of `23:00`.
    2. Adds 45 minutes to get the last quarter-hour (`23:45` Stockholm time).
    3. The resulting datetime is converted to UTC.

    Parameters:
        date_str (str): A string representing the date in `YYYY-MM-DD` format.

    Returns:
        datetime: The UTC equivalent of `23:45` Stockholm time for the given date.

    Raises:
        ValueError: If the input format is incorrect.

    Example:
        ```python
        from datetime import datetime

        dt_utc = get_start_last_quarter_stockholm_date_utc("2025-02-01")
        print(dt_utc)  # Example output: 2025-02-01 22:45:00+00:00
        ```
    """
    return get_start_last_hour_stockholm_date_utc(date_str) + timedelta(minutes=45)

get_start_last_quarter_stockholm_day_utc(scope, day_delta=0)

Returns the start of the last quarter-hour of the day relative to the current day (Stockholm/Europe) in UTC.

This function: 1. Calls get_start_last_hour_stockholm_day_utc to get the UTC equivalent of 23:00. 2. Adds 45 minutes to get the last quarter-hour (23:45 Stockholm time). 3. The resulting datetime is converted to UTC.

Parameters:

Name Type Description Default
scope str

"today", "future", or "past".

required
day_delta int

Number of days to shift in the given scope. Defaults to 0.

0

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 23:45 Stockholm time for the given scope.

Raises:

Type Description
ValueError

If scope is invalid.

ValueError

If day_delta is not a non-negative integer.

Example
from datetime import datetime

dt_utc_today = get_start_last_quarter_stockholm_day_utc("today")
print(dt_utc_today)  # Example output: 2025-02-01 22:45:00+00:00

dt_utc_future = get_start_last_quarter_stockholm_day_utc("future", 3)
print(dt_utc_future)  # Example output: 2025-02-04 22:45:00+00:00

dt_utc_past = get_start_last_quarter_stockholm_day_utc("past", 5)
print(dt_utc_past)  # Example output: 2025-01-27 22:45:00+00:00
Source code in physical_operations_utils/time_utils.py
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
def get_start_last_quarter_stockholm_day_utc(
    scope: str, day_delta: int = 0
) -> datetime:
    """
    Returns the start of the last quarter-hour of the day relative to the current day
    (Stockholm/Europe) in UTC.

    This function:
    1. Calls `get_start_last_hour_stockholm_day_utc` to get the UTC equivalent of `23:00`.
    2. Adds 45 minutes to get the last quarter-hour (`23:45` Stockholm time).
    3. The resulting datetime is converted to UTC.

    Parameters:
        scope (str): `"today"`, `"future"`, or `"past"`.
        day_delta (int, optional): Number of days to shift in the given scope. Defaults to 0.

    Returns:
        datetime: The UTC equivalent of `23:45` Stockholm time for the given scope.

    Raises:
        ValueError: If `scope` is invalid.
        ValueError: If `day_delta` is not a non-negative integer.

    Example:
        ```python
        from datetime import datetime

        dt_utc_today = get_start_last_quarter_stockholm_day_utc("today")
        print(dt_utc_today)  # Example output: 2025-02-01 22:45:00+00:00

        dt_utc_future = get_start_last_quarter_stockholm_day_utc("future", 3)
        print(dt_utc_future)  # Example output: 2025-02-04 22:45:00+00:00

        dt_utc_past = get_start_last_quarter_stockholm_day_utc("past", 5)
        print(dt_utc_past)  # Example output: 2025-01-27 22:45:00+00:00
        ```
    """
    return get_start_last_hour_stockholm_day_utc(scope, day_delta) + timedelta(
        minutes=45
    )

get_start_middle_hour_stockholm_day_utc(scope, day_delta=0)

Returns the start of the middle hour (12:00) of a given day relative to the current day (Stockholm/Europe) in UTC.

This function: 1. Validates the scope ("today", "future", "past"). 2. Validates that day_delta is a non-negative integer. 3. Retrieves the current time in Stockholm set to 12:00. 4. Adjusts the date forward or backward based on scope. 5. Converts the resulting datetime to UTC.

Parameters:

Name Type Description Default
scope str

"today", "future", or "past".

required
day_delta int

Number of days to shift in the given scope. Defaults to 0.

0

Returns:

Name Type Description
datetime datetime

The UTC equivalent of 12:00 Stockholm time for the given scope.

Raises:

Type Description
ValueError

If scope is invalid.

ValueError

If day_delta is not a non-negative integer.

Example
from datetime import datetime

dt_utc_today = get_start_middle_hour_stockholm_day_utc("today")
print(dt_utc_today)  # Example output: 2025-02-01 11:00:00+00:00

dt_utc_future = get_start_middle_hour_stockholm_day_utc("future", 3)
print(dt_utc_future)  # Example output: 2025-02-04 11:00:00+00:00

dt_utc_past = get_start_middle_hour_stockholm_day_utc("past", 5)
print(dt_utc_past)  # Example output: 2025-01-27 11:00:00+00:00
Source code in physical_operations_utils/time_utils.py
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
def get_start_middle_hour_stockholm_day_utc(scope: str, day_delta: int = 0) -> datetime:
    """
    Returns the start of the middle hour (12:00) of a given day relative to the
    current day (Stockholm/Europe) in UTC.

    This function:
    1. Validates the scope (`"today"`, `"future"`, `"past"`).
    2. Validates that `day_delta` is a non-negative integer.
    3. Retrieves the current time in Stockholm set to `12:00`.
    4. Adjusts the date forward or backward based on `scope`.
    5. Converts the resulting datetime to UTC.

    Parameters:
        scope (str): `"today"`, `"future"`, or `"past"`.
        day_delta (int, optional): Number of days to shift in the given scope. Defaults to 0.

    Returns:
        datetime: The UTC equivalent of `12:00` Stockholm time for the given scope.

    Raises:
        ValueError: If `scope` is invalid.
        ValueError: If `day_delta` is not a non-negative integer.

    Example:
        ```python
        from datetime import datetime

        dt_utc_today = get_start_middle_hour_stockholm_day_utc("today")
        print(dt_utc_today)  # Example output: 2025-02-01 11:00:00+00:00

        dt_utc_future = get_start_middle_hour_stockholm_day_utc("future", 3)
        print(dt_utc_future)  # Example output: 2025-02-04 11:00:00+00:00

        dt_utc_past = get_start_middle_hour_stockholm_day_utc("past", 5)
        print(dt_utc_past)  # Example output: 2025-01-27 11:00:00+00:00
        ```
    """
    scope = scope.lower()
    if scope not in ["today", "future", "past"]:
        raise ValueError("Scope must be 'today', 'future' or 'past'.")
    if not isinstance(day_delta, int) or day_delta < 0:
        raise ValueError("Day delta must be a positive integer or zero.")
    middle_hour_local = datetime.now(ZoneInfo(LOCAL_TIMEZONE_LITERAL)).replace(
        hour=12, minute=0, second=0, microsecond=0
    )
    if scope == "future":
        middle_hour_local += timedelta(days=day_delta)
    elif scope == "past":
        middle_hour_local -= timedelta(days=day_delta)
    return middle_hour_local.astimezone(tz=ZoneInfo(UTC_LITERAL))

get_utc_now_custom_precision(set_hour_0=False, set_minute_0=False, set_second_0=False, set_microsecond_0=False)

Returns the current UTC datetime with optional precision adjustments.

This function retrieves the current time in UTC and allows optional modification of specific time components (hour, minute, second, microsecond) to zero.

Parameters:

Name Type Description Default
set_hour_0 bool

If True, sets the hour to 00.

False
set_minute_0 bool

If True, sets the minutes to 00.

False
set_second_0 bool

If True, sets the seconds to 00.

False
set_microsecond_0 bool

If True, sets the microseconds to 000000.

False

Returns:

Name Type Description
datetime datetime

The current UTC datetime with the specified modifications.

Example
from datetime import datetime
dt1 = get_utc_now_custom_precision(set_hour_0=True, set_minute_0=True)
print(dt1)  # Example: 2025-01-31 00:00:45.123456+00:00

dt2 = get_utc_now_custom_precision(set_second_0=True, set_microsecond_0=True)
print(dt2)  # Example: 2025-01-31 14:32:00.000000+00:00
Source code in physical_operations_utils/time_utils.py
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
def get_utc_now_custom_precision(
    set_hour_0: bool = False,
    set_minute_0: bool = False,
    set_second_0: bool = False,
    set_microsecond_0: bool = False,
) -> datetime:
    """
    Returns the current UTC datetime with optional precision adjustments.

    This function retrieves the current time in UTC and allows optional
    modification of specific time components (hour, minute, second, microsecond)
    to zero.

    Parameters:
        set_hour_0 (bool, optional): If `True`, sets the hour to `00`.
        set_minute_0 (bool, optional): If `True`, sets the minutes to `00`.
        set_second_0 (bool, optional): If `True`, sets the seconds to `00`.
        set_microsecond_0 (bool, optional): If `True`, sets the microseconds to `000000`.

    Returns:
        datetime: The current UTC datetime with the specified modifications.

    Example:
        ```python
        from datetime import datetime
        dt1 = get_utc_now_custom_precision(set_hour_0=True, set_minute_0=True)
        print(dt1)  # Example: 2025-01-31 00:00:45.123456+00:00

        dt2 = get_utc_now_custom_precision(set_second_0=True, set_microsecond_0=True)
        print(dt2)  # Example: 2025-01-31 14:32:00.000000+00:00
        ```
    """
    now_utc = datetime.now(ZoneInfo(UTC_LITERAL))
    if set_hour_0:
        now_utc = now_utc.replace(hour=0)
    if set_minute_0:
        now_utc = now_utc.replace(minute=0)
    if set_second_0:
        now_utc = now_utc.replace(second=0)
    if set_microsecond_0:
        now_utc = now_utc.replace(microsecond=0)
    return now_utc

is_datetime_in_current_utc_hour(dt_utc)

Determines if a given UTC datetime falls within the current UTC hour.

This function: 1. Validates that the provided datetime is in UTC. 2. Retrieves the current UTC datetime with precision adjustments. 3. Compares the date and hour of the input datetime with the current UTC datetime. 4. Ensures that the input datetime's minute is less than or equal to the current UTC minute.

Parameters:

Name Type Description Default
dt_utc datetime

A UTC datetime object to check.

required

Returns:

Name Type Description
bool bool

True if the input datetime is within the current UTC hour and minute is <= now, False otherwise.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt = datetime.now(ZoneInfo("UTC"))
is_current = is_datetime_in_current_utc_hour(dt)
print(is_current)  # True if dt is within the current UTC hour
Source code in physical_operations_utils/time_utils.py
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
def is_datetime_in_current_utc_hour(dt_utc: datetime) -> bool:
    """
    Determines if a given UTC datetime falls within the current UTC hour.

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Retrieves the current UTC datetime with precision adjustments.
    3. Compares the date and hour of the input datetime with the current UTC datetime.
    4. Ensures that the input datetime's minute is less than or equal to the current UTC minute.

    Parameters:
        dt_utc (datetime): A UTC datetime object to check.

    Returns:
        bool: `True` if the input datetime is within the current UTC hour and minute is <= now, `False` otherwise.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt = datetime.now(ZoneInfo("UTC"))
        is_current = is_datetime_in_current_utc_hour(dt)
        print(is_current)  # True if dt is within the current UTC hour
        ```
    """
    validate_datetime_in_utc(dt_utc)
    now_utc = get_utc_now_custom_precision()
    is_today = dt_utc.date() == now_utc.date()
    return is_today and dt_utc.hour == now_utc.hour and dt_utc.minute <= now_utc.minute

is_fall_dst_change_day(dt_utc)

Determines if the given UTC datetime falls on a daylight saving time (DST) transition day when clocks are set back (fall transition).

This function: 1. Validates that the provided datetime is in UTC. 2. Converts the datetime to the local Stockholm timezone. 3. Calculates the total number of hours in that local day. 4. Returns True if the day contains 25 hours (indicating a fall DST change).

Parameters:

Name Type Description Default
dt_utc datetime

A UTC datetime object to check.

required

Returns:

Name Type Description
bool bool

True if the date is a fall DST transition day (i.e., contains 25 hours), False otherwise.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_fall_dst = datetime(2025, 10, 26, 12, 0, tzinfo=ZoneInfo("UTC"))  # Example fall DST change
is_fall_dst = is_fall_dst_change_day(dt_fall_dst)  # Returns True if it's a fall DST change day

dt_non_dst = datetime(2025, 10, 25, 12, 0, tzinfo=ZoneInfo("UTC"))
is_fall_dst = is_fall_dst_change_day(dt_non_dst)  # Returns False
Source code in physical_operations_utils/time_utils.py
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
def is_fall_dst_change_day(dt_utc: datetime) -> bool:
    """
    Determines if the given UTC datetime falls on a daylight saving time (DST)
    transition day when clocks are set back (fall transition).

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Converts the datetime to the local Stockholm timezone.
    3. Calculates the total number of hours in that local day.
    4. Returns `True` if the day contains 25 hours (indicating a fall DST change).

    Parameters:
        dt_utc (datetime): A UTC datetime object to check.

    Returns:
        bool: `True` if the date is a fall DST transition day (i.e., contains 25 hours),
              `False` otherwise.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_fall_dst = datetime(2025, 10, 26, 12, 0, tzinfo=ZoneInfo("UTC"))  # Example fall DST change
        is_fall_dst = is_fall_dst_change_day(dt_fall_dst)  # Returns True if it's a fall DST change day

        dt_non_dst = datetime(2025, 10, 25, 12, 0, tzinfo=ZoneInfo("UTC"))
        is_fall_dst = is_fall_dst_change_day(dt_non_dst)  # Returns False
        ```
    """
    validate_datetime_in_utc(dt_utc)
    dt_local = dt_utc.astimezone(ZoneInfo(LOCAL_TIMEZONE_LITERAL))
    day_start = dt_local.replace(hour=0, minute=0, second=0, microsecond=0)
    day_end = (dt_local + timedelta(days=1)).replace(
        hour=0, minute=0, second=0, microsecond=0
    )
    day_start_utc = day_start.astimezone(tz=ZoneInfo(UTC_LITERAL))
    day_end_utc = day_end.astimezone(tz=ZoneInfo(UTC_LITERAL))
    number_of_hours = int((day_end_utc - day_start_utc).total_seconds() / 3600)
    return number_of_hours == 25

is_spring_dst_change_day(dt_utc)

Determines if the given UTC datetime falls on a daylight saving time (DST) transition day when clocks are set forward (spring transition).

This function: 1. Validates that the provided datetime is in UTC. 2. Converts the datetime to the local Stockholm timezone. 3. Calculates the total number of hours in that local day. 4. Returns True if the day contains 23 hours (indicating a spring DST change).

Parameters:

Name Type Description Default
dt_utc datetime

A UTC datetime object to check.

required

Returns:

Name Type Description
bool bool

True if the date is a spring DST transition day (i.e., contains 23 hours), False otherwise.

Raises:

Type Description
ValueError

If the provided datetime is not in UTC.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_spring_dst = datetime(2025, 3, 30, 12, 0, tzinfo=ZoneInfo("UTC"))  # Example spring DST change
is_spring_dst = is_spring_dst_change_day(dt_spring_dst)  # Returns True

dt_non_dst = datetime(2025, 3, 29, 12, 0, tzinfo=ZoneInfo("UTC"))
is_spring_dst = is_spring_dst_change_day(dt_non_dst)  # Returns False
Source code in physical_operations_utils/time_utils.py
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
def is_spring_dst_change_day(dt_utc: datetime) -> bool:
    """
    Determines if the given UTC datetime falls on a daylight saving time (DST)
    transition day when clocks are set forward (spring transition).

    This function:
    1. Validates that the provided datetime is in UTC.
    2. Converts the datetime to the local Stockholm timezone.
    3. Calculates the total number of hours in that local day.
    4. Returns `True` if the day contains 23 hours (indicating a spring DST change).

    Parameters:
        dt_utc (datetime): A UTC datetime object to check.

    Returns:
        bool: `True` if the date is a spring DST transition day (i.e., contains 23 hours),
              `False` otherwise.

    Raises:
        ValueError: If the provided datetime is not in UTC.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_spring_dst = datetime(2025, 3, 30, 12, 0, tzinfo=ZoneInfo("UTC"))  # Example spring DST change
        is_spring_dst = is_spring_dst_change_day(dt_spring_dst)  # Returns True

        dt_non_dst = datetime(2025, 3, 29, 12, 0, tzinfo=ZoneInfo("UTC"))
        is_spring_dst = is_spring_dst_change_day(dt_non_dst)  # Returns False
        ```
    """
    validate_datetime_in_utc(dt_utc)
    dt_local = dt_utc.astimezone(ZoneInfo(LOCAL_TIMEZONE_LITERAL))
    day_start = dt_local.replace(hour=0, minute=0, second=0, microsecond=0)
    day_end = (dt_local + timedelta(days=1)).replace(
        hour=0, minute=0, second=0, microsecond=0
    )
    day_start_utc = day_start.astimezone(tz=ZoneInfo(UTC_LITERAL))
    day_end_utc = day_end.astimezone(tz=ZoneInfo(UTC_LITERAL))
    number_of_hours = int((day_end_utc - day_start_utc).total_seconds() / 3600)
    return number_of_hours == 23

nordpool_product_namer(start_time_lb_utc, resolution_seconds)

Generates a Nord Pool product name based on a given start time in UTC and resolution.

This function: 1. Validates that start_time_lb_utc is a UTC-aware datetime. 2. Converts the given UTC datetime to Stockholm time. 3. Based on the resolution_seconds, determines whether the product is an hourly or quarter-hourly product. 4. Constructs a product name in the format: - Hourly (3600s): "PH-YYYYMMDD-HH" - Half-hourly (1800s): "HH-YYYYMMDD-HH" - Quarter-hourly (900s): "QH-YYYYMMDD-QH" 5. Raises a ValueError if an unsupported resolution is provided.

Parameters:

Name Type Description Default
start_time_lb_utc datetime

The lower-bound start time in UTC.

required
resolution_seconds int

The product resolution in seconds (either 3600 for an hour, 1800 for a half-hour or 900 for a quarter-hour).

required

Returns:

Name Type Description
str str

A formatted product name.

Raises:

Type Description
ValueError

If resolution_seconds is not 3600 (hourly), 1800 (half-hourly) or 900 (quarter-hourly).

ValueError

If start_time_lb_utc is not a UTC-aware datetime.

Example
from datetime import datetime
from zoneinfo import ZoneInfo

dt_utc = datetime(2025, 1, 30, 12, 0, tzinfo=ZoneInfo("UTC"))

hourly_product = nordpool_product_namer(dt_utc, 3600)
print(hourly_product)  # Output: "PH-20250130-14"

half_hourly_product = nordpool_product_namer(dt_utc, 1800)
print(half_hourly_product)  # Output: "HH-20250130-25"

quarter_hour_product = nordpool_product_namer(dt_utc, 900)
print(quarter_hour_product)  # Output: "QH-20250130-49"
Source code in physical_operations_utils/time_utils.py
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
def nordpool_product_namer(start_time_lb_utc: datetime, resolution_seconds: int) -> str:
    """
    Generates a Nord Pool product name based on a given start time in UTC and resolution.

    This function:
    1. Validates that `start_time_lb_utc` is a UTC-aware datetime.
    2. Converts the given UTC datetime to Stockholm time.
    3. Based on the `resolution_seconds`, determines whether the product is an hourly or quarter-hourly product.
    4. Constructs a product name in the format:
       - **Hourly (`3600s`)**: `"PH-YYYYMMDD-HH"`
       - **Half-hourly (`1800s`)**: `"HH-YYYYMMDD-HH"`
       - **Quarter-hourly (`900s`)**: `"QH-YYYYMMDD-QH"`
    5. Raises a `ValueError` if an unsupported resolution is provided.

    Parameters:
        start_time_lb_utc (datetime): The lower-bound start time in UTC.
        resolution_seconds (int): The product resolution in seconds (either `3600` for an hour, `1800` for a half-hour or `900` for a quarter-hour).

    Returns:
        str: A formatted product name.

    Raises:
        ValueError: If `resolution_seconds` is not `3600` (hourly), `1800` (half-hourly) or `900` (quarter-hourly).
        ValueError: If `start_time_lb_utc` is not a UTC-aware datetime.

    Example:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        dt_utc = datetime(2025, 1, 30, 12, 0, tzinfo=ZoneInfo("UTC"))

        hourly_product = nordpool_product_namer(dt_utc, 3600)
        print(hourly_product)  # Output: "PH-20250130-14"

        half_hourly_product = nordpool_product_namer(dt_utc, 1800)
        print(half_hourly_product)  # Output: "HH-20250130-25"

        quarter_hour_product = nordpool_product_namer(dt_utc, 900)
        print(quarter_hour_product)  # Output: "QH-20250130-49"
        ```
    """
    validate_datetime_in_utc(start_time_lb_utc)
    start_time_lb_stockholm = start_time_lb_utc.astimezone(
        tz=ZoneInfo(LOCAL_TIMEZONE_LITERAL)
    )
    if resolution_seconds == 3600:
        product_name_prefix = "PH-"
        power_hour = get_nordpool_power_hour_from_utc_datetime(start_time_lb_utc)
        return f"{product_name_prefix}{start_time_lb_stockholm.strftime('%Y%m%d')}-{power_hour}"
    elif resolution_seconds == 1800:
        product_name_prefix = "HH-"
        half_hour = get_nordpool_half_hour_from_utc_datetime(start_time_lb_utc)
        return f"{product_name_prefix}{start_time_lb_stockholm.strftime('%Y%m%d')}-{half_hour}"
    elif resolution_seconds == 900:
        product_name_prefix = "QH-"
        quarter_hour = get_nordpool_quarter_hour_from_utc_datetime(start_time_lb_utc)
        return f"{product_name_prefix}{start_time_lb_stockholm.strftime('%Y%m%d')}-{quarter_hour}"
    else:
        raise ValueError(f"Resolution {resolution_seconds} not supported")

validate_datetime_in_utc(dt)

Validates that a datetime object is in UTC.

This function checks if the provided datetime object is in UTC timezone. If not, it raises a ValueError.

Parameters:

Name Type Description Default
dt datetime

A datetime object to validate.

required

Raises:

Type Description
ValueError

If the datetime object is not in UTC.

Examples:

from datetime import datetime
from zoneinfo import ZoneInfo

# Valid UTC datetime
validate_datetime_in_utc(datetime(2025, 1, 1, 12, 0, tzinfo=ZoneInfo("UTC")))

# Invalid datetime (no timezone)
validate_datetime_in_utc(datetime(2025, 1, 1, 12, 0))  # Raises ValueError
Source code in physical_operations_utils/time_utils.py
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
def validate_datetime_in_utc(dt: datetime):
    """
    Validates that a datetime object is in UTC.

    This function checks if the provided datetime object is in UTC timezone.
    If not, it raises a ValueError.

    Args:
        dt (datetime): A datetime object to validate.

    Raises:
        ValueError: If the datetime object is not in UTC.

    Examples:
        ```python
        from datetime import datetime
        from zoneinfo import ZoneInfo

        # Valid UTC datetime
        validate_datetime_in_utc(datetime(2025, 1, 1, 12, 0, tzinfo=ZoneInfo("UTC")))

        # Invalid datetime (no timezone)
        validate_datetime_in_utc(datetime(2025, 1, 1, 12, 0))  # Raises ValueError
        ```
    """
    if str(dt.tzinfo) != str(ZoneInfo(UTC_LITERAL)):
        raise ValueError("Datetime must be in UTC")

validate_df_column_is_stockholm_datetime(df, column_name)

Validates that a specified column in a pandas DataFrame contains timezone-aware datetime values in the Stockholm timezone.

This function checks: 1. Whether the column exists in the DataFrame. 2. Whether the column contains datetime values. 3. Whether the datetime values are timezone-aware. 4. Whether the timezone is set to "Europe/Stockholm".

Parameters:

Name Type Description Default
df DataFrame

The DataFrame containing the column to validate.

required
column_name str

The name of the column to check.

required

Raises:

Type Description
ValueError

If the column is missing from the DataFrame.

ValueError

If the column is not of datetime type.

ValueError

If the column contains timezone-naive datetimes.

ValueError

If the column is not in the "Europe/Stockholm" timezone.

Example
import pandas as pd
from zoneinfo import ZoneInfo

df = pd.DataFrame({
    "timestamp": pd.date_range("2025-01-01 12:00", periods=3, tz=ZoneInfo("Europe/Stockholm"))
})

validate_df_column_is_stockholm_datetime(df, "timestamp")  # No error raised

df_invalid = pd.DataFrame({
    "timestamp": pd.date_range("2025-01-01 12:00", periods=3)  # No timezone
})

validate_df_column_is_stockholm_datetime(df_invalid, "timestamp")  # Raises ValueError
Source code in physical_operations_utils/time_utils.py
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
def validate_df_column_is_stockholm_datetime(df: pd.DataFrame, column_name: str):
    """
    Validates that a specified column in a pandas DataFrame contains
    timezone-aware datetime values in the Stockholm timezone.

    This function checks:
    1. Whether the column exists in the DataFrame.
    2. Whether the column contains datetime values.
    3. Whether the datetime values are timezone-aware.
    4. Whether the timezone is set to "Europe/Stockholm".

    Parameters:
        df (pd.DataFrame): The DataFrame containing the column to validate.
        column_name (str): The name of the column to check.

    Raises:
        ValueError: If the column is missing from the DataFrame.
        ValueError: If the column is not of datetime type.
        ValueError: If the column contains timezone-naive datetimes.
        ValueError: If the column is not in the "Europe/Stockholm" timezone.

    Example:
        ```python
        import pandas as pd
        from zoneinfo import ZoneInfo

        df = pd.DataFrame({
            "timestamp": pd.date_range("2025-01-01 12:00", periods=3, tz=ZoneInfo("Europe/Stockholm"))
        })

        validate_df_column_is_stockholm_datetime(df, "timestamp")  # No error raised

        df_invalid = pd.DataFrame({
            "timestamp": pd.date_range("2025-01-01 12:00", periods=3)  # No timezone
        })

        validate_df_column_is_stockholm_datetime(df_invalid, "timestamp")  # Raises ValueError
        ```
    """
    if column_name not in df.columns:
        raise ValueError(f"Column {column_name} not found in DataFrame")
    if not pd.api.types.is_datetime64_any_dtype(df[column_name]):
        raise ValueError(f"Column {column_name} is not a datetime object.")
    if df[column_name].dt.tz is None:
        raise ValueError(f"Column {column_name} is not timezone aware.")
    if str(df[column_name].dt.tz) != LOCAL_TIMEZONE_LITERAL:
        raise ValueError(f"Column {column_name} is not in Stockholm timezone.")

validate_df_column_is_utc_datetime(df, column_name)

Validates that a specified column in a pandas DataFrame contains timezone-aware datetime values in UTC.

This function checks: 1. Whether the column exists in the DataFrame. 2. Whether the column contains datetime values. 3. Whether the datetime values are timezone-aware. 4. Whether the timezone is set to UTC.

Parameters:

Name Type Description Default
df DataFrame

The DataFrame containing the column to validate.

required
column_name str

The name of the column to check.

required

Raises:

Type Description
ValueError

If the column is missing from the DataFrame.

ValueError

If the column is not of datetime type.

ValueError

If the column contains timezone-naive datetimes.

ValueError

If the column is not in UTC timezone.

Example
import pandas as pd
from zoneinfo import ZoneInfo

df = pd.DataFrame({
    "timestamp": pd.date_range("2025-01-01 12:00", periods=3, tz=ZoneInfo("UTC"))
})

validate_df_column_is_utc_datetime(df, "timestamp")  # No error raised

df_invalid = pd.DataFrame({
    "timestamp": pd.date_range("2025-01-01 12:00", periods=3)  # No timezone
})

validate_df_column_is_utc_datetime(df_invalid, "timestamp")  # Raises ValueError
Source code in physical_operations_utils/time_utils.py
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
def validate_df_column_is_utc_datetime(df: pd.DataFrame, column_name: str):
    """
    Validates that a specified column in a pandas DataFrame contains
    timezone-aware datetime values in UTC.

    This function checks:
    1. Whether the column exists in the DataFrame.
    2. Whether the column contains datetime values.
    3. Whether the datetime values are timezone-aware.
    4. Whether the timezone is set to UTC.

    Parameters:
        df (pd.DataFrame): The DataFrame containing the column to validate.
        column_name (str): The name of the column to check.

    Raises:
        ValueError: If the column is missing from the DataFrame.
        ValueError: If the column is not of datetime type.
        ValueError: If the column contains timezone-naive datetimes.
        ValueError: If the column is not in UTC timezone.

    Example:
        ```python
        import pandas as pd
        from zoneinfo import ZoneInfo

        df = pd.DataFrame({
            "timestamp": pd.date_range("2025-01-01 12:00", periods=3, tz=ZoneInfo("UTC"))
        })

        validate_df_column_is_utc_datetime(df, "timestamp")  # No error raised

        df_invalid = pd.DataFrame({
            "timestamp": pd.date_range("2025-01-01 12:00", periods=3)  # No timezone
        })

        validate_df_column_is_utc_datetime(df_invalid, "timestamp")  # Raises ValueError
        ```
    """
    if column_name not in df.columns:
        raise ValueError(f"Column {column_name} not found in DataFrame")
    if not pd.api.types.is_datetime64_any_dtype(df[column_name]):
        raise ValueError(f"Column {column_name} is not a datetime object.")
    if df[column_name].dt.tz is None:
        raise ValueError(f"Column {column_name} is not timezone aware.")
    if str(df[column_name].dt.tz) != "UTC":
        raise ValueError(f"Column {column_name} is not in UTC.")

validate_start_time_before_end_time(start_utc, end_utc)

Validates that the start time occurs before the end time.

Parameters: start_utc (datetime): The start time in UTC. end_utc (datetime): The end time in UTC.

Raises: ValueError: If the start time is not before the end time.

Source code in physical_operations_utils/time_utils.py
967
968
969
970
971
972
973
974
975
976
977
978
979
def validate_start_time_before_end_time(start_utc: datetime, end_utc: datetime):
    """
    Validates that the start time occurs before the end time.

    Parameters:
    start_utc (datetime): The start time in UTC.
    end_utc (datetime): The end time in UTC.

    Raises:
    ValueError: If the start time is not before the end time.
    """
    if start_utc >= end_utc:
        raise ValueError("Start time must be before end time")