File size: 3,671 Bytes
e466dd5
 
 
 
e3a1efe
 
 
e466dd5
e3a1efe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e466dd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e3a1efe
e466dd5
 
 
 
 
e3a1efe
e466dd5
 
 
 
 
e3a1efe
e466dd5
 
 
 
 
 
 
 
 
 
e3a1efe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
43
44
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
86
87
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
# =========================
#     WORKING HOURS CONFIG
# =========================

# Working hours: 9:00-18:00 (20 slots) = 20 slots per working day
# Each slot is 30 minutes, starting at 9:00 AM
SLOTS_PER_WORKING_DAY = 20  # 9:00-18:00 (9 hours * 2 slots/hour)
MORNING_SLOTS = 8  # 9:00-13:00 (4 hours * 2 slots/hour)
AFTERNOON_SLOTS = 10  # 14:00-18:00 (4 hours * 2 slots/hour)
LUNCH_BREAK_START_SLOT = 8  # 13:00-14:00
LUNCH_BREAK_END_SLOT = 10  # 14:00

from datetime import datetime, date, time, timezone, timedelta


def slot_to_datetime(slot: int, base_date: date = None, base_timezone=None) -> datetime:
    """
    Convert a slot index to a naive datetime in local time, accounting for working days.

    Args:
        slot: The slot index (each slot = 30 minutes within working hours)
        base_date: Base date for slot 0 (defaults to today)
        base_timezone: Ignored (kept for API compatibility)

    Returns:
        datetime: The corresponding naive datetime in local time
    """
    if base_date is None:
        base_date = date.today()

    # Calculate which working day and slot within that day
    working_day = get_working_day_from_slot(slot)
    slot_within_day = get_slot_within_day(slot)

    # Get the actual calendar date for this working day
    target_date = base_date + timedelta(days=working_day)

    # Calculate time within the working day (9:00 AM + slot_within_day * 30 minutes)
    minutes_from_9am = slot_within_day * 30
    target_time = datetime.combine(
        target_date, datetime.min.time().replace(hour=9)
    ) + timedelta(minutes=minutes_from_9am)

    return target_time


def get_working_day_from_slot(slot: int) -> int:
    """Get the working day index (0=first working day) from a slot.

    Args:
        slot (int): The slot index.

    Returns:
        int: The working day index (0-based).
    """
    return slot // SLOTS_PER_WORKING_DAY


def get_slot_within_day(slot: int) -> int:
    """Get the slot position within a working day (0-19).

    Args:
        slot (int): The slot index.

    Returns:
        int: The slot position within the day (0-19).
    """
    return slot % SLOTS_PER_WORKING_DAY


def task_spans_lunch_break(task) -> bool:
    """Check if a task spans across the lunch break period (13:00-14:00).

    Args:
        task: The task to check.

    Returns:
        bool: True if the task spans across lunch break.
    """
    start_slot_in_day = get_slot_within_day(task.start_slot)
    end_slot_in_day = start_slot_in_day + task.duration_slots - 1

    # Check if task overlaps with lunch break slots (8-9, which is 13:00-14:00)
    return (
        start_slot_in_day <= LUNCH_BREAK_END_SLOT - 1
        and end_slot_in_day >= LUNCH_BREAK_START_SLOT
    )


def is_weekend_slot(slot: int) -> bool:
    """Check if a slot falls on a weekend.

    Args:
        slot: The slot index

    Returns:
        bool: True if the slot is on a weekend
    """
    working_day = get_working_day_from_slot(slot)
    # For simplicity, assume every 7th day starting from day 5 and 6 are weekends
    # This is a simplification - in practice you'd want to use actual calendar logic
    day_of_week = working_day % 7
    return day_of_week >= 5  # Saturday (5) and Sunday (6)


def get_slot_date(slot: int, base_date: date = None) -> date:
    """Get the date for a given slot.

    Args:
        slot: The slot index
        base_date: Base date for slot 0 (defaults to today)

    Returns:
        date: The date for this slot
    """
    if base_date is None:
        base_date = date.today()

    working_days = get_working_day_from_slot(slot)
    return base_date + timedelta(days=working_days)