blob: c7e6d0c09aa8174ad7c99c586db86f9e7c0e80c0 [file] [log] [blame]
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from __future__ import annotations
import datetime
import pendulum
import pytest
from pendulum.tz.timezone import FixedTimezone, Timezone
from airflow.utils import timezone
from airflow.utils.timezone import coerce_datetime, parse_timezone
CET = Timezone("Europe/Paris")
EAT = Timezone("Africa/Nairobi") # Africa/Nairobi
ICT = Timezone("Asia/Bangkok") # Asia/Bangkok
UTC = timezone.utc
class TestTimezone:
def test_is_aware(self):
assert timezone.is_localized(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
assert not timezone.is_localized(datetime.datetime(2011, 9, 1, 13, 20, 30))
def test_is_naive(self):
assert not timezone.is_naive(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))
assert timezone.is_naive(datetime.datetime(2011, 9, 1, 13, 20, 30))
def test_utcnow(self):
now = timezone.utcnow()
assert timezone.is_localized(now)
assert now.replace(tzinfo=None) == now.astimezone(UTC).replace(tzinfo=None)
def test_convert_to_utc(self):
naive = datetime.datetime(2011, 9, 1, 13, 20, 30)
utc = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=UTC)
assert utc == timezone.convert_to_utc(naive)
eat = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
utc = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)
assert utc == timezone.convert_to_utc(eat)
def test_make_naive(self):
assert timezone.make_naive(
datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), EAT
) == datetime.datetime(2011, 9, 1, 13, 20, 30)
assert timezone.make_naive(
datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT), EAT
) == datetime.datetime(2011, 9, 1, 13, 20, 30)
with pytest.raises(ValueError):
timezone.make_naive(datetime.datetime(2011, 9, 1, 13, 20, 30), EAT)
def test_make_aware(self):
assert timezone.make_aware(datetime.datetime(2011, 9, 1, 13, 20, 30), EAT) == datetime.datetime(
2011, 9, 1, 13, 20, 30, tzinfo=EAT
)
with pytest.raises(ValueError):
timezone.make_aware(datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), EAT)
def test_td_format(self):
td = datetime.timedelta(seconds=3752)
assert timezone.td_format(td) == "1h:2M:32s"
td = 3200.0
assert timezone.td_format(td) == "53M:20s"
td = 3200
assert timezone.td_format(td) == "53M:20s"
td = 0.123
assert timezone.td_format(td) == "<1s"
td = None
assert timezone.td_format(td) is None
td = datetime.timedelta(seconds=300752)
assert timezone.td_format(td) == "3d:11h:32M:32s"
td = 434343600.0
assert timezone.td_format(td) == "13y:11m:17d:3h"
@pytest.mark.parametrize(
"input_datetime, output_datetime",
[
pytest.param(None, None, id="None datetime"),
pytest.param(
pendulum.DateTime(2021, 11, 1),
pendulum.DateTime(2021, 11, 1, tzinfo=UTC),
id="Non aware pendulum Datetime",
),
pytest.param(
pendulum.DateTime(2021, 11, 1, tzinfo=CET),
pendulum.DateTime(2021, 11, 1, tzinfo=CET),
id="Aware pendulum Datetime",
),
pytest.param(
datetime.datetime(2021, 11, 1),
pendulum.DateTime(2021, 11, 1, tzinfo=UTC),
id="Non aware datetime",
),
pytest.param(
datetime.datetime(2021, 11, 1, tzinfo=CET),
pendulum.DateTime(2021, 11, 1, tzinfo=CET),
id="Aware datetime",
),
],
)
def test_coerce_datetime(input_datetime, output_datetime):
assert output_datetime == coerce_datetime(input_datetime)
@pytest.mark.parametrize(
"tz_name",
[
pytest.param("Europe/Paris", id="CET"),
pytest.param("Africa/Nairobi", id="EAT"),
pytest.param("Asia/Bangkok", id="ICT"),
],
)
def test_parse_timezone_iana(tz_name: str):
tz = parse_timezone(tz_name)
assert tz.name == tz_name
assert parse_timezone(tz_name) is tz
@pytest.mark.parametrize("tz_name", ["utc", "UTC", "uTc"])
def test_parse_timezone_utc(tz_name):
tz = parse_timezone(tz_name)
assert tz.name == "UTC"
assert parse_timezone(tz_name) is tz
assert tz is timezone.utc, "Expected that UTC timezone is same object as `airflow.utils.timezone.utc`"
@pytest.mark.parametrize(
"tz_offset, expected_offset, expected_name",
[
pytest.param(0, 0, "+00:00", id="zero-offset"),
pytest.param(-3600, -3600, "-01:00", id="1-hour-behind"),
pytest.param(19800, 19800, "+05:30", id="5.5-hours-ahead"),
],
)
def test_parse_timezone_offset(tz_offset: int, expected_offset, expected_name):
tz = parse_timezone(tz_offset)
assert hasattr(tz, "offset")
assert tz.offset == expected_offset
assert tz.name == expected_name
assert parse_timezone(tz_offset) is tz
@pytest.mark.parametrize(
"tz",
[
pytest.param(None, id="implicit"),
pytest.param(timezone.utc, id="explicit"),
pytest.param("UTC", id="utc-literal"),
],
)
def test_from_timestamp_utc(tz):
from_ts = timezone.from_timestamp(0) if tz is None else timezone.from_timestamp(0, tz=tz)
assert from_ts == pendulum.DateTime(1970, 1, 1, tzinfo=timezone.utc)
assert from_ts.tzinfo == timezone.utc
@pytest.mark.parametrize("tz", ["local", "LOCAL"])
def test_from_timestamp_local(tz):
local_tz = timezone.local_timezone()
from_ts = timezone.from_timestamp(0, tz=tz)
assert from_ts == pendulum.DateTime(1970, 1, 1, tzinfo=timezone.utc)
assert from_ts.tzinfo == local_tz
@pytest.mark.parametrize(
"tz, iana_timezone",
[
pytest.param(Timezone("Europe/Paris"), "Europe/Paris", id="pendulum-timezone"),
pytest.param("America/New_York", "America/New_York", id="IANA-timezone"),
],
)
def test_from_timestamp_iana_timezones(tz, iana_timezone):
from_ts = timezone.from_timestamp(0, tz=tz)
assert from_ts == pendulum.DateTime(1970, 1, 1, tzinfo=timezone.utc)
# In pendulum 2 there is a problem with compare tzinfo object (caching?), so we check the name
assert from_ts.tzinfo.name == iana_timezone
assert isinstance(from_ts.tzinfo, Timezone)
@pytest.mark.parametrize("utc_offset", [3600, -7200])
def test_from_timestamp_fixed_timezone(utc_offset):
from_ts = timezone.from_timestamp(0, tz=FixedTimezone(utc_offset))
assert from_ts == pendulum.DateTime(1970, 1, 1, tzinfo=timezone.utc)
assert from_ts.utcoffset() == datetime.timedelta(seconds=utc_offset)