mcp-github-project-manager
by kunwarVivek
from freezegun import freeze_time
from mcp.shared.exceptions import McpError
import pytest
from mcp_server_time.server import TimeServer
@pytest.mark.parametrize(
"test_time,timezone,expected",
[
# UTC+1 non-DST
(
"2024-01-01 12:00:00+00:00",
"Europe/Warsaw",
{
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T13:00:00+01:00",
"is_dst": False,
},
),
# UTC non-DST
(
"2024-01-01 12:00:00+00:00",
"Europe/London",
{
"timezone": "Europe/London",
"datetime": "2024-01-01T12:00:00+00:00",
"is_dst": False,
},
),
# UTC-5 non-DST
(
"2024-01-01 12:00:00-00:00",
"America/New_York",
{
"timezone": "America/New_York",
"datetime": "2024-01-01T07:00:00-05:00",
"is_dst": False,
},
),
# UTC+1 DST
(
"2024-03-31 12:00:00+00:00",
"Europe/Warsaw",
{
"timezone": "Europe/Warsaw",
"datetime": "2024-03-31T14:00:00+02:00",
"is_dst": True,
},
),
# UTC DST
(
"2024-03-31 12:00:00+00:00",
"Europe/London",
{
"timezone": "Europe/London",
"datetime": "2024-03-31T13:00:00+01:00",
"is_dst": True,
},
),
# UTC-5 DST
(
"2024-03-31 12:00:00-00:00",
"America/New_York",
{
"timezone": "America/New_York",
"datetime": "2024-03-31T08:00:00-04:00",
"is_dst": True,
},
),
],
)
def test_get_current_time(test_time, timezone, expected):
with freeze_time(test_time):
time_server = TimeServer()
result = time_server.get_current_time(timezone)
assert result.timezone == expected["timezone"]
assert result.datetime == expected["datetime"]
assert result.is_dst == expected["is_dst"]
def test_get_current_time_with_invalid_timezone():
time_server = TimeServer()
with pytest.raises(
McpError,
match=r"Invalid timezone: 'No time zone found with key Invalid/Timezone'",
):
time_server.get_current_time("Invalid/Timezone")
@pytest.mark.parametrize(
"source_tz,time_str,target_tz,expected_error",
[
(
"invalid_tz",
"12:00",
"Europe/London",
"Invalid timezone: 'No time zone found with key invalid_tz'",
),
(
"Europe/Warsaw",
"12:00",
"invalid_tz",
"Invalid timezone: 'No time zone found with key invalid_tz'",
),
(
"Europe/Warsaw",
"25:00",
"Europe/London",
"Invalid time format. Expected HH:MM [24-hour format]",
),
],
)
def test_convert_time_errors(source_tz, time_str, target_tz, expected_error):
time_server = TimeServer()
with pytest.raises((McpError, ValueError), match=expected_error):
time_server.convert_time(source_tz, time_str, target_tz)
@pytest.mark.parametrize(
"test_time,source_tz,time_str,target_tz,expected",
[
# Basic case: Standard time conversion between Warsaw and London (1 hour difference)
# Warsaw is UTC+1, London is UTC+0
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Europe/London",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Europe/London",
"datetime": "2024-01-01T11:00:00+00:00",
"is_dst": False,
},
"time_difference": "-1.0h",
},
),
# Reverse case of above: London to Warsaw conversion
# Shows how time difference is positive when going east
(
"2024-01-01 00:00:00+00:00",
"Europe/London",
"12:00",
"Europe/Warsaw",
{
"source": {
"timezone": "Europe/London",
"datetime": "2024-01-01T12:00:00+00:00",
"is_dst": False,
},
"target": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T13:00:00+01:00",
"is_dst": False,
},
"time_difference": "+1.0h",
},
),
# Edge case: Different DST periods between Europe and USA
# Europe ends DST on Oct 27, while USA waits until Nov 3
# This creates a one-week period where Europe is in standard time but USA still observes DST
(
"2024-10-28 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"America/New_York",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-10-28T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "America/New_York",
"datetime": "2024-10-28T07:00:00-04:00",
"is_dst": True,
},
"time_difference": "-5.0h",
},
),
# Follow-up to previous case: After both regions end DST
# Shows how time difference increases by 1 hour when USA also ends DST
(
"2024-11-04 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"America/New_York",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-11-04T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "America/New_York",
"datetime": "2024-11-04T06:00:00-05:00",
"is_dst": False,
},
"time_difference": "-6.0h",
},
),
# Edge case: Nepal's unusual UTC+5:45 offset
# One of the few time zones using 45-minute offset
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Asia/Kathmandu",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Asia/Kathmandu",
"datetime": "2024-01-01T16:45:00+05:45",
"is_dst": False,
},
"time_difference": "+4.75h",
},
),
# Reverse case for Nepal
# Demonstrates how 45-minute offset works in opposite direction
(
"2024-01-01 00:00:00+00:00",
"Asia/Kathmandu",
"12:00",
"Europe/Warsaw",
{
"source": {
"timezone": "Asia/Kathmandu",
"datetime": "2024-01-01T12:00:00+05:45",
"is_dst": False,
},
"target": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T07:15:00+01:00",
"is_dst": False,
},
"time_difference": "-4.75h",
},
),
# Edge case: Lord Howe Island's unique DST rules
# One of the few places using 30-minute DST shift
# During summer (DST), they use UTC+11
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Australia/Lord_Howe",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Australia/Lord_Howe",
"datetime": "2024-01-01T22:00:00+11:00",
"is_dst": True,
},
"time_difference": "+10.0h",
},
),
# Second Lord Howe Island case: During their standard time
# Shows transition to UTC+10:30 after DST ends
(
"2024-04-07 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Australia/Lord_Howe",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-04-07T12:00:00+02:00",
"is_dst": True,
},
"target": {
"timezone": "Australia/Lord_Howe",
"datetime": "2024-04-07T20:30:00+10:30",
"is_dst": False,
},
"time_difference": "+8.5h",
},
),
# Edge case: Date line crossing with Samoa
# Demonstrates how a single time conversion can result in a date change
# Samoa is UTC+13, creating almost a full day difference with Warsaw
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"23:00",
"Pacific/Apia",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T23:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Pacific/Apia",
"datetime": "2024-01-02T11:00:00+13:00",
"is_dst": False,
},
"time_difference": "+12.0h",
},
),
# Edge case: Iran's unusual half-hour offset
# Demonstrates conversion with Iran's UTC+3:30 timezone
(
"2024-03-21 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Asia/Tehran",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-03-21T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Asia/Tehran",
"datetime": "2024-03-21T14:30:00+03:30",
"is_dst": False,
},
"time_difference": "+2.5h",
},
),
# Edge case: Venezuela's unusual -4:30 offset (historical)
# In 2016, Venezuela moved from -4:30 to -4:00
# Useful for testing historical dates
(
"2016-04-30 00:00:00+00:00", # Just before the change
"Europe/Warsaw",
"12:00",
"America/Caracas",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2016-04-30T12:00:00+02:00",
"is_dst": True,
},
"target": {
"timezone": "America/Caracas",
"datetime": "2016-04-30T05:30:00-04:30",
"is_dst": False,
},
"time_difference": "-6.5h",
},
),
# Edge case: Israel's variable DST
# Israel's DST changes don't follow a fixed pattern
# They often change dates year-to-year based on Hebrew calendar
(
"2024-10-27 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Asia/Jerusalem",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-10-27T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Asia/Jerusalem",
"datetime": "2024-10-27T13:00:00+02:00",
"is_dst": False,
},
"time_difference": "+1.0h",
},
),
# Edge case: Antarctica/Troll station
# Only timezone that uses UTC+0 in winter and UTC+2 in summer
# One of the few zones with exactly 2 hours DST difference
(
"2024-03-31 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Antarctica/Troll",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-03-31T12:00:00+02:00",
"is_dst": True,
},
"target": {
"timezone": "Antarctica/Troll",
"datetime": "2024-03-31T12:00:00+02:00",
"is_dst": True,
},
"time_difference": "+0.0h",
},
),
# Edge case: Kiribati date line anomaly
# After skipping Dec 31, 1994, eastern Kiribati is UTC+14
# The furthest forward timezone in the world
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"23:00",
"Pacific/Kiritimati",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T23:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Pacific/Kiritimati",
"datetime": "2024-01-02T12:00:00+14:00",
"is_dst": False,
},
"time_difference": "+13.0h",
},
),
# Edge case: Chatham Islands, New Zealand
# Uses unusual 45-minute offset AND observes DST
# UTC+12:45 in standard time, UTC+13:45 in DST
(
"2024-01-01 00:00:00+00:00",
"Europe/Warsaw",
"12:00",
"Pacific/Chatham",
{
"source": {
"timezone": "Europe/Warsaw",
"datetime": "2024-01-01T12:00:00+01:00",
"is_dst": False,
},
"target": {
"timezone": "Pacific/Chatham",
"datetime": "2024-01-02T00:45:00+13:45",
"is_dst": True,
},
"time_difference": "+12.75h",
},
),
],
)
def test_convert_time(test_time, source_tz, time_str, target_tz, expected):
with freeze_time(test_time):
time_server = TimeServer()
result = time_server.convert_time(source_tz, time_str, target_tz)
assert result.source.timezone == expected["source"]["timezone"]
assert result.target.timezone == expected["target"]["timezone"]
assert result.source.datetime == expected["source"]["datetime"]
assert result.target.datetime == expected["target"]["datetime"]
assert result.source.is_dst == expected["source"]["is_dst"]
assert result.target.is_dst == expected["target"]["is_dst"]
assert result.time_difference == expected["time_difference"]