Чтобы создать на Mac метку публикации с месяцем и днём, запросите эти два поля с помощью форматировщика Foundation в Swift и позвольте локали определить их порядок. Сначала решите, что представляет собой входное значение: календарную дату или временную метку. От этого зависит, должна ли смена часового пояса менять отображаемый день.

В этом руководстве показан небольшой скрипт для Терминала, который создаёт метки публикаций по синтетическим датам. Для основной операции он использует Date.FormatStyle, а также показывает альтернативу на основе шаблона для существующего кода с DateFormatter. Apple документирует оба способа локализованного вывода дат. Date.FormatStyle, DateFormatter

Запрашивайте месяц и день как поля, а не как готовый шаблон

Шаблон вроде MMMM d фиксирует порядок: сначала месяц, затем день. Запрос локализованных полей указывает, что нужно включить, а порядок оставляет на усмотрение форматировщика. В Swift нужное выражение — Date.FormatStyle().month(.wide).day(). Apple прямо указывает, что порядок модификаторов форматирования не влияет на результирующую строку. Поэтому вызов .day() первым не заставляет выводить сначала день. Date.FormatStyle

Для синтетической даты публикации — 12 ноября 2026 года — примеры меток: November 12 с en_US и 12 November с en_GB. Это явные тестовые локали; рабочий скрипт также выводит метку с текущей локалью Mac. Apple демонстрирует такое же различие в порядке месяца и дня с помощью локализованного шаблона API. DateFormatter

В Unicode лежащий в основе этого принцип описан через скелеты дат: они содержат запрашиваемые поля и их длину, а программное обеспечение сопоставляет их с подходящим локализованным шаблоном. Например, MMMd запрашивает сокращённое название месяца и день. Он не задаёт косую черту, пробел или итоговый порядок отображения. Полезный принцип для разных API: выберите компоненты, а затем позвольте форматировщику собрать метку. Технический стандарт Unicode № 35, часть 4: даты

Определите, что означает дата публикации

Перед форматированием выберите подходящую строку в таблице ниже. Это поможет избежать ситуации, когда корректно работающий форматировщик показывает неожиданный календарный день.

Входное значение Значение Решение при форматировании
Год 2026, месяц 11, день 12 Гражданская дата: один день в указанном календаре Сохраните эти поля; при преобразовании через Date используйте единообразные календарь и часовой пояс
2026-11-12T00:30:00Z Временная метка, обозначающая момент времени Разберите момент времени, затем выберите часовой пояс для его отображения
+02:00 Числовое смещение относительно UTC Позволяет соотнести локальную временную метку с UTC, но не указывает именованный часовой пояс
Europe/London Идентификатор именованного часового пояса Используйте его правила для отображаемого момента времени

Apple определяет Date как момент времени, не зависящий от календаря или часового пояса. DateComponents хранит такие поля, как год, месяц и день, которые интерпретируются с учётом календаря и часового пояса. Поэтому для записи публикации, содержащей только дату, нужно явно задать правило преобразования при передаче значения API, форматирующему Date. Date, DateComponents

Для этой задачи правило простое: сохраните исходные год, месяц и день, создайте григорианскую дату на полдень по UTC и форматируйте её с теми же календарём и часовым поясом UTC. Полдень — лишь выбранная для этого примера точка привязки. Именно единообразие при создании и форматировании позволяет сохранить дату. Эта точка привязки не обозначает фактическое время публикации.

Для настоящей временной метки вместо этого выберите нужный часовой пояс отображения. Локаль управляет представлением, но не задаёт часовой пояс временной метки. У именованного часового пояса смещение может различаться в зависимости от момента времени, а фиксированное числовое смещение таких правил не содержит. Apple предоставляет и идентификаторы часовых поясов, и операции получения смещения для заданной даты. RFC 3339 определяет смещения временных меток, а RFC 9557 расширяет это представление, позволяя указывать дополнительные сведения, включая часовой пояс. TimeZone, RFC 3339, RFC 9557

Запустите в Терминале готовый форматировщик месяца и дня

Используйте macOS 12 или более позднюю версию, а также набор инструментов Swift и SDK для macOS. Apple указывает, что использованный здесь инициализатор Date.FormatStyle доступен начиная с macOS 12. На официальной странице установки Swift приведены инструкции для Mac, включая установку Xcode для разработки под платформы Apple. Инициализатор формата, Установка Swift — macOS

Сохраните приведённую ниже программу целиком в обычном текстовом редакторе под именем MonthDay.swift. В ней есть собственные синтетические примеры данных, проверка полей календаря и примеры с текущей и явными локалями. Также программа выводит эквивалентный результат на основе шаблона, чтобы можно было сравнить два API.

import Foundation

enum LabelError: Error {
    case unavailableUTC
    case invalidPublicationDate
}

func main() throws {
    guard let utc = TimeZone(secondsFromGMT: 0) else {
        throw LabelError.unavailableUTC
    }

    var calendar = Calendar(identifier: .gregorian)
    calendar.timeZone = utc

    // Synthetic civil publication date, not an actual timestamp.
    var fields = DateComponents()
    fields.calendar = calendar
    fields.timeZone = utc
    fields.year = 2026
    fields.month = 11
    fields.day = 12
    fields.hour = 12

    guard fields.isValidDate(in: calendar),
          let publicationDate = calendar.date(from: fields) else {
        throw LabelError.invalidPublicationDate
    }

    let previews: [(String, Locale)] = [
        ("Current locale", .current),
        ("en_US", Locale(identifier: "en_US")),
        ("en_GB", Locale(identifier: "en_GB"))
    ]

    for (name, locale) in previews {
        let style = Date.FormatStyle(
            locale: locale,
            calendar: calendar,
            timeZone: utc
        )
        .month(.wide)
        .day()

        print("\(name): \(publicationDate.formatted(style))")

        let formatter = DateFormatter()
        formatter.locale = locale
        formatter.calendar = calendar
        formatter.timeZone = utc
        formatter.setLocalizedDateFormatFromTemplate("MMMMd")

        print("\(name), template: \(formatter.string(from: publicationDate))")
    }
}

try main()

Откройте Терминал, перейдите в папку с файлом и выполните команду swift MonthDay.swift. Если набор инструментов Swift недоступен, сначала выполните официальные инструкции по установке для Mac. В рамках этого исследования пример был сверён с документацией, но не запускался, поскольку Swift был недоступен.

Два явных примера должны показать порядок «сначала месяц» и «сначала день» для одной даты — 12 ноября. Строка с текущей локалью использует настройку локали вашего Mac. Оба пути форматирования запрашивают полное название месяца и день, не добавляя год или время. Рассматривайте результат как локализованный текст для отображения; не полагайтесь при дальнейшей обработке на конкретный пробел или знак пунктуации. Модель сопоставления шаблонов Unicode допускает порядок, зависящий от локали. Технический стандарт Unicode № 35, часть 4: даты

Григорианский календарь в этой программе используется намеренно: синтетические исходные поля заданы в этом календаре. Он остаётся неизменным, а локаль меняет представление. Если исходные даты заданы в другом календаре, сначала интерпретируйте их поля в этом календаре, а затем решайте, как их отображать. Для этого инициализатор Apple позволяет отдельно задать локаль, календарь и часовой пояс. Инициализатор формата

Используйте локализованный шаблон в существующем коде DateFormatter

Второй путь в программе использует setLocalizedDateFormatFromTemplate("MMMMd"). Здесь MMMM запрашивает полное название месяца, а d — день месяца. Строка служит шаблоном, передаваемым методу локализации; её не присваивают напрямую как итоговый dateFormat. В собственном примере Apple используется именно этот шаблон, который формирует разный порядок месяца и дня. DateFormatter

Задайте locale перед вызовом метода шаблона. Apple прямо требует соблюдать этот порядок, поскольку метод формирует формат на основе локали форматировщика в момент вызова. Если позже выбрать другую локаль, вызовите метод шаблона ещё раз. В примере для каждого вывода создаётся новый форматировщик, чтобы эту зависимость было видно. setLocalizedDateFormatFromTemplate(_:)

Для этой задачи выберите подходящий вариант в зависимости от существующего кода: модификаторы полей в современном API Swift или метод локализованного шаблона в рабочем процессе с DateFormatter. Не собирайте строку из отдельно отформатированных месяца и дня с собственным разделителем: так вы лишаете форматировщик даты возможности выбирать их порядок.

Обрабатывайте временные метки так, чтобы случайно не изменить смысл задачи

Предположим, что вместо даты источник передаёт синтетическую временную метку 2026-11-12T00:30:00Z. Она обозначает момент времени. Отображение со смещением +00:00 даёт 12 ноября; отображение того же момента со смещением -02:00 даёт 11 ноября, 22:30. Это пример расчёта по смещению, а не реальное событие публикации. RFC 3339 определяет числовое смещение как разницу между местным временем и UTC. RFC 3339, раздел 4.2

Разбирайте такие значения с помощью API ISO 8601, настроенного для фактического формата, а затем форматируйте полученный момент времени с выбранным часовым поясом отображения. Apple документирует ISO8601DateFormatter и параметр .withInternetDateTime для строк даты и времени RFC 3339. Этот параметр относится к настройке разбора и отделён от запроса локализованного вывода месяца и дня. Параметры формата ISO8601DateFormatter

Не применяйте этот сценарий для временных меток к источнику, в котором указана только гражданская дата, если вы намеренно не задали правило преобразования. Также не считайте, что суффикс Z указывает именованный часовой пояс публикации. В RFC 9557 сведения об именованном часовом поясе рассматриваются как дополнительные данные сверх базового представления временной метки. RFC 9557

Разбирайтесь в причинах неожиданной метки в правильном порядке

Определите, какое решение нужно изменить, выполнив проверку в таком порядке:

  1. Неверно указан день: проверьте, является ли входное значение гражданской датой или моментом времени. Для программы с гражданской датой используйте UTC и при создании, и при форматировании. Для момента времени проверьте, правильно ли выбран часовой пояс отображения.
  2. Месяц и день идут не в том порядке: проверьте локаль, переданную форматировщику, и убедитесь, что dateFormat не задан напрямую, а строки не объединяются вручную.
  3. Изменение порядка модификаторов ничего не меняет: такое поведение задокументировано. Выберите подходящую локаль вместо того, чтобы менять местами .month() и .day().
  4. Вывод шаблона сохраняет прежний порядок: задайте новую локаль, затем сформируйте формат заново с помощью setLocalizedDateFormatFromTemplate.
  5. Исходные поля недопустимы: сообщите об ошибке или исправьте входные данные. Полная программа проверяет допустимость значений до преобразования, а не подставляет другую дату публикации.

Эти проверки следуют из документированного разграничения между моментом времени, его интерпретацией в календаре и локализованным представлением. Apple документирует независимость от порядка модификаторов, последовательность настройки шаблона и проверку компонентов даты в соответствующих API. Date.FormatStyle, setLocalizedDateFormatFromTemplate(_:), DateComponents

После создания метки сохраняйте полную исходную дату или временную метку. Строка с месяцем и днём намеренно не содержит год, а метка временной метки также не содержит время и часовой пояс; такой текст не сохраняет исходное значение целиком. Чтобы получить надёжную метку публикации, сохраните исходное значение, осознанно выберите календарь и часовой пояс и позвольте локали определить порядок месяца и дня.

Источники