Python · Синтаксис · Промежуточный

Модули datetime и json

10 задач

Работа с датами и временем с помощью модуля datetime и сериализация данных с помощью модуля json.

Основы datetime: naive vs aware, zoneinfo, strptime/strftime

#
**Основы datetime** ```python from datetime import datetime, date, time, timezone # Текущее UTC время (naive vs aware) datetime.now() # naive — без часового пояса, использует локальные часы datetime.utcnow() # naive UTC — устарел в Python 3.12 datetime.now(timezone.utc) # aware UTC — предпочтительный # Создание конкретных дат d = date(2024, 3, 15) # год, месяц, день dt = datetime(2024, 3, 15, 10, 30) # год, месяц, день, час, минута ``` **Naive vs aware datetime** Объект *naive* datetime не имеет информации о часовом поясе — Python не знает, это UTC, Нью-Йорк или Токио. Объект *aware* datetime содержит `tzinfo` и может сравниваться между часовыми поясами: ```python from datetime import datetime, timezone, timedelta naive = datetime(2024, 3, 15, 10, 0) # без tzinfo aware = datetime(2024, 3, 15, 10, 0, tzinfo=timezone.utc) # UTC # Сравнение naive и aware невозможно: naive < aware # TypeError: can't compare offset-naive and offset-aware # Кастомный сдвиг часового пояса: tz_kyiv = timezone(timedelta(hours=3)) # UTC+3 dt_kyiv = datetime(2024, 3, 15, 13, 0, tzinfo=tz_kyiv) ``` **zoneinfo — именованные часовые пояса (Python 3.9+)** ```python from zoneinfo import ZoneInfo tz = ZoneInfo('Europe/Kyiv') dt = datetime(2024, 3, 15, 10, 0, tzinfo=tz) dt.isoformat() # '2024-03-15T10:00:00+02:00' # Конвертировать между часовыми поясами: dt_ny = dt.astimezone(ZoneInfo('America/New_York')) # Тот же момент, другое локальное время ``` **Парсинг и форматирование** ```python # Парсить из строки dt = datetime.strptime('2024-03-15 10:30', '%Y-%m-%d %H:%M') # Форматировать в строку dt.strftime('%d %B %Y') # '15 March 2024' dt.isoformat() # '2024-03-15T10:30:00' # Парсить ISO-формат напрямую (Python 3.7+): datetime.fromisoformat('2024-03-15T10:30:00') # без Z до Python 3.11 ``` Распространённые коды `strftime` / `strptime`: ``` %Y 4-значный год %m месяц (01-12) %d день (01-31) %H час (00-23) %M минута (00-59) %S секунда (00-59) %A название дня %B название месяца %f микросекунды ```

Арифметика timedelta, модуль calendar и isoweekday

#
**timedelta — арифметика дат** ```python from datetime import datetime, timedelta now = datetime(2024, 3, 15, 10, 0) # Добавить / вычесть время tomorrow = now + timedelta(days=1) next_week = now + timedelta(weeks=1) two_hours = now + timedelta(hours=2) yesterday = now - timedelta(days=1) # Разница между двумя datetime даёт timedelta start = datetime(2024, 1, 1) end = datetime(2024, 3, 15) diff = end - start # timedelta(days=74) diff.days # 74 diff.total_seconds() # 6393600.0 ``` **Компоненты timedelta** ```python td = timedelta(days=2, hours=3, minutes=30) td.days # 2 td.seconds # 12600 (только секунды, не вошедшие в дни!) td.total_seconds() # 185400.0 (всегда используй для сравнений) # Разложить total_seconds на часы и минуты: total = int(td.total_seconds()) hours, rem = divmod(total, 3600) minutes, _ = divmod(rem, 60) print(f'{hours}ч {minutes}мин') # '51ч 30мин' ``` **Модуль calendar** ```python import calendar # Количество дней в месяце (учитывает високосные годы): calendar.monthrange(2024, 2) # (3, 29) — начинается в четверг, 29 дней calendar.monthrange(2024, 2)[1] # 29 # День недели (0=Понедельник, 6=Воскресенье): calendar.weekday(2024, 3, 15) # 4 — Пятница # Проверить високосный год: calendar.isleap(2024) # True # .isoweekday() на datetime: 1=Понедельник, 7=Воскресенье (ISO стандарт) from datetime import date date(2024, 3, 15).isoweekday() # 5 — Пятница date(2024, 3, 15).weekday() # 4 — Пятница (с нуля) ``` **date.today() vs datetime.now()** ```python from datetime import date, datetime date.today() # date(2024, 3, 15) — только дата, без времени datetime.today() # naive datetime с локальным временем datetime.now(timezone.utc) # aware UTC datetime — используй для логов/API ```

json.dumps/loads vs dump/load (файлы), сериализация datetime, object_hook

#
**json.dumps / json.loads — строки** ```python import json data = {'name': 'Alice', 'age': 30, 'active': True} # Сериализовать в JSON-строку text = json.dumps(data) # '{"name": "Alice", "age": 30, "active": true}' text = json.dumps(data, indent=2) # с отступами text = json.dumps(data, ensure_ascii=False) # сохранить unicode символы # Десериализовать из JSON-строки obj = json.loads(text) # {'name': 'Alice', 'age': 30, 'active': True} ``` **json.dump / json.load — файлы** `json.dump` / `json.load` (без 's') работают с **файловыми объектами**, не строками: ```python # Записать в файл with open('data.json', 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) # Прочитать из файла with open('data.json', 'r', encoding='utf-8') as f: obj = json.load(f) ``` Подсказка: `dumps`/`loads` → **s**tring (строка); `dump`/`load` → файл. **Сериализация datetime** JSON не имеет типа даты. Стандартный подход — строки ISO 8601: ```python from datetime import datetime, timezone import json now = datetime.now(timezone.utc) # json.dumps не знает, как сериализовать datetime: json.dumps({'created': now}) # TypeError: Object of type datetime is not JSON serializable # Решение 1 — конвертировать вручную: json.dumps({'created': now.isoformat()}) # Решение 2 — кастомная функция default: def json_default(obj): if isinstance(obj, datetime): return obj.isoformat() raise TypeError(f'Не сериализуется: {type(obj)}') json.dumps({'created': now}, default=json_default) ``` **Парсинг datetime обратно** ```python # json.loads даёт строки, не datetime: obj = json.loads('{"created": "2024-03-15T10:30:00+00:00"}') obj['created'] # '2024-03-15T10:30:00+00:00' — всё ещё строка # Конвертировать вручную: dt = datetime.fromisoformat(obj['created']) # Python 3.7+ # Или через hook: ISO_KEYS = {'created', 'updated', 'timestamp'} def parse_dates(d): for key in ISO_KEYS & d.keys(): try: d[key] = datetime.fromisoformat(d[key]) except (ValueError, TypeError): pass return d obj = json.loads(text, object_hook=parse_dates) # obj['created'] теперь является объектом datetime ```
01

#

Разберите строку '2024-03-15' в объект `datetime.date` и верните его.

from datetime import date

def parse_date(s):
    # ваш код здесь
    pass

print(parse_date('2024-03-15'))  # 2024-03-15
print(type(parse_date('2024-03-15')))  # <class 'datetime.date'>
Решение
from datetime import date

def parse_date(s):
    return date.fromisoformat(s)
02

#

Разберите строку '15/03/2024 14:30' в объект `datetime` с помощью `strptime`.

from datetime import datetime

def parse_datetime(s):
    # ваш код здесь
    pass

dt = parse_datetime('15/03/2024 14:30')
print(dt.year, dt.month, dt.day)   # 2024 3 15
print(dt.hour, dt.minute)           # 14 30
Решение
from datetime import datetime

def parse_datetime(s):
    return datetime.strptime(s, '%d/%m/%Y %H:%M')
03

#

Для заданного объекта `datetime` верните строку в формате 'March 15, 2024' (полное название месяца, день, год).

from datetime import datetime

def format_date(dt):
    # ваш код здесь
    pass

from datetime import datetime
print(format_date(datetime(2024, 3, 15)))   # March 15, 2024
print(format_date(datetime(2024, 11, 7)))   # November 7, 2024
Решение
from datetime import datetime

def format_date(dt):
    return dt.strftime('%B %-d, %Y')
04

#

Для двух объектов `date` верните количество дней между ними (всегда положительное).

from datetime import date

def days_between(d1, d2):
    # ваш код здесь
    pass

from datetime import date
print(days_between(date(2024, 1, 1), date(2024, 3, 15)))  # 74
print(days_between(date(2024, 3, 15), date(2024, 1, 1)))  # 74
Решение
from datetime import date

def days_between(d1, d2):
    return abs((d2 - d1).days)
05

#

Сериализуйте словарь в строку JSON. Словарь может содержать объекты date — преобразуйте их в строки ISO (YYYY-MM-DD) перед сериализацией.

import json
from datetime import date

def serialize(data):
    # ваш код здесь
    pass

d = {'name': 'Alice', 'birthday': date(1990, 5, 21), 'score': 42}
print(serialize(d))  # '{"name": "Alice", "birthday": "1990-05-21", "score": 42}'
Решение
import json
from datetime import date

def serialize(data):
    def default(obj):
        if isinstance(obj, date):
            return obj.isoformat()
        raise TypeError(f'Object of type {type(obj)} is not JSON serializable')
    return json.dumps(data, default=default)
06

#

Разберите строку JSON в объект Python. Если ключ заканчивается на '_date', преобразуйте его значение из строки в `datetime.date`.

import json
from datetime import date

def deserialize(s):
    # ваш код здесь
    pass

s = '{"name": "Alice", "start_date": "2024-01-15", "score": 42}'
result = deserialize(s)
print(result['start_date'])        # 2024-01-15
print(type(result['start_date']))  # <class 'datetime.date'>
print(result['score'])             # 42
Решение
import json
from datetime import date

def deserialize(s):
    def object_hook(d):
        for k, v in d.items():
            if k.endswith('_date') and isinstance(v, str):
                d[k] = date.fromisoformat(v)
        return d
    return json.loads(s, object_hook=object_hook)
07

#

Для списка строк дат в формате ISO верните их отсортированными от самой старой к самой новой.

def sort_dates(dates):
    # ваш код здесь
    pass

dates = ['2024-11-01', '2023-03-15', '2024-01-20', '2022-12-31']
print(sort_dates(dates))
# ['2022-12-31', '2023-03-15', '2024-01-20', '2024-11-01']
Решение
def sort_dates(dates):
    return sorted(dates)
08

#

Для списка словарей событий с ключом 'date' (строка ISO) верните событие с самой последней датой.

def latest_event(events):
    # ваш код здесь
    pass

events = [
    {'name': 'Conference', 'date': '2024-09-12'},
    {'name': 'Workshop',   'date': '2024-11-03'},
    {'name': 'Meetup',     'date': '2024-07-20'},
]
print(latest_event(events)['name'])  # Workshop
Решение
def latest_event(events):
    return max(events, key=lambda e: e['date'])
09

#

Для заданной даты верните дату первого дня того же месяца.

from datetime import date

def first_of_month(d):
    # ваш код здесь
    pass

from datetime import date
print(first_of_month(date(2024, 3, 15)))  # 2024-03-01
print(first_of_month(date(2024, 12, 31))) # 2024-12-01
Решение
from datetime import date

def first_of_month(d):
    return d.replace(day=1)
10

#

Для списка JSON-записей (каждый с полем 'amount') вычислите общую сумму. Верните 0, если список пуст.

import json

def total_amount(records_json):
    # ваш код здесь
    pass

records = '[{"item": "book", "amount": 12.5}, {"item": "pen", "amount": 3.0}, {"item": "bag", "amount": 25.0}]'
print(total_amount(records))    # 40.5
print(total_amount('[]'))       # 0
Решение
import json

def total_amount(records_json):
    records = json.loads(records_json)
    return sum(r['amount'] for r in records)