adium/adium

Parents 90c61369e36d
Children 94eac2b970cf
NSCalendarDate is deprecated. I've updated the ISO8601 parser to github.com/boredzo/iso-8601-date-formatter/commit/0da1785 and updated all uses of NSCalendarDate.
  • +10 -46
    Adium.xcodeproj/project.pbxproj
  • +1 -0
    Frameworks/AIUtilities Framework/Source/AIDateAdditions.h
  • +11 -0
    Frameworks/AIUtilities Framework/Source/AIDateAdditions.m
  • +79 -0
    Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.h
  • +895 -0
    Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.m
  • +0 -47
    Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.h
  • +0 -664
    Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m
  • +0 -46
    Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.h
  • +0 -177
    Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.m
  • +3 -3
    Frameworks/Adium Framework/Source/AIContentObject.m
  • +5 -4
    Other/Adium Spotlight Importer/GetMetadataForFile.m
  • +13 -23
    Other/Adium Spotlight Importer/GetMetadataForHTMLLog.m
  • +1 -1
    Plugins/Error Message Handler/ErrorMessageHandlerPlugin.m
  • +3 -3
    Plugins/Message Alias Support/AIMessageAliasPlugin.m
  • +0 -30
    Source/AICalendarDate.h
  • +0 -65
    Source/AICalendarDate.m
  • +7 -3
    Source/AIChatLog.h
  • +17 -38
    Source/AIChatLog.m
  • +14 -10
    Source/AILogDateFormatter.m
  • +1 -3
    Source/AILogViewerWindowController.h
  • +37 -55
    Source/AILogViewerWindowController.m
  • +3 -1
    Source/AILoggerPlugin.h
  • +10 -24
    Source/AILoggerPlugin.m
  • +2 -1
    Source/AIXMLChatlogConverter.h
  • +7 -4
    Source/AIXMLChatlogConverter.m
  • +4 -2
    Source/BGICLogImportController.m
  • +2 -1
    Source/DCMessageContextDisplayPlugin.h
  • +4 -3
    Source/DCMessageContextDisplayPlugin.m
  • +2 -2
    Source/ESDebugController.m
  • +0 -28
    Source/scandate.h
  • +0 -149
    Source/scandate.m
  • +0 -25
    UnitTests/TestScandate.h
  • +0 -48
    UnitTests/TestScandate.m
  • --- a/Adium.xcodeproj/project.pbxproj Sun Jun 02 12:26:04 2013 -0400
    +++ b/Adium.xcodeproj/project.pbxproj Mon Jun 03 17:17:12 2013 -0400
    @@ -193,8 +193,6 @@
    318EA69C0D7A659900EDB105 /* TestColorAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 318EA69B0D7A659900EDB105 /* TestColorAdditions.m */; };
    319B29800CE8EC6F00C65398 /* TestDateAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 319B297F0CE8EC6E00C65398 /* TestDateAdditions.m */; };
    31A764B90DA572B8000AC729 /* AutoHyperlinks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3496A8EA07CE6CA30055BBAB /* AutoHyperlinks.framework */; };
    - 31DDDA7112BDD5CE0048F6C0 /* scandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */; };
    - 31DDDA7A12BDD6E90048F6C0 /* TestScandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA7912BDD6E90048F6C0 /* TestScandate.m */; };
    31E0CD810C5EEF5200271DB1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31E0CD800C5EEF5200271DB1 /* CoreAudio.framework */; };
    31FA804C0D4A8EB200ABE634 /* Adium.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 0CAC6A130C0C657A0090AE95 /* Adium.sdef */; };
    3402D5A5080DBC91004E50B4 /* SortConfiguration.xib in Resources */ = {isa = PBXBuildFile; fileRef = 347E791D07CAA52300350507 /* SortConfiguration.xib */; };
    @@ -226,7 +224,6 @@
    3419DE7D0A81042F00C3FC68 /* lock-unlocked.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 34DFF52607A62EB000B92233 /* lock-unlocked.tiff */; };
    3419DE7F0A81042F00C3FC68 /* msg-send-file.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 34D8326307CBD598006466F2 /* msg-send-file.tiff */; };
    3419DE820A81042F00C3FC68 /* ToolbarPrefs.plist in Resources */ = {isa = PBXBuildFile; fileRef = 34D8327607CBD5B3006466F2 /* ToolbarPrefs.plist */; };
    - 3419DF3B0A8119BF00C3FC68 /* AICalendarDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 340D0A000A7DD1F40059A3AF /* AICalendarDate.m */; };
    3419E2DF0A81445800C3FC68 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3419E2DE0A81445800C3FC68 /* Localizable.strings */; };
    3419F7760531512200C68BA3 /* Adium.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34BD9DE105314751000AB133 /* Adium.framework */; };
    3419FE230531586A00C68BA3 /* Adium.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 34BD9DE105314751000AB133 /* Adium.framework */; };
    @@ -1050,9 +1047,10 @@
    5A6298DF1682BD9100F51DED /* AIAdvancedPreferencePane.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A1E24A815BC746B003A6F9E /* AIAdvancedPreferencePane.m */; };
    5A6298E31682BD9D00F51DED /* AIAdvancedPreferencePane.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A1E24A715BC746B003A6F9E /* AIAdvancedPreferencePane.h */; settings = {ATTRIBUTES = (Public, ); }; };
    5A799674133C3D6F0005AC6A /* MessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5A1FEA601334549300C14951 /* MessageView.xib */; };
    - 5A804FEA12BE9E84007CDC1B /* scandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */; };
    - 5A80508512BE9F00007CDC1B /* scandate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */; };
    5A8F637614DD1B21008412C8 /* Preferences-Privacy.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5A8F637514DD1B21008412C8 /* Preferences-Privacy.xib */; };
    + 5A914166175BD960002139D5 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A914164175BD960002139D5 /* ISO8601DateFormatter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    + 5A914167175BD960002139D5 /* ISO8601DateFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A914165175BD960002139D5 /* ISO8601DateFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; };
    + 5A914168175BD97F002139D5 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A914164175BD960002139D5 /* ISO8601DateFormatter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
    5A94397B1279ECB800FDD81D /* AIImgurImageUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A94397A1279ECB800FDD81D /* AIImgurImageUploader.m */; };
    5A9A9F8911F2951400328DF9 /* AIDoNothingContactAlertPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A9F8811F2951400328DF9 /* AIDoNothingContactAlertPlugin.m */; };
    5A9A9F8B11F295EB00328DF9 /* events-do-nothing.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 5A9A9F8A11F295EB00328DF9 /* events-do-nothing.tiff */; };
    @@ -1252,10 +1250,6 @@
    633400BC0F9C14C2003C77A9 /* AIWindowAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6334FFF10F9C14C1003C77A9 /* AIWindowAdditions.m */; };
    633400BF0F9C14C2003C77A9 /* OWAddressBookAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6334FFF40F9C14C1003C77A9 /* OWAddressBookAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
    633400C00F9C14C2003C77A9 /* OWAddressBookAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6334FFF50F9C14C1003C77A9 /* OWAddressBookAdditions.m */; };
    - 633400C10F9C14C2003C77A9 /* NSCalendarDate+ISO8601Parsing.h in Headers */ = {isa = PBXBuildFile; fileRef = 6334FFF60F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.h */; settings = {ATTRIBUTES = (Public, ); }; };
    - 633400C20F9C14C2003C77A9 /* NSCalendarDate+ISO8601Parsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 6334FFF70F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.m */; };
    - 633400C30F9C14C2003C77A9 /* NSCalendarDate+ISO8601Unparsing.h in Headers */ = {isa = PBXBuildFile; fileRef = 6334FFF80F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.h */; settings = {ATTRIBUTES = (Public, ); }; };
    - 633400C40F9C14C2003C77A9 /* NSCalendarDate+ISO8601Unparsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 6334FFF90F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.m */; };
    633400C50F9C14C2003C77A9 /* AIWindowControllerAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6334FFFA0F9C14C1003C77A9 /* AIWindowControllerAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
    633400C60F9C14C2003C77A9 /* AIWindowControllerAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6334FFFB0F9C14C1003C77A9 /* AIWindowControllerAdditions.m */; };
    633400C70F9C14C2003C77A9 /* AIPasteboardAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6334FFFC0F9C14C1003C77A9 /* AIPasteboardAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
    @@ -1279,7 +1273,6 @@
    633D4FB70F9D30E3004F491E /* GetMetadataForFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 633D4FB10F9D30E3004F491E /* GetMetadataForFile.m */; };
    633D4FB80F9D30E3004F491E /* GetMetadataForHTMLLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 633D4FB30F9D30E3004F491E /* GetMetadataForHTMLLog.m */; };
    633D4FB90F9D30E3004F491E /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 633D4FB40F9D30E3004F491E /* main.c */; };
    - 633D4FBE0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 633D4FBD0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.m */; };
    633D4FC20F9D3116004F491E /* schema.xml in Resources */ = {isa = PBXBuildFile; fileRef = 633D4FC00F9D3116004F491E /* schema.xml */; };
    633D514F0F9D31BE004F491E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 633D50ED0F9D31BD004F491E /* InfoPlist.strings */; };
    633D51500F9D31BE004F491E /* schema.strings in Resources */ = {isa = PBXBuildFile; fileRef = 633D50EF0F9D31BD004F491E /* schema.strings */; };
    @@ -2009,10 +2002,6 @@
    318EA69B0D7A659900EDB105 /* TestColorAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestColorAdditions.m; path = UnitTests/TestColorAdditions.m; sourceTree = "<group>"; };
    319B29420CE8D28300C65398 /* TestDateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestDateAdditions.h; path = UnitTests/TestDateAdditions.h; sourceTree = "<group>"; };
    319B297F0CE8EC6E00C65398 /* TestDateAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestDateAdditions.m; path = UnitTests/TestDateAdditions.m; sourceTree = "<group>"; };
    - 31DDDA6E12BDD5CE0048F6C0 /* scandate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scandate.h; path = Source/scandate.h; sourceTree = "<group>"; };
    - 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = scandate.m; path = Source/scandate.m; sourceTree = "<group>"; };
    - 31DDDA7812BDD6E90048F6C0 /* TestScandate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestScandate.h; path = UnitTests/TestScandate.h; sourceTree = "<group>"; };
    - 31DDDA7912BDD6E90048F6C0 /* TestScandate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestScandate.m; path = UnitTests/TestScandate.m; sourceTree = "<group>"; };
    31DDDAF112BDE54B0048F6C0 /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Prefix.pch; path = "Other/Adium Spotlight Importer/Prefix.pch"; sourceTree = "<group>"; };
    31E0CD800C5EEF5200271DB1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = "<absolute>"; };
    31E84DF10C7F387800674BCA /* AIUnitTestUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIUnitTestUtilities.h; path = UnitTests/AIUnitTestUtilities.h; sourceTree = "<group>"; };
    @@ -2075,8 +2064,6 @@
    340D03A80A7D798A0059A3AF /* AIControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIControllerProtocol.h"; sourceTree = "<group>"; };
    340D081F0A7DBFE40059A3AF /* AIChatControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIChatControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIChatControllerProtocol.h"; sourceTree = "<group>"; };
    340D083F0A7DC1540059A3AF /* AIContactAlertsControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIContactAlertsControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIContactAlertsControllerProtocol.h"; sourceTree = "<group>"; };
    - 340D09FF0A7DD1F40059A3AF /* AICalendarDate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AICalendarDate.h; path = Source/AICalendarDate.h; sourceTree = "<group>"; };
    - 340D0A000A7DD1F40059A3AF /* AICalendarDate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AICalendarDate.m; path = Source/AICalendarDate.m; sourceTree = "<group>"; };
    340D0D5B0A7E72030059A3AF /* AIContactControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIContactControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIContactControllerProtocol.h"; sourceTree = "<group>"; };
    340D0D630A7E72DA0059A3AF /* AIContentControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIContentControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIContentControllerProtocol.h"; sourceTree = "<group>"; };
    340D0D720A7E750E0059A3AF /* AIDockControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIDockControllerProtocol.h; path = "Frameworks/Adium Framework/Source/AIDockControllerProtocol.h"; sourceTree = "<group>"; };
    @@ -4002,6 +3989,8 @@
    5A80CCAD121F5A7B00D0670A /* uk */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xib; name = uk; path = Resources/uk.lproj/StatusSortConfiguration.xib; sourceTree = "<group>"; };
    5A80CCAE121F5A7B00D0670A /* uk */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xib; name = uk; path = "Plugins/WebKit Message View/Resources/uk.lproj/WebKitPreferencesView.xib"; sourceTree = "<group>"; };
    5A8F637514DD1B21008412C8 /* Preferences-Privacy.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = "Preferences-Privacy.xib"; path = "Resources/Preferences-Privacy.xib"; sourceTree = "<group>"; };
    + 5A914164175BD960002139D5 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ISO8601DateFormatter.m; path = "Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.m"; sourceTree = "<group>"; };
    + 5A914165175BD960002139D5 /* ISO8601DateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ISO8601DateFormatter.h; path = "Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.h"; sourceTree = "<group>"; };
    5A9439791279ECB800FDD81D /* AIImgurImageUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIImgurImageUploader.h; path = "Plugins/Image Uploading Plugin/AIImgurImageUploader.h"; sourceTree = "<group>"; };
    5A94397A1279ECB800FDD81D /* AIImgurImageUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIImgurImageUploader.m; path = "Plugins/Image Uploading Plugin/AIImgurImageUploader.m"; sourceTree = "<group>"; };
    5A9A9F8711F2951400328DF9 /* AIDoNothingContactAlertPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AIDoNothingContactAlertPlugin.h; sourceTree = "<group>"; };
    @@ -4207,10 +4196,6 @@
    6334FFF30F9C14C1003C77A9 /* AIFontAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIFontAdditions.h; path = "Frameworks/AIUtilities Framework/Source/AIFontAdditions.h"; sourceTree = "<group>"; };
    6334FFF40F9C14C1003C77A9 /* OWAddressBookAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWAddressBookAdditions.h; path = "Frameworks/AIUtilities Framework/Source/OWAddressBookAdditions.h"; sourceTree = "<group>"; };
    6334FFF50F9C14C1003C77A9 /* OWAddressBookAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWAddressBookAdditions.m; path = "Frameworks/AIUtilities Framework/Source/OWAddressBookAdditions.m"; sourceTree = "<group>"; };
    - 6334FFF60F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendarDate+ISO8601Parsing.h"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.h"; sourceTree = "<group>"; };
    - 6334FFF70F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCalendarDate+ISO8601Parsing.m"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m"; sourceTree = "<group>"; };
    - 6334FFF80F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendarDate+ISO8601Unparsing.h"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.h"; sourceTree = "<group>"; };
    - 6334FFF90F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCalendarDate+ISO8601Unparsing.m"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.m"; sourceTree = "<group>"; };
    6334FFFA0F9C14C1003C77A9 /* AIWindowControllerAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIWindowControllerAdditions.h; path = "Frameworks/AIUtilities Framework/Source/AIWindowControllerAdditions.h"; sourceTree = "<group>"; };
    6334FFFB0F9C14C1003C77A9 /* AIWindowControllerAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIWindowControllerAdditions.m; path = "Frameworks/AIUtilities Framework/Source/AIWindowControllerAdditions.m"; sourceTree = "<group>"; };
    6334FFFC0F9C14C1003C77A9 /* AIPasteboardAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIPasteboardAdditions.h; path = "Frameworks/AIUtilities Framework/Source/AIPasteboardAdditions.h"; sourceTree = "<group>"; };
    @@ -4224,8 +4209,6 @@
    633D4FB30F9D30E3004F491E /* GetMetadataForHTMLLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GetMetadataForHTMLLog.m; path = "Other/Adium Spotlight Importer/GetMetadataForHTMLLog.m"; sourceTree = "<group>"; };
    633D4FB40F9D30E3004F491E /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = "Other/Adium Spotlight Importer/main.c"; sourceTree = "<group>"; };
    633D4FB50F9D30E3004F491E /* maintest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = maintest.m; path = "Other/Adium Spotlight Importer/maintest.m"; sourceTree = "<group>"; };
    - 633D4FBC0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendarDate+ISO8601Parsing.h"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.h"; sourceTree = "<group>"; };
    - 633D4FBD0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCalendarDate+ISO8601Parsing.m"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m"; sourceTree = "<group>"; };
    633D4FC00F9D3116004F491E /* schema.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = schema.xml; path = "Other/Adium Spotlight Importer/schema.xml"; sourceTree = "<group>"; };
    633D4FC10F9D3116004F491E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Other/Adium Spotlight Importer/Info.plist"; sourceTree = "<group>"; };
    633D50550F9D31B8004F491E /* AdiumSpotlightImporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AdiumSpotlightImporter.h; path = "Other/Adium Spotlight Importer/AdiumSpotlightImporter.h"; sourceTree = "<group>"; };
    @@ -4287,7 +4270,6 @@
    638392F609D4D67A0067B9B7 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Sparkle.framework; sourceTree = "<group>"; };
    638BC1FA0FC932E000CE7600 /* AIObjectDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIObjectDebug.h; path = Source/AIObjectDebug.h; sourceTree = "<group>"; };
    638BC1FB0FC932E000CE7600 /* AIObjectDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIObjectDebug.m; path = Source/AIObjectDebug.m; sourceTree = "<group>"; };
    - 639DFB440F981C7C003C9A32 /* NSCalendarDate+ISO8601Parsing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCalendarDate+ISO8601Parsing.h"; path = "Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.h"; sourceTree = "<group>"; };
    63A3A9670F9C35D3006C9CB0 /* AIUtilities_Framework.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = AIUtilities_Framework.plist; sourceTree = "<group>"; };
    63A3A9690F9C35D3006C9CB0 /* ca */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
    63A3A96A0F9C35D3006C9CB0 /* cs */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
    @@ -5167,8 +5149,6 @@
    31FA7F100D4A75D000ABE634 /* TestRichTextCoercion.m */,
    634BCD1D0DDC1542005AF1C2 /* TestMutableStringAdditions.h */,
    634BCD1E0DDC1542005AF1C2 /* TestMutableStringAdditions.m */,
    - 31DDDA7812BDD6E90048F6C0 /* TestScandate.h */,
    - 31DDDA7912BDD6E90048F6C0 /* TestScandate.m */,
    31034EFD0C8142680003F5AA /* TestStringAdditions.h */,
    31034EFE0C8142680003F5AA /* TestStringAdditions.m */,
    31034F0B0C8142720003F5AA /* UTF8Snowman.txt */,
    @@ -5595,7 +5575,6 @@
    3459CBE00A25FFA100ECC256 /* Log Indexing */ = {
    isa = PBXGroup;
    children = (
    - 639DFB440F981C7C003C9A32 /* NSCalendarDate+ISO8601Parsing.h */,
    3459CBEA0A25FFBE00ECC256 /* GetMetadataForHTMLLog.h */,
    3459CBE90A25FFBE00ECC256 /* GetMetadataForHTMLLog.m */,
    3459CBEC0A25FFBF00ECC256 /* AdiumSpotlightImporter.h */,
    @@ -7202,8 +7181,6 @@
    349B359E0A5F2231008BE092 /* Views and Cells */,
    3456231F0A3771E100E7FC97 /* AIChatLog.h */,
    3456231E0A3771E100E7FC97 /* AIChatLog.m */,
    - 31DDDA6E12BDD5CE0048F6C0 /* scandate.h */,
    - 31DDDA6F12BDD5CE0048F6C0 /* scandate.m */,
    345623250A3771F400E7FC97 /* AILogToGroup.h */,
    345623240A3771F300E7FC97 /* AILogToGroup.m */,
    345623270A3771F400E7FC97 /* AILogFromGroup.h */,
    @@ -7216,8 +7193,6 @@
    34F849570A4AF04D0002A017 /* AILogFileUpgradeWindowController.m */,
    34B5E25D0A7C94D3005186E6 /* AILogDateFormatter.h */,
    34B5E25E0A7C94D3005186E6 /* AILogDateFormatter.m */,
    - 340D09FF0A7DD1F40059A3AF /* AICalendarDate.h */,
    - 340D0A000A7DD1F40059A3AF /* AICalendarDate.m */,
    );
    name = "Log Viewer";
    sourceTree = "<group>";
    @@ -7901,6 +7876,8 @@
    6334FFA90F9C14C1003C77A9 /* Additions */ = {
    isa = PBXGroup;
    children = (
    + 5A914165175BD960002139D5 /* ISO8601DateFormatter.h */,
    + 5A914164175BD960002139D5 /* ISO8601DateFormatter.m */,
    6334FFAC0F9C14C1003C77A9 /* AIApplicationAdditions.h */,
    6334FFAD0F9C14C1003C77A9 /* AIApplicationAdditions.m */,
    6334FFAE0F9C14C1003C77A9 /* AIArrayAdditions.h */,
    @@ -7973,10 +7950,6 @@
    6334FFF30F9C14C1003C77A9 /* AIFontAdditions.h */,
    6334FFF40F9C14C1003C77A9 /* OWAddressBookAdditions.h */,
    6334FFF50F9C14C1003C77A9 /* OWAddressBookAdditions.m */,
    - 6334FFF60F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.h */,
    - 6334FFF70F9C14C1003C77A9 /* NSCalendarDate+ISO8601Parsing.m */,
    - 6334FFF80F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.h */,
    - 6334FFF90F9C14C1003C77A9 /* NSCalendarDate+ISO8601Unparsing.m */,
    6334FFFA0F9C14C1003C77A9 /* AIWindowControllerAdditions.h */,
    6334FFFB0F9C14C1003C77A9 /* AIWindowControllerAdditions.m */,
    6334FFFC0F9C14C1003C77A9 /* AIPasteboardAdditions.h */,
    @@ -8035,8 +8008,6 @@
    633D4FBB0F9D30FA004F491E /* External Sources */ = {
    isa = PBXGroup;
    children = (
    - 633D4FBC0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.h */,
    - 633D4FBD0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.m */,
    );
    name = "External Sources";
    sourceTree = "<group>";
    @@ -9284,8 +9255,6 @@
    633400B90F9C14C2003C77A9 /* AIViewAdditions.h in Headers */,
    633400BB0F9C14C2003C77A9 /* AIWindowAdditions.h in Headers */,
    633400BF0F9C14C2003C77A9 /* OWAddressBookAdditions.h in Headers */,
    - 633400C10F9C14C2003C77A9 /* NSCalendarDate+ISO8601Parsing.h in Headers */,
    - 633400C30F9C14C2003C77A9 /* NSCalendarDate+ISO8601Unparsing.h in Headers */,
    633400C50F9C14C2003C77A9 /* AIWindowControllerAdditions.h in Headers */,
    633400C70F9C14C2003C77A9 /* AIPasteboardAdditions.h in Headers */,
    633400D00F9C14E0003C77A9 /* AIUtilities.framework_Prefix.pch in Headers */,
    @@ -9298,6 +9267,7 @@
    5ACF27391392C74A004B6AEF /* AIHighlightingTextField.h in Headers */,
    5ACF273B1392C74A004B6AEF /* AIPreferenceCollectionView.h in Headers */,
    5AA2A0EE14B3EFF500B4DB65 /* AIOSCompatibility.h in Headers */,
    + 5A914167175BD960002139D5 /* ISO8601DateFormatter.h in Headers */,
    );
    runOnlyForDeploymentPostprocessing = 0;
    };
    @@ -10139,8 +10109,6 @@
    318EA69C0D7A659900EDB105 /* TestColorAdditions.m in Sources */,
    634BCD1F0DDC1542005AF1C2 /* TestMutableStringAdditions.m in Sources */,
    3107D5250F63134F0051DDD5 /* TestAttributedStringAdditions.m in Sources */,
    - 31DDDA7112BDD5CE0048F6C0 /* scandate.m in Sources */,
    - 31DDDA7A12BDD6E90048F6C0 /* TestScandate.m in Sources */,
    );
    runOnlyForDeploymentPostprocessing = 0;
    };
    @@ -10508,7 +10476,6 @@
    34DC88810A7EEE2F003E1636 /* AdiumSpeech.m in Sources */,
    34DC88840A7EEE2F003E1636 /* AdiumIdleManager.m in Sources */,
    34DC88880A7EEE2F003E1636 /* XtrasInstaller.m in Sources */,
    - 3419DF3B0A8119BF00C3FC68 /* AICalendarDate.m in Sources */,
    34DC12FC0A81415D00D710F3 /* ESApplescriptabilityController.m in Sources */,
    34DC13040A81417600D710F3 /* AILoginWindowController.m in Sources */,
    C44BA7830AAB696400C7504F /* SetupAssistantBoxBackgroundView.m in Sources */,
    @@ -10580,7 +10547,6 @@
    76C1AF9C125A906A00D269A9 /* AIAdiumURLProtocol.m in Sources */,
    5A94397B1279ECB800FDD81D /* AIImgurImageUploader.m in Sources */,
    349062A2127F7E6900FC313F /* AITemporaryIRCAccountWindowController.m in Sources */,
    - 5A80508512BE9F00007CDC1B /* scandate.m in Sources */,
    1154F50B12E1476900B8CA27 /* AILogByAccountWindowController.m in Sources */,
    766ABAB61306D1020049FFB7 /* AIUnreadMessagesTooltip.m in Sources */,
    5A5F8BBD12D560E400019727 /* AIDockNameOverlay.m in Sources */,
    @@ -10800,8 +10766,6 @@
    633400BA0F9C14C2003C77A9 /* AIViewAdditions.m in Sources */,
    633400BC0F9C14C2003C77A9 /* AIWindowAdditions.m in Sources */,
    633400C00F9C14C2003C77A9 /* OWAddressBookAdditions.m in Sources */,
    - 633400C20F9C14C2003C77A9 /* NSCalendarDate+ISO8601Parsing.m in Sources */,
    - 633400C40F9C14C2003C77A9 /* NSCalendarDate+ISO8601Unparsing.m in Sources */,
    633400C60F9C14C2003C77A9 /* AIWindowControllerAdditions.m in Sources */,
    633400C80F9C14C2003C77A9 /* AIPasteboardAdditions.m in Sources */,
    63BB1CC90F9EDDB600424B80 /* AISharedWriterQueue.m in Sources */,
    @@ -10810,6 +10774,7 @@
    9718DF46136A2C5200013760 /* AIImageCollectionView.m in Sources */,
    5ACF273A1392C74A004B6AEF /* AIHighlightingTextField.m in Sources */,
    5ACF273C1392C74A004B6AEF /* AIPreferenceCollectionView.m in Sources */,
    + 5A914166175BD960002139D5 /* ISO8601DateFormatter.m in Sources */,
    );
    runOnlyForDeploymentPostprocessing = 0;
    };
    @@ -10821,8 +10786,7 @@
    633D4FB70F9D30E3004F491E /* GetMetadataForFile.m in Sources */,
    633D4FB80F9D30E3004F491E /* GetMetadataForHTMLLog.m in Sources */,
    633D4FB90F9D30E3004F491E /* main.c in Sources */,
    - 633D4FBE0F9D30FA004F491E /* NSCalendarDate+ISO8601Parsing.m in Sources */,
    - 5A804FEA12BE9E84007CDC1B /* scandate.m in Sources */,
    + 5A914168175BD97F002139D5 /* ISO8601DateFormatter.m in Sources */,
    );
    runOnlyForDeploymentPostprocessing = 0;
    };
    --- a/Frameworks/AIUtilities Framework/Source/AIDateAdditions.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Frameworks/AIUtilities Framework/Source/AIDateAdditions.h Mon Jun 03 17:17:12 2013 -0400
    @@ -35,5 +35,6 @@
    hours:(out NSInteger *)outHours
    minutes:(out NSInteger *)outMinutes
    seconds:(out NSTimeInterval *)outSeconds;
    ++ (BOOL)isDate:(NSDate *)date1 sameDayAsDate:(NSDate *)date2;
    @end
    --- a/Frameworks/AIUtilities Framework/Source/AIDateAdditions.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Frameworks/AIUtilities Framework/Source/AIDateAdditions.m Mon Jun 03 17:17:12 2013 -0400
    @@ -42,4 +42,15 @@
    if (outWeeks) *outWeeks = workInterval;
    }
    ++ (BOOL)isDate:(NSDate *)date1 sameDayAsDate:(NSDate *)date2
    +{
    + NSCalendar *calendar = [NSCalendar currentCalendar];
    +
    + unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    + NSDateComponents *comp1 = [calendar components:unitFlags fromDate:date1];
    + NSDateComponents *comp2 = [calendar components:unitFlags fromDate:date2];
    +
    + return (comp1.day == comp2.day && comp1.month == comp2.month && comp1.year == comp2.year);
    +}
    +
    @end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.h Mon Jun 03 17:17:12 2013 -0400
    @@ -0,0 +1,79 @@
    +/*ISO8601DateFormatter.h
    + *
    + *Created by Peter Hosey on 2009-04-11.
    + *Copyright 2009 Peter Hosey. All rights reserved.
    + */
    +
    +#import <Foundation/Foundation.h>
    +
    +/*This class converts dates to and from ISO 8601 strings. A good introduction to ISO 8601: <http://www.cl.cam.ac.uk/~mgk25/iso-time.html>
    + *
    + *Parsing can be done strictly, or not. When you parse loosely, leading whitespace is ignored, as is anything after the date.
    + *The loose parser will return an NSDate for this string: @" \t\r\n\f\t 2006-03-02!!!"
    + *Leading non-whitespace will not be ignored; the string will be rejected, and nil returned. See the README that came with this addition.
    + *
    + *The strict parser will only accept a string if the date is the entire string. The above string would be rejected immediately, solely on these grounds.
    + *Also, the loose parser provides some extensions that the strict parser doesn't.
    + *For example, the standard says for "-DDD" (an ordinal date in the implied year) that the logical representation (meaning, hierarchically) would be "--DDD", but because that extra hyphen is "superfluous", it was omitted.
    + *The loose parser will accept the extra hyphen; the strict parser will not.
    + *A full list of these extensions is in the README file.
    + */
    +
    +/*The format to either expect or produce.
    + *Calendar format is YYYY-MM-DD.
    + *Ordinal format is YYYY-DDD, where DDD ranges from 1 to 366; for example, 2009-32 is 2009-02-01.
    + *Week format is YYYY-Www-D, where ww ranges from 1 to 53 (the 'W' is literal) and D ranges from 1 to 7; for example, 2009-W05-07.
    + */
    +enum {
    + ISO8601DateFormatCalendar,
    + ISO8601DateFormatOrdinal,
    + ISO8601DateFormatWeek,
    +};
    +typedef NSUInteger ISO8601DateFormat;
    +
    +//The default separator for time values. Currently, this is ':'.
    +extern unichar ISO8601DefaultTimeSeparatorCharacter;
    +
    +@interface ISO8601DateFormatter: NSFormatter
    +{
    + NSString *lastUsedFormatString;
    + NSDateFormatter *unparsingFormatter;
    +
    + NSCalendar *parsingCalendar, *unparsingCalendar;
    +
    + NSTimeZone *defaultTimeZone;
    + ISO8601DateFormat format;
    + unichar timeSeparator;
    + BOOL includeTime;
    + BOOL parsesStrictly;
    +}
    +
    +//Call this if you get a memory warning.
    ++ (void) purgeGlobalCaches;
    +
    +@property(nonatomic, retain) NSTimeZone *defaultTimeZone;
    +
    +#pragma mark Parsing
    +
    +//As a formatter, this object converts strings to dates.
    +
    +@property BOOL parsesStrictly;
    +
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string;
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone;
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange;
    +
    +- (NSDate *) dateFromString:(NSString *)string;
    +- (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone;
    +- (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange;
    +
    +#pragma mark Unparsing
    +
    +@property ISO8601DateFormat format;
    +@property BOOL includeTime;
    +@property unichar timeSeparator;
    +
    +- (NSString *) stringFromDate:(NSDate *)date;
    +- (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
    +
    +@end
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/Frameworks/AIUtilities Framework/Source/ISO8601DateFormatter.m Mon Jun 03 17:17:12 2013 -0400
    @@ -0,0 +1,895 @@
    +/*ISO8601DateFormatter.m
    + *
    + *Created by Peter Hosey on 2009-04-11.
    + *Copyright 2009 Peter Hosey. All rights reserved.
    + */
    +
    +#import <Foundation/Foundation.h>
    +#import "ISO8601DateFormatter.h"
    +
    +#ifndef DEFAULT_TIME_SEPARATOR
    +# define DEFAULT_TIME_SEPARATOR ':'
    +#endif
    +unichar ISO8601DefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR;
    +
    +//Unicode date formats.
    +#define ISO_CALENDAR_DATE_FORMAT @"yyyy-MM-dd"
    +//#define ISO_WEEK_DATE_FORMAT @"YYYY-'W'ww-ee" //Doesn't actually work because NSDateComponents counts the weekday starting at 1.
    +#define ISO_ORDINAL_DATE_FORMAT @"yyyy-DDD"
    +#define ISO_TIME_FORMAT @"HH:mm:ss"
    +#define ISO_TIME_WITH_TIMEZONE_FORMAT ISO_TIME_FORMAT @"Z"
    +//printf formats.
    +#define ISO_TIMEZONE_UTC_FORMAT @"Z"
    +#define ISO_TIMEZONE_OFFSET_FORMAT @"%+.2d%.2d"
    +
    +@interface ISO8601DateFormatter(UnparsingPrivate)
    +
    +- (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep;
    +
    +- (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone;
    +- (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone;
    +
    +@end
    +
    +static NSMutableDictionary *timeZonesByOffset;
    +
    +@implementation ISO8601DateFormatter
    +
    ++ (void) initialize {
    + if (!timeZonesByOffset) {
    + timeZonesByOffset = [[NSMutableDictionary alloc] init];
    + }
    +}
    +
    ++ (void) purgeGlobalCaches {
    + NSMutableDictionary *oldCache = timeZonesByOffset;
    + timeZonesByOffset = nil;
    + [oldCache release];
    +}
    +
    +- (NSCalendar *) makeCalendarWithDesiredConfiguration {
    + NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
    + calendar.firstWeekday = 2; //Monday
    + calendar.timeZone = [NSTimeZone defaultTimeZone];
    + return calendar;
    +}
    +
    +- (id) init {
    + if ((self = [super init])) {
    + parsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain];
    + unparsingCalendar = [[self makeCalendarWithDesiredConfiguration] retain];
    +
    + format = ISO8601DateFormatCalendar;
    + timeSeparator = ISO8601DefaultTimeSeparatorCharacter;
    + includeTime = NO;
    + parsesStrictly = NO;
    + }
    + return self;
    +}
    +- (void) dealloc {
    + [defaultTimeZone release];
    +
    + [unparsingFormatter release];
    + [lastUsedFormatString release];
    + [parsingCalendar release];
    + [unparsingCalendar release];
    +
    + [super dealloc];
    +}
    +
    +@synthesize defaultTimeZone;
    +- (void) setDefaultTimeZone:(NSTimeZone *)tz {
    + if (defaultTimeZone != tz) {
    + [defaultTimeZone release];
    + defaultTimeZone = [tz retain];
    +
    + unparsingCalendar.timeZone = defaultTimeZone;
    + }
    +}
    +
    +//The following properties are only here because GCC doesn't like @synthesize in category implementations.
    +
    +#pragma mark Parsing
    +
    +@synthesize parsesStrictly;
    +
    +static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits);
    +static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits);
    +static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next);
    +static double read_double(const unsigned char *str, const unsigned char **next);
    +static BOOL is_leap_year(NSUInteger year);
    +
    +/*Valid ISO 8601 date formats:
    + *
    + *YYYYMMDD
    + *YYYY-MM-DD
    + *YYYY-MM
    + *YYYY
    + *YY //century
    + * //Implied century: YY is 00-99
    + * YYMMDD
    + * YY-MM-DD
    + * -YYMM
    + * -YY-MM
    + * -YY
    + * //Implied year
    + * --MMDD
    + * --MM-DD
    + * --MM
    + * //Implied year and month
    + * ---DD
    + * //Ordinal dates: DDD is the number of the day in the year (1-366)
    + *YYYYDDD
    + *YYYY-DDD
    + * YYDDD
    + * YY-DDD
    + * -DDD
    + * //Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
    + *yyyyWwwd
    + *yyyy-Www-d
    + *yyyyWww
    + *yyyy-Www
    + *yyWwwd
    + *yy-Www-d
    + *yyWww
    + *yy-Www
    + * //Year of the implied decade
    + *-yWwwd
    + *-y-Www-d
    + *-yWww
    + *-y-Www
    + * //Week and day of implied year
    + * -Wwwd
    + * -Www-d
    + * //Week only of implied year
    + * -Www
    + * //Day only of implied week
    + * -W-d
    + */
    +
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string {
    + return [self dateComponentsFromString:string timeZone:NULL];
    +}
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone {
    + return [self dateComponentsFromString:string timeZone:outTimeZone range:NULL];
    +}
    +- (NSDateComponents *) dateComponentsFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange {
    + NSDate *now = [NSDate date];
    +
    + NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
    + NSDateComponents *nowComponents = [parsingCalendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:now];
    +
    + NSUInteger
    + //Date
    + year,
    + month_or_week = 0U,
    + day = 0U,
    + //Time
    + hour = 0U;
    + NSTimeInterval
    + minute = 0.0,
    + second = 0.0;
    + //Time zone
    + NSInteger tz_hour = 0;
    + NSInteger tz_minute = 0;
    +
    + enum {
    + monthAndDate,
    + week,
    + dateOnly
    + } dateSpecification = monthAndDate;
    +
    + BOOL strict = self.parsesStrictly;
    + unichar timeSep = self.timeSeparator;
    +
    + if (strict) timeSep = ISO8601DefaultTimeSeparatorCharacter;
    + NSAssert(timeSep != '\0', @"Time separator must not be NUL.");
    +
    + BOOL isValidDate = ([string length] > 0U);
    + NSTimeZone *timeZone = nil;
    +
    + const unsigned char *ch = (const unsigned char *)[string UTF8String];
    +
    + NSRange range = { 0U, 0U };
    + const unsigned char *start_of_date = NULL;
    + if (strict && isspace(*ch)) {
    + range.location = NSNotFound;
    + isValidDate = NO;
    + } else {
    + //Skip leading whitespace.
    + NSUInteger i = 0U;
    + for(NSUInteger len = strlen((const char *)ch); i < len; ++i) {
    + if (!isspace(ch[i]))
    + break;
    + }
    +
    + range.location = i;
    + ch += i;
    + start_of_date = ch;
    +
    + NSUInteger segment;
    + NSUInteger num_leading_hyphens = 0U, num_digits = 0U;
    +
    + if (*ch == 'T') {
    + //There is no date here, only a time. Set the date to now; then we'll parse the time.
    + isValidDate = isdigit(*++ch);
    +
    + year = nowComponents.year;
    + month_or_week = nowComponents.month;
    + day = nowComponents.day;
    + } else {
    + while(*ch == '-') {
    + ++num_leading_hyphens;
    + ++ch;
    + }
    +
    + segment = read_segment(ch, &ch, &num_digits);
    + switch(num_digits) {
    + case 0:
    + if (*ch == 'W') {
    + if ((ch[1] == '-') && isdigit(ch[2]) && ((num_leading_hyphens == 1U) || ((num_leading_hyphens == 2U) && !strict))) {
    + year = nowComponents.year;
    + month_or_week = 1U;
    + ch += 2;
    + goto parseDayAfterWeek;
    + } else if (num_leading_hyphens == 1U) {
    + year = nowComponents.year;
    + goto parseWeekAndDay;
    + } else
    + isValidDate = NO;
    + } else
    + isValidDate = NO;
    + break;
    +
    + case 8: //YYYY MM DD
    + if (num_leading_hyphens > 0U)
    + isValidDate = NO;
    + else {
    + day = segment % 100U;
    + segment /= 100U;
    + month_or_week = segment % 100U;
    + year = segment / 100U;
    + }
    + break;
    +
    + case 6: //YYMMDD (implicit century)
    + if (num_leading_hyphens > 0U)
    + isValidDate = NO;
    + else {
    + day = segment % 100U;
    + segment /= 100U;
    + month_or_week = segment % 100U;
    + year = nowComponents.year;
    + year -= (year % 100U);
    + year += segment / 100U;
    + }
    + break;
    +
    + case 4:
    + switch(num_leading_hyphens) {
    + case 0: //YYYY
    + year = segment;
    +
    + if (*ch == '-') ++ch;
    +
    + if (!isdigit(*ch)) {
    + if (*ch == 'W')
    + goto parseWeekAndDay;
    + else
    + month_or_week = day = 1U;
    + } else {
    + segment = read_segment(ch, &ch, &num_digits);
    + switch(num_digits) {
    + case 4: //MMDD
    + day = segment % 100U;
    + month_or_week = segment / 100U;
    + break;
    +
    + case 2: //MM
    + month_or_week = segment;
    +
    + if (*ch == '-') ++ch;
    + if (!isdigit(*ch))
    + day = 1U;
    + else
    + day = read_segment(ch, &ch, NULL);
    + break;
    +
    + case 3: //DDD
    + day = segment % 1000U;
    + dateSpecification = dateOnly;
    + if (strict && (day > (365U + is_leap_year(year))))
    + isValidDate = NO;
    + break;
    +
    + default:
    + isValidDate = NO;
    + }
    + }
    + break;
    +
    + case 1: //YYMM
    + month_or_week = segment % 100U;
    + year = segment / 100U;
    +
    + if (*ch == '-') ++ch;
    + if (!isdigit(*ch))
    + day = 1U;
    + else
    + day = read_segment(ch, &ch, NULL);
    +
    + break;
    +
    + case 2: //MMDD
    + day = segment % 100U;
    + month_or_week = segment / 100U;
    + year = nowComponents.year;
    +
    + break;
    +
    + default:
    + isValidDate = NO;
    + } //switch(num_leading_hyphens) (4 digits)
    + break;
    +
    + case 1:
    + if (strict) {
    + //Two digits only - never just one.
    + if (num_leading_hyphens == 1U) {
    + if (*ch == '-') ++ch;
    + if (*++ch == 'W') {
    + year = nowComponents.year;
    + year -= (year % 10U);
    + year += segment;
    + goto parseWeekAndDay;
    + } else
    + isValidDate = NO;
    + } else
    + isValidDate = NO;
    + break;
    + }
    + case 2:
    + switch(num_leading_hyphens) {
    + case 0:
    + if (*ch == '-') {
    + //Implicit century
    + year = nowComponents.year;
    + year -= (year % 100U);
    + year += segment;
    +
    + if (*++ch == 'W')
    + goto parseWeekAndDay;
    + else if (!isdigit(*ch)) {
    + goto centuryOnly;
    + } else {
    + //Get month and/or date.
    + segment = read_segment_4digits(ch, &ch, &num_digits);
    + NSLog(@"(%@) parsing month; segment is %lu and ch is %s", string, (unsigned long)segment, ch);
    + switch(num_digits) {
    + case 4: //YY-MMDD
    + day = segment % 100U;
    + month_or_week = segment / 100U;
    + break;
    +
    + case 1: //YY-M; YY-M-DD (extension)
    + if (strict) {
    + isValidDate = NO;
    + break;
    + }
    + case 2: //YY-MM; YY-MM-DD
    + month_or_week = segment;
    + if (*ch == '-') {
    + if (isdigit(*++ch))
    + day = read_segment_2digits(ch, &ch);
    + else
    + day = 1U;
    + } else
    + day = 1U;
    + break;
    +
    + case 3: //Ordinal date.
    + day = segment;
    + dateSpecification = dateOnly;
    + break;
    + }
    + }
    + } else if (*ch == 'W') {
    + year = nowComponents.year;
    + year -= (year % 100U);
    + year += segment;
    +
    + parseWeekAndDay: //*ch should be 'W' here.
    + if (!isdigit(*++ch)) {
    + //Not really a week-based date; just a year followed by '-W'.
    + if (strict)
    + isValidDate = NO;
    + else
    + month_or_week = day = 1U;
    + } else {
    + month_or_week = read_segment_2digits(ch, &ch);
    + if (*ch == '-') ++ch;
    + parseDayAfterWeek:
    + day = isdigit(*ch) ? read_segment_2digits(ch, &ch) : 1U;
    + dateSpecification = week;
    + }
    + } else {
    + //Century only. Assume current year.
    + centuryOnly:
    + year = segment * 100U + nowComponents.year % 100U;
    + month_or_week = day = 1U;
    + }
    + break;
    +
    + case 1:; //-YY; -YY-MM (implicit century)
    + NSLog(@"(%@) found %lu digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %lu", string, (unsigned long)num_digits, (unsigned long)segment);
    + NSUInteger current_year = nowComponents.year;
    + NSUInteger current_century = (current_year % 100U);
    + year = segment + (current_year - current_century);
    + if (num_digits == 1U) //implied decade
    + year += current_century - (current_year % 10U);
    +
    + if (*ch == '-') {
    + ++ch;
    + month_or_week = read_segment_2digits(ch, &ch);
    + NSLog(@"(%@) month is %lu", string, (unsigned long)month_or_week);
    + }
    +
    + day = 1U;
    + break;
    +
    + case 2: //--MM; --MM-DD
    + year = nowComponents.year;
    + month_or_week = segment;
    + if (*ch == '-') {
    + ++ch;
    + day = read_segment_2digits(ch, &ch);
    + }
    + break;
    +
    + case 3: //---DD
    + year = nowComponents.year;
    + month_or_week = nowComponents.month;
    + day = segment;
    + break;
    +
    + default:
    + isValidDate = NO;
    + } //switch(num_leading_hyphens) (2 digits)
    + break;
    +
    + case 7: //YYYY DDD (ordinal date)
    + if (num_leading_hyphens > 0U)
    + isValidDate = NO;
    + else {
    + day = segment % 1000U;
    + year = segment / 1000U;
    + dateSpecification = dateOnly;
    + if (strict && (day > (365U + is_leap_year(year))))
    + isValidDate = NO;
    + }
    + break;
    +
    + case 3: //--DDD (ordinal date, implicit year)
    + //Technically, the standard only allows one hyphen. But it says that two hyphens is the logical implementation, and one was dropped for brevity. So I have chosen to allow the missing hyphen.
    + if ((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict))
    + isValidDate = NO;
    + else {
    + day = segment;
    + year = nowComponents.year;
    + dateSpecification = dateOnly;
    + if (strict && (day > (365U + is_leap_year(year))))
    + isValidDate = NO;
    + }
    + break;
    +
    + default:
    + isValidDate = NO;
    + }
    + }
    +
    + if (isValidDate) {
    + if (isspace(*ch) || (*ch == 'T')) ++ch;
    +
    + if (isdigit(*ch)) {
    + hour = read_segment_2digits(ch, &ch);
    + if (*ch == timeSep) {
    + ++ch;
    + if ((timeSep == ',') || (timeSep == '.')) {
    + //We can't do fractional minutes when '.' is the segment separator.
    + //Only allow whole minutes and whole seconds.
    + minute = read_segment_2digits(ch, &ch);
    + if (*ch == timeSep) {
    + ++ch;
    + second = read_segment_2digits(ch, &ch);
    + }
    + } else {
    + //Allow a fractional minute.
    + //If we don't get a fraction, look for a seconds segment.
    + //Otherwise, the fraction of a minute is the seconds.
    + minute = read_double(ch, &ch);
    + second = modf(minute, &minute);
    + if (second > DBL_EPSILON)
    + second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30).
    + else if (*ch == timeSep) {
    + ++ch;
    + second = read_double(ch, &ch);
    + }
    + }
    + }
    +
    + if (!strict) {
    + if (isspace(*ch)) ++ch;
    + }
    +
    + switch(*ch) {
    + case 'Z':
    + timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
    + break;
    +
    + case '+':
    + case '-':;
    + BOOL negative = (*ch == '-');
    + if (isdigit(*++ch)) {
    + //Read hour offset.
    + segment = *ch - '0';
    + if (isdigit(*++ch)) {
    + segment *= 10U;
    + segment += *(ch++) - '0';
    + }
    + tz_hour = (NSInteger)segment;
    + if (negative) tz_hour = -tz_hour;
    +
    + //Optional separator.
    + if (*ch == timeSep) ++ch;
    +
    + if (isdigit(*ch)) {
    + //Read minute offset.
    + segment = *ch - '0';
    + if (isdigit(*++ch)) {
    + segment *= 10U;
    + segment += *ch - '0';
    + }
    + tz_minute = segment;
    + if (negative) tz_minute = -tz_minute;
    + }
    +
    + NSInteger timeZoneOffset = (tz_hour * 3600) + (tz_minute * 60);
    + NSNumber *offsetNum = [NSNumber numberWithInteger:timeZoneOffset];
    + timeZone = [timeZonesByOffset objectForKey:offsetNum];
    + if (!timeZone) {
    + timeZone = [NSTimeZone timeZoneForSecondsFromGMT:timeZoneOffset];
    + if (timeZone)
    + [timeZonesByOffset setObject:timeZone forKey:offsetNum];
    + }
    + }
    + }
    + }
    + }
    +
    + if (isValidDate) {
    + components.year = year;
    + components.day = day;
    + components.hour = hour;
    + components.minute = (NSInteger)minute;
    + components.second = (NSInteger)second;
    +
    + switch(dateSpecification) {
    + case monthAndDate:
    + components.month = month_or_week;
    + break;
    +
    + case week:;
    + //Adapted from <http://personal.ecu.edu/mccartyr/ISOwdALG.txt>.
    + //This works by converting the week date into an ordinal date, then letting the next case handle it.
    + NSUInteger prevYear = year - 1U;
    + NSUInteger YY = prevYear % 100U;
    + NSUInteger C = prevYear - YY;
    + NSUInteger G = YY + YY / 4U;
    + NSUInteger isLeapYear = (((C / 100U) % 4U) * 5U);
    + NSUInteger Jan1Weekday = (isLeapYear + G) % 7U;
    + enum { monday, tuesday, wednesday, thursday/*, friday, saturday, sunday*/ };
    + components.day = ((8U - Jan1Weekday) + (7U * (Jan1Weekday > thursday))) + (day - 1U) + (7U * (month_or_week - 2));
    +
    + case dateOnly: //An "ordinal date".
    + break;
    + }
    + }
    + } //if (!(strict && isdigit(ch[0])))
    +
    + if (outRange) {
    + if (isValidDate)
    + range.length = ch - start_of_date;
    + else
    + range.location = NSNotFound;
    +
    + *outRange = range;
    + }
    + if (outTimeZone) {
    + *outTimeZone = timeZone;
    + }
    +
    + return components;
    +}
    +
    +- (NSDate *) dateFromString:(NSString *)string {
    + return [self dateFromString:string timeZone:NULL];
    +}
    +- (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone {
    + return [self dateFromString:string timeZone:outTimeZone range:NULL];
    +}
    +- (NSDate *) dateFromString:(NSString *)string timeZone:(out NSTimeZone **)outTimeZone range:(out NSRange *)outRange {
    + NSTimeZone *timeZone = nil;
    + NSDateComponents *components = [self dateComponentsFromString:string timeZone:&timeZone range:outRange];
    + if (outTimeZone)
    + *outTimeZone = timeZone;
    + parsingCalendar.timeZone = timeZone;
    +
    + return [parsingCalendar dateFromComponents:components];
    +}
    +
    +- (BOOL)getObjectValue:(id *)outValue forString:(NSString *)string errorDescription:(NSString **)error {
    + NSDate *date = [self dateFromString:string];
    + if (outValue)
    + *outValue = date;
    + return (date != nil);
    +}
    +
    +#pragma mark Unparsing
    +
    +@synthesize format;
    +@synthesize includeTime;
    +@synthesize timeSeparator;
    +
    +- (NSString *) replaceColonsInString:(NSString *)timeFormat withTimeSeparator:(unichar)timeSep {
    + if (timeSep != ':') {
    + NSMutableString *timeFormatMutable = [[timeFormat mutableCopy] autorelease];
    + [timeFormatMutable replaceOccurrencesOfString:@":"
    + withString:[NSString stringWithCharacters:&timeSep length:1U]
    + options:NSBackwardsSearch | NSLiteralSearch
    + range:(NSRange){ 0UL, [timeFormat length] }];
    + timeFormat = timeFormatMutable;
    + }
    + return timeFormat;
    +}
    +
    +- (NSString *) stringFromDate:(NSDate *)date {
    + NSTimeZone *timeZone = self.defaultTimeZone;
    + if (!timeZone) timeZone = [NSTimeZone defaultTimeZone];
    + return [self stringFromDate:date timeZone:timeZone];
    +}
    +
    +- (NSString *) stringFromDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone {
    + switch (self.format) {
    + case ISO8601DateFormatCalendar:
    + return [self stringFromDate:date formatString:ISO_CALENDAR_DATE_FORMAT timeZone:timeZone];
    + case ISO8601DateFormatWeek:
    + return [self weekDateStringForDate:date timeZone:timeZone];
    + case ISO8601DateFormatOrdinal:
    + return [self stringFromDate:date formatString:ISO_ORDINAL_DATE_FORMAT timeZone:timeZone];
    + default:
    + [NSException raise:NSInternalInconsistencyException format:@"self.format was %lu, not calendar (%d), week (%d), or ordinal (%d)", (unsigned long)self.format, ISO8601DateFormatCalendar, ISO8601DateFormatWeek, ISO8601DateFormatOrdinal];
    + return nil;
    + }
    +}
    +
    +- (NSString *) stringFromDate:(NSDate *)date formatString:(NSString *)dateFormat timeZone:(NSTimeZone *)timeZone {
    + if (includeTime)
    + dateFormat = [dateFormat stringByAppendingFormat:@"'T'%@", [self replaceColonsInString:ISO_TIME_FORMAT withTimeSeparator:self.timeSeparator]];
    +
    + unparsingCalendar.timeZone = timeZone;
    +
    + if (dateFormat != lastUsedFormatString) {
    + [unparsingFormatter release];
    + unparsingFormatter = nil;
    +
    + [lastUsedFormatString release];
    + lastUsedFormatString = [dateFormat retain];
    + }
    +
    + if (!unparsingFormatter) {
    + unparsingFormatter = [[NSDateFormatter alloc] init];
    + unparsingFormatter.formatterBehavior = NSDateFormatterBehavior10_4;
    + unparsingFormatter.dateFormat = dateFormat;
    + unparsingFormatter.calendar = unparsingCalendar;
    + }
    +
    + NSString *str = [unparsingFormatter stringForObjectValue:date];
    +
    + if (includeTime) {
    + NSInteger offset = [timeZone secondsFromGMTForDate:date];
    + offset /= 60; //bring down to minutes
    + if (offset == 0)
    + str = [str stringByAppendingString:ISO_TIMEZONE_UTC_FORMAT];
    + else
    + str = [str stringByAppendingFormat:ISO_TIMEZONE_OFFSET_FORMAT, (int)(offset / 60), (int)(offset % 60)];
    + }
    +
    + //Undo the change we made earlier
    + unparsingCalendar.timeZone = self.defaultTimeZone;
    +
    + return str;
    +}
    +
    +- (NSString *) stringForObjectValue:(id)value {
    + NSParameterAssert([value isKindOfClass:[NSDate class]]);
    +
    + return [self stringFromDate:(NSDate *)value];
    +}
    +
    +/*Adapted from:
    + * Algorithm for Converting Gregorian Dates to ISO 8601 Week Date
    + * Rick McCarty, 1999
    + * http://personal.ecu.edu/mccartyr/ISOwdALG.txt
    + */
    +- (NSString *) weekDateStringForDate:(NSDate *)date timeZone:(NSTimeZone *)timeZone {
    + unparsingCalendar.timeZone = timeZone;
    + NSDateComponents *components = [unparsingCalendar components:NSYearCalendarUnit | NSWeekdayCalendarUnit | NSDayCalendarUnit fromDate:date];
    +
    + //Determine the ordinal date.
    + NSDateComponents *startOfYearComponents = [unparsingCalendar components:NSYearCalendarUnit fromDate:date];
    + startOfYearComponents.month = 1;
    + startOfYearComponents.day = 1;
    + NSDateComponents *ordinalComponents = [unparsingCalendar components:NSDayCalendarUnit fromDate:[unparsingCalendar dateFromComponents:startOfYearComponents] toDate:date options:0];
    + ordinalComponents.day += 1;
    +
    + enum {
    + monday, tuesday, wednesday, thursday, friday, saturday, sunday
    + };
    + enum {
    + january = 1, february, march,
    + april, may, june,
    + july, august, september,
    + october, november, december
    + };
    +
    + NSInteger year = components.year;
    + NSInteger week = 0;
    + //The old unparser added 6 to [calendarDate dayOfWeek], which was zero-based; components.weekday is one-based, so we now add only 5.
    + NSInteger dayOfWeek = (components.weekday + 5) % 7;
    + NSInteger dayOfYear = ordinalComponents.day;
    +
    + NSInteger prevYear = year - 1;
    +
    + BOOL yearIsLeapYear = is_leap_year(year);
    + BOOL prevYearIsLeapYear = is_leap_year(prevYear);
    +
    + NSInteger YY = prevYear % 100;
    + NSInteger C = prevYear - YY;
    + NSInteger G = YY + YY / 4;
    + NSInteger Jan1Weekday = (((((C / 100) % 4) * 5) + G) % 7);
    +
    + NSInteger weekday = ((dayOfYear + Jan1Weekday) - 1) % 7;
    +
    + if((dayOfYear <= (7 - Jan1Weekday)) && (Jan1Weekday > thursday)) {
    + week = 52 + ((Jan1Weekday == friday) || ((Jan1Weekday == saturday) && prevYearIsLeapYear));
    + --year;
    + } else {
    + NSInteger lengthOfYear = 365 + yearIsLeapYear;
    + if((lengthOfYear - dayOfYear) < (thursday - weekday)) {
    + ++year;
    + week = 1;
    + } else {
    + NSInteger J = dayOfYear + (sunday - weekday) + Jan1Weekday;
    + week = J / 7 - (Jan1Weekday > thursday);
    + }
    + }
    +
    + NSString *timeString;
    + if(includeTime) {
    + NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    + unichar timeSep = self.timeSeparator;
    + if (!timeSep) timeSep = ISO8601DefaultTimeSeparatorCharacter;
    + formatter.dateFormat = [self replaceColonsInString:ISO_TIME_WITH_TIMEZONE_FORMAT withTimeSeparator:timeSep];
    +
    + timeString = [formatter stringForObjectValue:date];
    +
    + [formatter release];
    + } else
    + timeString = @"";
    +
    + return [NSString stringWithFormat:@"%lu-W%02lu-%02lu%@", (unsigned long)year, (unsigned long)week, ((unsigned long)dayOfWeek) + 1U, timeString];
    +}
    +
    +@end
    +
    +static NSUInteger read_segment(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) {
    + NSUInteger num_digits = 0U;
    + NSUInteger value = 0U;
    +
    + while(isdigit(*str)) {
    + value *= 10U;
    + value += *str - '0';
    + ++num_digits;
    + ++str;
    + }
    +
    + if (next) *next = str;
    + if (out_num_digits) *out_num_digits = num_digits;
    +
    + return value;
    +}
    +static NSUInteger read_segment_4digits(const unsigned char *str, const unsigned char **next, NSUInteger *out_num_digits) {
    + NSUInteger num_digits = 0U;
    + NSUInteger value = 0U;
    +
    + if (isdigit(*str)) {
    + value += *(str++) - '0';
    + ++num_digits;
    + }
    +
    + if (isdigit(*str)) {
    + value *= 10U;
    + value += *(str++) - '0';
    + ++num_digits;
    + }
    +
    + if (isdigit(*str)) {
    + value *= 10U;
    + value += *(str++) - '0';
    + ++num_digits;
    + }
    +
    + if (isdigit(*str)) {
    + value *= 10U;
    + value += *(str++) - '0';
    + ++num_digits;
    + }
    +
    + if (next) *next = str;
    + if (out_num_digits) *out_num_digits = num_digits;
    +
    + return value;
    +}
    +static NSUInteger read_segment_2digits(const unsigned char *str, const unsigned char **next) {
    + NSUInteger value = 0U;
    +
    + if (isdigit(*str))
    + value += *str - '0';
    +
    + if (isdigit(*++str)) {
    + value *= 10U;
    + value += *(str++) - '0';
    + }
    +
    + if (next) *next = str;
    +
    + return value;
    +}
    +
    +//strtod doesn't support ',' as a separator. This does.
    +static double read_double(const unsigned char *str, const unsigned char **next) {
    + double value = 0.0;
    +
    + if (str) {
    + NSUInteger int_value = 0;
    +
    + while(isdigit(*str)) {
    + int_value *= 10U;
    + int_value += (*(str++) - '0');
    + }
    + value = int_value;
    +
    + if (((*str == ',') || (*str == '.'))) {
    + ++str;
    +
    + register double multiplier, multiplier_multiplier;
    + multiplier = multiplier_multiplier = 0.1;
    +
    + while(isdigit(*str)) {
    + value += (*(str++) - '0') * multiplier;
    + multiplier *= multiplier_multiplier;
    + }
    + }
    + }
    +
    + if (next) *next = str;
    +
    + return value;
    +}
    +
    +static BOOL is_leap_year(NSUInteger year) {
    + return \
    + ((year % 4U) == 0U)
    + && (((year % 100U) != 0U)
    + || ((year % 400U) == 0U));
    +}
    --- a/Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.h Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,47 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import <Foundation/Foundation.h>
    -
    -/*This addition parses ISO 8601 dates. A good introduction: <http://www.cl.cam.ac.uk/~mgk25/iso-time.html>
    - *
    - *Parsing can be done strictly, or not. When you parse loosely, leading whitespace is ignored, as is anything after the date.
    - *The loose parser will return an NSCalendarDate for this string: @" \t\r\n\f\t 2006-03-02!!!"
    - *Leading non-whitespace will not be ignored; the string will be rejected, and nil returned. See the README that came with this addition.
    - *
    - *The strict parser will only accept a string if the date is the entire string. The above string would be rejected immediately, solely on these grounds.
    - *Also, the loose parser provides some extensions that the strict parser doesn't.
    - *For example, the standard says for "-DDD" (an ordinal date in the implied year) that the logical representation (meaning, hierarchically) would be "--DDD", but because that extra hyphen is "superfluous", it was omitted.
    - *The loose parser will accept the extra hyphen; the strict parser will not.
    - *A full list of these extensions is in the README file.
    - */
    -
    -//The default separator for time values. Currently, this is ':'.
    -extern unichar ISO8601ParserDefaultTimeSeparatorCharacter;
    -
    -@interface NSCalendarDate(ISO8601Parsing)
    -
    -//This method is the one that does all the work. All the others are convenience methods.
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict getRange:(out NSRange *)outRange;
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict;
    -
    -//Strictly: NO.
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep getRange:(out NSRange *)outRange;
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep;
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str getRange:(out NSRange *)outRange;
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str;
    -
    -@end
    --- a/Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Parsing.m Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,664 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "NSCalendarDate+ISO8601Parsing.h"
    -
    -#ifndef DEFAULT_TIME_SEPARATOR
    -# define DEFAULT_TIME_SEPARATOR ':'
    -#endif
    -unichar ISO8601ParserDefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR;
    -
    -static unsigned read_segment(const unsigned char *str, const unsigned char **next, unsigned *out_num_digits) {
    - unsigned num_digits = 0U;
    - unsigned value = 0U;
    -
    - while(isdigit(*str)) {
    - value *= 10U;
    - value += *str - '0';
    - ++num_digits;
    - ++str;
    - }
    -
    - if(next) *next = str;
    - if(out_num_digits) *out_num_digits = num_digits;
    -
    - return value;
    -}
    -static unsigned read_segment_4digits(const unsigned char *str, const unsigned char **next, unsigned *out_num_digits) {
    - unsigned num_digits = 0U;
    - unsigned value = 0U;
    -
    - if(isdigit(*str)) {
    - value += *(str++) - '0';
    - ++num_digits;
    - }
    -
    - if(isdigit(*str)) {
    - value *= 10U;
    - value += *(str++) - '0';
    - ++num_digits;
    - }
    -
    - if(isdigit(*str)) {
    - value *= 10U;
    - value += *(str++) - '0';
    - ++num_digits;
    - }
    -
    - if(isdigit(*str)) {
    - value *= 10U;
    - value += *(str++) - '0';
    - ++num_digits;
    - }
    -
    - if(next) *next = str;
    - if(out_num_digits) *out_num_digits = num_digits;
    -
    - return value;
    -}
    -static unsigned read_segment_2digits(const unsigned char *str, const unsigned char **next) {
    - unsigned value = 0U;
    -
    - if(isdigit(*str))
    - value += *str - '0';
    -
    - if(isdigit(*++str)) {
    - value *= 10U;
    - value += *(str++) - '0';
    - }
    -
    - if(next) *next = str;
    -
    - return value;
    -}
    -
    -//strtod doesn't support ',' as a separator. This does.
    -static double read_double(const unsigned char *str, const unsigned char **next) {
    - double value = 0.0;
    -
    - if(str) {
    - unsigned int_value = 0;
    -
    - while(isdigit(*str)) {
    - int_value *= 10U;
    - int_value += (*(str++) - '0');
    - }
    - value = int_value;
    -
    - if(((*str == ',') || (*str == '.'))) {
    - ++str;
    -
    - double multiplier, multiplier_multiplier;
    - multiplier = multiplier_multiplier = 0.1;
    -
    - while(isdigit(*str)) {
    - value += (*(str++) - '0') * multiplier;
    - multiplier *= multiplier_multiplier;
    - }
    - }
    - }
    -
    - if(next) *next = str;
    -
    - return value;
    -}
    -
    -static BOOL is_leap_year(NSInteger year) {
    - return \
    - ((year % 4U) == 0U)
    - && (((year % 100U) != 0U)
    - || ((year % 400U) == 0U));
    -}
    -
    -@implementation NSCalendarDate(ISO8601Parsing)
    -
    -/*Valid ISO 8601 date formats:
    - *
    - *YYYYMMDD
    - *YYYY-MM-DD
    - *YYYY-MM
    - *YYYY
    - *YY //century
    - * //Implied century: YY is 00-99
    - * YYMMDD
    - * YY-MM-DD
    - * -YYMM
    - * -YY-MM
    - * -YY
    - * //Implied year
    - * --MMDD
    - * --MM-DD
    - * --MM
    - * //Implied year and month
    - * ---DD
    - * //Ordinal dates: DDD is the number of the day in the year (1-366)
    - *YYYYDDD
    - *YYYY-DDD
    - * YYDDD
    - * YY-DDD
    - * -DDD
    - * //Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
    - *yyyyWwwd
    - *yyyy-Www-d
    - *yyyyWww
    - *yyyy-Www
    - *yyWwwd
    - *yy-Www-d
    - *yyWww
    - *yy-Www
    - * //Year of the implied decade
    - *-yWwwd
    - *-y-Www-d
    - *-yWww
    - *-y-Www
    - * //Week and day of implied year
    - * -Wwwd
    - * -Www-d
    - * //Week only of implied year
    - * -Www
    - * //Day only of implied week
    - * -W-d
    - */
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict timeSeparator:(unichar)timeSep getRange:(out NSRange *)outRange {
    - if (!str || ![str length]) {
    - if (outRange) {
    - outRange->location = NSNotFound;
    - outRange->length = 0U;
    - }
    -
    - return nil;
    - }
    -
    - NSCalendarDate *now = [NSCalendarDate calendarDate];
    - NSUInteger
    - //Date
    - year = 0U,
    - month_or_week = 0U,
    - day = 0U,
    - //Time
    - hour = 0U;
    - NSTimeInterval
    - minute = 0.0,
    - second = 0.0;
    - //Time zone
    - signed tz_hour = 0;
    - signed tz_minute = 0;
    -
    - enum {
    - monthAndDate,
    - week,
    - dateOnly
    - } dateSpecification = monthAndDate;
    -
    - if(strict) timeSep = ISO8601ParserDefaultTimeSeparatorCharacter;
    - NSAssert(timeSep != '\0', @"Time separator must not be NUL.");
    -
    - BOOL isValidDate = ([str length] > 0U);
    - NSTimeZone *timeZone = nil;
    - NSCalendarDate *date = nil;
    -
    - const unsigned char *ch = (const unsigned char *)[str UTF8String];
    -
    - NSRange range = { 0U, 0U };
    - const unsigned char *start_of_date = 0;
    - if (strict && isspace(*ch)) {
    - range.location = NSNotFound;
    - isValidDate = NO;
    - } else {
    - //Skip leading whitespace.
    - NSUInteger i = 0U;
    - for(NSInteger len = strlen((const char *)ch); i < len; ++i) {
    - if(!isspace(ch[i]))
    - break;
    - }
    -
    - range.location = i;
    - ch += i;
    - start_of_date = ch;
    -
    - unsigned segment;
    - unsigned num_leading_hyphens = 0U, num_digits = 0U;
    -
    - if (*ch == 'T') {
    - //There is no date here, only a time. Set the date to now; then we'll parse the time.
    - isValidDate = isdigit(*++ch);
    -
    - year = [now yearOfCommonEra];
    - month_or_week = [now monthOfYear];
    - day = [now dayOfMonth];
    - } else {
    -
    - while(*ch == '-') {
    - ++num_leading_hyphens;
    - ++ch;
    - }
    -
    - segment = read_segment(ch, &ch, &num_digits);
    - switch (num_digits) {
    - case 0:
    - if(*ch == 'W') {
    - if((ch[1] == '-') && isdigit(ch[2]) && ((num_leading_hyphens == 1U) || ((num_leading_hyphens == 2U) && !strict))) {
    - year = [now yearOfCommonEra];
    - month_or_week = 1U;
    - ch += 2;
    - goto parseDayAfterWeek;
    - } else if(num_leading_hyphens == 1U) {
    - year = [now yearOfCommonEra];
    - goto parseWeekAndDay;
    - } else
    - isValidDate = NO;
    - } else
    - isValidDate = NO;
    - break;
    -
    - case 8: //YYYY MM DD
    - if(num_leading_hyphens > 0U)
    - isValidDate = NO;
    - else {
    - day = segment % 100U;
    - segment /= 100U;
    - month_or_week = segment % 100U;
    - year = segment / 100U;
    - }
    - break;
    -
    - case 6: //YYMMDD (implicit century)
    - if(num_leading_hyphens > 0U)
    - isValidDate = NO;
    - else {
    - day = segment % 100U;
    - segment /= 100U;
    - month_or_week = segment % 100U;
    - year = [now yearOfCommonEra];
    - year -= (year % 100U);
    - year += segment / 100U;
    - }
    - break;
    -
    - case 4:
    - switch(num_leading_hyphens) {
    - case 0: //YYYY
    - year = segment;
    -
    - if(*ch == '-') ++ch;
    -
    - if(!isdigit(*ch)) {
    - if(*ch == 'W')
    - goto parseWeekAndDay;
    - else
    - month_or_week = day = 1U;
    - } else {
    - segment = read_segment(ch, &ch, &num_digits);
    - switch(num_digits) {
    - case 4: //MMDD
    - day = segment % 100U;
    - month_or_week = segment / 100U;
    - break;
    -
    - case 2: //MM
    - month_or_week = segment;
    -
    - if(*ch == '-') ++ch;
    - if(!isdigit(*ch))
    - day = 1U;
    - else
    - day = read_segment(ch, &ch, NULL);
    - break;
    -
    - case 3: //DDD
    - day = segment % 1000U;
    - dateSpecification = dateOnly;
    - if(strict && (day > (365 + is_leap_year(year))))
    - isValidDate = NO;
    - break;
    -
    - default:
    - isValidDate = NO;
    - }
    - }
    - break;
    -
    - case 1: //YYMM
    - month_or_week = segment % 100U;
    - year = segment / 100U;
    -
    - if(*ch == '-') ++ch;
    - if(!isdigit(*ch))
    - day = 1U;
    - else
    - day = read_segment(ch, &ch, NULL);
    -
    - break;
    -
    - case 2: //MMDD
    - day = segment % 100U;
    - month_or_week = segment / 100U;
    - year = [now yearOfCommonEra];
    -
    - break;
    -
    - default:
    - isValidDate = NO;
    - } //switch(num_leading_hyphens) (4 digits)
    - break;
    -
    - case 1:
    - if(strict) {
    - //Two digits only - never just one.
    - if(num_leading_hyphens == 1U) {
    - if(*ch == '-') ++ch;
    - if(*++ch == 'W') {
    - year = [now yearOfCommonEra];
    - year -= (year % 10U);
    - year += segment;
    - goto parseWeekAndDay;
    - } else
    - isValidDate = NO;
    - } else
    - isValidDate = NO;
    - break;
    - }
    - case 2:
    - switch(num_leading_hyphens) {
    - case 0:
    - if(*ch == '-') {
    - //Implicit century
    - year = [now yearOfCommonEra];
    - year -= (year % 100U);
    - year += segment;
    -
    - if(*++ch == 'W')
    - goto parseWeekAndDay;
    - else if(!isdigit(*ch)) {
    - goto centuryOnly;
    - } else {
    - //Get month and/or date.
    - segment = read_segment_4digits(ch, &ch, &num_digits);
    - NSLog(@"(%@) parsing month; segment is %u and ch is %s", str, segment, ch);
    - switch(num_digits) {
    - case 4: //YY-MMDD
    - day = segment % 100U;
    - month_or_week = segment / 100U;
    - break;
    -
    - case 1: //YY-M; YY-M-DD (extension)
    - if(strict) {
    - isValidDate = NO;
    - break;
    - }
    - case 2: //YY-MM; YY-MM-DD
    - month_or_week = segment;
    - if(*ch == '-') {
    - if(isdigit(*++ch))
    - day = read_segment_2digits(ch, &ch);
    - else
    - day = 1U;
    - } else
    - day = 1U;
    - break;
    -
    - case 3: //Ordinal date.
    - day = segment;
    - dateSpecification = dateOnly;
    - break;
    - }
    - }
    - } else if(*ch == 'W') {
    - year = [now yearOfCommonEra];
    - year -= (year % 100U);
    - year += segment;
    -
    - parseWeekAndDay: //*ch should be 'W' here.
    - if(!isdigit(*++ch)) {
    - //Not really a week-based date; just a year followed by '-W'.
    - if(strict)
    - isValidDate = NO;
    - else
    - month_or_week = day = 1U;
    - } else {
    - month_or_week = read_segment_2digits(ch, &ch);
    - if(*ch == '-') ++ch;
    - parseDayAfterWeek:
    - day = isdigit(*ch) ? read_segment_2digits(ch, &ch) : 1U;
    - dateSpecification = week;
    - }
    - } else {
    - //Century only. Assume current year.
    - centuryOnly:
    - year = segment * 100U + [now yearOfCommonEra] % 100U;
    - month_or_week = day = 1U;
    - }
    - break;
    -
    - case 1:; //-YY; -YY-MM (implicit century)
    - NSLog(@"(%@) found %u digits and one hyphen, so this is either -YY or -YY-MM; segment (year) is %u", str, num_digits, segment);
    - NSInteger current_year = [now yearOfCommonEra];
    - NSInteger current_century = (current_year % 100);
    - year = segment + (current_year - century);
    - if(num_digits == 1U) //implied decade
    - year += current_century - (current_year % 10);
    -
    - if(*ch == '-') {
    - ++ch;
    - month_or_week = read_segment_2digits(ch, &ch);
    - NSLog(@"(%@) month is %lu", str, (unsigned long)month_or_week);
    - }
    -
    - day = 1U;
    - break;
    -
    - case 2: //--MM; --MM-DD
    - year = [now yearOfCommonEra];
    - month_or_week = segment;
    - if(*ch == '-') {
    - ++ch;
    - day = read_segment_2digits(ch, &ch);
    - }
    - break;
    -
    - case 3: //---DD
    - year = [now yearOfCommonEra];
    - month_or_week = [now monthOfYear];
    - day = segment;
    - break;
    -
    - default:
    - isValidDate = NO;
    - } //switch(num_leading_hyphens) (2 digits)
    - break;
    -
    - case 7: //YYYY DDD (ordinal date)
    - if(num_leading_hyphens > 0U)
    - isValidDate = NO;
    - else {
    - day = segment % 1000U;
    - year = segment / 1000U;
    - dateSpecification = dateOnly;
    - if(strict && (day > (365U + is_leap_year(year))))
    - isValidDate = NO;
    - }
    - break;
    -
    - case 3: //--DDD (ordinal date, implicit year)
    - //Technically, the standard only allows one hyphen. But it says that two hyphens is the logical implementation, and one was dropped for brevity. So I have chosen to allow the missing hyphen.
    - if((num_leading_hyphens < 1U) || ((num_leading_hyphens > 2U) && !strict))
    - isValidDate = NO;
    - else {
    - day = segment;
    - year = [now yearOfCommonEra];
    - dateSpecification = dateOnly;
    - if(strict && (day > (365U + is_leap_year(year))))
    - isValidDate = NO;
    - }
    - break;
    -
    - default:
    - isValidDate = NO;
    - }
    - }
    -
    - if (isValidDate) {
    - if (isspace(*ch) || (*ch == 'T')) ++ch;
    -
    - if (isdigit(*ch)) {
    - hour = read_segment_2digits(ch, &ch);
    - if(*ch == timeSep) {
    - ++ch;
    - if((timeSep == ',') || (timeSep == '.')) {
    - //We can't do fractional minutes when '.' is the segment separator.
    - //Only allow whole minutes and whole seconds.
    - minute = read_segment_2digits(ch, &ch);
    - if(*ch == timeSep) {
    - ++ch;
    - second = read_segment_2digits(ch, &ch);
    - }
    - } else {
    - //Allow a fractional minute.
    - //If we don't get a fraction, look for a seconds segment.
    - //Otherwise, the fraction of a minute is the seconds.
    - minute = read_double(ch, &ch);
    - second = modf(minute, &minute);
    - if(second > DBL_EPSILON)
    - second *= 60.0; //Convert fraction (e.g. .5) into seconds (e.g. 30).
    - else if(*ch == timeSep) {
    - ++ch;
    - second = read_double(ch, &ch);
    - }
    - }
    - }
    -
    - switch(*ch) {
    - case 'Z':
    - timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
    - break;
    -
    - case '+':
    - case '-':;
    - BOOL negative = (*ch == '-');
    - if(isdigit(*++ch)) {
    - //Read hour offset.
    - segment = *ch - '0';
    - if(isdigit(*++ch)) {
    - segment *= 10U;
    - segment += *(ch++) - '0';
    - }
    - tz_hour = (signed)segment;
    - if(negative) tz_hour = -tz_hour;
    -
    - //Optional separator.
    - if(*ch == timeSep) ++ch;
    -
    - if(isdigit(*ch)) {
    - //Read minute offset.
    - segment = *ch - '0';
    - if(isdigit(*++ch)) {
    - segment *= 10U;
    - segment += *ch - '0';
    - }
    - tz_minute = segment;
    - if(negative) tz_minute = -tz_minute;
    - }
    -
    - NSInteger secondsFromGMT = (tz_hour * 3600) + (tz_minute * 60);
    - static NSInteger lastUsedSecondsFromGMT = NSNotFound;
    - static NSTimeZone *lastUsedTimeZone;
    - if (secondsFromGMT == lastUsedSecondsFromGMT)
    - timeZone = lastUsedTimeZone;
    - else
    - timeZone = [NSTimeZone timeZoneForSecondsFromGMT:secondsFromGMT];
    - lastUsedSecondsFromGMT = secondsFromGMT;
    - lastUsedTimeZone = timeZone;
    - }
    - }
    - }
    - }
    -
    - if (isValidDate) {
    - switch (dateSpecification) {
    - case monthAndDate:
    - date = [NSCalendarDate dateWithYear:year
    - month:month_or_week
    - day:day
    - hour:hour
    - minute:(NSUInteger)minute
    - second:(NSUInteger)second
    - timeZone:timeZone];
    - break;
    -
    - case week:;
    - //Adapted from <http://personal.ecu.edu/mccartyr/ISOwdALG.txt>.
    - //This works by converting the week date into an ordinal date, then letting the next case handle it.
    - NSInteger prevYear = year - 1U;
    - NSInteger YY = prevYear % 100U;
    - NSInteger C = prevYear - YY;
    - NSInteger G = YY + YY / 4U;
    - NSInteger isLeapYear = (((C / 100U) % 4U) * 5U);
    - NSInteger Jan1Weekday = (isLeapYear + G) % 7U;
    - enum { monday, tuesday, wednesday, thursday/*, friday, saturday, sunday*/ };
    - day = ((8U - Jan1Weekday) + (7U * (Jan1Weekday > thursday))) + (day - 1U) + (7U * (month_or_week - 2));
    -
    - case dateOnly: //An "ordinal date".
    - date = [NSCalendarDate dateWithYear:year
    - month:1
    - day:1
    - hour:hour
    - minute:(NSUInteger)minute
    - second:(NSUInteger)second
    - timeZone:timeZone];
    - date = [date dateByAddingYears:0
    - months:0
    - days:(day - 1)
    - hours:0
    - minutes:0
    - seconds:0];
    - break;
    - }
    - }
    - } //if (!(strict && isdigit(ch[0])))
    -
    - if(outRange) {
    - if(isValidDate)
    - range.length = ch - start_of_date;
    - else
    - range.location = NSNotFound;
    -
    - *outRange = range;
    - }
    -
    - return date;
    -}
    -
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str {
    - return [self calendarDateWithString:str strictly:NO getRange:NULL];
    -}
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict {
    - return [self calendarDateWithString:str strictly:strict getRange:NULL];
    -}
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str strictly:(BOOL)strict getRange:(out NSRange *)outRange {
    - return [self calendarDateWithString:str strictly:strict timeSeparator:ISO8601ParserDefaultTimeSeparatorCharacter getRange:NULL];
    -}
    -
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep getRange:(out NSRange *)outRange {
    - return [self calendarDateWithString:str strictly:NO timeSeparator:timeSep getRange:outRange];
    -}
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str timeSeparator:(unichar)timeSep {
    - return [self calendarDateWithString:str strictly:NO timeSeparator:timeSep getRange:NULL];
    -}
    -+ (NSCalendarDate *)calendarDateWithString:(NSString *)str getRange:(out NSRange *)outRange {
    - return [self calendarDateWithString:str strictly:NO timeSeparator:ISO8601ParserDefaultTimeSeparatorCharacter getRange:outRange];
    -}
    -
    -@end
    --- a/Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.h Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,46 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import <Foundation/Foundation.h>
    -
    -/*This addition unparses dates to ISO 8601 strings. A good introduction to ISO 8601: <http://www.cl.cam.ac.uk/~mgk25/iso-time.html>
    - */
    -
    -//The default separator for time values. Currently, this is ':'.
    -extern unichar ISO8601UnparserDefaultTimeSeparatorCharacter;
    -
    -@interface NSCalendarDate(ISO8601Unparsing)
    -
    -- (NSString *)ISO8601DateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep;
    -- (NSString *)ISO8601WeekDateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep;
    -- (NSString *)ISO8601OrdinalDateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep;
    -
    -- (NSString *)ISO8601DateStringWithTime:(BOOL)includeTime;
    -- (NSString *)ISO8601WeekDateStringWithTime:(BOOL)includeTime;
    -- (NSString *)ISO8601OrdinalDateStringWithTime:(BOOL)includeTime;
    -
    -//includeTime: YES.
    -- (NSString *)ISO8601DateStringWithTimeSeparator:(unichar)timeSep;
    -- (NSString *)ISO8601WeekDateStringWithTimeSeparator:(unichar)timeSep;
    -- (NSString *)ISO8601OrdinalDateStringWithTimeSeparator:(unichar)timeSep;
    -
    -//includeTime: YES.
    -- (NSString *)ISO8601DateString;
    -- (NSString *)ISO8601WeekDateString;
    -- (NSString *)ISO8601OrdinalDateString;
    -
    -@end
    -
    --- a/Frameworks/AIUtilities Framework/Source/NSCalendarDate+ISO8601Unparsing.m Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,177 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#ifndef DEFAULT_TIME_SEPARATOR
    -# define DEFAULT_TIME_SEPARATOR ':'
    -#endif
    -unichar ISO8601UnparserDefaultTimeSeparatorCharacter = DEFAULT_TIME_SEPARATOR;
    -
    -static BOOL is_leap_year(NSInteger year) {
    - return \
    - ((year % 4) == 0)
    - && (((year % 100) != 0)
    - || ((year % 400) == 0));
    -}
    -
    -@interface NSString(ISO8601Unparsing)
    -
    -//Replace all occurrences of ':' with timeSep.
    -- (NSString *)prepareDateFormatWithTimeSeparator:(unichar)timeSep;
    -
    -@end
    -
    -@implementation NSCalendarDate(ISO8601Unparsing)
    -
    -#pragma mark Public methods
    -
    -- (NSString *)ISO8601DateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep {
    - NSString *dateFormat = [(includeTime ? @"%Y-%m-%dT%H:%M:%S" : @"%Y-%m-%d") prepareDateFormatWithTimeSeparator:timeSep];
    - NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:dateFormat allowNaturalLanguage:NO];
    - NSString *str = [formatter stringForObjectValue:self];
    - if(includeTime) {
    - NSInteger offset = [[self timeZone] secondsFromGMT];
    - offset /= 60; //bring down to minutes
    - if(offset == 0)
    - str = [str stringByAppendingString:@"Z"];
    - if(offset < 0)
    - str = [str stringByAppendingFormat:@"-%02ld:%02ld", -offset / 60, -offset % 60];
    - else
    - str = [str stringByAppendingFormat:@"+%02ld:%02ld", offset / 60, offset % 60];
    - }
    - return str;
    -}
    -/*Adapted from:
    - * Algorithm for Converting Gregorian Dates to ISO 8601 Week Date
    - * Rick McCarty, 1999
    - * http://personal.ecu.edu/mccartyr/ISOwdALG.txt
    - */
    -- (NSString *)ISO8601WeekDateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep {
    - enum {
    - monday, tuesday, wednesday, thursday, friday, saturday, sunday
    - };
    - enum {
    - january = 1U, february, march,
    - april, may, june,
    - july, august, september,
    - october, november, december
    - };
    -
    - NSInteger year = [self yearOfCommonEra];
    - NSInteger week = 0;
    - NSInteger dayOfWeek = ([self dayOfWeek] + 6) % 7;
    - NSInteger dayOfYear = [self dayOfYear];
    -
    - NSInteger prevYear = year - 1U;
    -
    - BOOL yearIsLeapYear = is_leap_year(year);
    - BOOL prevYearIsLeapYear = is_leap_year(prevYear);
    -
    - NSInteger YY = prevYear % 100;
    - NSInteger C = prevYear - YY;
    - NSInteger G = YY + YY / 4;
    - NSInteger Jan1Weekday = (((((C / 100) % 4) * 5) + G) % 7);
    -
    - NSInteger weekday = ((dayOfYear + Jan1Weekday) - 1) % 7;
    -
    - if((dayOfYear <= (7U - Jan1Weekday)) && (Jan1Weekday > thursday)) {
    - week = 52U + ((Jan1Weekday == friday) || ((Jan1Weekday == saturday) && prevYearIsLeapYear));
    - --year;
    - } else {
    - unsigned lengthOfYear = 365U + yearIsLeapYear;
    - if((lengthOfYear - dayOfYear) < (thursday - weekday)) {
    - ++year;
    - week = 1U;
    - } else {
    - NSInteger J = dayOfYear + (sunday - weekday) + Jan1Weekday;
    - week = J / 7U - (Jan1Weekday > thursday);
    - }
    - }
    -
    - NSString *timeString;
    - if(includeTime) {
    - NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:[@"T%H:%M:%S%z" prepareDateFormatWithTimeSeparator:timeSep] allowNaturalLanguage:NO];
    - timeString = [formatter stringForObjectValue:self];
    - } else
    - timeString = @"";
    -
    - return [NSString stringWithFormat:@"%lu-W%02lu-%02lu%@", year, week, dayOfWeek + 1U, timeString];
    -}
    -- (NSString *)ISO8601OrdinalDateStringWithTime:(BOOL)includeTime timeSeparator:(unichar)timeSep {
    - NSString *timeString;
    - if(includeTime) {
    - NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:[@"T%H:%M:%S%z" prepareDateFormatWithTimeSeparator:timeSep] allowNaturalLanguage:NO];
    - timeString = [formatter stringForObjectValue:self];
    - } else
    - timeString = @"";
    -
    - return [NSString stringWithFormat:@"%lu-%03lu%@", [self yearOfCommonEra], [self dayOfYear], timeString];
    -}
    -
    -#pragma mark -
    -
    -- (NSString *)ISO8601DateStringWithTime:(BOOL)includeTime {
    - return [self ISO8601DateStringWithTime:includeTime timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -- (NSString *)ISO8601WeekDateStringWithTime:(BOOL)includeTime {
    - return [self ISO8601WeekDateStringWithTime:includeTime timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -- (NSString *)ISO8601OrdinalDateStringWithTime:(BOOL)includeTime {
    - return [self ISO8601OrdinalDateStringWithTime:includeTime timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -
    -#pragma mark -
    -
    -- (NSString *)ISO8601DateStringWithTimeSeparator:(unichar)timeSep {
    - return [self ISO8601DateStringWithTime:YES timeSeparator:timeSep];
    -}
    -- (NSString *)ISO8601WeekDateStringWithTimeSeparator:(unichar)timeSep {
    - return [self ISO8601WeekDateStringWithTime:YES timeSeparator:timeSep];
    -}
    -- (NSString *)ISO8601OrdinalDateStringWithTimeSeparator:(unichar)timeSep {
    - return [self ISO8601OrdinalDateStringWithTime:YES timeSeparator:timeSep];
    -}
    -
    -#pragma mark -
    -
    -- (NSString *)ISO8601DateString {
    - return [self ISO8601DateStringWithTime:YES timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -- (NSString *)ISO8601WeekDateString {
    - return [self ISO8601WeekDateStringWithTime:YES timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -- (NSString *)ISO8601OrdinalDateString {
    - return [self ISO8601OrdinalDateStringWithTime:YES timeSeparator:ISO8601UnparserDefaultTimeSeparatorCharacter];
    -}
    -
    -@end
    -
    -@implementation NSString(ISO8601Unparsing)
    -
    -//Replace all occurrences of ':' with timeSep.
    -- (NSString *)prepareDateFormatWithTimeSeparator:(unichar)timeSep {
    - NSString *dateFormat = self;
    - if(timeSep != ':') {
    - NSMutableString *dateFormatMutable = [dateFormat mutableCopy];
    - [dateFormatMutable replaceOccurrencesOfString:@":"
    - withString:[NSString stringWithCharacters:&timeSep length:1U]
    - options:NSBackwardsSearch | NSLiteralSearch
    - range:(NSRange){ 0U, [dateFormat length] }];
    - dateFormat = dateFormatMutable;
    - }
    - return dateFormat;
    -}
    -
    -@end
    --- a/Frameworks/Adium Framework/Source/AIContentObject.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Frameworks/Adium Framework/Source/AIContentObject.m Mon Jun 03 17:17:12 2013 -0400
    @@ -18,6 +18,7 @@
    #import <Adium/AIChat.h>
    #import <Adium/AIContentControllerProtocol.h>
    #import <Adium/AIHTMLDecoder.h>
    +#import <AIUtilities/AIDateAdditions.h>
    @implementation AIContentObject
    @@ -122,10 +123,9 @@
    //Content is from the same day. If passed nil, content is from the current day.
    - (BOOL)isFromSameDayAsContent:(AIContentObject *)inContent
    {
    - NSCalendarDate *ourDate = [[self date] dateWithCalendarFormat:nil timeZone:nil];
    - NSCalendarDate *inDate = [(inContent ? [inContent date] : [NSDate date]) dateWithCalendarFormat:nil timeZone:nil];
    + NSDate *inDate = inContent ? [inContent date] : [NSDate date];
    - return [ourDate dayOfCommonEra] == [inDate dayOfCommonEra];
    + return [NSDate isDate:self.date sameDayAsDate:inDate];
    }
    //Content --------------------------------------------------------------------------------------------------------------
    --- a/Other/Adium Spotlight Importer/GetMetadataForFile.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Other/Adium Spotlight Importer/GetMetadataForFile.m Mon Jun 03 17:17:12 2013 -0400
    @@ -14,7 +14,7 @@
    * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    */
    #import "GetMetadataForHTMLLog.h"
    -#import "NSCalendarDate+ISO8601Parsing.h"
    +#import "ISO8601DateFormatter.h"
    /*
    Relevant keys from MDItem.h we use or may want to use:
    @@ -208,19 +208,20 @@
    forKey:@"com_adiumX_service"];
    NSArray *children = [[xmlDoc rootElement] children];
    - NSCalendarDate *startDate = nil, *endDate = nil;
    + NSDate *startDate = nil, *endDate = nil;
    if ([children count]) {
    NSString *dateStr;
    + ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
    dateStr = [[(NSXMLElement *)[children objectAtIndex:0] attributeForName:@"time"] objectValue];
    - startDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
    + startDate = (dateStr ? [formatter dateFromString:dateStr] : nil);
    if (startDate)
    [(NSMutableDictionary *)attributes setObject:startDate
    forKey:(NSString *)kMDItemContentCreationDate];
    dateStr = [[(NSXMLElement *)[children lastObject] attributeForName:@"time"] objectValue];
    - endDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
    + endDate = (dateStr ? [formatter dateFromString:dateStr] : nil);
    if (endDate)
    [(NSMutableDictionary *)attributes setObject:[NSNumber numberWithDouble:[endDate timeIntervalSinceDate:startDate]]
    forKey:(NSString *)kMDItemDurationSeconds];
    --- a/Other/Adium Spotlight Importer/GetMetadataForHTMLLog.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Other/Adium Spotlight Importer/GetMetadataForHTMLLog.m Mon Jun 03 17:17:12 2013 -0400
    @@ -17,36 +17,26 @@
    #import "GetMetadataForHTMLLog.h"
    #import "GetMetadataForHTMLLog-Additions.h"
    -#import "scandate.h"
    +#import <AIUtilities/ISO8601DateFormatter.h>
    static char *gaim_markup_strip_html(const char *str);
    -//Given an Adium log file name, return an NSCalendarDate for its creation date
    +//Given an Adium log file name, return an NSDate for its creation date
    static NSDate *dateFromHTMLLog(NSString *pathToFile)
    {
    - NSDate *date = nil;
    - unsigned long year = 0;
    - unsigned long month = 0;
    - unsigned long day = 0;
    - unsigned long hour = 0;
    - unsigned long minute = 0;
    - unsigned long second = 0;
    - long timeZoneOffset = +0;
    -
    - if (scandate([pathToFile UTF8String], &year, &month, &day, /*outHasTime*/ NULL, &hour, &minute, &second, &timeZoneOffset)) {
    - if (year && month && day) {
    - NSCalendarDate *calendarDate = [NSCalendarDate dateWithYear:year
    - month:month
    - day:day
    - hour:hour
    - minute:minute
    - second:second
    - timeZone:[NSTimeZone timeZoneForSecondsFromGMT:(NSInteger)timeZoneOffset]];
    - date = [NSDate dateWithTimeIntervalSince1970:[calendarDate timeIntervalSince1970]];
    + ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
    + formatter.timeSeparator = '.';
    + NSRange openParenRange, closeParenRange;
    +
    + if ([pathToFile hasSuffix:@".chatlog"] && (openParenRange = [pathToFile rangeOfString:@"(" options:NSBackwardsSearch]).location != NSNotFound) {
    + openParenRange = NSMakeRange(openParenRange.location, [pathToFile length] - openParenRange.location);
    + if ((closeParenRange = [pathToFile rangeOfString:@")" options:0 range:openParenRange]).location != NSNotFound) {
    + //Add and subtract one to remove the parenthesis
    + NSString *dateString = [pathToFile substringWithRange:NSMakeRange(openParenRange.location + 1, (closeParenRange.location - openParenRange.location))];
    + return [formatter dateFromString:dateString];
    }
    }
    -
    - return date;
    + return nil;
    }
    NSString *CopyTextContentForHTMLLogData(NSData *logData) {
    --- a/Plugins/Error Message Handler/ErrorMessageHandlerPlugin.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Plugins/Error Message Handler/ErrorMessageHandlerPlugin.m Mon Jun 03 17:17:12 2013 -0400
    @@ -111,7 +111,7 @@
    __block NSString *dateString;
    [NSDateFormatter withLocalizedDateFormatterShowingSeconds:NO showingAMorPM:YES perform:^(NSDateFormatter *dateFormatter){
    - dateString = [dateFormatter stringFromDate:[NSCalendarDate calendarDate]];
    + dateString = [dateFormatter stringFromDate:[NSDate date]];
    }];
    NSString *alertText = [[details objectForKey:KEY_ALERT_TEXT] lastPathComponent];
    --- a/Plugins/Message Alias Support/AIMessageAliasPlugin.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Plugins/Message Alias Support/AIMessageAliasPlugin.m Mon Jun 03 17:17:12 2013 -0400
    @@ -167,7 +167,7 @@
    //Current Date
    if ([self string:str containsValidKeyword:@"%d"]) {
    - NSCalendarDate *currentDate = [NSCalendarDate calendarDate];
    + NSDate *currentDate = [NSDate date];
    __block NSString *calendarFormat;
    [NSDateFormatter withLocalizedShortDateFormatterPerform:^(NSDateFormatter *dateFormatter){
    calendarFormat = [dateFormatter dateFormat];
    @@ -176,14 +176,14 @@
    if (!newAttributedString) newAttributedString = [attributedString mutableCopy];
    [newAttributedString replaceOccurrencesOfString:@"%d"
    - withString:[currentDate descriptionWithCalendarFormat:calendarFormat]
    + withString:[currentDate descriptionWithCalendarFormat:calendarFormat timeZone:nil locale:nil]
    options:NSLiteralSearch
    range:NSMakeRange(0, [newAttributedString length])];
    }
    //Current Time
    if ([self string:str containsValidKeyword:@"%t"]) {
    - NSCalendarDate *currentDate = [NSCalendarDate calendarDate];
    + NSDate *currentDate = [NSDate date];
    if (!newAttributedString) newAttributedString = [attributedString mutableCopy];
    --- a/Source/AICalendarDate.h Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,30 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -
    -typedef enum {
    - AIDayGranularity = 0,
    - AISecondGranularity
    -} AICalendarDateGranularity;
    -
    -@interface AICalendarDate : NSCalendarDate {
    - AICalendarDateGranularity granularity;
    -}
    -
    -- (void)setGranularity:(AICalendarDateGranularity)inGranularity;
    -- (AICalendarDateGranularity)granularity;
    -
    -@end
    --- a/Source/AICalendarDate.m Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,65 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "AICalendarDate.h"
    -
    -
    -@implementation AICalendarDate
    -/*!
    - * @brief Initialize with coder
    - */
    -- (id)initWithCoder:(NSCoder *)decoder
    -{
    - if ((self = [super initWithCoder:decoder])) {
    - if ([decoder allowsKeyedCoding]) {
    - granularity = [[decoder decodeObjectForKey:@"Granularity"] intValue];
    - } else {
    - granularity = [[decoder decodeObject] intValue];
    - }
    - }
    -
    - return self;
    -}
    -
    -- (void)encodeWithCoder:(NSCoder *)encoder
    -{
    - [super encodeWithCoder:encoder];
    -
    - if ([encoder allowsKeyedCoding]) {
    - [encoder encodeObject:[NSNumber numberWithInteger:granularity] forKey:@"Granularity"];
    -
    - } else {
    - [encoder encodeObject:[NSNumber numberWithInteger:granularity]];
    - }
    -}
    -
    -- (id)copyWithZone:(NSZone *)inZone
    -{
    - AICalendarDate *newDate = [super copyWithZone:inZone];
    - newDate->granularity = granularity;
    -
    - return newDate;
    -}
    -
    -- (void)setGranularity:(AICalendarDateGranularity)inGranularity
    -{
    - granularity = inGranularity;
    -}
    -- (AICalendarDateGranularity)granularity
    -{
    - return granularity;
    -}
    -@end
    --- a/Source/AIChatLog.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AIChatLog.h Mon Jun 03 17:17:12 2013 -0400
    @@ -16,14 +16,19 @@
    #define ChatLog_WillDelete @"ChatLog_WillDelete"
    +@class ISO8601DateFormatter;
    +
    +NSDate *dateFromFileName(NSString *fileName);
    +
    @interface AIChatLog : NSObject <NSXMLParserDelegate> {
    NSString *relativePath;
    NSString *from;
    NSString *to;
    NSString *serviceClass;
    - NSCalendarDate *date;
    + NSDate *date;
    CGFloat rankingPercentage;
    CGFloat rankingValue;
    + ISO8601DateFormatter *formatter;
    }
    - (id)initWithPath:(NSString *)inPath from:(NSString *)inFrom to:(NSString *)inTo serviceClass:(NSString *)inServiceClass;
    @@ -34,14 +39,13 @@
    - (NSString *)from;
    - (NSString *)to;
    - (NSString *)serviceClass;
    -- (NSCalendarDate *)date;
    +- (NSDate *)date;
    - (CGFloat)rankingPercentage;
    - (void)setRankingPercentage:(CGFloat)inRankingPercentage;
    - (CGFloat)rankingValueOnArbitraryScale;
    - (void)setRankingValueOnArbitraryScale:(CGFloat)inRankingValue;
    //Comparisons
    -- (BOOL)isFromSameDayAsDate:(NSCalendarDate *)inDate;
    - (NSComparisonResult)compareTo:(AIChatLog *)inLog;
    - (NSComparisonResult)compareToReverse:(AIChatLog *)inLog;
    - (NSComparisonResult)compareFrom:(AIChatLog *)inLog;
    --- a/Source/AIChatLog.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AIChatLog.m Mon Jun 03 17:17:12 2013 -0400
    @@ -17,15 +17,11 @@
    #import "AIChatLog.h"
    #import "AILogViewerWindowController.h"
    #import "AILoggerPlugin.h"
    -#import "AICalendarDate.h"
    -#import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
    -#import "scandate.h"
    +#import <AIUtilities/ISO8601DateFormatter.h>
    @implementation AIChatLog
    -static NSCalendarDate *dateFromFileName(NSString *fileName);
    -
    - (id)initWithPath:(NSString *)inPath from:(NSString *)inFrom to:(NSString *)inTo serviceClass:(NSString *)inServiceClass
    {
    if ((self = [super init])) {
    @@ -34,6 +30,8 @@
    to = inTo;
    serviceClass = inServiceClass;
    rankingPercentage = 0;
    +
    + formatter = [[ISO8601DateFormatter alloc] init];
    }
    return self;
    @@ -82,7 +80,7 @@
    - (NSString *)serviceClass{
    return serviceClass;
    }
    -- (NSCalendarDate *)date{
    +- (NSDate *)date{
    //Determine the date of this log lazily
    if (!date) {
    date = dateFromFileName([relativePath lastPathComponent]);
    @@ -104,7 +102,7 @@
    //Stop at the first element with a date.
    NSString *dateString = nil;
    if ((dateString = [attributeDict objectForKey:@"time"])) {
    - date = [NSCalendarDate calendarDateWithString:dateString strictly:YES];
    + date = [formatter dateFromString:dateString];
    if (date)
    [parser abortParsing];
    }
    @@ -128,11 +126,6 @@
    return rankingValue;
    }
    -- (BOOL)isFromSameDayAsDate:(NSCalendarDate *)inDate
    -{
    - return [[self date] dayOfCommonEra] == [inDate dayOfCommonEra];
    -}
    -
    #pragma mark Sort Selectors
    //Sort by To, then Date
    @@ -295,35 +288,21 @@
    #pragma mark Date utilities
    -//Given an Adium log file name, return an NSCalendarDate with year, month, and day specified
    -static NSCalendarDate *dateFromFileName(NSString *fileName)
    +//Given an Adium log file name, return an NSDate with year, month, and day specified
    +NSDate *dateFromFileName(NSString *fileName)
    {
    - unsigned long year = 0;
    - unsigned long month = 0;
    - unsigned long day = 0;
    - unsigned long hour = 0;
    - unsigned long minute = 0;
    - unsigned long second = 0;
    - long tzone = NSNotFound;
    - BOOL hasTime = NO;
    -
    - if (scandate([fileName UTF8String], &year, &month, &day, &hasTime, &hour, &minute, &second, &tzone)) {
    - if (year && month && day) {
    - AICalendarDate *calendarDate;
    -
    - calendarDate = [AICalendarDate dateWithYear:year
    - month:month
    - day:day
    - hour:hour
    - minute:minute
    - second:second
    - timeZone:((tzone == NSNotFound) ? nil : [NSTimeZone timeZoneForSecondsFromGMT:(tzone * 60)])];
    - [calendarDate setGranularity:(hasTime ? AISecondGranularity : AIDayGranularity)];
    -
    - return calendarDate;
    + ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
    + formatter.timeSeparator = '.';
    + NSRange openParenRange, closeParenRange;
    +
    + if ([fileName hasSuffix:@".chatlog"] && (openParenRange = [fileName rangeOfString:@"(" options:NSBackwardsSearch]).location != NSNotFound) {
    + openParenRange = NSMakeRange(openParenRange.location, [fileName length] - openParenRange.location);
    + if ((closeParenRange = [fileName rangeOfString:@")" options:0 range:openParenRange]).location != NSNotFound) {
    + //Add and subtract one to remove the parenthesis
    + NSString *dateString = [fileName substringWithRange:NSMakeRange(openParenRange.location + 1, (closeParenRange.location - openParenRange.location))];
    + return [formatter dateFromString:dateString];
    }
    }
    -
    return nil;
    }
    --- a/Source/AILogDateFormatter.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AILogDateFormatter.m Mon Jun 03 17:17:12 2013 -0400
    @@ -15,7 +15,7 @@
    */
    #import "AILogDateFormatter.h"
    -#import "AICalendarDate.h"
    +#import <AIUtilities/AIDateAdditions.h>
    @implementation AILogDateFormatter
    @@ -23,15 +23,20 @@
    {
    NSString *returnValue = nil;
    - if ([self respondsToSelector:@selector(timeStyle)] && [date isKindOfClass:[AICalendarDate class]]) {
    - NSInteger today = [[NSCalendarDate calendarDate] dayOfCommonEra];
    - NSInteger dateDay = [(AICalendarDate *)date dayOfCommonEra];
    + if ([self respondsToSelector:@selector(timeStyle)]) {
    NSDateFormatterStyle timeStyle = [self timeStyle];
    +
    + NSDate *dateToday = [NSDate date];
    + NSCalendar *calendar = [NSCalendar currentCalendar];
    + NSDateComponents *comps = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
    + fromDate:dateToday];
    + comps.day--;
    + BOOL today = [NSDate isDate:dateToday sameDayAsDate:date];
    + BOOL yesterday = [NSDate isDate:[calendar dateFromComponents:comps] sameDayAsDate:date];
    - if ((dateDay == today) || (dateDay == (today - 1))) {
    - NSString *dayString = (dateDay == today) ? AILocalizedString(@"Today", "Day designation for the current day") : AILocalizedString(@"Yesterday", "Day designation for the previous day");
    - if ((timeStyle != NSDateFormatterNoStyle) &&
    - ([(AICalendarDate *)date granularity] == AISecondGranularity)) {
    + if (today || yesterday) {
    + NSString *dayString = today ? AILocalizedString(@"Today", "Day designation for the current day") : AILocalizedString(@"Yesterday", "Day designation for the previous day");
    + if (timeStyle != NSDateFormatterNoStyle) {
    //Supposed to show time, and the date has sufficient granularity to show it
    NSDateFormatterStyle dateStyle = [self dateStyle];
    NSMutableString *mutableString = [dayString mutableCopy];
    @@ -45,8 +50,7 @@
    }
    } else {
    - if ((timeStyle != NSDateFormatterNoStyle) &&
    - ([(AICalendarDate *)date granularity] == AIDayGranularity)) {
    + if (timeStyle != NSDateFormatterNoStyle) {
    //Currently supposed to show time, but the date does not have that level of granularity
    [self setTimeStyle:NSDateFormatterNoStyle];
    --- a/Source/AILogViewerWindowController.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AILogViewerWindowController.h Mon Jun 03 17:17:12 2013 -0400
    @@ -90,9 +90,7 @@
    NSMutableSet *contactIDsToFilter;
    AIDateType filterDateType;
    - NSCalendarDate *filterDate;
    - NSInteger firstDayOfWeek;
    - BOOL iCalFirstDayOfWeekDetermined;
    + NSDate *filterDate;
    NSMutableDictionary *logToGroupDict;
    NSMutableDictionary *logFromGroupDict;
    --- a/Source/AILogViewerWindowController.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AILogViewerWindowController.m Mon Jun 03 17:17:12 2013 -0400
    @@ -40,6 +40,7 @@
    #import <AIUtilities/AIArrayAdditions.h>
    #import <AIUtilities/AIAttributedStringAdditions.h>
    #import <AIUtilities/AIDateFormatterAdditions.h>
    +#import <AIUtilities/AIDateAdditions.h>
    #import <AIUtilities/AIFileManagerAdditions.h>
    #import <AIUtilities/AIImageAdditions.h>
    #import <AIUtilities/AIImageDrawingAdditions.h>
    @@ -1446,7 +1447,7 @@
    matchesDateFilter = ([[inChatLog date] timeIntervalSinceDate:filterDate] < 0);
    break;
    case AIDateTypeExactly:
    - matchesDateFilter = [inChatLog isFromSameDayAsDate:filterDate];
    + matchesDateFilter = [NSDate isDate:[inChatLog date] sameDayAsDate:filterDate];
    break;
    default:
    matchesDateFilter = YES;
    @@ -1733,7 +1734,7 @@
    {
    UInt32 lastUpdate = TickCount();
    - NSCalendarDate *searchStringDate = nil;
    + NSDate *searchStringDate = nil;
    if ((mode == LOG_SEARCH_DATE) && (searchString != nil)) {
    searchStringDate = [[NSDate dateWithNaturalLanguageString:searchString] dateWithCalendarFormat:nil timeZone:nil];
    @@ -1771,7 +1772,7 @@
    */
    if ((mode != LOG_SEARCH_DATE) ||
    (!searchString) ||
    - (searchStringDate && [theLog isFromSameDayAsDate:searchStringDate])) {
    + (searchStringDate && [NSDate isDate:[theLog date] sameDayAsDate:searchStringDate])) {
    if ([self chatLogMatchesDateFilter:theLog]) {
    //Add the log
    @@ -2404,25 +2405,11 @@
    return menuItem;
    }
    -- (NSInteger)daysSinceStartOfWeekGivenToday:(NSCalendarDate *)today
    +- (NSInteger)daysSinceStartOfWeekGivenToday:(NSDate *)today
    {
    - NSInteger todayDayOfWeek = [today dayOfWeek];
    -
    - //Try to look at the iCal preferences if possible
    - if (!iCalFirstDayOfWeekDetermined) {
    - CFPropertyListRef iCalFirstDayOfWeek = CFPreferencesCopyAppValue(CFSTR("first day of week"),CFSTR("com.apple.iCal"));
    - if (iCalFirstDayOfWeek) {
    - //This should return a CFNumberRef... we're using another app's prefs, so make sure.
    - if (CFGetTypeID(iCalFirstDayOfWeek) == CFNumberGetTypeID()) {
    - firstDayOfWeek = [(__bridge NSNumber *)iCalFirstDayOfWeek integerValue];
    - }
    -
    - CFRelease(iCalFirstDayOfWeek);
    - }
    -
    - //Don't check again
    - iCalFirstDayOfWeekDetermined = YES;
    - }
    + NSInteger todayDayOfWeek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit
    + fromDate:today] weekday];
    + NSInteger firstDayOfWeek = [[NSCalendar currentCalendar] firstWeekday];
    return ((todayDayOfWeek >= firstDayOfWeek) ? (todayDayOfWeek - firstDayOfWeek) : ((todayDayOfWeek + 7) - firstDayOfWeek));
    }
    @@ -2916,9 +2903,6 @@
    - (void)configureDateFilter
    {
    - firstDayOfWeek = 0; /* Sunday */
    - iCalFirstDayOfWeekDetermined = NO;
    -
    [popUp_dateFilter setMenu:[self dateTypeMenu]];
    NSInteger idx = [popUp_dateFilter indexOfItemWithTag:AIDateTypeAnyDate];
    if(idx != NSNotFound)
    @@ -2981,7 +2965,9 @@
    {
    BOOL showDatePicker = NO;
    - NSCalendarDate *today = [NSCalendarDate date];
    + NSDate *today = [NSDate date];
    + NSCalendar *calendar = [NSCalendar currentCalendar];
    + NSDateComponents *comps = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:today];
    filterDate = nil;
    @@ -2997,52 +2983,48 @@
    case AIDateTypeSinceYesterday:
    filterDateType = AIDateTypeAfter;
    - filterDate = [today dateByAddingYears:0
    - months:0
    - days:-1
    - hours:-[today hourOfDay]
    - minutes:-[today minuteOfHour]
    - seconds:-([today secondOfMinute] + 1)];
    + comps.day--;
    + comps.hour -= comps.hour;
    + comps.minute -= comps.minute;
    + comps.second -= comps.second + 1;
    + filterDate = [calendar dateFromComponents:comps];
    break;
    case AIDateTypeThisWeek:
    filterDateType = AIDateTypeAfter;
    - filterDate = [today dateByAddingYears:0
    - months:0
    - days:-[self daysSinceStartOfWeekGivenToday:today]
    - hours:-[today hourOfDay]
    - minutes:-[today minuteOfHour]
    - seconds:-([today secondOfMinute] + 1)];
    + comps.day -= [self daysSinceStartOfWeekGivenToday:today];
    + comps.hour -= comps.hour;
    + comps.minute -= comps.minute;
    + comps.second -= comps.second + 1;
    + filterDate = [calendar dateFromComponents:comps];
    break;
    case AIDateTypeWithinLastTwoWeeks:
    filterDateType = AIDateTypeAfter;
    - filterDate = [today dateByAddingYears:0
    - months:0
    - days:-14
    - hours:-[today hourOfDay]
    - minutes:-[today minuteOfHour]
    - seconds:-([today secondOfMinute] + 1)];
    + comps.day -= 14;
    + comps.hour -= comps.hour;
    + comps.minute -= comps.minute;
    + comps.second -= comps.second + 1;
    + filterDate = [calendar dateFromComponents:comps];
    break;
    case AIDateTypeThisMonth:
    filterDateType = AIDateTypeAfter;
    - filterDate = [[NSCalendarDate date] dateByAddingYears:0
    - months:0
    - days:-[today dayOfMonth]
    - hours:0
    - minutes:0
    - seconds:-1];
    + comps.day -= comps.day;
    + comps.hour -= comps.hour;
    + comps.minute -= comps.minute;
    + comps.second -= comps.second + 1;
    + filterDate = [calendar dateFromComponents:comps];
    break;
    case AIDateTypeWithinLastTwoMonths:
    filterDateType = AIDateTypeAfter;
    - filterDate = [[NSCalendarDate date] dateByAddingYears:0
    - months:-1
    - days:-[today dayOfMonth]
    - hours:0
    - minutes:0
    - seconds:-1];
    + comps.month--;
    + comps.day -= comps.day;
    + comps.hour = 0;
    + comps.minute = 0;
    + comps.second--;
    + filterDate = [calendar dateFromComponents:comps];
    break;
    default:
    --- a/Source/AILoggerPlugin.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AILoggerPlugin.h Mon Jun 03 17:17:12 2013 -0400
    @@ -28,7 +28,7 @@
    #define XML_LOGGING_NAMESPACE @"http://purl.org/net/ulf/ns/0.4-02"
    -@class AIAccount, AIHTMLDecoder, AIChat;
    +@class AIAccount, AIHTMLDecoder, AIChat, ISO8601DateFormatter;
    @interface AILoggerPlugin : AIPlugin {
    @@ -45,6 +45,8 @@
    NSDictionary *statusTranslation;
    BOOL logHTML;
    + ISO8601DateFormatter *formatter;
    +
    // Log Indexing
    NSMutableSet *dirtyLogSet;
    BOOL indexingAllowed;
    --- a/Source/AILoggerPlugin.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AILoggerPlugin.m Mon Jun 03 17:17:12 2013 -0400
    @@ -40,8 +40,7 @@
    #import <AIUtilities/AIStringAdditions.h>
    #import <AIUtilities/AIToolbarUtilities.h>
    #import <AIUtilities/AIImageAdditions.h>
    -#import <AIUtilities/NSCalendarDate+ISO8601Unparsing.h>
    -#import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
    +#import <AIUtilities/ISO8601DateFormatter.h>
    #import "AILogFileUpgradeWindowController.h"
    @@ -144,7 +143,6 @@
    @end
    #pragma mark Private Function Prototypes
    -NSCalendarDate* getDateFromPath(NSString *path);
    NSComparisonResult sortPaths(NSString *path1, NSString *path2, void *context);
    #pragma mark -
    @@ -211,6 +209,8 @@
    jobSemaphore = dispatch_semaphore_create(3 * cpuCount);
    logLoadingPrefetchSemaphore = dispatch_semaphore_create(3 * cpuCount + 1); //prefetch one log
    + formatter = [[ISO8601DateFormatter alloc] init];
    + formatter.includeTime = YES;
    self.xhtmlDecoder = [[AIHTMLDecoder alloc] initWithHeaders:NO
    fontTags:YES
    @@ -332,7 +332,9 @@
    ioQueue = nil;
    jobSemaphore = nil;
    loggerPluginGroup = nil;
    + formatter = nil;
    }
    +
    #pragma mark AILoggerPlugin Plubic Methods
    //Paths
    + (NSString *)logBasePath
    @@ -371,7 +373,7 @@
    NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self pathForLogsLikeChat:chat] error:NULL];
    NSMutableArray *dates = [NSMutableArray arrayWithCapacity:files.count];
    for (NSString *path in files) {
    - id date = getDateFromPath(path);
    + id date = dateFromFileName(path);
    [dates addObject:date ?: [NSNull null]];
    }
    @@ -538,22 +540,6 @@
    #pragma mark -
    #pragma mark Private Functions
    -NSCalendarDate* getDateFromPath(NSString *path)
    -{
    - NSRange openParenRange, closeParenRange;
    -
    - if ([path hasSuffix:@".chatlog"] && (openParenRange = [path rangeOfString:@"(" options:NSBackwardsSearch]).location != NSNotFound) {
    - openParenRange = NSMakeRange(openParenRange.location, [path length] - openParenRange.location);
    - if ((closeParenRange = [path rangeOfString:@")" options:0 range:openParenRange]).location != NSNotFound) {
    - //Add and subtract one to remove the parenthesis
    - NSString *dateString = [path substringWithRange:NSMakeRange(openParenRange.location + 1, (closeParenRange.location - openParenRange.location))];
    - NSCalendarDate *date = [NSCalendarDate calendarDateWithString:dateString timeSeparator:'.'];
    - return date;
    - }
    - }
    - return nil;
    -}
    -
    NSComparisonResult sortPaths(NSString *path1, NSString *path2, void *context)
    {
    NSDictionary *cache = (__bridge NSDictionary *)context;
    @@ -1023,7 +1009,7 @@
    @autoreleasepool {
    BOOL dirty = NO;
    NSString *contentType = [content type];
    - NSString *date = [[[content date] dateWithCalendarFormat:nil timeZone:nil] ISO8601DateString];
    + NSString *date = [formatter stringFromDate:[[content date] dateWithCalendarFormat:nil timeZone:nil]];
    if ([contentType isEqualToString:CONTENT_MESSAGE_TYPE] ||
    [contentType isEqualToString:CONTENT_CONTEXT_TYPE]) {
    @@ -1170,7 +1156,7 @@
    AIXMLElement *eventElement = [[AIXMLElement alloc] initWithName:@"event"];
    [eventElement setAttributeNames:[NSArray arrayWithObjects:@"type", @"sender", @"time", nil]
    - values:[NSArray arrayWithObjects:@"windowOpened", chat.account.UID, [[[NSDate date] dateWithCalendarFormat:nil timeZone:nil] ISO8601DateString], nil]];
    + values:[NSArray arrayWithObjects:@"windowOpened", chat.account.UID, [formatter stringFromDate:[[NSDate date] dateWithCalendarFormat:nil timeZone:nil]], nil]];
    [appender appendElement:eventElement];
    @@ -1192,7 +1178,7 @@
    AIXMLElement *eventElement = [[AIXMLElement alloc] initWithName:@"event"];
    [eventElement setAttributeNames:[NSArray arrayWithObjects:@"type", @"sender", @"time", nil]
    - values:[NSArray arrayWithObjects:@"windowClosed", chat.account.UID, [[[NSDate date] dateWithCalendarFormat:nil timeZone:nil] ISO8601DateString], nil]];
    + values:[NSArray arrayWithObjects:@"windowClosed", chat.account.UID, [formatter stringFromDate:[[NSDate date] dateWithCalendarFormat:nil timeZone:nil]], nil]];
    [appender appendElement:eventElement];
    @@ -1252,7 +1238,7 @@
    AIXMLElement *eventElement = [[AIXMLElement alloc] initWithName:@"event"];
    [eventElement setAttributeNames:[NSArray arrayWithObjects:@"type", @"sender", @"time", nil]
    - values:[NSArray arrayWithObjects:@"windowOpened", chat.account.UID, [[[NSDate date] dateWithCalendarFormat:nil timeZone:nil] ISO8601DateString], nil]];
    + values:[NSArray arrayWithObjects:@"windowOpened", chat.account.UID, [formatter stringFromDate:[[NSDate date] dateWithCalendarFormat:nil timeZone:nil]], nil]];
    [appender appendElement:eventElement];
    --- a/Source/AIXMLChatlogConverter.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AIXMLChatlogConverter.h Mon Jun 03 17:17:12 2013 -0400
    @@ -25,12 +25,13 @@
    AIScreenName = 4
    } AINameFormat;
    -@class AIHTMLDecoder;
    +@class AIHTMLDecoder, ISO8601DateFormatter;
    @interface AIXMLChatlogConverter : NSObject {
    NSDictionary *statusLookup;
    NSAttributedString *newlineAttributedString;
    AIHTMLDecoder *htmlDecoder;
    + ISO8601DateFormatter *formatter;
    }
    + (NSAttributedString *)readFile:(NSString *)filePath withOptions:(NSDictionary *)options;
    --- a/Source/AIXMLChatlogConverter.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/AIXMLChatlogConverter.m Mon Jun 03 17:17:12 2013 -0400
    @@ -22,7 +22,7 @@
    #import <Adium/AIAccountControllerProtocol.h>
    #import <Adium/AIContentControllerProtocol.h>
    #import <Adium/AIStatusControllerProtocol.h>
    -#import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
    +#import <AIUtilities/ISO8601DateFormatter.h>
    #import <AIUtilities/AIDateFormatterAdditions.h>
    #import <AIUtilities/AIStringAdditions.h>
    #import <AIUtilities/AIColorAdditions.h>
    @@ -140,7 +140,9 @@
    [adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK], @"notAtMyDesk",
    [adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_IN_OFFICE], @"notInTheOffice",
    [adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT], @"steppedOut",
    - nil];
    + nil];
    +
    + formatter = [[ISO8601DateFormatter alloc] init];
    }
    return self;
    @@ -148,6 +150,7 @@
    - (void)dealloc
    {
    + [formatter release];
    [statusLookup release];
    [htmlDecoder release];
    [super dealloc];
    @@ -211,7 +214,7 @@
    if ([type isEqualToString:@"message"] || [type isEqualToString:@"action"]) {
    NSString *senderAlias = [[attributes objectForKey:@"alias"] stringValue];
    NSString *dateStr = [[attributes objectForKey:@"time"] stringValue];
    - NSDate *date = dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil;
    + NSDate *date = dateStr ? [formatter dateFromString:dateStr] : nil;
    NSString *sender = [[attributes objectForKey:@"sender"] stringValue];
    NSString *shownSender = (senderAlias ? senderAlias : sender);
    BOOL autoResponse = [[[attributes objectForKey:@"auto"] stringValue] isEqualToString:@"true"];
    @@ -300,7 +303,7 @@
    [output appendAttributedString:newlineAttributedString];
    } else if ([type isEqualToString:@"status"]) {
    NSString *dateStr = [[attributes objectForKey:@"time"] stringValue];
    - NSDate *date = dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil;
    + NSDate *date = dateStr ? [formatter dateFromString:dateStr] : nil;
    NSString *status = [[attributes objectForKey:@"type"] stringValue];
    NSMutableString *messageXML = [NSMutableString string];
    --- a/Source/BGICLogImportController.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/BGICLogImportController.m Mon Jun 03 17:17:12 2013 -0400
    @@ -21,7 +21,7 @@
    #import <Adium/AIXMLElement.h>
    #import <Adium/AIHTMLDecoder.h>
    #import <Adium/AILoginControllerProtocol.h>
    -#import <AIUtilities/NSCalendarDate+ISO8601Unparsing.h>
    +#import <AIUtilities/ISO8601DateFormatter.h>
    #import <AIUtilities/AIFileManagerAdditions.h>
    // InstantMessage and other iChat transcript classes are from Spiny Software's Logorrhea, used with permission.
    @@ -108,13 +108,15 @@
    AIXMLAppender *appender = [AIXMLAppender documentWithPath:documentPath rootElement:rootElement];
    NSString *imagesPath = [appender.path stringByDeletingLastPathComponent];
    + ISO8601DateFormatter *formatter = [[ISO8601DateFormatter alloc] init];
    + formatter.includeTime = YES;
    // sequentially add the messages from the iChat transcript sans attributed text features
    for(NSInteger i = 0; i < [[rawChat objectAtIndex:2] count]; i++)
    {
    NSMutableArray *attributeKeys = [NSMutableArray arrayWithObjects:@"sender", @"time", nil];
    NSMutableArray *attributeValues = [NSMutableArray arrayWithObjects:
    ([[(InstantMessage *)[[rawChat objectAtIndex:2] objectAtIndex:i] sender] senderID] != nil ? [[(InstantMessage *)[[rawChat objectAtIndex:2] objectAtIndex:i] sender] senderID] : @""),
    - [[[(InstantMessage *)[[rawChat objectAtIndex:2] objectAtIndex:i] date] dateWithCalendarFormat:nil timeZone:nil] ISO8601DateString],
    + [formatter stringFromDate:[[(InstantMessage *)[[rawChat objectAtIndex:2] objectAtIndex:i] date] dateWithCalendarFormat:nil timeZone:nil]],
    nil];
    NSMutableString *chatContents = [[xhtmlDecoder encodeHTML:[[[rawChat objectAtIndex:2] objectAtIndex:i] text] imagesPath:imagesPath] mutableCopy];
    --- a/Source/DCMessageContextDisplayPlugin.h Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/DCMessageContextDisplayPlugin.h Mon Jun 03 17:17:12 2013 -0400
    @@ -25,12 +25,13 @@
    #define CONTEXT_DISPLAY_DEFAULTS @"MessageContextDisplayDefaults"
    -@class SMSQLiteLoggerPlugin;
    +@class SMSQLiteLoggerPlugin, ISO8601DateFormatter;
    @interface DCMessageContextDisplayPlugin : AIPlugin {
    BOOL isObserving;
    BOOL shouldDisplay;
    NSInteger linesToDisplay;
    + ISO8601DateFormatter *formatter;
    }
    + (DCMessageContextDisplayPlugin *)sharedInstance;
    --- a/Source/DCMessageContextDisplayPlugin.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/DCMessageContextDisplayPlugin.m Mon Jun 03 17:17:12 2013 -0400
    @@ -32,7 +32,7 @@
    #import <LMX/LMXParser.h>
    #import <Adium/AIXMLElement.h>
    #import <AIUtilities/AIStringAdditions.h>
    -#import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
    +#import <AIUtilities/ISO8601DateFormatter.h>
    #import <Adium/AIHTMLDecoder.h>
    #define RESTORED_CHAT_CONTEXT_LINE_NUMBER 50
    @@ -77,6 +77,7 @@
    [adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_LOGGING];
    sharedInstance = self;
    + formatter = [[ISO8601DateFormatter alloc] init];
    }
    /**
    @@ -381,7 +382,7 @@
    NSString *timeString = [attributes objectForKey:@"time"];
    //Create the context object
    if (timeString) {
    - NSCalendarDate *timeVal = [NSCalendarDate calendarDateWithString:timeString];
    + NSDate *timeVal = [formatter dateFromString:timeString];
    NSString *autoreplyAttribute = [attributes objectForKey:@"auto"];
    NSString *sender = [NSString stringWithFormat:@"%@.%@", serviceName, [attributes objectForKey:@"sender"]];
    @@ -430,7 +431,7 @@
    NSString *timeString = [attributes objectForKey:@"time"];
    if (timeString) {
    - NSCalendarDate *timeVal = [NSCalendarDate calendarDateWithString:timeString];
    + NSDate *timeVal = [formatter dateFromString:timeString];
    AIContentStatus *status = [[AIContentStatus alloc] initWithChat:chat source:nil destination:nil date:timeVal];
    --- a/Source/ESDebugController.m Sun Jun 02 12:26:04 2013 -0400
    +++ b/Source/ESDebugController.m Mon Jun 03 17:17:12 2013 -0400
    @@ -172,7 +172,7 @@
    {
    if (!debugLogFile) {
    NSFileManager *mgr = [NSFileManager defaultManager];
    - NSCalendarDate *date = [NSCalendarDate calendarDate];
    + NSDate *date = [NSDate date];
    NSString *folder, *dateString, *filename, *pathname;
    NSUInteger counter = 0;
    int fd;
    @@ -193,7 +193,7 @@
    *the date is in YYYY-MM-DD format. duplicates are disambiguated with
    *' 1', ' 2', ' 3', etc. appendages.
    */
    - filename = dateString = [date descriptionWithCalendarFormat:@"%Y-%m-%d"];
    + filename = dateString = [date descriptionWithCalendarFormat:@"%Y-%m-%d" timeZone:nil locale:nil];
    while([mgr fileExistsAtPath:(pathname = [folder stringByAppendingPathComponent:[filename stringByAppendingPathExtension:@"log"]])]) {
    filename = [dateString stringByAppendingFormat:@" %lu", ++counter];
    }
    --- a/Source/scandate.h Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -/*Input: A string in UTF-8 encoding containing an ISO 8601 date within a parenthesis.
    - *Output:
    - *- Year, month, and date
    - *- Whether time was found
    - *- The hour, minute, and second of that time
    - *- The time zone offset as a single number of minutes
    - *- (Return value) Whether a date was found.
    - */
    -BOOL scandate(const char *sample,
    - unsigned long *outyear, unsigned long *outmonth, unsigned long *outdate,
    - BOOL *outHasTime, unsigned long *outhour, unsigned long *outminute, unsigned long *outsecond,
    - long *outtimezone);
    --- a/Source/scandate.m Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,149 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "scandate.h"
    -
    -BOOL scandate(const char *sample,
    - unsigned long *outyear, unsigned long *outmonth, unsigned long *outdate,
    - BOOL *outHasTime, unsigned long *outhour, unsigned long *outminute, unsigned long *outsecond,
    - long *outtimezone)
    -{
    - BOOL success = YES;
    - unsigned long component;
    -
    - const char *lastOpenParenthesis = NULL;
    -
    - //Read a '(', followed by a date.
    - //First, find the '('.
    - while (*sample != '\0') {
    - if (*sample == '(')
    - lastOpenParenthesis = sample;
    - ++sample;
    - }
    -
    - if (!lastOpenParenthesis) {
    - success = NO;
    - goto fail;
    - }
    - sample = lastOpenParenthesis;
    -
    - //current character is a '(' now, so skip over it.
    - ++sample; //start with the next character
    -
    - /*get the year*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outyear) *outyear = component;
    - }
    -
    - /*get the month*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outmonth) *outmonth = component;
    - }
    -
    - /*get the date*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outdate) *outdate = component;
    - }
    -
    - if (*sample == 'T') {
    - ++sample; //start with the next character
    - if (outHasTime) *outHasTime = YES;
    -
    - /*get the hour*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outhour) *outhour = component;
    - }
    -
    - /*get the minute*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outminute) *outminute = component;
    - }
    -
    - /*get the second*/ {
    - while (*sample && (*sample < '0' || *sample > '9')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - component = strtoul(sample, (char **)&sample, 10);
    - if (outsecond) *outsecond = component;
    - }
    -
    - /*get the time zone*/ {
    - while (*sample && ((*sample < '0' || *sample > '9') && *sample != '-' && *sample != '+')) ++sample;
    - if (!*sample) {
    - success = NO;
    - goto fail;
    - }
    - long timezone_sign = 1;
    - if(*sample == '+') {
    - ++sample;
    - } else if(*sample == '-') {
    - timezone_sign = -1;
    - ++sample;
    - } else if (*sample) {
    - //There's something here, but it's not a time zone. Bail.
    - success = NO;
    - goto fail;
    - }
    - long timezone_hr = 0;
    - if (*sample >= '0' || *sample <= '9') {
    - timezone_hr += *(sample++) - '0';
    - }
    - if (*sample >= '0' || *sample <= '9') {
    - timezone_hr *= 10;
    - timezone_hr += *(sample++) - '0';
    - }
    - long timezone_min = 0;
    - if (*sample >= '0' || *sample <= '9') {
    - timezone_min += *(sample++) - '0';
    - }
    - if (*sample >= '0' || *sample <= '9') {
    - timezone_min *= 10;
    - timezone_min += *(sample++) - '0';
    - }
    - if (outtimezone) *outtimezone = (timezone_hr * 60 + timezone_min) * timezone_sign;
    - }
    - }
    -
    -fail:
    - return success;
    -}
    --- a/UnitTests/TestScandate.h Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,25 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import <Cocoa/Cocoa.h>
    -#import <SenTestingKit/SenTestingKit.h>
    -
    -@interface TestScandate : SenTestCase
    -{}
    -
    -- (void) testEricRichiesTwitterTimelineLogFilename;
    -
    -@end
    --- a/UnitTests/TestScandate.m Sun Jun 02 12:26:04 2013 -0400
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,48 +0,0 @@
    -/*
    - * Adium is the legal property of its developers, whose names are listed in the copyright file included
    - * with this source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
    - * General Public License as published by the Free Software Foundation; either version 2 of the License,
    - * or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
    - * Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License along with this program; if not,
    - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    - */
    -
    -#import "TestScandate.h"
    -
    -#import "scandate.h"
    -
    -@implementation TestScandate
    -
    -- (void) testEricRichiesTwitterTimelineLogFilename {
    - static const char EricRichiesTwitterTimelineLogFilename[] = "timeline (edr1084) (2010-12-18T17.42.58-0500).chatlog";
    - unsigned long correctYear = 2010, correctMonth = 12, correctDayOfMonth = 18;
    - BOOL correctDidFindTime = YES;
    - unsigned long correctHour = 17, correctMinute = 42, correctSecond = 58;
    - long correctTimeZoneOffsetInMinutes = -(5 * 60);
    - BOOL correctDidFindDate = YES;
    -
    - unsigned long foundYear, foundMonth, foundDayOfMonth;
    - BOOL didFindTime;
    - unsigned long foundHour, foundMinute, foundSecond;
    - long foundTimeZoneOffsetInMinutes;
    -
    - BOOL didFindDate = scandate(EricRichiesTwitterTimelineLogFilename, &foundYear, &foundMonth, &foundDayOfMonth, &didFindTime, &foundHour, &foundMinute, &foundSecond, &foundTimeZoneOffsetInMinutes);
    - STAssertEquals(didFindDate, correctDidFindDate, @"No date found in this string! '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundYear, correctYear, @"Wrong year found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundMonth, correctMonth, @"Wrong month found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundDayOfMonth, correctDayOfMonth, @"Wrong day-of-month found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(didFindTime, correctDidFindTime, @"No time found in this string! '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundHour, correctHour, @"Wrong hour found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundMinute, correctMinute, @"Wrong minute found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundSecond, correctSecond, @"Wrong second found in '%s'", EricRichiesTwitterTimelineLogFilename);
    - STAssertEquals(foundTimeZoneOffsetInMinutes, correctTimeZoneOffsetInMinutes, @"Wrong time zone offset found in '%s'", EricRichiesTwitterTimelineLogFilename);
    -}
    -
    -@end