问题描述
在调用 NSISO8601DateFormatter 以从 NSDate 格式化时间戳时,我想设置格式选项。 像 the documentation for NSISO8601DateFormatOptions
中描述的 NSISO8601DateFormatWithSpaceBetweenDateAndTime我似乎不知道如何在 Python 中指定必要的导入,以及如何应用这些选项。我在 the library repo 中找不到 NSISO8601DateFormatter,例如NSTimeZone 但 NSISO8601DateFormatter 导入有效。
我已经尝试使用带注释的行进行导入,但这给出了 ImportError: cannot import name
...
我正在运行 macOS 11.2.3 和默认 python 2.7.16 的 Mac 上进行测试
示例输出:
2021-04-01T12:55:41+02:00 [py_objc_timestamp] timestamp from PyObjC
所需输出的示例:
2021-04-01 12:55:41+02:00 [py_objc_timestamp] timestamp from PyObjC
这是我的测试代码,除了注释掉的行之外,它还能工作。
#!/usr/bin/python
# coding=utf-8
import CoreFoundation
from Foundation import (NSDate,NSISO8601DateFormatter,NSTimeZone)
#from Foundation import kcfISO8601DateFormatWithSpaceBetweenDateAndTime
#from Foundation import NSISO8601DateFormatOptions
def timestamp(dt=None):
if not dt:
dt = NSDate.date() # use Now
the_format = NSISO8601DateFormatter.alloc().init()
the_format.setTimeZone_(NSTimeZone.localTimeZone())
#the_format.formatOptions = { kcfISO8601DateFormatWithSpaceBetweenDateAndTime }
timestamp = the_format.stringFromDate_(dt)
return timestamp
if __name__ == '__main__':
print (timestamp() + " [py_objc_timestamp] timestamp from PyObjC")
解决方法
免责声明:手头没有我的 mac,无法测试任何这些
然而,这是您要查找的标题: https://github.com/apple/swift-corelibs-foundation/blob/main/CoreFoundation/Locale.subproj/CFDateFormatter.h#L53
这些是常量。
这是我们如何加载这些:
from Foundation import NSBundle
# Load the framework bundle by its identifier
Foundation_bundle = NSBundle.bundleWithIdentifier_("com.apple.Foundation")
objc.loadBundleVariables(Foundation_bundle,globals(),[('kCFISO8601DateFormatWithSpaceBetweenDateAndTime','@')])
这会将常量作为您现在可以使用的 python 变量加载。
这里有更多信息(ctrl + f loadBundleVariables):
https://pyobjc.readthedocs.io/en/latest/metadata/manual.html
看起来那些枚举常量不是在 PyObjC 的 bridgesupport
文件中定义的,但它们的值仍然可以通过在 Foundation 文档中查找来使用。请注意,在创建格式掩码时,all 需要包括各个部分:
kCFISO8601DateFormatWithFullDate = 275
kCFISO8601DateFormatWithFullTime = 1632
kCFISO8601DateFormatWithSpaceBetweenDateAndTime = 128
通常使用按位运算来获取掩码,但将它们相加得到的值为 2035。
另一个问题是设置格式选项。目前这是一个属性,但在这种情况下需要使用 setter 方法(它可能因特定框架而异)。
将所有这些放在一起,不需要注释的导入语句,格式化语句(在 Catalina 和 Big Sur 中测试)变为:
the_format.setFormatOptions_(2035)
,
在其他两个答案中得到 krit 和 red_menace 的帮助后回答我自己的问题,并做了一些我自己的研究。
关于无法从 Foundation 导入 NSISO8601DateFormatOptions 常量,我能找到的最好线索是我正在寻找的常量 kCFISO8601DateFormatWithFractionalSeconds
只出现在 a PyObjC Foundation metadata file 的 aliases
中。相比之下,可以从 CoreFoundation 导入的 kCFPreferencesCurrentHost
出现在该存储库中类似元数据文件的 constants
中。
也许 PyObjC 的未来版本将支持导入这些常量。
这是我更新的测试代码,它演示了我们如何手动定义不同的常量,我添加了所有常量,以防有类似需求的人来这里查看。
#!/usr/bin/python
# coding=utf-8
# File: py_objc_timestamp.py
# Demonstrates how to get ISO8601 / RFC3339 Internet timestamps
# using PyObjC bridge and Foundation framework
import CoreFoundation
from Foundation import (NSDate,NSISO8601DateFormatter,NSTimeZone)
'''
It doesn’t look like NSISO8601DateFormatOptions constants are covered by PyObjC’s
bridgesupport file - see https://stackoverflow.com/a/66933245/4326287
They're listed not as constants or enums but as aliases in
https://github.com/ronaldoussoren/pyobjc/blob/master/pyobjc-framework-Cocoa/Lib/CoreFoundation/_metadata.py
'''
try:
from Foundation import (kCFISO8601DateFormatWithInternetDateTime,\
kCFISO8601DateFormatWithFullTime)
except:
'''
manually defining constants for option flag bits,gleaning constants and values from
https://github.com/apple/swift-corelibs-foundation/blob/main/CoreFoundation/Locale.subproj/CFDateFormatter.h#L53
'''
print("[py_objc_timestamp] import of NSISO8601DateFormatOptions constants from Foundation failed,defining manually")
kCFISO8601DateFormatWithYear = 1 << 0
kCFISO8601DateFormatWithMonth = 1 << 1
kCFISO8601DateFormatWithWeekOfYear = 1 << 2
kCFISO8601DateFormatWithDay = 1 << 4
kCFISO8601DateFormatWithTime = 1 << 5
kCFISO8601DateFormatWithTimeZone = 1 << 6
kCFISO8601DateFormatWithSpaceBetweenDateAndTime = 1 << 7
kCFISO8601DateFormatWithDashSeparatorInDate = 1 << 8
kCFISO8601DateFormatWithColonSeparatorInTime = 1 << 9
kCFISO8601DateFormatWithColonSeparatorInTimeZone = 1 << 10
kCFISO8601DateFormatWithFractionalSeconds = 1 << 11
#kCFISO8601DateFormatWithFullDate = 275
kCFISO8601DateFormatWithFullDate = kCFISO8601DateFormatWithYear | \
kCFISO8601DateFormatWithMonth | kCFISO8601DateFormatWithDay | \
kCFISO8601DateFormatWithDashSeparatorInDate
#kCFISO8601DateFormatWithFullTime = 1632
kCFISO8601DateFormatWithFullTime = kCFISO8601DateFormatWithTime | \
kCFISO8601DateFormatWithColonSeparatorInTime | \
kCFISO8601DateFormatWithTimeZone | \
kCFISO8601DateFormatWithColonSeparatorInTimeZone
# Default - int(1907)
kCFISO8601DateFormatWithInternetDateTime = kCFISO8601DateFormatWithFullDate | \
kCFISO8601DateFormatWithFullTime
# define as global,need to setup only once
timestamp_format = NSISO8601DateFormatter.alloc().init()
# I'm a fan of local timezone timestamps rather than UTC,easier in testing software
timestamp_format.setTimeZone_(NSTimeZone.localTimeZone())
# Give us a Internet style,timezone-aware format,space instead of T for readability
timestamp_format_format_options = kCFISO8601DateFormatWithInternetDateTime | \
kCFISO8601DateFormatWithSpaceBetweenDateAndTime
print("[py_objc_timestamp] NSISO8601DateFormatOptions add up to: %i" % timestamp_format_format_options)
timestamp_format.setFormatOptions_(timestamp_format_format_options)
# If you absolutely must have speed rather than readability,you can strip the
# formatting constants and hardcode the combined value instead
# timestamp_format.setFormatOptions_(2035)
def timestamp(dt=None):
if not dt:
dt = NSDate.date() # use now
timestamp = timestamp_format.stringFromDate_(dt)
return timestamp
if __name__ == '__main__':
print (timestamp() + " [py_objc_timestamp] timestamp from PyObjC")
输出示例:
[py_objc_timestamp] import of NSISO8601DateFormatOptions constants from Foundation failed,defining manually
[py_objc_timestamp] NSISO8601DateFormatOptions add up to: 2035
2021-04-04 20:09:43+02:00 [py_objc_timestamp] timestamp from PyObjC