$ sudo teach IT
Модуль 10 · Полезные модули

Урок 10.2 — Decimal, DateTime, TimeSpan

Точная десятичная арифметика, работа с датами и временными интервалами.

В этом уроке мы познакомимся с тремя важными типами данных: decimal для точных денежных расчётов, DateTime для работы с датами и TimeSpan для интервалов времени. Эти типы — основа практически любого бизнес-приложения.

💰

Тип decimal — точная десятичная арифметика

Ты уже знаешь тип double для дробных чисел. Но у него есть проблема: он хранит числа в двоичной системе. Это приводит к ошибкам округления в десятичных расчётах:

C# · Проблема double
double a = 0.1;
double b = 0.2;
Console.WriteLine(a + b);        // 0.30000000000000004 (не 0.3!)
Console.WriteLine(a + b == 0.3); // False — ошибка округления!

// Это проблема для финансов:
double price = 1.99;
double tax = price * 0.1;
Console.WriteLine(tax);  // 0.19900000000000001 вместо 0.199

Тип decimal решает эту проблему. Он хранит числа в десятичной системе (binary-coded decimal) и даёт точные результаты:

C# · Тип decimal
decimal a = 0.1m;  // суффикс m — значит decimal
decimal b = 0.2m;
Console.WriteLine(a + b);        // 0.3 (точно!)
Console.WriteLine(a + b == 0.3m); // True

decimal price = 1.99m;
decimal tax = price * 0.1m;
Console.WriteLine(tax);  // 0.199 (точно!)

// Синтаксис: суффикс m или M
decimal salary = 125000.50m;
decimal discount = 0.15m;
decimal total = salary - salary * discount;
Console.WriteLine(total);  // 106250.425

Когда использовать decimal vs double:

Сценарий Тип Причина
Деньги, цены, налоги decimal Точность до копейки обязательна
Научные расчёты double Нужна скорость, погрешность допустима
Координаты GPS double Большой диапазон значений
Бухгалтерия decimal Закон требует точности до 0.01
3D-графика float/double Скорость важнее точности
Проценты, курсы валют decimal Нужна предсказуемая точность

⚠️ decimal медленнее double (~10-20x), но для финансов это не проблема. Главное — точность. Также у decimal меньше диапазон: ±79 × 10²⁷, против ±5 × 10⁻³²⁴ для double. Но для денег этого хватает с запасом.

📅

DateTime — работа с датами и временем

DateTime — это структура для работы с датой и временем. Она хранит дату от 01.01.0001 до 31.12.9999 и время с точностью до 100 наносекунд (тик).

Создание DateTime

C# · Создание DateTime
using System;

// Текущая дата и время (локальные)
DateTime now = DateTime.Now;
Console.WriteLine(now);  // 23.06.2026 15:45:30

// Текущая дата и время (UTC)
DateTime utcNow = DateTime.UtcNow;
Console.WriteLine(utcNow);  // 23.06.2026 12:45:30 (на 3 часа меньше, если MSK)

// Только дата (без времени)
DateTime today = DateTime.Today;
Console.WriteLine(today);  // 23.06.2026 0:00:00

// Конкретная дата и время
DateTime specific = new DateTime(2026, 1, 15);
Console.WriteLine(specific);  // 15.01.2026 0:00:00

// Дата + время
DateTime specificTime = new DateTime(2026, 1, 15, 14, 30, 0);
Console.WriteLine(specificTime);  // 15.01.2026 14:30:00

Форматирование дат

C# · Форматирование DateTime
DateTime now = DateTime.Now;

// Стандартные форматы
Console.WriteLine(now.ToString("dd.MM.yyyy"));       // 23.06.2026
Console.WriteLine(now.ToString("dd.MM.yyyy HH:mm")); // 23.06.2026 15:45
Console.WriteLine(now.ToString("yyyy-MM-dd"));        // 2026-06-23
Console.WriteLine(now.ToString("HH:mm:ss"));          // 15:45:30
Console.WriteLine(now.ToString("dddd"));              // Tuesday (день недели)
Console.WriteLine(now.ToString("MMMM yyyy"));         // June 2026

// Спецификаторы формата:
// dd   — день (01-31)
// MM   — месяц (01-12)
// yyyy — год (2026)
// HH   — часы (00-23)
// mm   — минуты (00-59)
// ss   — секунды (00-59)

Парсинг дат

C# · Парсинг DateTime
string dateStr = "15.01.2026";

// DateTime.Parse() — выбросит исключение при ошибке
DateTime date = DateTime.Parse(dateStr);
Console.WriteLine(date);  // 15.01.2026 0:00:00

// DateTime.TryParse() — безопасное преобразование (без исключений)
if (DateTime.TryParse("32.13.2026", out DateTime result))
    Console.WriteLine(result);
else
    Console.WriteLine("Некорректная дата!");  // сюда

// Парсинг с определённым форматом
string customDate = "2026-01-15 14:30:00";
DateTime parsed = DateTime.ParseExact(
    customDate,
    "yyyy-MM-dd HH:mm:ss",
    null  // null = текущая культура
);
Console.WriteLine(parsed);

Арифметика дат

C# · Арифметика DateTime
DateTime today = DateTime.Today;

// Добавление дней, месяцев, лет
DateTime tomorrow = today.AddDays(1);
DateTime nextWeek = today.AddDays(7);
DateTime nextMonth = today.AddMonths(1);
DateTime nextYear = today.AddYears(1);
DateTime lastYear = today.AddYears(-1);

Console.WriteLine($"Завтра: {tomorrow:dd.MM.yyyy}");
Console.WriteLine($"Через неделю: {nextWeek:dd.MM.yyyy}");
Console.WriteLine($"Через месяц: {nextMonth:dd.MM.yyyy}");

// Пример: последний день месяца
DateTime firstOfNextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
DateTime lastOfMonth = firstOfNextMonth.AddDays(-1);
Console.WriteLine($"Последний день месяца: {lastOfMonth:dd.MM.yyyy}");
💡 DateTime — это value type (структура). Минимальное значение: DateTime.MinValue (01.01.0001). Максимальное: DateTime.MaxValue (31.12.9999).
⏱️

TimeSpan — интервалы времени

TimeSpan представляет интервал времени: разница между двумя датами, продолжительность операции, таймаут. В отличие от DateTime, TimeSpan хранит не момент во времени, а промежуток.

Создание TimeSpan

C# · Создание TimeSpan
// Конструктор: new TimeSpan(дни, часы, минуты, секунды)
TimeSpan oneHour = new TimeSpan(1, 0, 0);          // 1 час
TimeSpan halfHour = new TimeSpan(0, 30, 0);          // 30 минут
TimeSpan workDay = new TimeSpan(8, 0, 0);            // 8 часов
TimeSpan precise = new TimeSpan(1, 2, 30, 15, 500);  // 1д 2ч 30м 15с 500мс

// Статические фабричные методы
TimeSpan twoDays = TimeSpan.FromDays(2);
TimeSpan threeHours = TimeSpan.FromHours(3);
TimeSpan fifteenMinutes = TimeSpan.FromMinutes(15);
TimeSpan tenSeconds = TimeSpan.FromSeconds(10);
TimeSpan fiveMs = TimeSpan.FromMilliseconds(5);

// TimeSpan.Zero — нулевой интервал
TimeSpan zero = TimeSpan.Zero;

// Свойства
Console.WriteLine(precise.Days);       // 1
Console.WriteLine(precise.Hours);      // 2
Console.WriteLine(precise.Minutes);    // 30
Console.WriteLine(precise.TotalHours); // 26.5 (общее количество часов!)
Console.WriteLine(precise.TotalMinutes); // 1590.25

Арифметика дат с TimeSpan

C# · DateTime + TimeSpan
DateTime start = new DateTime(2026, 1, 1, 9, 0, 0);  // 01.01.2026 09:00
TimeSpan duration = TimeSpan.FromHours(2.5);          // 2 часа 30 минут

DateTime end = start + duration;
Console.WriteLine(end);  // 01.01.2026 11:30:00

// Разница между датами — всегда TimeSpan
DateTime deadline = new DateTime(2026, 6, 30);
DateTime today = DateTime.Today;
TimeSpan remaining = deadline - today;

Console.WriteLine($"До дедлайна: {remaining.Days} дней");
Console.WriteLine($"Всего часов: {remaining.TotalHours:F1}");

// Сравнение TimeSpan
TimeSpan task1 = TimeSpan.FromMinutes(25);
TimeSpan task2 = TimeSpan.FromMinutes(40);

if (task1 < task2)
    Console.WriteLine("Задача 1 короче");

Console.WriteLine(task1 + task2);  // 01:05:00 (сумма интервалов)

Практический пример — сколько тебе дней от роду:

C# · Возраст в днях
DateTime birthDate = new DateTime(2000, 6, 15);
DateTime today = DateTime.Today;

TimeSpan ageInDays = today - birthDate;
Console.WriteLine($"Тебе {ageInDays.Days} дней от роду!");

// Возраст в годах (приблизительно)
int ageYears = today.Year - birthDate.Year;
if (today < birthDate.AddYears(ageYears))
    ageYears--;
Console.WriteLine($"Тебе примерно {ageYears} лет");
🧩

Практический пример — расчёт зарплаты

C# · Расчёт зарплаты
using System;

class Program
{
    static void Main()
    {
        // Ставка в час (используем decimal для денег!)
        decimal hourlyRate = 450.50m;

        // Рабочее время: 09:00 до 17:30 с перерывом 30 минут
        DateTime start = new DateTime(2026, 6, 23, 9, 0, 0);
        DateTime end =   new DateTime(2026, 6, 23, 17, 30, 0);
        TimeSpan breakTime = TimeSpan.FromMinutes(30);

        // Чистое рабочее время
        TimeSpan workTime = end - start - breakTime;
        Console.WriteLine($"Отработано: {workTime.Hours} ч {workTime.Minutes} мин");

        // Расчёт зарплаты
        decimal salary = hourlyRate * (decimal)workTime.TotalHours;
        Console.WriteLine($"Зарплата за день: {salary:F2} руб.");

        // Следующая зарплата через 2 недели
        DateTime nextPay = DateTime.Today.AddDays(14);
        Console.WriteLine($"Следующая зарплата: {nextPay:dd.MM.yyyy}");

        // Вычитаем налог 13%
        decimal tax = salary * 0.13m;
        decimal afterTax = salary - tax;
        Console.WriteLine($"После вычета налога: {afterTax:F2} руб.");
    }
}

// Вывод:
// Отработано: 8 ч 0 мин
// Зарплата за день: 3604.00 руб.
// Следующая зарплата: 07.07.2026
// После вычета налога: 3135.48 руб.
💡 Обрати внимание на (decimal)workTime.TotalHours. TotalHours возвращает double, мы преобразуем его в decimal для точного умножения.
🧪

Мини-тест

Проверь свои знания.

Вопрос 1

Какой суффикс нужно добавить к числу, чтобы это был decimal?

A) d   B) m   C) dec

Вопрос 2

Что вернёт DateTime.Now?

A) Только дату   B) Текущую дату и время в локальном часовом поясе   C) UTC время

Вопрос 3

Какой тип получится при вычитании двух DateTime?

A) DateTime   B) TimeSpan   C) int

Вопрос 4

Какой тип выбрать для хранения цены товара?

A) double   B) decimal   C) float

Вопрос 5

Что вернёт new DateTime(2026, 6, 15).AddMonths(1)?

A) 15.07.2026   B) 15.07.2027   C) 15.05.2026

💡 Ответы: 1 — B, 2 — B, 3 — B, 4 — B, 5 — A
🎮

Задача

Задание: Календарь платежей

Напиши программу для расчёта графика ежемесячных платежей по кредиту:

  1. Сумма кредита: 1 000 000 руб. (decimal)
  2. Процентная ставка: 12% годовых (0.12)
  3. Срок: 12 месяцев
  4. Дата выдачи: 15 января 2026
  5. Рассчитай ежемесячный платёж по формуле аннуитета и выведи график:
  6. Для каждого месяца выведи: дата платежа, сумма платежа, остаток долга

Формула аннуитетного платежа:

payment = S * (r * (1 + r)^n) / ((1 + r)^n - 1)

где S = сумма, r = месячная ставка (0.12 / 12 = 0.01), n = количество месяцев.

Дополнительно: используй decimal для всех денежных расчётов. Выводи даты в формате «15.02.2026».

💡 Для возведения в степень используй Math.Pow(). Она принимает double, поэтому преобразуй результат обратно в decimal.
📌

Что важно запомнить

1️⃣
decimal — для точных денежных расчётов (суффикс m). Не используй double для денег!
2️⃣
DateTime — дата и время. Now (локальное), UtcNow, Today. Методы: AddDays, AddMonths, AddYears.
3️⃣
TimeSpan — интервал времени. Разница дат → TimeSpan. date1 - date2.
4️⃣
Форматирование: ToString("dd.MM.yyyy"). Парсинг: TryParse() — безопасно.
5️⃣
Properties: Days/Hours/Minutes (составляющие) vs TotalHours/TotalMinutes (общее количество).

Тест: 10.2: Decimal, DateTime, TimeSpan

3 вопроса

Decimal и DateTime

Premium