├── .swift-version ├── GrandTimeDemo ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── LICENSE ├── AppDelegate.swift ├── Base.lproj │ └── LaunchScreen.storyboard ├── ViewController.swift ├── Info.plist ├── SceneDelegate.swift ├── TimerViewController.swift └── README.md ├── Tests ├── LinuxMain.swift └── GrandTimeTests │ ├── XCTestManifests.swift │ └── GrandTimeTests.swift ├── GrandTime.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── GrandTime_Info.plist ├── GrandTimeTests_Info.plist ├── xcshareddata │ └── xcschemes │ │ ├── GrandTime-Package.xcscheme │ │ └── GrandTimeDemo.xcscheme └── project.pbxproj ├── Package.swift ├── LICENSE ├── .gitignore ├── Sources ├── GrandTimer.swift ├── TimeSpan.swift └── GrandTime.swift └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /GrandTimeDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import GrandTimeTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += GrandTimeTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/GrandTimeTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(GrandTimeTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/GrandTimeTests/GrandTimeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import GrandTime 3 | 4 | final class GrandTimeTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(GrandTime().text, "Hello, World!") 10 | } 11 | 12 | static var allTests = [ 13 | ("testExample", testExample), 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/GrandTime_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/GrandTimeTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "GrandTime", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "GrandTime", 12 | targets: ["GrandTime"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "GrandTime",dependencies: [], path:"Sources"), 23 | .testTarget( 24 | name: "GrandTimeTests", 25 | dependencies: ["GrandTime"]), 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Stan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GrandTimeDemo/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Stan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GrandTimeDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by Stan Hu on 2020/3/17. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .DS_Store 6 | */.DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /GrandTimeDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GrandTimeDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /GrandTimeDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by Stan Hu on 2020/3/17. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | let btnAddMonth = UIButton() 12 | let btnDeductMonth = UIButton() 13 | let lblMonth = UILabel() 14 | var time:DateTime! 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | view.backgroundColor = UIColor.white 18 | time = DateTime.parse("2017-12-12", format: "yyyy-MM-dd")! 19 | 20 | lblMonth.text = DateTime.now.format() 21 | lblMonth.frame = CGRect(x: 100, y: 300, width: 250, height: 40) 22 | view.addSubview(lblMonth) 23 | 24 | btnAddMonth.setTitle("加一个月", for: .normal) 25 | btnAddMonth.setTitleColor(UIColor.black, for: .normal) 26 | btnAddMonth.frame = CGRect(x: 100, y: 100, width: 100, height: 40) 27 | btnAddMonth.addTarget(self, action: #selector(addMonth), for: .touchUpInside) 28 | view.addSubview(btnAddMonth) 29 | 30 | btnDeductMonth.setTitle("减一个月", for: .normal) 31 | btnDeductMonth.setTitleColor(UIColor.black, for: .normal) 32 | btnDeductMonth.frame = CGRect(x: 100, y: 200, width: 100, height: 40) 33 | btnDeductMonth.addTarget(self, action: #selector(deductMonth), for: .touchUpInside) 34 | view.addSubview(btnDeductMonth) 35 | // Do any additional setup after loading the view. 36 | 37 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(nextPage)) 38 | } 39 | 40 | @objc func addMonth() { 41 | time.selfAddMonth(1) 42 | print(time!) 43 | lblMonth.text = time.format() 44 | } 45 | 46 | @objc func deductMonth() { 47 | time.selfAddMonth(-1) 48 | print(time!) 49 | lblMonth.text = time.format() 50 | } 51 | 52 | @objc func nextPage() { 53 | navigationController?.pushViewController(TimerViewController(), animated: true) 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /GrandTimeDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/xcshareddata/xcschemes/GrandTime-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /GrandTimeDemo/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by Stan Hu on 2020/3/17. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let sc = (scene as? UIWindowScene ) else { return } 20 | window = UIWindow(windowScene: sc) 21 | let mainViewController = ViewController() 22 | let rootNavigationController = UINavigationController(rootViewController: mainViewController) 23 | window?.rootViewController = rootNavigationController 24 | window?.makeKeyAndVisible() 25 | } 26 | 27 | func sceneDidDisconnect(_ scene: UIScene) { 28 | // Called as the scene is being released by the system. 29 | // This occurs shortly after the scene enters the background, or when its session is discarded. 30 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 31 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 32 | } 33 | 34 | func sceneDidBecomeActive(_ scene: UIScene) { 35 | // Called when the scene has moved from an inactive state to an active state. 36 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 37 | } 38 | 39 | func sceneWillResignActive(_ scene: UIScene) { 40 | // Called when the scene will move from an active state to an inactive state. 41 | // This may occur due to temporary interruptions (ex. an incoming phone call). 42 | } 43 | 44 | func sceneWillEnterForeground(_ scene: UIScene) { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | func sceneDidEnterBackground(_ scene: UIScene) { 50 | // Called as the scene transitions from the foreground to the background. 51 | // Use this method to save data, release shared resources, and store enough scene-specific state information 52 | // to restore the scene back to its current state. 53 | } 54 | 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/xcshareddata/xcschemes/GrandTimeDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /GrandTimeDemo/TimerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerViewController.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by Stan Hu on 2020/3/17. 6 | // 7 | 8 | import UIKit 9 | 10 | class TimerViewController: UIViewController { 11 | let lblTImerBlock = UILabel() 12 | let lblTimer = UILabel() 13 | var timer:GrandTimer? 14 | var timer2:GrandTimer? 15 | var seco = 0 16 | var seco2 = 0 17 | var isPause = false 18 | let btnPause = UIButton() 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | view.backgroundColor = UIColor.white 22 | lblTimer.frame = CGRect(x: 100, y: 100, width: 200, height: 50) 23 | view.addSubview(lblTimer) 24 | lblTImerBlock.frame = CGRect(x: 100, y: 300, width: 200, height: 50) 25 | view.addSubview(lblTImerBlock) 26 | 27 | btnPause.setTitle("暂停", for: .normal) 28 | btnPause.setTitleColor(UIColor.black, for: .normal) 29 | btnPause.frame = CGRect(x: 100, y: 500, width: 100, height: 40) 30 | btnPause.addTarget(self, action: #selector(pauseTimer(_:)), for: .touchUpInside) 31 | view.addSubview(btnPause) 32 | 33 | // let dispatch = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT) 34 | // timer = GrandTimer.scheduleTimerWithTimeSpan(TimeSpan.fromSeconds(1), target: self, sel: #selector(TimerViewController.tick), userInfo: nil, repeats: true, dispatchQueue: dispatch) 35 | // timer?.fire() 36 | // GrandTimer.every(TimeSpan.fromSeconds(5)) { 37 | // self.seco2 = self.seco2 + 1 38 | // self.lblTimer.text = "\(self.seco2)" 39 | // } 40 | 41 | // timer = GrandTimer.scheduleTimerWithTimeSpan(TimeSpan.fromSeconds(1), block: { 42 | // self.seco2 = self.seco2 + 1 43 | // self.lblTimer.text = "\(self.seco2)" 44 | // 45 | // }, repeats: true, dispatchQueue: dispatch) 46 | 47 | 48 | //Issue2: I must use a var to receive the GrandTimer static func which is to return a GrandTimer instance. I don't know why 49 | //IF this werid stuff can not fix . I will feel unhappy. 50 | // I know wahy this weill not work .because the when out of the code bound, and there is no strong refrence to this timer. so the system will recycle the 51 | //timer .So it need a var to receive the Timer 52 | weak var weakSelf = self 53 | //使用者要注意,这个timer本身是weak的,所以需要一个外部变量来强引用, 54 | //如果要让timer正确地释放内存,那么要使用weakself 55 | timer = GrandTimer.every(TimeSpan.fromSeconds(1)) { 56 | weakSelf!.seco2 = weakSelf!.seco2 + 1 57 | weakSelf!.lblTimer.text = "\(weakSelf!.seco2)" 58 | } 59 | lblTImerBlock.text = "3秒后变红" 60 | timer?.fire() 61 | 62 | timer2 = GrandTimer.after(TimeSpan.fromSeconds(3), block: { 63 | weakSelf?.lblTImerBlock.backgroundColor = UIColor.red 64 | }) 65 | timer2?.fire() 66 | 67 | } 68 | 69 | 70 | 71 | func tick() { 72 | seco = seco + 1 73 | DispatchQueue.main.async { 74 | self.lblTimer.text = "\(self.seco)" 75 | } 76 | } 77 | 78 | @objc func pauseTimer(_ sender: UIButton) { 79 | isPause = !isPause 80 | if isPause{ 81 | timer?.pause() 82 | } 83 | else{ 84 | timer?.fire() 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Sources/GrandTimer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GrandTimer.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by HuStan on 6/26/16. 6 | // Copyright © 2016 StanHu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class GrandTimer: NSObject { 12 | 13 | struct timerFlags { 14 | static var timerIsInvalid:UInt32 = 0 15 | } 16 | 17 | var timeSpan:TimeSpan? 18 | weak var target:AnyObject? 19 | var selector:Selector? 20 | internal var userInfo:AnyObject? 21 | var repeats:Bool = false 22 | var privateSerialQueue:DispatchQueue? 23 | var timer:DispatchSource? 24 | var block:(()->Void)? 25 | var isNeedCount = false 26 | 27 | fileprivate override init() { 28 | super.init() 29 | } 30 | 31 | private convenience init(timespan:TimeSpan,target:AnyObject,sel:Selector,userInfo:AnyObject?,repeats:Bool,dispatchQueue:DispatchQueue){ 32 | self.init() 33 | self.timeSpan = timespan 34 | self.target = target 35 | self.selector = sel 36 | self.userInfo = userInfo 37 | self.repeats = repeats 38 | self.privateSerialQueue = dispatchQueue 39 | self.timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: self.privateSerialQueue) as? DispatchSource 40 | } 41 | 42 | private convenience init(timespan:TimeSpan,block:@escaping ()->Void, repeats:Bool,dispatchQueue:DispatchQueue) { 43 | self.init() 44 | self.timeSpan = timespan 45 | self.block = block 46 | self.repeats = repeats 47 | self.privateSerialQueue = dispatchQueue 48 | self.timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: UInt(0)), queue: self.privateSerialQueue) as? DispatchSource 49 | } 50 | 51 | public static func scheduleTimerWithTimeSpan(_ timespan:TimeSpan,target:AnyObject,sel:Selector,userInfo:AnyObject?,repeats:Bool,dispatchQueue:DispatchQueue)->GrandTimer{ 52 | let timer = GrandTimer(timespan: timespan, target: target, sel: sel, userInfo: userInfo, repeats: repeats, dispatchQueue: dispatchQueue) 53 | timer.schedule() 54 | return timer 55 | } 56 | 57 | public static func scheduleTimerWithTimeSpan(_ timespan:TimeSpan,block:@escaping ()->Void,repeats:Bool,dispatchQueue:DispatchQueue)->GrandTimer{ 58 | let timer = GrandTimer(timespan: timespan, block: block, repeats: repeats, dispatchQueue: dispatchQueue) 59 | timer.schedule() 60 | return timer 61 | } 62 | 63 | public static func after(_ timeSpan:TimeSpan,block:@escaping ()->Void)->GrandTimer{ 64 | let privateQueueName = "grandTimeAfter\(arc4random())" 65 | let dispatch = DispatchQueue(label: privateQueueName, attributes: DispatchQueue.Attributes.concurrent) 66 | let timer = GrandTimer.scheduleTimerWithTimeSpan(timeSpan, block: block, repeats: false, dispatchQueue: dispatch) 67 | return timer 68 | } 69 | 70 | public static func every(_ timeSpan:TimeSpan,block:@escaping ()->Void)->GrandTimer{ 71 | let privateQueueName = "grandTimeEvery\(arc4random())" 72 | let dispatch = DispatchQueue(label: privateQueueName, attributes: DispatchQueue.Attributes.concurrent) 73 | let timer = GrandTimer.scheduleTimerWithTimeSpan(timeSpan, block: block, repeats: true, dispatchQueue: dispatch) 74 | return timer 75 | } 76 | 77 | open func schedule() { 78 | resetTimerProperties() 79 | weak var weakSelf = self 80 | self.timer!.setEventHandler { 81 | weakSelf?.timerFired() 82 | } 83 | timer?.resume() 84 | } 85 | 86 | deinit{ 87 | invalidate() 88 | } 89 | 90 | open func fire() { 91 | isNeedCount = true 92 | } 93 | 94 | open func invalidate() { 95 | if !OSAtomicTestAndSetBarrier(7, &timerFlags.timerIsInvalid) { 96 | if let timer = self.timer{ 97 | self.privateSerialQueue!.async(execute: { 98 | timer.cancel() 99 | }) 100 | } 101 | } 102 | } 103 | 104 | open func pause() { 105 | isNeedCount = false 106 | } 107 | 108 | 109 | open func timerFired() { 110 | if !isNeedCount{ 111 | return 112 | } 113 | if OSAtomicAnd32OrigBarrier(1, &timerFlags.timerIsInvalid) < 0{ 114 | return 115 | } 116 | if let blk = block{ 117 | DispatchQueue.main.async(execute: { 118 | blk() 119 | }) 120 | } 121 | 122 | let _ = self.target?.perform(self.selector!, with: self) 123 | if !self.repeats { 124 | self.invalidate() 125 | } 126 | } 127 | 128 | var _tolerance:TimeSpan = TimeSpan() 129 | open var tolerance:TimeSpan?{ 130 | set{ 131 | objc_sync_enter(self) 132 | if newValue != nil && _tolerance != newValue{ 133 | _tolerance = newValue! 134 | } 135 | resetTimerProperties() 136 | objc_sync_exit(self) 137 | 138 | } 139 | get{ 140 | objc_sync_enter(self) 141 | let t = _tolerance 142 | objc_sync_exit(self) 143 | return t 144 | } 145 | } 146 | 147 | func resetTimerProperties() { 148 | let intervalInNanoseconds = DispatchTimeInterval.milliseconds(self.timeSpan!.ticks) 149 | self.timer!.schedule(deadline: DispatchTime.now() + intervalInNanoseconds, repeating: intervalInNanoseconds) 150 | } 151 | 152 | override open var description: String{ 153 | return "timeSpan = \(String(describing: timeSpan)) target = \(String(describing: target)) selector = \(String(describing: selector)) userinfo = \(String(describing: userInfo)) repeats = \(repeats) timer= \(String(describing: timer))" 154 | } 155 | 156 | 157 | } 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GrandTime 2 | 【开发GrandTime的目的很简单,是用来代替NSDate,它提供了用户友好的,方便的简单的API,同时还有一个弱Timer,用来代替NSTimer】 3 | 4 | 5 | 6 | 【GrandTime是一个简单的,用户友好的同时也很强力的时间工具,大家都明白的,iOS自带的NSDate类很不好,非常难用,所以我开发了GrandTime来代替NSDate(并非所有功能),GrandTime由DateTime,TimeSpan 和 GrandTimer组成, GrandTimer是一个弱Timer,它可以自动回收内存当ViewController被pop出去时,下面我将在demo代码里战展示】 7 | 8 | ## 【关键特点】 9 | + 【非常友好和简单的API设计】 10 | + 【使用专门的类TimeSpan来表示时间间隔】 11 | + 【重载了操作符,可以使用操作符来极为简单地操作DateTime】 12 | + 【GrandTimer是一个weak 计时器。当ViewController被弹出时,他会自动停止调用并销毁】 13 | 14 | ## 【系统要求】 15 | 【Xcode 7.3 and iOS 8.0 最新的Swift版本】 16 | 17 | ##【安装】 18 | + 【使用Cocoapods安装, pod 'GrandTime'】 19 | + 【手动导入,只要把项目拖进你的文件就行】 20 | 21 | ## 【怎么使用】 22 | 【请参考以下代码】 23 | 24 | ```Swift 25 | print("----------------------------------下面是DateTime----------------------------------") 26 | 27 | //正面看看构造函数 28 | let a = DateTime() //直接初始化 29 | print(a) 30 | let c = DateTime(date: NSDate(timeInterval: 3600, sinceDate: NSDate())) //使用NSDate初始化 31 | print(c) 32 | let e = DateTime(tick: 1000000) //使用Tick初始化 从1970年开始 33 | print(e) 34 | let f = DateTime(tickSinceNow: 60000) //使用Tick初始化 从现在年开始 35 | print(f) 36 | let g = DateTime(timestamp: 100000)//使用Stamp初始化 37 | print(g) 38 | let h = DateTime(year: 2008, month: 2, day: 29) //使用年月日初始化 39 | print(h) 40 | let i = DateTime(year: 2016, month: 12, day: 12, hour: 11, minute: 44, second: 12, millisecond: 111)!//使用年月日时分秒毫秒初始化 41 | print(i) 42 | 43 | 44 | let timeSpanOneMinute = TimeSpan.fromMinuts(1) //声明一个一分钟的TimeSpan 45 | let dToOneMinuteBefore = a - timeSpanOneMinute // 一分钟前 46 | print("一分钟前\(dToOneMinuteBefore)") 47 | let dToOneMinuteAfter = a + timeSpanOneMinute // 一分钟后 48 | print("一分钟后\(dToOneMinuteAfter)") 49 | 50 | //两个DateTime相减生成一个TimeSpan 51 | let span = c - a 52 | 53 | print("a和c相差一小时\(span)") 54 | 55 | print("a>c:\(a>c)") 56 | rint("ac:\(a>c)") 56 | rint("a TimeSpan { 23 | let tick = left.ticks + right.ticks 24 | assert(tick < TimeSpan.Max!.ticks,"two timespan add can not big than max time span") 25 | return TimeSpan(ticks: tick) 26 | } 27 | 28 | public func -(left:TimeSpan,right:TimeSpan) -> TimeSpan { 29 | let tick = left.ticks - right.ticks 30 | assert(tick > 0,"two timespan subtruct can not less than 0") 31 | return TimeSpan(ticks: tick) 32 | } 33 | public func <(lhs: TimeSpan, rhs: TimeSpan) -> Bool 34 | { 35 | return lhs.compareTo(rhs) < 0 36 | } 37 | public func <=(lhs: TimeSpan, rhs: TimeSpan) -> Bool 38 | { 39 | return lhs.compareTo(rhs) <= 0 40 | } 41 | 42 | public func >(lhs: TimeSpan, rhs: TimeSpan) -> Bool 43 | { 44 | return lhs.compareTo(rhs) > 0 45 | } 46 | public func >=(lhs: TimeSpan, rhs: TimeSpan) -> Bool 47 | { 48 | return lhs.compareTo(rhs) >= 0 49 | } 50 | 51 | public func == (lhs: TimeSpan, rhs: TimeSpan) -> Bool 52 | { 53 | return lhs.compareTo(rhs) == 0 54 | } 55 | 56 | 57 | open class TimeSpan: NSObject,Comparable { 58 | 59 | 60 | public static let Max = TimeSpan(days: 100000, hours: 23, minutes: 59, seconds: 59, milliseconds: 999) 61 | public static let Zero = TimeSpan(days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0) 62 | fileprivate var _day = 0 63 | fileprivate var _hour = 0 64 | fileprivate var _minute = 0 65 | fileprivate var _second = 0 66 | fileprivate var _millisecond = 0 67 | fileprivate var _ticks = 0 68 | public override init() { 69 | super.init() 70 | } 71 | 72 | public convenience init(ticks:Int) { 73 | self.init() 74 | assert(ticks >= 0, "ticks must >= 0") 75 | _ticks = ticks 76 | setTimes() 77 | } 78 | 79 | public convenience init?(hours:Int,minutes:Int,seconds:Int) { 80 | self.init() 81 | if hours < 0 { 82 | print("TimeSpan warning: hours can not less than 0") 83 | return nil 84 | } 85 | else if hours > 23 { 86 | print("TimeSpan warning: hours can not bigger than 23") 87 | return nil 88 | } 89 | else if minutes < 0 { 90 | print("TimeSpan warning: minutes can not less than 0") 91 | return nil 92 | } 93 | else if minutes > 59 { 94 | print("TimeSpan warning: minutes can not bigger than 59") 95 | return nil 96 | } 97 | else if seconds < 0 { 98 | print("TimeSpan warning: seconds can not less than 0") 99 | return nil 100 | } 101 | else if seconds > 59{ 102 | print("TimeSpan warning: seconds can not bigger than 59") 103 | return nil 104 | } 105 | _hour = hours 106 | _minute = minutes 107 | _second = seconds 108 | _ticks = hours * TickPerHour + _minute * TickPerMinute + _second * TickPerSecond 109 | } 110 | 111 | public convenience init?(days:Int,hours:Int,minutes:Int,seconds:Int) { 112 | if days < 0 { 113 | print("TimeSpan warning: days can not less than 0") 114 | return nil 115 | } 116 | self.init(hours:hours,minutes: minutes,seconds: seconds) 117 | _day = days 118 | _ticks = _ticks + _day * TickPerDay 119 | } 120 | 121 | public convenience init?(days:Int,hours:Int,minutes:Int,seconds:Int,milliseconds:Int) { 122 | if milliseconds < 0 { 123 | print("TimeSpan warning: milliseconds can not less than 0") 124 | return nil 125 | } 126 | else if milliseconds > 999{ 127 | print("TimeSpan warning: milliseconds can not bigger than 999") 128 | return nil 129 | } 130 | self.init(days:days,hours:hours,minutes: minutes,seconds: seconds) 131 | _millisecond = milliseconds 132 | _ticks = _ticks + milliseconds 133 | } 134 | 135 | open var days:Int{ 136 | get{ 137 | return _day 138 | } 139 | set{ 140 | assert(newValue > 0, "days must > 0") 141 | _ticks = _ticks - _day * TickPerDay 142 | _day = newValue 143 | _ticks = _ticks + _day * TickPerDay 144 | } 145 | } 146 | 147 | open var hours:Int{ 148 | get{ 149 | return _hour 150 | } 151 | set{ 152 | assert(newValue > 0 && newValue < 24, "hours must > 0 and < 24") 153 | _ticks = _ticks - _hour * TickPerHour 154 | _hour = newValue 155 | _ticks = _ticks + _hour * TickPerHour 156 | } 157 | } 158 | 159 | open var minutes:Int{ 160 | get{ 161 | return _minute 162 | } 163 | set{ 164 | assert(newValue > 0 && newValue < 60, "minutes must > 0 and < 60") 165 | _ticks = _ticks - _minute * TickPerMinute 166 | _minute = newValue 167 | _ticks = _ticks + _minute * TickPerMinute 168 | } 169 | } 170 | 171 | open var seconds:Int{ 172 | get{ 173 | return _second 174 | } 175 | set{ 176 | assert(newValue > 0 && newValue < 60, "seconds must > 0 and < 60") 177 | _ticks = _ticks - _second * TickPerSecond 178 | _second = newValue 179 | _ticks = _ticks + _minute * TickPerMinute 180 | 181 | } 182 | } 183 | 184 | open var milliseconds:Int{ 185 | get{ 186 | return _millisecond 187 | } 188 | set{ 189 | assert(newValue > 0 && newValue < 999, "milliseconds must > 0 and < 999") 190 | _ticks = _ticks - _millisecond 191 | _millisecond = newValue 192 | _ticks = _ticks - _millisecond 193 | } 194 | } 195 | 196 | open var ticks:Int{ 197 | get{ 198 | return _ticks 199 | } 200 | set{ 201 | _ticks = newValue 202 | setTimes() 203 | } 204 | } 205 | 206 | open var totalDays:Double{ 207 | return Double(_ticks) / Double(TickPerDay) 208 | } 209 | 210 | open var totalHours:Double{ 211 | return Double(_ticks) / Double(TickPerHour) 212 | } 213 | 214 | open var totalMinutes:Double{ 215 | return Double(_ticks) / Double(TickPerMinute) 216 | } 217 | 218 | open var totalSeconds:Double{ 219 | return Double(_ticks) / Double(TickPerSecond) 220 | } 221 | 222 | public static func compare(_ t1:TimeSpan,t2:TimeSpan)->Int{ 223 | if t1.ticks > t2.ticks { 224 | return 1 225 | } 226 | else if t1.ticks < t2.ticks{ 227 | return -1 228 | } 229 | return 0 230 | } 231 | 232 | public static func equl(_ t1:TimeSpan,t2:TimeSpan)->Bool{ 233 | return t1.ticks == t2.ticks 234 | } 235 | 236 | public static func fromDays(_ days:Double)->TimeSpan{ 237 | assert(days >= 0, "days must >= 0") 238 | return TimeSpan(ticks: Int(days * Double(TickPerDay))) 239 | } 240 | 241 | public static func fromHours(_ hours:Double)->TimeSpan{ 242 | assert(hours >= 0, "hours must >= 0") //这里就不需要<24了 243 | return TimeSpan(ticks: Int(hours * Double(TickPerHour))) 244 | } 245 | 246 | public static func fromMinuts(_ minutes:Double)->TimeSpan{ 247 | assert(minutes >= 0, "minutes must >= 0")//这里就不需要<60了 248 | return TimeSpan(ticks: Int(minutes * Double(TickPerMinute))) 249 | } 250 | 251 | public static func fromSeconds(_ seconds:Double)->TimeSpan{ 252 | assert(seconds >= 0, "minutes must >= 0")//这里就不需要<60了 253 | return TimeSpan(ticks: Int(seconds * Double(TickPerSecond))) 254 | } 255 | public static func fromTicks(_ ticks:Int)->TimeSpan{ 256 | assert(ticks >= 0, "minutes must >= 0")//这里就不需要<60了 257 | return TimeSpan(ticks: ticks) 258 | } 259 | 260 | //默认格式: day hors:minutes:seconds:milliseconds 261 | //这个地方不太好处理 262 | //这里可能要用正则,字符單解析一直是个大难题,这就是为什么编译器这么难写 263 | //C#里面有-的TimeSpan 我觉得没有必要 264 | //这个还是要做一做的 265 | // 默认和格式是 dd HH:mm:ss 266 | //这时用个枚举就好了,只支持三种格式,选择太多其实并不好 267 | public static func parse(_ time:String,format:TimeSpanFormat)->TimeSpan?{ 268 | let t = time.trimmingCharacters(in: .whitespaces) 269 | switch format { 270 | case .dayFormat: 271 | if !DateTime.RegexTool.init("^\\d+\\s\\d{2}:\\d{2}:\\d{2}$").match(input: t){ 272 | print("you time \(time) did not comfirm the timespan format dd HH:mm:ss") 273 | return nil 274 | } 275 | let day = Int(t.split(separator: " ").first!)! 276 | let time = String(t.split(separator: " ").last!) 277 | let hour = Int(time.split(separator: ":")[0])! 278 | let min = Int(time.split(separator: ":")[1])! 279 | let sec = Int(time.split(separator: ":")[2])! 280 | return TimeSpan(days: day, hours: hour, minutes: min, seconds: sec) 281 | case .timeFormat: 282 | if !DateTime.RegexTool.init("^\\d{2}:\\d{2}:\\d{2}$").match(input: t){ 283 | print("you time \(time) did not comfirm the timespan format HH:mm:ss") 284 | return nil 285 | } 286 | let hour = Int(t.split(separator: ":")[0])! 287 | let min = Int(t.split(separator: ":")[1])! 288 | let sec = Int(t.split(separator: ":")[2])! 289 | return TimeSpan(days: 0, hours: hour, minutes: min, seconds: sec) 290 | case .msecFormat: 291 | if !DateTime.RegexTool.init("^\\d{2}:\\d{2}:\\d{2}:\\d{3}$").match(input: t){ 292 | print("you time \(time) did not comfirm the timespan format HH:mm:ss:SSS") 293 | return nil 294 | } 295 | let hour = Int(t.split(separator: ":")[0])! 296 | let min = Int(t.split(separator: ":")[1])! 297 | let sec = Int(t.split(separator: ":")[2])! 298 | let msec = Int(t.split(separator: ":")[3])! 299 | return TimeSpan(days: 0, hours: hour, minutes: min, seconds: sec, milliseconds: msec) 300 | case .allFormat: 301 | if !DateTime.RegexTool.init("^\\d+\\s\\d{2}:\\d{2}:\\d{2}:\\d{3}$").match(input: t){ 302 | print("you time \(time) did not comfirm the timespan format dd HH:mm:ss:SSS") 303 | return nil 304 | } 305 | let day = Int(t.split(separator: " ").first!)! 306 | let time = String(t.split(separator: " ").last!) 307 | let hour = Int(time.split(separator: ":")[0])! 308 | let min = Int(time.split(separator: ":")[1])! 309 | let sec = Int(time.split(separator: ":")[2])! 310 | let msec = Int(time.split(separator: ":")[3])! 311 | return TimeSpan(days: day, hours: hour, minutes: min, seconds: sec, milliseconds: msec) 312 | } 313 | } 314 | 315 | 316 | 317 | open func add(_ time:TimeSpan)->TimeSpan{ 318 | let tick = time.ticks + ticks 319 | assert(ticks < TimeSpan.Max!.ticks,"the added value must < max") 320 | return TimeSpan(ticks: tick) 321 | } 322 | 323 | 324 | open func compareTo(_ time:TimeSpan) -> Int { 325 | return TimeSpan.compare(self, t2: time) 326 | } 327 | 328 | // public override var hash: Int{ 329 | // return 330 | // } 331 | 332 | // public override func isEqual(object: AnyObject?) -> Bool { 333 | // if let time = object as TimeSpan{ 334 | // return time == self 335 | // } 336 | // return false 337 | // } 338 | 339 | 340 | open func subtract(_ time:TimeSpan) -> TimeSpan { 341 | let tick = ticks - time.ticks 342 | assert(ticks > 0,"tick must > 0") 343 | return TimeSpan(ticks: tick) 344 | } 345 | 346 | 347 | open override var description: String{ 348 | get{ 349 | let hour = String(format: "%02d", hours) 350 | let minute = String(format: "%02d", minutes) 351 | let second = String(format: "%02d", seconds) 352 | let millisecond = String(format: "%03d", milliseconds) 353 | if days > 0{ 354 | return "\(days) \(hour):\(minute):\(second):\(millisecond)" 355 | } 356 | else { 357 | return "\(hours):\(minute):\(second):\(millisecond)" 358 | } 359 | } 360 | } 361 | 362 | 363 | //这里有前面补0的问题,其实可以用格式的大小写来解决 364 | open func format(format:String = "dd HH:mm:ss") -> String { 365 | let minute = String(format: "%02d", minutes) 366 | let second = String(format: "%02d", seconds) 367 | let millisecond = String(format: "%03d", milliseconds) 368 | var result = format 369 | if format.contains("dd"){ 370 | result = result.replacingOccurrences(of: "dd", with: String(days)) 371 | } 372 | if format.contains("HH") { 373 | result = result.replacingOccurrences(of: "HH", with: String(format: "%02d", hours)) 374 | } 375 | if format.contains("mm") { 376 | result = result.replacingOccurrences(of: "mm", with: String(minute)) 377 | } 378 | if format.contains("ss") { 379 | result = result.replacingOccurrences(of: "ss", with: String(second)) 380 | } 381 | if format.contains("SSS") { 382 | result = result.replacingOccurrences(of: "SSS", with: String(millisecond)) 383 | } 384 | return result 385 | } 386 | 387 | fileprivate func setTimes(){ 388 | if _ticks > 0 { 389 | _day = _ticks / TickPerDay 390 | _hour = (_ticks - _day * TickPerDay) / TickPerHour 391 | _minute = (_ticks - _day * TickPerDay - _hour * TickPerHour) / TickPerMinute 392 | _second = (_ticks - _day * TickPerDay - _hour * TickPerHour - _minute * TickPerMinute) / TickPerSecond 393 | _millisecond = _ticks - _day * TickPerDay - _hour * TickPerHour - _minute * TickPerMinute - _second * TickPerSecond 394 | } 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /Sources/GrandTime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GrandTime.swift 3 | // GrandTimeDemo 4 | // 5 | // Created by HuStan on 6/23/16. 6 | // Copyright © 2016 StanHu. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | //这个类就完全参考C#的DateTime 11 | //基本功能先这样 12 | //下面有个定义, Timestamp是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数 13 | // ticks 表示0001 年 1 月 1 日午夜 00:00:00 以来所经历的 1000 微秒数,即Ticks的属性 1Ticks表示1毫秒 14 | // 所以这个要重写 15 | public enum DateTimeKind:Int{ 16 | case unspecified=0,utc,local 17 | } 18 | 19 | public enum DayOfWeek:Int{ 20 | case sunday = 0,monday,tuesday,wendesday,thursday,friday,saturday 21 | public var description:String{ 22 | switch self { 23 | case .monday: 24 | return "星期一" 25 | case .tuesday: 26 | return "星期二" 27 | case .wendesday: 28 | return "星期三" 29 | case .thursday: 30 | return "星期四" 31 | case .friday: 32 | return "星期五" 33 | case .saturday: 34 | return "星期六" 35 | case .sunday: 36 | return "星期天" 37 | } 38 | } 39 | } 40 | 41 | 42 | 43 | enum DisplayDateTimeStyleLanguage { 44 | case cn,us 45 | } 46 | 47 | public let LeapYearMonth = [31,29,31,30,31,30,31,31,30,31,30,31] 48 | public let NotLeapYearMonth = [31,28,31,30,31,30,31,31,30,31,30,31] 49 | //public let TickTo1970 = -62167219200 //这个时间戳是 0000-01-01 00:00:00的时间戳 50 | //public let TickTo1970 = -2197208776 //这个时间戳是 1900-01-01 00:00:00的时间戳。//从这个时间算吧,没办法 51 | //Issue 点 52 | // 用这个版本来发布到Cocoapods发现报错,但是我用模拟器完全OK,就是因为这个数,超出32位的大小了,当跑在真机上或者模拟 53 | //器上时,是可以用64位的,但是当选择generic ios device 来编译代码时,会出现可能是32位的情况,32位是不能放下这么大的数的, 54 | //所以就出错了 55 | //这个会有点难,要算的东西有点多 56 | //不行。这个问题还是得修复,因为打包时还是得有32位,会报错的 57 | public func -(left:DateTime,right:DateTime) -> TimeSpan? { 58 | let ms = left.dateTime.timeIntervalSince(right.dateTime) 59 | if ms < 0 { 60 | print("DateTime warning: left time must bigger then right time") 61 | return nil 62 | } 63 | return TimeSpan(ticks: Int(ms) * 1000) 64 | } 65 | 66 | public func +(left:DateTime,right:TimeSpan) -> DateTime { 67 | let ms = left.timestamp + right.seconds 68 | return DateTime(timestamp: ms)! 69 | } 70 | 71 | public func -(left:DateTime,right:TimeSpan) -> DateTime { 72 | var ms = left.timestamp - right.seconds 73 | if ms < 0 { 74 | ms = 0 75 | } 76 | return DateTime(timestamp: ms)! 77 | 78 | } 79 | 80 | public func >(lhs: DateTime, rhs: DateTime) -> Bool { 81 | let ms = lhs.dateTime.timeIntervalSince(rhs.dateTime) 82 | return ms > 0 83 | } 84 | 85 | public func >=(lhs: DateTime, rhs: DateTime) -> Bool { 86 | let ms = lhs.dateTime.timeIntervalSince(rhs.dateTime) 87 | return ms >= 0 88 | } 89 | 90 | public func <(lhs: DateTime, rhs: DateTime) -> Bool { 91 | let ms = lhs.dateTime.timeIntervalSince(rhs.dateTime) 92 | return ms < 0 93 | } 94 | 95 | 96 | public func <=(lhs: DateTime, rhs: DateTime) -> Bool { 97 | let ms = lhs.dateTime.timeIntervalSince(rhs.dateTime) 98 | return ms <= 0 99 | } 100 | 101 | 102 | open class DateTime: NSObject,Comparable { 103 | 104 | //最小是0001 年 1 月 1 日午夜 00:00:00 不用这个了,没有任何意义 105 | //open static let minDateTime = DateTime(ticks: 0) 106 | //这里最大值一直不明确,但是我已经试过了,iOS里面最大的NSDate是一个非常大有年份,我可以保证没有人可以用到的 107 | public static let maxDateTime = DateTime(date: Date(timeIntervalSince1970: TimeInterval(Int.max) / 100000)) 108 | 109 | 110 | 111 | fileprivate static let dateFormatter = DateFormatter() 112 | 113 | fileprivate static var dateComponent = DateComponents() 114 | 115 | static var timeZone = NSTimeZone.system{ 116 | didSet{ 117 | DateTime.dateFormatter.timeZone = timeZone 118 | } 119 | } 120 | let offSetInterval = DateTime.timeZone.secondsFromGMT() 121 | 122 | fileprivate var dateTime:Date{ 123 | // issue1 when in the init func .the disSet perocess do not work. this indeed not word. but it affect 124 | didSet{ 125 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 126 | } 127 | } 128 | open var timeZone = TimeZone.current //这个要不要自己封装? 先用系统的吧 129 | open var dateTImeKind = DateTimeKind.unspecified 130 | fileprivate var internalDateComponent:DateComponents 131 | public override init() { 132 | internalDateComponent = DateComponents() 133 | dateTime = Date() 134 | DateTime.dateFormatter.timeZone = DateTime.timeZone 135 | super.init() 136 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 137 | } 138 | 139 | 140 | public convenience init(date:Date) { 141 | self.init() 142 | dateTime = date 143 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 144 | } 145 | 146 | public convenience init?(timestamp:Int){ 147 | self.init() 148 | // if timestamp < TickTo1970 { 149 | // print("DateTime warning: timestamp can not less than -62167219200") 150 | // return nil 151 | // } 152 | if timestamp > Int.max / 100000{ 153 | print("DateTime warning: timestamp can not bigger than Int.max / 100000") 154 | return nil 155 | } 156 | dateTime = Date(timeIntervalSince1970: TimeInterval(timestamp)) 157 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 158 | } 159 | 160 | public convenience init?(year:Int,month:Int,day:Int) { 161 | self.init() 162 | if year < 0 { 163 | print("DateTime warning: year can not less than 0") 164 | return nil 165 | } 166 | else if year > 1000000 { 167 | print("DateTime warning: year can not bigger than 1000000") 168 | return nil 169 | } 170 | else if month < 1 { 171 | print("DateTime warning: month can not less than 1") 172 | return nil 173 | } 174 | else if month > 12 { 175 | print("DateTime warning: month can not bigger than 12") 176 | return nil 177 | } 178 | else if day < 1 { 179 | print("DateTime warning: day can not less than 0") 180 | return nil 181 | } 182 | else{ 183 | var maxDays = 30 184 | if DateTime.isLeapYeay(year){ 185 | maxDays = LeapYearMonth[month-1] 186 | } 187 | else{ 188 | maxDays = NotLeapYearMonth[month-1] 189 | } 190 | if day > maxDays { 191 | print("DateTime warning: day can not bigger than \(maxDays)") 192 | return nil 193 | } 194 | } 195 | DateTime.dateComponent.year = year 196 | DateTime.dateComponent.month = month 197 | DateTime.dateComponent.day = day 198 | if let date = Calendar.current.date(from: DateTime.dateComponent){ 199 | dateTime = date 200 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 201 | } 202 | else{ 203 | print("DateTime warning: time Component data have issue") 204 | return nil 205 | } 206 | } 207 | 208 | public convenience init?(year:Int,month:Int,day:Int,hour:Int,minute:Int,second:Int) { 209 | self.init(year:year,month:month,day:day) 210 | if hour < 0 { 211 | print("DateTime warning: hour can not less than 0") 212 | return nil 213 | } 214 | else if hour > 23 { 215 | print("DateTime warning: hour can not bigger than 23") 216 | return nil 217 | } 218 | else if minute < 0 { 219 | print("DateTime warning: minute can not less than 0") 220 | return nil 221 | } 222 | else if minute > 59 { 223 | print("DateTime warning: minute can not bigger than 59") 224 | return nil 225 | } 226 | else if second < 0 { 227 | print("DateTime warning: second can not less than 0") 228 | return nil 229 | } 230 | else if second > 59{ 231 | print("DateTime warning: second can not bigger than 59") 232 | return nil 233 | } 234 | DateTime.dateComponent.hour = hour 235 | DateTime.dateComponent.minute = minute 236 | DateTime.dateComponent.second = second 237 | if let date = Calendar.current.date(from: DateTime.dateComponent){ 238 | dateTime = date 239 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 240 | } 241 | else{ 242 | print("DateTime warning: time Component data have issue") 243 | return nil 244 | } 245 | } 246 | 247 | public convenience init?(year:Int,month:Int,day:Int,hour:Int,minute:Int,second:Int,millisecond:Int) { 248 | self.init(year:year,month:month,day:day,hour: hour,minute: minute,second: second) 249 | if millisecond < 0 { 250 | print("DateTime warning: millisecond can not less than 0") 251 | return nil 252 | } 253 | else if millisecond > 999{ 254 | print("DateTime warning: millisecond can not bigger than 999") 255 | return nil 256 | } 257 | DateTime.dateComponent.nanosecond = millisecond 258 | if let date = Calendar.current.date(from: DateTime.dateComponent){ 259 | dateTime = date 260 | internalDateComponent = (Calendar.current as NSCalendar).components([.weekday,.weekOfYear,.year,.month,.day,.hour,.minute,.second,.nanosecond,.quarter,.weekOfMonth,.weekOfYear], from: dateTime) 261 | } 262 | else{ 263 | print("DateTime warning: time Component data have issue") 264 | return nil 265 | } 266 | } 267 | 268 | open var local = Locale(identifier: "zh_CN") 269 | 270 | public static var now:DateTime{ 271 | return DateTime() 272 | } 273 | 274 | open var dayOfWeek:DayOfWeek{ 275 | get{ 276 | let cal = Calendar.current 277 | let cmp = (cal as NSCalendar).component([.weekday], from: dateTime) 278 | return DayOfWeek(rawValue: cmp - 1)! 279 | } 280 | } 281 | 282 | 283 | 284 | open var year:Int{ 285 | get{ 286 | return internalDateComponent.year! 287 | } 288 | set{ 289 | internalDateComponent.year = newValue 290 | if let date = Calendar.current.date(from: internalDateComponent){ 291 | dateTime = date 292 | } 293 | else{ 294 | print("DateTime warning: year have issue") 295 | } 296 | } 297 | } 298 | 299 | open var month:Int{ 300 | get{ 301 | return internalDateComponent.month! 302 | } 303 | set{ 304 | internalDateComponent.month = newValue 305 | if let date = Calendar.current.date(from: internalDateComponent){ 306 | dateTime = date 307 | } 308 | else{ 309 | print("DateTime warning: month have issue") 310 | } 311 | } 312 | } 313 | 314 | 315 | open var day:Int{ 316 | get{ 317 | return internalDateComponent.day! 318 | } 319 | set{ 320 | internalDateComponent.day = newValue 321 | if let date = Calendar.current.date(from: internalDateComponent){ 322 | dateTime = date 323 | } 324 | else{ 325 | print("DateTime warning: day have issue") 326 | } 327 | } 328 | } 329 | 330 | open var hour:Int{ 331 | get{ 332 | return internalDateComponent.hour! 333 | } 334 | set{ 335 | internalDateComponent.hour = newValue 336 | if let date = Calendar.current.date(from: internalDateComponent){ 337 | dateTime = date 338 | } 339 | else{ 340 | print("DateTime warning: hore have issue") 341 | } 342 | } 343 | } 344 | 345 | open var minute:Int{ 346 | get{ 347 | return internalDateComponent.minute! 348 | } 349 | set{ 350 | internalDateComponent.minute = newValue 351 | if let date = Calendar.current.date(from: internalDateComponent){ 352 | dateTime = date 353 | } 354 | else{ 355 | print("DateTime warning: minute have issue") 356 | } 357 | } 358 | } 359 | 360 | open var second:Int{ 361 | get{ 362 | return internalDateComponent.second! 363 | } 364 | set{ 365 | internalDateComponent.second = newValue 366 | if let date = Calendar.current.date(from: internalDateComponent){ 367 | dateTime = date 368 | } 369 | else{ 370 | print("DateTime warning: second have issue") 371 | } 372 | } 373 | } 374 | 375 | open var millisecond:Int{ 376 | get{ 377 | return internalDateComponent.nanosecond! / 1000000 378 | } 379 | set{ 380 | internalDateComponent.nanosecond = newValue * 1000000 381 | if let date = Calendar.current.date(from: internalDateComponent){ 382 | dateTime = date 383 | } 384 | else{ 385 | print("DateTime warning: millisecond have issue") 386 | } 387 | } 388 | } 389 | 390 | open var nanosecond:Int{ 391 | get{ 392 | return internalDateComponent.nanosecond! 393 | } 394 | set{ 395 | internalDateComponent.nanosecond = newValue 396 | if let date = Calendar.current.date(from: internalDateComponent){ 397 | dateTime = date 398 | } 399 | else{ 400 | print("DateTime warning: millisecond have issue") 401 | } 402 | } 403 | } 404 | 405 | open var date:DateTime{ 406 | return DateTime(year: year, month: month, day: day)! 407 | } 408 | 409 | open var weekDay:DayOfWeek{ 410 | return DayOfWeek(rawValue: internalDateComponent.weekday! - 1)! 411 | } 412 | 413 | open var quarter:Int{ 414 | return internalDateComponent.quarter! + 1 //系统是从0开始的,这里我们一般从1开始 415 | } 416 | 417 | open var weekOfMonth:Int{ 418 | return internalDateComponent.weekOfMonth! 419 | } 420 | 421 | open var weekOfYear:Int{ 422 | return internalDateComponent.weekOfYear! 423 | } 424 | 425 | 426 | open var timestamp:Int{ 427 | return Int(dateTime.timeIntervalSince1970) 428 | } 429 | 430 | //以后再做 431 | // public var utcDateTime:Date{ 432 | // return NSTimeZone 433 | // } 434 | 435 | 436 | open override var description: String{ 437 | return format() 438 | } 439 | 440 | open override var debugDescription: String{ 441 | return description 442 | } 443 | 444 | open var dayOfYear:Int{ 445 | get{ 446 | let month = self.month 447 | let day = self.day 448 | var days = 0 449 | if DateTime.isLeapYeay(self.year) { 450 | var i = 0 451 | while i < month { 452 | days += LeapYearMonth[i] 453 | i = i + 1 454 | } 455 | return days + day 456 | } 457 | else{ 458 | var i = 0 459 | while i < month { 460 | days += NotLeapYearMonth[i] 461 | i = i + 1 462 | } 463 | return days + day 464 | } 465 | } 466 | } 467 | 468 | public static func isLeapYeay(_ year:Int)->Bool{ 469 | return year % 4 == 0 && year % 100 != 0 470 | } 471 | 472 | 473 | 474 | open func toOCDate()->Date{ 475 | return dateTime 476 | } 477 | 478 | // 这里目前不能传负数,但是如果是Int,类型,是应该可以接受负数的 479 | //这个地方有争议。很大有问题,不建议使用 480 | open func selfAddMonth(_ months:Int) { 481 | var i = month 482 | var currentYear = year 483 | //可以将month转化成day 484 | var days = 0 485 | if months > 0 { 486 | while i < months + month{ 487 | if DateTime.isLeapYeay(currentYear) { 488 | days = days + LeapYearMonth[i % 12] 489 | } 490 | else{ 491 | days = days + NotLeapYearMonth[i % 12] 492 | } 493 | if i % 12 == 0 { 494 | currentYear = currentYear + 1 495 | } 496 | i = i + 1 497 | } 498 | selfAddDays(Double(days)) 499 | } 500 | 501 | if months < 0 { 502 | i = month - 1 503 | while i >= months + month{ 504 | if DateTime.isLeapYeay(currentYear) { 505 | days = days + LeapYearMonth[abs(i) % 12] 506 | } 507 | else{ 508 | days = days + NotLeapYearMonth[abs(i) % 12] 509 | } 510 | if abs(i) % 12 == 0 { 511 | currentYear = currentYear - 1 512 | } 513 | i = i - 1 514 | } 515 | selfAddDays(Double(-days)) 516 | } 517 | } 518 | 519 | open func selfAddYears(_ years:Int){ 520 | if year + years > 1000000 { 521 | print("DateTime warning: years is too big") 522 | return 523 | } 524 | selfAddMonth(years * 12) //这样应该要吧 525 | } 526 | 527 | open func selfAddDays(_ days:Double) { 528 | selfAddMilliSeconds(days * Double(TickPerDay)) 529 | } 530 | 531 | open func selfAddHours(_ hours:Double) { 532 | selfAddMilliSeconds(hours * Double(TickPerHour)) 533 | } 534 | 535 | open func selfAddMinutes(_ minutes:Double) { 536 | selfAddMilliSeconds(minutes * Double(TickPerMinute)) 537 | } 538 | 539 | open func selfAddSeconds(_ seconds:Double) { 540 | selfAddMilliSeconds(seconds * Double(TickPerSecond)) 541 | } 542 | 543 | open func selfAddMilliSeconds(_ milliSeconds:Double){ 544 | dateTime = dateTime.addingTimeInterval(milliSeconds / 1000) 545 | } 546 | 547 | open func addMilliSeconds(_ milliSeconds:Double)->DateTime{ 548 | let copy = self.copy() as! DateTime 549 | copy.selfAddMilliSeconds(milliSeconds) 550 | return copy 551 | } 552 | 553 | open func addSeconds(_ seconds:Double) ->DateTime { 554 | let copy = self.copy() as! DateTime 555 | copy.selfAddMilliSeconds(seconds * Double(TickPerSecond)) 556 | return copy 557 | } 558 | 559 | open func addMinutes(_ minutes:Double)->DateTime { 560 | let copy = self.copy() as! DateTime 561 | copy.selfAddMilliSeconds(minutes * Double(TickPerMinute)) 562 | return copy 563 | } 564 | 565 | open func addHours(_ hours:Double) -> DateTime{ 566 | let copy = self.copy() as! DateTime 567 | copy.selfAddMilliSeconds(hours * Double(TickPerHour)) 568 | return copy 569 | } 570 | 571 | open func addDays(_ days:Double) ->DateTime{ 572 | let copy = self.copy() as! DateTime 573 | copy.selfAddMilliSeconds(days * Double(TickPerDay)) 574 | return copy 575 | } 576 | 577 | open func addYears(_ years:Int)->DateTime{ 578 | let copy = self.copy() as! DateTime 579 | copy.selfAddMonth(years * 12) //这样应该要吧 580 | return copy 581 | } 582 | 583 | open func addMonth(_ months:Int) ->DateTime{ 584 | let copy = self.copy() as! DateTime 585 | copy.selfAddMonth(months) 586 | return copy 587 | } 588 | 589 | 590 | public static func compare(_ left:DateTime,right:DateTime)->Int{ 591 | let result = left.dateTime.compare(right.dateTime) 592 | if result == .orderedAscending { 593 | return -1 594 | } 595 | else if result == .orderedDescending{ 596 | return 1 597 | } 598 | return 0 599 | } 600 | 601 | open func compareTo(_ time:DateTime) -> Int { 602 | return DateTime.compare(self, right: time) 603 | } 604 | 605 | public static func daysInMonth(_ year:Int,month:Int) -> Int? { 606 | if month < 1 { 607 | print("DateTime warning: month can not less than 1") 608 | return nil 609 | } 610 | else if month > 12 { 611 | print("DateTime warning: month can not bigger than 12") 612 | return nil 613 | } 614 | if DateTime.isLeapYeay(year) { 615 | return LeapYearMonth[month] 616 | } 617 | else{ 618 | return NotLeapYearMonth[month] 619 | } 620 | } 621 | 622 | open func daysInMonth() -> Int { 623 | 624 | if DateTime.isLeapYeay(self.year) { 625 | return LeapYearMonth[self.month] 626 | } 627 | else{ 628 | return NotLeapYearMonth[self.month] 629 | } 630 | } 631 | 632 | public static func equals(_ left:DateTime,right:DateTime)->Bool{ 633 | return DateTime.compare(left, right: right) == 0 634 | } 635 | 636 | open func equals(_ time:DateTime) -> Bool { 637 | return DateTime.equals(self, right: time) 638 | } 639 | 640 | //最好用一个单例子来实现NSDateFormatter,因为NSDateFormatter 641 | //很吃资源 642 | open func format(_ format:String = "yyyy-MM-dd HH:mm:ss:SSS") -> String { 643 | DateTime.dateFormatter.dateFormat = format 644 | return DateTime.dateFormatter.string(from: dateTime) 645 | } 646 | 647 | open func format(_ dateFormat:DateFormatter.Style,timeFormat:DateFormatter.Style)->String{ 648 | DateTime.dateFormatter.locale = local 649 | DateTime.dateFormatter.dateStyle = dateFormat 650 | DateTime.dateFormatter.timeStyle = timeFormat 651 | return DateTime.dateFormatter.string(from: dateTime) 652 | } 653 | 654 | 655 | 656 | open var dateString:String{ 657 | return format("yyyy-MM-dd") 658 | } 659 | 660 | open var timeString:String{ 661 | return format("HH:mm:ss") 662 | } 663 | 664 | open var time:DateTime{ 665 | return DateTime(year: 0, month: 0, day: 0, hour: hour, minute: minute, second: second)! 666 | } 667 | 668 | var secondsToZeroTime:Int{ 669 | return hour * 3600 + minute * 60 + second 670 | } 671 | 672 | //这里还需要各种转化为时间的Style,需要补上 673 | 674 | 675 | public static func parse(_ time:String) -> DateTime? { 676 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" 677 | if let date = dateFormatter.date(from: time){ 678 | return DateTime(date: date) 679 | } 680 | else{ 681 | return nil 682 | } 683 | } 684 | 685 | public static func parse(_ time:String,format:String) -> DateTime? { 686 | dateFormatter.dateFormat = format 687 | if let date = dateFormatter.date(from: time){ 688 | return DateTime(date: date) 689 | } 690 | else{ 691 | return nil 692 | } 693 | } 694 | 695 | open override var hash: Int{ 696 | return timestamp.hashValue 697 | } 698 | 699 | 700 | open override func copy() -> Any { 701 | return DateTime(date: dateTime) 702 | } 703 | 704 | struct RegexTool { 705 | let regex:NSRegularExpression? 706 | init(_ pattern:String){ 707 | regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) 708 | } 709 | func match(input:String)->Bool{ 710 | if let matches = regex?.matches(in: input, options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: NSMakeRange(0, (input as NSString).length)) { 711 | return matches.count > 0 712 | } 713 | else{ 714 | return false 715 | } 716 | } 717 | } 718 | 719 | } 720 | -------------------------------------------------------------------------------- /GrandTime.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "GrandTime::GrandTimePackageTests::ProductTarget" /* GrandTimePackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_34 /* Build configuration list for PBXAggregateTarget "GrandTimePackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_37 /* PBXTargetDependency */, 17 | ); 18 | name = GrandTimePackageTests; 19 | productName = GrandTimePackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | AEE313772420A58700C32768 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE313762420A58700C32768 /* AppDelegate.swift */; }; 25 | AEE313792420A58700C32768 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE313782420A58700C32768 /* SceneDelegate.swift */; }; 26 | AEE3137B2420A58700C32768 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE3137A2420A58700C32768 /* ViewController.swift */; }; 27 | AEE313802420A58A00C32768 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AEE3137F2420A58A00C32768 /* Assets.xcassets */; }; 28 | AEE313832420A58A00C32768 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AEE313812420A58A00C32768 /* LaunchScreen.storyboard */; }; 29 | AEE313892420A68B00C32768 /* TimerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE313882420A68B00C32768 /* TimerViewController.swift */; }; 30 | AEE3138A2420A83000C32768 /* GrandTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* GrandTime.swift */; }; 31 | AEE3138B2420A83200C32768 /* GrandTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* GrandTimer.swift */; }; 32 | AEE3138C2420A83400C32768 /* TimeSpan.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* TimeSpan.swift */; }; 33 | OBJ_23 /* GrandTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* GrandTime.swift */; }; 34 | OBJ_24 /* GrandTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* GrandTimer.swift */; }; 35 | OBJ_25 /* TimeSpan.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* TimeSpan.swift */; }; 36 | OBJ_32 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 37 | OBJ_43 /* GrandTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* GrandTimeTests.swift */; }; 38 | OBJ_44 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* XCTestManifests.swift */; }; 39 | OBJ_46 /* GrandTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "GrandTime::GrandTime::Product" /* GrandTime.framework */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | AEE3136E2420A56500C32768 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = OBJ_1 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = "GrandTime::GrandTime"; 48 | remoteInfo = GrandTime; 49 | }; 50 | AEE3136F2420A56600C32768 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = OBJ_1 /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = "GrandTime::GrandTimeTests"; 55 | remoteInfo = GrandTimeTests; 56 | }; 57 | /* End PBXContainerItemProxy section */ 58 | 59 | /* Begin PBXFileReference section */ 60 | AEE313742420A58700C32768 /* GrandTimeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GrandTimeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | AEE313762420A58700C32768 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 62 | AEE313782420A58700C32768 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 63 | AEE3137A2420A58700C32768 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 64 | AEE3137F2420A58A00C32768 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 65 | AEE313822420A58A00C32768 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 66 | AEE313842420A58A00C32768 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | AEE313882420A68B00C32768 /* TimerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerViewController.swift; sourceTree = ""; }; 68 | "GrandTime::GrandTime::Product" /* GrandTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GrandTime.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | "GrandTime::GrandTimeTests::Product" /* GrandTimeTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = GrandTimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | OBJ_10 /* TimeSpan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSpan.swift; sourceTree = ""; }; 71 | OBJ_13 /* GrandTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrandTimeTests.swift; sourceTree = ""; }; 72 | OBJ_14 /* XCTestManifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestManifests.swift; sourceTree = ""; }; 73 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 74 | OBJ_8 /* GrandTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrandTime.swift; sourceTree = ""; }; 75 | OBJ_9 /* GrandTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrandTimer.swift; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | AEE313712420A58700C32768 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | OBJ_26 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 0; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | OBJ_45 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 0; 96 | files = ( 97 | OBJ_46 /* GrandTime.framework in Frameworks */, 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | /* End PBXFrameworksBuildPhase section */ 102 | 103 | /* Begin PBXGroup section */ 104 | AEE313752420A58700C32768 /* GrandTimeDemo */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | AEE313762420A58700C32768 /* AppDelegate.swift */, 108 | AEE313782420A58700C32768 /* SceneDelegate.swift */, 109 | AEE3137A2420A58700C32768 /* ViewController.swift */, 110 | AEE313882420A68B00C32768 /* TimerViewController.swift */, 111 | AEE3137F2420A58A00C32768 /* Assets.xcassets */, 112 | AEE313812420A58A00C32768 /* LaunchScreen.storyboard */, 113 | AEE313842420A58A00C32768 /* Info.plist */, 114 | ); 115 | path = GrandTimeDemo; 116 | sourceTree = ""; 117 | }; 118 | OBJ_11 /* Tests */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | OBJ_12 /* GrandTimeTests */, 122 | ); 123 | name = Tests; 124 | sourceTree = SOURCE_ROOT; 125 | }; 126 | OBJ_12 /* GrandTimeTests */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | OBJ_13 /* GrandTimeTests.swift */, 130 | OBJ_14 /* XCTestManifests.swift */, 131 | ); 132 | name = GrandTimeTests; 133 | path = Tests/GrandTimeTests; 134 | sourceTree = SOURCE_ROOT; 135 | }; 136 | OBJ_15 /* Products */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | "GrandTime::GrandTimeTests::Product" /* GrandTimeTests.xctest */, 140 | "GrandTime::GrandTime::Product" /* GrandTime.framework */, 141 | AEE313742420A58700C32768 /* GrandTimeDemo.app */, 142 | ); 143 | name = Products; 144 | sourceTree = BUILT_PRODUCTS_DIR; 145 | }; 146 | OBJ_5 /* */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | OBJ_6 /* Package.swift */, 150 | OBJ_7 /* Sources */, 151 | OBJ_11 /* Tests */, 152 | AEE313752420A58700C32768 /* GrandTimeDemo */, 153 | OBJ_15 /* Products */, 154 | ); 155 | name = ""; 156 | sourceTree = ""; 157 | }; 158 | OBJ_7 /* Sources */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | OBJ_8 /* GrandTime.swift */, 162 | OBJ_9 /* GrandTimer.swift */, 163 | OBJ_10 /* TimeSpan.swift */, 164 | ); 165 | path = Sources; 166 | sourceTree = SOURCE_ROOT; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | AEE313732420A58700C32768 /* GrandTimeDemo */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = AEE313872420A58A00C32768 /* Build configuration list for PBXNativeTarget "GrandTimeDemo" */; 174 | buildPhases = ( 175 | AEE313702420A58700C32768 /* Sources */, 176 | AEE313712420A58700C32768 /* Frameworks */, 177 | AEE313722420A58700C32768 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = GrandTimeDemo; 184 | productName = GrandTimeDemo; 185 | productReference = AEE313742420A58700C32768 /* GrandTimeDemo.app */; 186 | productType = "com.apple.product-type.application"; 187 | }; 188 | "GrandTime::GrandTime" /* GrandTime */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = OBJ_19 /* Build configuration list for PBXNativeTarget "GrandTime" */; 191 | buildPhases = ( 192 | OBJ_22 /* Sources */, 193 | OBJ_26 /* Frameworks */, 194 | ); 195 | buildRules = ( 196 | ); 197 | dependencies = ( 198 | ); 199 | name = GrandTime; 200 | productName = GrandTime; 201 | productReference = "GrandTime::GrandTime::Product" /* GrandTime.framework */; 202 | productType = "com.apple.product-type.framework"; 203 | }; 204 | "GrandTime::GrandTimeTests" /* GrandTimeTests */ = { 205 | isa = PBXNativeTarget; 206 | buildConfigurationList = OBJ_39 /* Build configuration list for PBXNativeTarget "GrandTimeTests" */; 207 | buildPhases = ( 208 | OBJ_42 /* Sources */, 209 | OBJ_45 /* Frameworks */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | OBJ_47 /* PBXTargetDependency */, 215 | ); 216 | name = GrandTimeTests; 217 | productName = GrandTimeTests; 218 | productReference = "GrandTime::GrandTimeTests::Product" /* GrandTimeTests.xctest */; 219 | productType = "com.apple.product-type.bundle.unit-test"; 220 | }; 221 | "GrandTime::SwiftPMPackageDescription" /* GrandTimePackageDescription */ = { 222 | isa = PBXNativeTarget; 223 | buildConfigurationList = OBJ_28 /* Build configuration list for PBXNativeTarget "GrandTimePackageDescription" */; 224 | buildPhases = ( 225 | OBJ_31 /* Sources */, 226 | ); 227 | buildRules = ( 228 | ); 229 | dependencies = ( 230 | ); 231 | name = GrandTimePackageDescription; 232 | productName = GrandTimePackageDescription; 233 | productType = "com.apple.product-type.framework"; 234 | }; 235 | /* End PBXNativeTarget section */ 236 | 237 | /* Begin PBXProject section */ 238 | OBJ_1 /* Project object */ = { 239 | isa = PBXProject; 240 | attributes = { 241 | LastSwiftMigration = 9999; 242 | LastSwiftUpdateCheck = 1130; 243 | LastUpgradeCheck = 9999; 244 | TargetAttributes = { 245 | AEE313732420A58700C32768 = { 246 | CreatedOnToolsVersion = 11.3; 247 | DevelopmentTeam = 3YCGH6CCXA; 248 | ProvisioningStyle = Automatic; 249 | }; 250 | }; 251 | }; 252 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "GrandTime" */; 253 | compatibilityVersion = "Xcode 3.2"; 254 | developmentRegion = en; 255 | hasScannedForEncodings = 0; 256 | knownRegions = ( 257 | en, 258 | Base, 259 | ); 260 | mainGroup = OBJ_5 /* */; 261 | productRefGroup = OBJ_15 /* Products */; 262 | projectDirPath = ""; 263 | projectRoot = ""; 264 | targets = ( 265 | "GrandTime::GrandTime" /* GrandTime */, 266 | "GrandTime::SwiftPMPackageDescription" /* GrandTimePackageDescription */, 267 | "GrandTime::GrandTimePackageTests::ProductTarget" /* GrandTimePackageTests */, 268 | "GrandTime::GrandTimeTests" /* GrandTimeTests */, 269 | AEE313732420A58700C32768 /* GrandTimeDemo */, 270 | ); 271 | }; 272 | /* End PBXProject section */ 273 | 274 | /* Begin PBXResourcesBuildPhase section */ 275 | AEE313722420A58700C32768 /* Resources */ = { 276 | isa = PBXResourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | AEE313832420A58A00C32768 /* LaunchScreen.storyboard in Resources */, 280 | AEE313802420A58A00C32768 /* Assets.xcassets in Resources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXResourcesBuildPhase section */ 285 | 286 | /* Begin PBXSourcesBuildPhase section */ 287 | AEE313702420A58700C32768 /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | AEE3138A2420A83000C32768 /* GrandTime.swift in Sources */, 292 | AEE3138C2420A83400C32768 /* TimeSpan.swift in Sources */, 293 | AEE313892420A68B00C32768 /* TimerViewController.swift in Sources */, 294 | AEE3138B2420A83200C32768 /* GrandTimer.swift in Sources */, 295 | AEE3137B2420A58700C32768 /* ViewController.swift in Sources */, 296 | AEE313772420A58700C32768 /* AppDelegate.swift in Sources */, 297 | AEE313792420A58700C32768 /* SceneDelegate.swift in Sources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | OBJ_22 /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 0; 304 | files = ( 305 | OBJ_23 /* GrandTime.swift in Sources */, 306 | OBJ_24 /* GrandTimer.swift in Sources */, 307 | OBJ_25 /* TimeSpan.swift in Sources */, 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | OBJ_31 /* Sources */ = { 312 | isa = PBXSourcesBuildPhase; 313 | buildActionMask = 0; 314 | files = ( 315 | OBJ_32 /* Package.swift in Sources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | OBJ_42 /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 0; 322 | files = ( 323 | OBJ_43 /* GrandTimeTests.swift in Sources */, 324 | OBJ_44 /* XCTestManifests.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | /* End PBXSourcesBuildPhase section */ 329 | 330 | /* Begin PBXTargetDependency section */ 331 | OBJ_37 /* PBXTargetDependency */ = { 332 | isa = PBXTargetDependency; 333 | target = "GrandTime::GrandTimeTests" /* GrandTimeTests */; 334 | targetProxy = AEE3136F2420A56600C32768 /* PBXContainerItemProxy */; 335 | }; 336 | OBJ_47 /* PBXTargetDependency */ = { 337 | isa = PBXTargetDependency; 338 | target = "GrandTime::GrandTime" /* GrandTime */; 339 | targetProxy = AEE3136E2420A56500C32768 /* PBXContainerItemProxy */; 340 | }; 341 | /* End PBXTargetDependency section */ 342 | 343 | /* Begin PBXVariantGroup section */ 344 | AEE313812420A58A00C32768 /* LaunchScreen.storyboard */ = { 345 | isa = PBXVariantGroup; 346 | children = ( 347 | AEE313822420A58A00C32768 /* Base */, 348 | ); 349 | name = LaunchScreen.storyboard; 350 | sourceTree = ""; 351 | }; 352 | /* End PBXVariantGroup section */ 353 | 354 | /* Begin XCBuildConfiguration section */ 355 | AEE313852420A58A00C32768 /* Debug */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ALWAYS_SEARCH_USER_PATHS = NO; 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CLANG_ANALYZER_NONNULL = YES; 361 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_WEAK = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 373 | CLANG_WARN_EMPTY_BODY = YES; 374 | CLANG_WARN_ENUM_CONVERSION = YES; 375 | CLANG_WARN_INFINITE_RECURSION = YES; 376 | CLANG_WARN_INT_CONVERSION = YES; 377 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 378 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 379 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 381 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 382 | CLANG_WARN_STRICT_PROTOTYPES = YES; 383 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 384 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 385 | CLANG_WARN_UNREACHABLE_CODE = YES; 386 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 387 | CODE_SIGN_STYLE = Automatic; 388 | DEVELOPMENT_TEAM = 3YCGH6CCXA; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | ENABLE_TESTABILITY = YES; 391 | GCC_C_LANGUAGE_STANDARD = gnu11; 392 | GCC_DYNAMIC_NO_PIC = NO; 393 | GCC_NO_COMMON_BLOCKS = YES; 394 | GCC_PREPROCESSOR_DEFINITIONS = ( 395 | "DEBUG=1", 396 | "$(inherited)", 397 | ); 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | INFOPLIST_FILE = GrandTimeDemo/Info.plist; 405 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 407 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 408 | MTL_FAST_MATH = YES; 409 | PRODUCT_BUNDLE_IDENTIFIER = ShadowEdge.GrandTimeDemo; 410 | PRODUCT_NAME = "$(TARGET_NAME)"; 411 | SDKROOT = iphoneos; 412 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 413 | SWIFT_VERSION = 5.0; 414 | TARGETED_DEVICE_FAMILY = 1; 415 | }; 416 | name = Debug; 417 | }; 418 | AEE313862420A58A00C32768 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ALWAYS_SEARCH_USER_PATHS = NO; 422 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 423 | CLANG_ANALYZER_NONNULL = YES; 424 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 425 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 426 | CLANG_CXX_LIBRARY = "libc++"; 427 | CLANG_ENABLE_MODULES = YES; 428 | CLANG_ENABLE_OBJC_WEAK = YES; 429 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 430 | CLANG_WARN_BOOL_CONVERSION = YES; 431 | CLANG_WARN_COMMA = YES; 432 | CLANG_WARN_CONSTANT_CONVERSION = YES; 433 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INFINITE_RECURSION = YES; 439 | CLANG_WARN_INT_CONVERSION = YES; 440 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 442 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 443 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 444 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 445 | CLANG_WARN_STRICT_PROTOTYPES = YES; 446 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 447 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 448 | CLANG_WARN_UNREACHABLE_CODE = YES; 449 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 450 | CODE_SIGN_STYLE = Automatic; 451 | COPY_PHASE_STRIP = NO; 452 | DEVELOPMENT_TEAM = 3YCGH6CCXA; 453 | ENABLE_NS_ASSERTIONS = NO; 454 | ENABLE_STRICT_OBJC_MSGSEND = YES; 455 | GCC_C_LANGUAGE_STANDARD = gnu11; 456 | GCC_NO_COMMON_BLOCKS = YES; 457 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 458 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 459 | GCC_WARN_UNDECLARED_SELECTOR = YES; 460 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 461 | GCC_WARN_UNUSED_FUNCTION = YES; 462 | GCC_WARN_UNUSED_VARIABLE = YES; 463 | INFOPLIST_FILE = GrandTimeDemo/Info.plist; 464 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | MTL_ENABLE_DEBUG_INFO = NO; 467 | MTL_FAST_MATH = YES; 468 | PRODUCT_BUNDLE_IDENTIFIER = ShadowEdge.GrandTimeDemo; 469 | PRODUCT_NAME = "$(TARGET_NAME)"; 470 | SDKROOT = iphoneos; 471 | SWIFT_VERSION = 5.0; 472 | TARGETED_DEVICE_FAMILY = 1; 473 | VALIDATE_PRODUCT = YES; 474 | }; 475 | name = Release; 476 | }; 477 | OBJ_20 /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ENABLE_TESTABILITY = YES; 481 | FRAMEWORK_SEARCH_PATHS = ( 482 | "$(inherited)", 483 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 484 | ); 485 | HEADER_SEARCH_PATHS = "$(inherited)"; 486 | INFOPLIST_FILE = GrandTime.xcodeproj/GrandTime_Info.plist; 487 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 489 | MACOSX_DEPLOYMENT_TARGET = 10.10; 490 | OTHER_CFLAGS = "$(inherited)"; 491 | OTHER_LDFLAGS = "$(inherited)"; 492 | OTHER_SWIFT_FLAGS = "$(inherited)"; 493 | PRODUCT_BUNDLE_IDENTIFIER = GrandTime; 494 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 495 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 496 | SKIP_INSTALL = YES; 497 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 498 | SWIFT_VERSION = 5.0; 499 | TARGET_NAME = GrandTime; 500 | TVOS_DEPLOYMENT_TARGET = 9.0; 501 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 502 | }; 503 | name = Debug; 504 | }; 505 | OBJ_21 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | ENABLE_TESTABILITY = YES; 509 | FRAMEWORK_SEARCH_PATHS = ( 510 | "$(inherited)", 511 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 512 | ); 513 | HEADER_SEARCH_PATHS = "$(inherited)"; 514 | INFOPLIST_FILE = GrandTime.xcodeproj/GrandTime_Info.plist; 515 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 516 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 517 | MACOSX_DEPLOYMENT_TARGET = 10.10; 518 | OTHER_CFLAGS = "$(inherited)"; 519 | OTHER_LDFLAGS = "$(inherited)"; 520 | OTHER_SWIFT_FLAGS = "$(inherited)"; 521 | PRODUCT_BUNDLE_IDENTIFIER = GrandTime; 522 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 523 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 524 | SKIP_INSTALL = YES; 525 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 526 | SWIFT_VERSION = 5.0; 527 | TARGET_NAME = GrandTime; 528 | TVOS_DEPLOYMENT_TARGET = 9.0; 529 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 530 | }; 531 | name = Release; 532 | }; 533 | OBJ_29 /* Debug */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | LD = /usr/bin/true; 537 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.1"; 538 | SWIFT_VERSION = 5.0; 539 | }; 540 | name = Debug; 541 | }; 542 | OBJ_3 /* Debug */ = { 543 | isa = XCBuildConfiguration; 544 | buildSettings = { 545 | CLANG_ENABLE_OBJC_ARC = YES; 546 | COMBINE_HIDPI_IMAGES = YES; 547 | COPY_PHASE_STRIP = NO; 548 | DEBUG_INFORMATION_FORMAT = dwarf; 549 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 550 | ENABLE_NS_ASSERTIONS = YES; 551 | GCC_OPTIMIZATION_LEVEL = 0; 552 | GCC_PREPROCESSOR_DEFINITIONS = ( 553 | "$(inherited)", 554 | "SWIFT_PACKAGE=1", 555 | "DEBUG=1", 556 | ); 557 | MACOSX_DEPLOYMENT_TARGET = 10.10; 558 | ONLY_ACTIVE_ARCH = YES; 559 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 560 | PRODUCT_NAME = "$(TARGET_NAME)"; 561 | SDKROOT = macosx; 562 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 563 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; 564 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 565 | USE_HEADERMAP = NO; 566 | }; 567 | name = Debug; 568 | }; 569 | OBJ_30 /* Release */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | LD = /usr/bin/true; 573 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.1"; 574 | SWIFT_VERSION = 5.0; 575 | }; 576 | name = Release; 577 | }; 578 | OBJ_35 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | }; 582 | name = Debug; 583 | }; 584 | OBJ_36 /* Release */ = { 585 | isa = XCBuildConfiguration; 586 | buildSettings = { 587 | }; 588 | name = Release; 589 | }; 590 | OBJ_4 /* Release */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | CLANG_ENABLE_OBJC_ARC = YES; 594 | COMBINE_HIDPI_IMAGES = YES; 595 | COPY_PHASE_STRIP = YES; 596 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 597 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 598 | GCC_OPTIMIZATION_LEVEL = s; 599 | GCC_PREPROCESSOR_DEFINITIONS = ( 600 | "$(inherited)", 601 | "SWIFT_PACKAGE=1", 602 | ); 603 | MACOSX_DEPLOYMENT_TARGET = 10.10; 604 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 605 | PRODUCT_NAME = "$(TARGET_NAME)"; 606 | SDKROOT = macosx; 607 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 608 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; 609 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 610 | USE_HEADERMAP = NO; 611 | }; 612 | name = Release; 613 | }; 614 | OBJ_40 /* Debug */ = { 615 | isa = XCBuildConfiguration; 616 | buildSettings = { 617 | CLANG_ENABLE_MODULES = YES; 618 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 619 | FRAMEWORK_SEARCH_PATHS = ( 620 | "$(inherited)", 621 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 622 | ); 623 | HEADER_SEARCH_PATHS = "$(inherited)"; 624 | INFOPLIST_FILE = GrandTime.xcodeproj/GrandTimeTests_Info.plist; 625 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 626 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 627 | MACOSX_DEPLOYMENT_TARGET = 10.10; 628 | OTHER_CFLAGS = "$(inherited)"; 629 | OTHER_LDFLAGS = "$(inherited)"; 630 | OTHER_SWIFT_FLAGS = "$(inherited)"; 631 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 632 | SWIFT_VERSION = 5.0; 633 | TARGET_NAME = GrandTimeTests; 634 | TVOS_DEPLOYMENT_TARGET = 9.0; 635 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 636 | }; 637 | name = Debug; 638 | }; 639 | OBJ_41 /* Release */ = { 640 | isa = XCBuildConfiguration; 641 | buildSettings = { 642 | CLANG_ENABLE_MODULES = YES; 643 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 644 | FRAMEWORK_SEARCH_PATHS = ( 645 | "$(inherited)", 646 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 647 | ); 648 | HEADER_SEARCH_PATHS = "$(inherited)"; 649 | INFOPLIST_FILE = GrandTime.xcodeproj/GrandTimeTests_Info.plist; 650 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 651 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 652 | MACOSX_DEPLOYMENT_TARGET = 10.10; 653 | OTHER_CFLAGS = "$(inherited)"; 654 | OTHER_LDFLAGS = "$(inherited)"; 655 | OTHER_SWIFT_FLAGS = "$(inherited)"; 656 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 657 | SWIFT_VERSION = 5.0; 658 | TARGET_NAME = GrandTimeTests; 659 | TVOS_DEPLOYMENT_TARGET = 9.0; 660 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 661 | }; 662 | name = Release; 663 | }; 664 | /* End XCBuildConfiguration section */ 665 | 666 | /* Begin XCConfigurationList section */ 667 | AEE313872420A58A00C32768 /* Build configuration list for PBXNativeTarget "GrandTimeDemo" */ = { 668 | isa = XCConfigurationList; 669 | buildConfigurations = ( 670 | AEE313852420A58A00C32768 /* Debug */, 671 | AEE313862420A58A00C32768 /* Release */, 672 | ); 673 | defaultConfigurationIsVisible = 0; 674 | defaultConfigurationName = Release; 675 | }; 676 | OBJ_19 /* Build configuration list for PBXNativeTarget "GrandTime" */ = { 677 | isa = XCConfigurationList; 678 | buildConfigurations = ( 679 | OBJ_20 /* Debug */, 680 | OBJ_21 /* Release */, 681 | ); 682 | defaultConfigurationIsVisible = 0; 683 | defaultConfigurationName = Release; 684 | }; 685 | OBJ_2 /* Build configuration list for PBXProject "GrandTime" */ = { 686 | isa = XCConfigurationList; 687 | buildConfigurations = ( 688 | OBJ_3 /* Debug */, 689 | OBJ_4 /* Release */, 690 | ); 691 | defaultConfigurationIsVisible = 0; 692 | defaultConfigurationName = Release; 693 | }; 694 | OBJ_28 /* Build configuration list for PBXNativeTarget "GrandTimePackageDescription" */ = { 695 | isa = XCConfigurationList; 696 | buildConfigurations = ( 697 | OBJ_29 /* Debug */, 698 | OBJ_30 /* Release */, 699 | ); 700 | defaultConfigurationIsVisible = 0; 701 | defaultConfigurationName = Release; 702 | }; 703 | OBJ_34 /* Build configuration list for PBXAggregateTarget "GrandTimePackageTests" */ = { 704 | isa = XCConfigurationList; 705 | buildConfigurations = ( 706 | OBJ_35 /* Debug */, 707 | OBJ_36 /* Release */, 708 | ); 709 | defaultConfigurationIsVisible = 0; 710 | defaultConfigurationName = Release; 711 | }; 712 | OBJ_39 /* Build configuration list for PBXNativeTarget "GrandTimeTests" */ = { 713 | isa = XCConfigurationList; 714 | buildConfigurations = ( 715 | OBJ_40 /* Debug */, 716 | OBJ_41 /* Release */, 717 | ); 718 | defaultConfigurationIsVisible = 0; 719 | defaultConfigurationName = Release; 720 | }; 721 | /* End XCConfigurationList section */ 722 | }; 723 | rootObject = OBJ_1 /* Project object */; 724 | } 725 | --------------------------------------------------------------------------------