Как передать информацию о дате в AppleScript?

Я делаю одно приложение, которое может запускать AppleScript через NSAppleScript. Все было хорошо, но я не смог выяснить, как передать информацию о дате из моего приложения в AppleScript. (Так как AppleScript имеет тип даты, я полагаю, это возможно) Способ передачи параметров в AppleScript через NSAppleEventDescriptor. Я узнал от Google, что могу передать его как typeLongDateTime type:

- (id)initWithDate:(NSDate *)date {
     LongDateTime ldt;  
     UCConvertCFAbsoluteTimeToLongDateTime(CFDateGetAbsoluteTime((CFDateRef)date), &ldt);
     return [self initWithDescriptorType:typeLongDateTime
                                   bytes:&ldt
                                  length:sizeof(ldt)];
}

К сожалению, тип LongDateTime давно исчез, потому что я использую Swift и под OS X 10.10. Даже функция базовых служб UCConvertCFAbsoluteTimeToLongDateTime уже удалена из 10.10.3.

Теперь я застрял.

У вас есть идеи, которые вдохновляют?

4 ответа

Решение

Кажется, что LongDateTime 64-разрядное целое число со знаком, представляющее дату d как количество секунд с 1 января 1904 года по Гринвичу, скорректированное на смещение часового пояса для d в местном часовом поясе (включая смещение летнего времени, если летнее время активно в d).

Следующий код дает тот же результат, что и ваш код Objective C для всех дат, которые я тестировал (зимнее и летнее время).

class DateEventDescriptor : NSAppleEventDescriptor {

    convenience init?(date : NSDate) {
        let secondsSince2001 = Int64(date.timeIntervalSinceReferenceDate)
        var secondsSince1904 = secondsSince2001 + 3061152000
        secondsSince1904 += Int64(NSTimeZone.localTimeZone().secondsFromGMTForDate(date))
        self.init(descriptorType: DescType(typeLongDateTime),
            bytes: &secondsSince1904, length: sizeofValue(secondsSince1904))
    }
}

Обновление для Swift 3:

class DateEventDescriptor : NSAppleEventDescriptor {

    convenience init?(date: Date) {
        let secondsSince2001 = Int64(date.timeIntervalSinceReferenceDate)
        var secondsSince1904 = secondsSince2001 + 3061152000
        secondsSince1904 += Int64(NSTimeZone.local.secondsFromGMT(for: date))
        self.init(descriptorType: DescType(typeLongDateTime),
                  bytes: &secondsSince1904, length: MemoryLayout.size(ofValue: secondsSince1904))
    }
}

Обновление для macOS 10.11:

Начиная с MacOS 10.11 есть

 NSAppleEventDescriptor(date: Date)

инициализатор, так что вышеуказанный обходной путь больше не требуется. (Спасибо @Wevah за эту информацию.)

Вдохновленный Мартином, я узнал, что тип LongDateTime - это просто то, что записывает временной интервал с полуночи 1904-01-01. И AppleScript использует его для представления дат. Однако одна странная вещь в AppleScript состоит в том, что для типа даты нет понятия часового пояса. Таким образом, простое прохождение временного интервала с 1904-01-01 до 00:00:00 +0000 приведет только к тому, что итоговая дата в AppleScript покажет время в GMT. Вот почему я попробовал предложение Мартина, но в AppleScript мне показывали неверное время. Поскольку это данные, связанные с разницей во времени, я получил следующий способ работы для меня:

convenience init?(date: NSDate) {
    struct StaticWrapper {
        static var longDateTimeReferenceDate: NSDate!
    }
    if StaticWrapper.longDateTimeReferenceDate == nil {
        let formatter = NSDateFormatter()
        let c = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
        formatter.calendar = c
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        StaticWrapper.longDateTimeReferenceDate = formatter.dateFromString("1904-01-01 00:00:00")
    }

    var secondsSince1904 = Int64(date.timeIntervalSinceDate(StaticWrapper.longDateTimeReferenceDate))
    self.init(descriptorType: DescType(typeLongDateTime), bytes: &secondsSince1904, length: sizeofValue(secondsSince1904))
}

Информация о часовом поясе не указывается в форматере даты, который неявно включает текущий часовой пояс. Поэтому полученный интервал времени заставит AppleScript показывать время в местном часовом поясе. Который ведет себя как команда AppleScript "текущая дата".

Существует малоизвестная константа CoreFoundation kCFAbsoluteTimeIntervalSince1904 представляющий разницу между 1904 и 2001 годами. Это расширение NSDate преобразует NSDate в NSAppleEventDescriptor и наоборот

extension NSDate {

  func appleScriptDate() -> NSAppleEventDescriptor
  {
    var secondsSince1904 = Int64(self.timeIntervalSinceReferenceDate + kCFAbsoluteTimeIntervalSince1904)
    return NSAppleEventDescriptor(descriptorType: DescType(typeLongDateTime), bytes: &secondsSince1904, length: sizeofValue(secondsSince1904))!
  }

  convenience init(appleScriptDate : NSAppleEventDescriptor)
  {
    var secondsSince1904 : Int64 = 0
    let data = appleScriptDate.data
    data.getBytes(&secondsSince1904, length: data.length)
    self.init(timeIntervalSinceReferenceDate:NSTimeInterval(secondsSince1904) - kCFAbsoluteTimeIntervalSince1904)
  }

}

Если вам нужно настроить информацию о часовом поясе (преобразование даты в AppleScript не сохраняет часовой пояс), добавьте NSTimeZone.systemTimeZone().secondsFromGMT в Свифте или time to GMT в AppleScript

Я обновил расширение vadian для Swift 3:

extension NSDate {

    func appleScriptDate() -> NSAppleEventDescriptor
    {
        var secondsSince1904 = Int64(self.timeIntervalSinceReferenceDate + kCFAbsoluteTimeIntervalSince1904)
        return NSAppleEventDescriptor(descriptorType: DescType(typeLongDateTime), bytes: &secondsSince1904, length: MemoryLayout.size(ofValue: secondsSince1904))!
    }

    convenience init(appleScriptDate : NSAppleEventDescriptor)
    {
        var secondsSince1904 : Int64 = 0
        withUnsafeMutablePointer(to: &secondsSince1904) {
            _ = appleScriptDate.data.copyBytes(
                to: UnsafeMutableBufferPointer(start: $0, count: 4),
                from: 0..<4)
        }
        self.init(timeIntervalSinceReferenceDate:TimeInterval(secondsSince1904) - kCFAbsoluteTimeIntervalSince1904)
    }
}
Другие вопросы по тегам