timezone-utils.cjs•4.45 kB
/**
 * Утилиты для работы с часовыми поясами
 * Определение UTC offset и DST для расчета времени рождения
 */
// Основные города и их часовые пояса
const CITY_TIMEZONES = {
  'Москва': { tz: 'Europe/Moscow', lat: 55.7558, lon: 37.6173 },
  'Санкт-Петербург': { tz: 'Europe/Moscow', lat: 59.9343, lon: 30.3351 },
  'Новосибирск': { tz: 'Asia/Novosibirsk', lat: 55.0084, lon: 82.9357 },
  'Екатеринбург': { tz: 'Asia/Yekaterinburg', lat: 56.8431, lon: 60.6454 },
  'Казань': { tz: 'Europe/Moscow', lat: 55.8304, lon: 49.0661 },
  'Нижний Новгород': { tz: 'Europe/Moscow', lat: 56.2965, lon: 43.9361 },
  'Киев': { tz: 'Europe/Kiev', lat: 50.4501, lon: 30.5234 },
  'Минск': { tz: 'Europe/Minsk', lat: 53.9045, lon: 27.5615 },
  'Алматы': { tz: 'Asia/Almaty', lat: 43.2389, lon: 76.8897 },
  'Ташкент': { tz: 'Asia/Tashkent', lat: 41.2995, lon: 69.2401 },
};
// UTC offsets по времени года (для упрощенной модели)
const TIMEZONE_OFFSETS = {
  'Europe/Moscow': {
    standard: 3, // UTC+3 зимой
    daylight: 3  // UTC+3 круглый год с 2014
  },
  'Asia/Yekaterinburg': {
    standard: 5,
    daylight: 5
  },
  'Asia/Novosibirsk': {
    standard: 7,
    daylight: 7
  },
  'Europe/Kiev': {
    standard: 2,
    daylight: 3
  },
  'Europe/Minsk': {
    standard: 3,
    daylight: 3
  },
  'Asia/Almaty': {
    standard: 6,
    daylight: 6
  },
  'Asia/Tashkent': {
    standard: 5,
    daylight: 5
  }
};
/**
 * Определить часовой пояс и координаты по названию города
 */
function getLocationInfo(cityName) {
  // Нормализация названия
  const normalized = cityName.toLowerCase().trim();
  
  // Поиск в кэше городов
  for (const [city, info] of Object.entries(CITY_TIMEZONES)) {
    if (city.toLowerCase().includes(normalized) || normalized.includes(city.toLowerCase())) {
      return { city, ...info };
    }
  }
  
  // Значения по умолчанию (Москва)
  console.warn(`Unknown city: ${cityName}, using Moscow defaults`);
  return CITY_TIMEZONES['Москва'];
}
/**
 * Определить UTC offset для даты (учитывая DST)
 * Упрощенная версия - для полной точности используйте библиотеку timezone
 */
function getUTCOffset(cityName, birthDate) {
  const [year, month, day] = birthDate.split('-').map(Number);
  const locationInfo = getLocationInfo(cityName);
  const timezone = locationInfo.tz;
  
  if (!TIMEZONE_OFFSETS[timezone]) {
    console.warn(`Unknown timezone: ${timezone}, using UTC+3`);
    return 3;
  }
  
  const offsets = TIMEZONE_OFFSETS[timezone];
  
  // Упрощенное определение DST
  // Летнее время обычно: март-октябрь (в Северном полушарии)
  const isDST = month >= 3 && month <= 10;
  
  // Некоторые страны отменили DST
  if (timezone === 'Europe/Moscow' && year >= 2014) {
    return 3; // После 2014 DST отменен в России
  }
  
  return isDST ? offsets.daylight : offsets.standard;
}
/**
 * Конвертировать local time в UTC
 */
function localTimeToUTC(localTime, utcOffset) {
  const [hour, minute] = localTime.split(':').map(Number);
  const utcHour = hour - utcOffset;
  
  // Обработка перехода через полуночь
  let adjustedHour = utcHour;
  if (adjustedHour < 0) adjustedHour += 24;
  if (adjustedHour >= 24) adjustedHour -= 24;
  
  return {
    hour: adjustedHour,
    minute: minute,
    offset: utcOffset
  };
}
/**
 * Конвертировать local time в UTC (с учетом даты)
 */
function convertToUTC(birthDate, birthTime, cityName) {
  const [year, month, day] = birthDate.split('-').map(Number);
  const utcOffset = getUTCOffset(cityName, birthDate);
  const { hour, minute } = localTimeToUTC(birthTime, utcOffset);
  
  return {
    utcYear: year,
    utcMonth: month,
    utcDay: day,
    utcHour: hour,
    utcMinute: minute,
    offset: utcOffset,
    originalLocalTime: birthTime
  };
}
module.exports = {
  getLocationInfo,
  getUTCOffset,
  localTimeToUTC,
  convertToUTC,
  CITY_TIMEZONES,
  TIMEZONE_OFFSETS
};