├── .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 |
--------------------------------------------------------------------------------