Make diction of docs and api naming more corresponding with official api.
This commit is contained in:
parent
4cd608d123
commit
d4f25b6031
|
@ -24,7 +24,6 @@
|
|||
OBJ_49 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Atomic.swift */; };
|
||||
OBJ_50 /* Bag.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* Bag.swift */; };
|
||||
OBJ_51 /* DeinitObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* DeinitObserver.swift */; };
|
||||
OBJ_52 /* Deprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Deprecated.swift */; };
|
||||
OBJ_53 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Extensions.swift */; };
|
||||
OBJ_54 /* Interval.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* Interval.swift */; };
|
||||
OBJ_55 /* Monthday.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* Monthday.swift */; };
|
||||
|
@ -70,7 +69,6 @@
|
|||
/* Begin PBXFileReference section */
|
||||
OBJ_10 /* Bag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bag.swift; sourceTree = "<group>"; };
|
||||
OBJ_11 /* DeinitObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeinitObserver.swift; sourceTree = "<group>"; };
|
||||
OBJ_12 /* Deprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecated.swift; sourceTree = "<group>"; };
|
||||
OBJ_13 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||
OBJ_14 /* Interval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interval.swift; sourceTree = "<group>"; };
|
||||
OBJ_15 /* Monthday.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monthday.swift; sourceTree = "<group>"; };
|
||||
|
@ -157,7 +155,7 @@
|
|||
name = Products;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
OBJ_5 /* */ = {
|
||||
OBJ_5 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
OBJ_6 /* Package.swift */,
|
||||
|
@ -170,7 +168,6 @@
|
|||
OBJ_42 /* README.md */,
|
||||
OBJ_43 /* README.zh_cn.md */,
|
||||
);
|
||||
name = "";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
OBJ_7 /* Sources */ = {
|
||||
|
@ -187,7 +184,6 @@
|
|||
OBJ_9 /* Atomic.swift */,
|
||||
OBJ_10 /* Bag.swift */,
|
||||
OBJ_11 /* DeinitObserver.swift */,
|
||||
OBJ_12 /* Deprecated.swift */,
|
||||
OBJ_13 /* Extensions.swift */,
|
||||
OBJ_14 /* Interval.swift */,
|
||||
OBJ_15 /* Monthday.swift */,
|
||||
|
@ -271,7 +267,7 @@
|
|||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = OBJ_5 /* */;
|
||||
mainGroup = OBJ_5;
|
||||
productRefGroup = OBJ_36 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
|
@ -292,7 +288,6 @@
|
|||
OBJ_49 /* Atomic.swift in Sources */,
|
||||
OBJ_50 /* Bag.swift in Sources */,
|
||||
OBJ_51 /* DeinitObserver.swift in Sources */,
|
||||
OBJ_52 /* Deprecated.swift in Sources */,
|
||||
OBJ_53 /* Extensions.swift in Sources */,
|
||||
OBJ_54 /* Interval.swift in Sources */,
|
||||
OBJ_55 /* Monthday.swift in Sources */,
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
import Foundation
|
||||
|
||||
/// Represents a box that can read and write the underlying value atomically.
|
||||
/// An atomic box that can read and write the underlying value atomically.
|
||||
final class Atomic<T> {
|
||||
|
||||
private var v: T
|
||||
private var val: T
|
||||
private let lock = NSLock()
|
||||
|
||||
/// Init with the underlying value.
|
||||
init(_ value: T) {
|
||||
self.v = value
|
||||
}
|
||||
|
||||
/// Creates a snapshot of the value nonatomically.
|
||||
/// Create an atomic box with the given initial value.
|
||||
@inline(__always)
|
||||
func snapshot() -> T {
|
||||
return v
|
||||
init(_ value: T) {
|
||||
self.val = value
|
||||
}
|
||||
|
||||
/// Reads the value atomically.
|
||||
/// Reads the current value atomically.
|
||||
@inline(__always)
|
||||
func read<U>(_ body: (T) -> U) -> U {
|
||||
return lock.withLock { body(v) }
|
||||
return lock.withLock { body(val) }
|
||||
}
|
||||
|
||||
/// Writes the value atomically.
|
||||
/// Reads the current value atomically.
|
||||
@inline(__always)
|
||||
func readVoid(_ body: (T) -> Void) {
|
||||
lock.withLockVoid { body(val) }
|
||||
}
|
||||
|
||||
/// Writes the current value atomically.
|
||||
@inline(__always)
|
||||
func write<U>(_ body: (inout T) -> U) -> U {
|
||||
return lock.withLock { body(&v) }
|
||||
return lock.withLock { body(&val) }
|
||||
}
|
||||
|
||||
/// Writes the current value atomically.
|
||||
@inline(__always)
|
||||
func writeVoid(_ body: (inout T) -> Void) {
|
||||
lock.withLockVoid { body(&val) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
/// A unique key used to operate the corresponding element from a bag.
|
||||
/// A unique key for removing an element from a bag.
|
||||
struct BagKey: Equatable {
|
||||
|
||||
fileprivate let i: UInt64
|
||||
|
@ -15,7 +15,7 @@ struct BagKey: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
/// A generator used to generate a sequence of unique `BagKey`.
|
||||
/// A generator that can generate a sequence of unique `BagKey`.
|
||||
///
|
||||
/// let k1 = gen.next()
|
||||
/// let k2 = gen.next()
|
||||
|
@ -26,8 +26,8 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
|
|||
|
||||
private var k = BagKey(underlying: 0)
|
||||
|
||||
/// Advances to the next element and returns it, or nil if no next element exists.
|
||||
mutating func next() -> Element? {
|
||||
/// Advances to the next key and returns it, or nil if no next key exists.
|
||||
mutating func next() -> BagKey? {
|
||||
if k.i == UInt64.max {
|
||||
return nil
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
/// A data structure used to store a sequence of elements.
|
||||
/// An ordered sequence.
|
||||
///
|
||||
/// let k1 = bag.append(e1)
|
||||
/// let k2 = bag.append(e2)
|
||||
|
@ -46,48 +46,48 @@ struct BagKeyGenerator: Sequence, IteratorProtocol {
|
|||
/// // -> e2
|
||||
/// }
|
||||
///
|
||||
/// bag.delete(k1)
|
||||
/// bag.removeValue(for: k1)
|
||||
struct Bag<Element> {
|
||||
|
||||
private typealias Entry = (key: BagKey, element: Element)
|
||||
private typealias Entry = (key: BagKey, val: Element)
|
||||
|
||||
private var keys = BagKeyGenerator()
|
||||
private var keyGen = BagKeyGenerator()
|
||||
private var entries: [Entry] = []
|
||||
|
||||
/// Pushes the given element on to the end of this container.
|
||||
/// Appends a new element at the end of this bag.
|
||||
@discardableResult
|
||||
mutating func append(_ new: Element) -> BagKey {
|
||||
let key = keys.next()!
|
||||
let key = keyGen.next()!
|
||||
|
||||
let entry = (key: key, element: new)
|
||||
let entry = (key: key, val: new)
|
||||
entries.append(entry)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
/// Returns the element for key if key is in this container.
|
||||
func get(_ key: BagKey) -> Element? {
|
||||
/// Returns the element associated with a given key.
|
||||
func value(for key: BagKey) -> Element? {
|
||||
if let entry = entries.first(where: { $0.key == key }) {
|
||||
return entry.element
|
||||
return entry.val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Deletes the element with the given key and returns this element.
|
||||
/// Removes the given key and its associated element from this bag.
|
||||
@discardableResult
|
||||
mutating func delete(_ key: BagKey) -> Element? {
|
||||
mutating func removeValue(for key: BagKey) -> Element? {
|
||||
if let i = entries.firstIndex(where: { $0.key == key }) {
|
||||
return entries.remove(at: i).element
|
||||
return entries.remove(at: i).val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Removes all elements from this containers.
|
||||
mutating func clear() {
|
||||
/// Removes all elements from this bag.
|
||||
mutating func removeAll() {
|
||||
entries.removeAll()
|
||||
}
|
||||
|
||||
/// The number of elements in this containers.
|
||||
/// The number of elements in this bag.
|
||||
var count: Int {
|
||||
return entries.count
|
||||
}
|
||||
|
@ -95,11 +95,12 @@ struct Bag<Element> {
|
|||
|
||||
extension Bag: Sequence {
|
||||
|
||||
/// Returns an iterator over the elements of this containers.
|
||||
/// Returns an iterator over the elements of this bag.
|
||||
@inline(__always)
|
||||
func makeIterator() -> AnyIterator<Element> {
|
||||
var iterator = entries.makeIterator()
|
||||
return AnyIterator<Element> {
|
||||
return iterator.next()?.element
|
||||
return iterator.next()?.val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ import Foundation
|
|||
|
||||
#if canImport(ObjectiveC)
|
||||
|
||||
private var DEINIT_OBSERVER_KEY: Void = ()
|
||||
|
||||
/// Used to observe object deinit.
|
||||
/// An observer that receives deinit event of the object.
|
||||
///
|
||||
/// let observer = DeinitObserver.observe(target) {
|
||||
/// print("\(target) deinit")
|
||||
|
@ -13,38 +11,40 @@ private var DEINIT_OBSERVER_KEY: Void = ()
|
|||
/// observer.cancel()
|
||||
class DeinitObserver {
|
||||
|
||||
private var associateKey: Void = ()
|
||||
|
||||
private(set) weak var observed: AnyObject?
|
||||
|
||||
private var action: (() -> Void)?
|
||||
private var block: (() -> Void)?
|
||||
|
||||
private init(_ action: @escaping () -> Void) {
|
||||
self.action = action
|
||||
private init(_ block: @escaping () -> Void) {
|
||||
self.block = block
|
||||
}
|
||||
|
||||
/// Add observer.
|
||||
/// Observe deinit event of the object.
|
||||
@discardableResult
|
||||
static func observe(
|
||||
_ object: AnyObject,
|
||||
onDeinit action: @escaping () -> Void
|
||||
using block: @escaping () -> Void
|
||||
) -> DeinitObserver {
|
||||
let observer = DeinitObserver(action)
|
||||
let observer = DeinitObserver(block)
|
||||
observer.observed = object
|
||||
|
||||
objc_setAssociatedObject(object, &DEINIT_OBSERVER_KEY, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
objc_setAssociatedObject(object, &observer.associateKey, observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
|
||||
return observer
|
||||
}
|
||||
|
||||
/// Remove observer.
|
||||
/// Cancel observing.
|
||||
func cancel() {
|
||||
action = nil
|
||||
block = nil
|
||||
if let o = observed {
|
||||
objc_setAssociatedObject(o, &DEINIT_OBSERVER_KEY, nil, .OBJC_ASSOCIATION_ASSIGN)
|
||||
objc_setAssociatedObject(o, &associateKey, nil, .OBJC_ASSOCIATION_ASSIGN)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
action?()
|
||||
block?()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// Deprecated.swift
|
||||
// Schedule
|
||||
//
|
||||
// Created by Quentin Jin on 2019/3/4.
|
||||
// Copyright © 2019 Schedule. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Monthday {
|
||||
|
||||
/// A Boolean value indicating whether today is the monthday.
|
||||
@available(*, deprecated, message: "Use date.is(_:Monthday)")
|
||||
public var isToday: Bool {
|
||||
let components = toDateComponents()
|
||||
|
||||
let m = Calendar.gregorian.component(.month, from: Date())
|
||||
let d = Calendar.gregorian.component(.day, from: Date())
|
||||
|
||||
return m == components.month && d == components.day
|
||||
}
|
||||
}
|
||||
|
||||
extension Weekday {
|
||||
|
||||
/// A Boolean value indicating whether today is the weekday.
|
||||
@available(*, deprecated, message: "Use date.is(_:Weekday)")
|
||||
public var isToday: Bool {
|
||||
return Calendar.gregorian.component(.weekday, from: Date()) == rawValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Time {
|
||||
|
||||
/// The interval between this time and zero o'clock.
|
||||
@available(*, deprecated, renamed: "intervalSinceStartOfDay")
|
||||
public var intervalSinceZeroClock: Interval {
|
||||
return hour.hours + minute.minutes + second.seconds + nanosecond.nanoseconds
|
||||
}
|
||||
}
|
||||
|
||||
extension Plan {
|
||||
|
||||
/// Creates a plan from an interval sequence.
|
||||
/// The task will be executed after each interval in the sequence.
|
||||
@available(*, deprecated, message: "Use Plan.of")
|
||||
public static func from<S>(_ sequence: S) -> Plan where S: Sequence, S.Element == Interval {
|
||||
return Plan.of(sequence)
|
||||
}
|
||||
|
||||
/// Creates a plan from a date sequence.
|
||||
/// The task will be executed at each date in the sequence.
|
||||
/// - Note: Returns `Plan.never` if given no parameters.
|
||||
@available(*, deprecated, message: "Use Plan.of")
|
||||
public static func from<S>(_ sequence: S) -> Plan where S: Sequence, S.Element == Date {
|
||||
return Plan.make(sequence.makeIterator)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import Foundation
|
|||
|
||||
extension Double {
|
||||
|
||||
/// Returns a value of this number clamped to `Int.min...Int.max`.
|
||||
func clampedToInt() -> Int {
|
||||
if self >= Double(Int.max) { return Int.max }
|
||||
if self <= Double(Int.min) { return Int.min }
|
||||
|
@ -11,13 +12,11 @@ extension Double {
|
|||
|
||||
extension Int {
|
||||
|
||||
/// Returns the sum of the two given values, in case of any overflow,
|
||||
/// the result will be clamped to int.
|
||||
func clampedAdding(_ other: Int) -> Int {
|
||||
return (Double(self) + Double(other)).clampedToInt()
|
||||
}
|
||||
|
||||
func clampedSubtracting(_ other: Int) -> Int {
|
||||
return clampedAdding(-other)
|
||||
}
|
||||
}
|
||||
|
||||
extension Calendar {
|
||||
|
@ -47,9 +46,9 @@ extension NSLocking {
|
|||
return try body()
|
||||
}
|
||||
|
||||
/// Executes a closure returning a value while acquiring the lock.
|
||||
/// Executes a closure while acquiring the lock.
|
||||
@inline(__always)
|
||||
func withLock(_ body: () throws -> Void) rethrows {
|
||||
func withLockVoid(_ body: () throws -> Void) rethrows {
|
||||
lock(); defer { unlock() }
|
||||
try body()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
/// `Interval` represents a length of time.
|
||||
/// Type used to represent a time-based amount of time, such as '34.5 seconds'.
|
||||
public struct Interval {
|
||||
|
||||
/// The length of this interval in nanoseconds.
|
||||
|
@ -13,36 +13,26 @@ public struct Interval {
|
|||
}
|
||||
|
||||
// MARK: - Describing
|
||||
|
||||
extension Interval {
|
||||
|
||||
/// A boolean value indicating whether this interval is less than zero.
|
||||
/// A Boolean value indicating whether this interval is less than zero.
|
||||
///
|
||||
/// An interval can be negative:
|
||||
///
|
||||
/// - The interval from 6:00 to 7:00 is `1.hour`,
|
||||
/// but the interval from 7:00 to 6:00 is `-1.hour`.
|
||||
/// In this case, `-1.hour` means **one hour ago**.
|
||||
///
|
||||
/// - The interval comparing `3.hour` to `1.hour` is `2.hour`,
|
||||
/// but the interval comparing `1.hour` to `3.hour` is `-2.hour`.
|
||||
/// In this case, `-2.hour` means **two hours shorter**
|
||||
/// An Interval represents a directed distance between two points
|
||||
/// on the time-line and can therefore be positive, zero or negative.
|
||||
public var isNegative: Bool {
|
||||
return nanoseconds < 0
|
||||
}
|
||||
|
||||
/// See `isNegative`.
|
||||
public var isPositive: Bool {
|
||||
return nanoseconds > 0
|
||||
/// A copy of this duration with a positive length.
|
||||
public var abs: Interval {
|
||||
return Interval(nanoseconds: Swift.abs(nanoseconds))
|
||||
}
|
||||
|
||||
/// The absolute value of the length of this interval in nanoseconds.
|
||||
public var magnitude: Double {
|
||||
return nanoseconds.magnitude
|
||||
}
|
||||
|
||||
/// The additive inverse of this interval.
|
||||
public var opposite: Interval {
|
||||
return (-nanoseconds).nanoseconds
|
||||
/// A copy of this interval with the length negated.
|
||||
public var negated: Interval {
|
||||
return Interval(nanoseconds: -nanoseconds)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,18 +65,22 @@ extension Interval: CustomDebugStringConvertible {
|
|||
}
|
||||
|
||||
// MARK: - Comparing
|
||||
|
||||
extension Interval: Comparable {
|
||||
|
||||
/// Compares two intervals.
|
||||
/// Compares two intervals and returns a comparison result value
|
||||
/// that indicates the sort order of two intervals.
|
||||
///
|
||||
/// The comparison is magnitude independent, a positive interval is
|
||||
/// always ordered ascending to a negative interval.
|
||||
/// A positive interval is always ordered ascending to a negative interval.
|
||||
public func compare(_ other: Interval) -> ComparisonResult {
|
||||
let now = Date()
|
||||
return now.adding(self).compare(now.adding(other))
|
||||
let d = nanoseconds - other.nanoseconds
|
||||
|
||||
if d < 0 { return .orderedAscending }
|
||||
if d > 0 { return .orderedDescending }
|
||||
return .orderedSame
|
||||
}
|
||||
|
||||
/// Returns a boolean value indicating whether the first interval is
|
||||
/// Returns a Boolean value indicating whether the first interval is
|
||||
/// less than the second interval.
|
||||
///
|
||||
/// A negative interval is always less than a positive interval.
|
||||
|
@ -94,43 +88,23 @@ extension Interval: Comparable {
|
|||
return lhs.compare(rhs) == .orderedAscending
|
||||
}
|
||||
|
||||
/// Returns a boolean value indicating whether this interval is longer
|
||||
/// than the given value.
|
||||
/// Returns a Boolean value indicating whether this interval is longer
|
||||
/// than the given interval.
|
||||
public func isLonger(than other: Interval) -> Bool {
|
||||
return magnitude > other.magnitude
|
||||
return abs > other.abs
|
||||
}
|
||||
|
||||
/// Returns a boolean value indicating whether this interval is shorter
|
||||
/// than the given value.
|
||||
/// Returns a Boolean value indicating whether this interval is shorter
|
||||
/// than the given interval.
|
||||
public func isShorter(than other: Interval) -> Bool {
|
||||
return magnitude < other.magnitude
|
||||
}
|
||||
|
||||
/// Returns the longest interval of the given values.
|
||||
public static func longest(_ intervals: Interval...) -> Interval {
|
||||
return longest(intervals)!
|
||||
}
|
||||
|
||||
/// Returns the longest interval of the given values.
|
||||
public static func longest(_ intervals: [Interval]) -> Interval? {
|
||||
guard !intervals.isEmpty else { return nil }
|
||||
return intervals.sorted(by: { $0.magnitude > $1.magnitude })[0]
|
||||
}
|
||||
|
||||
/// Returns the shortest interval of the given values.
|
||||
public static func shortest(_ intervals: Interval...) -> Interval {
|
||||
return shortest(intervals)!
|
||||
}
|
||||
|
||||
/// Returns the shortest interval of the given values.
|
||||
public static func shortest(_ intervals: [Interval]) -> Interval? {
|
||||
guard !intervals.isEmpty else { return nil }
|
||||
return intervals.sorted(by: { $0.magnitude < $1.magnitude })[0]
|
||||
return abs < other.abs
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Adding & Subtracting
|
||||
|
||||
extension Interval {
|
||||
|
||||
/// Returns a new interval by multipling this interval by the given number.
|
||||
///
|
||||
/// 1.hour * 2 == 2.hours
|
||||
|
@ -144,13 +118,6 @@ extension Interval {
|
|||
public func adding(_ other: Interval) -> Interval {
|
||||
return Interval(nanoseconds: nanoseconds + other.nanoseconds)
|
||||
}
|
||||
|
||||
/// Returns a new interval by subtracting the given interval from this interval.
|
||||
///
|
||||
/// 2.hours - 1.hour == 1.hour
|
||||
public func subtracting(_ other: Interval) -> Interval {
|
||||
return Interval(nanoseconds: nanoseconds - other.nanoseconds)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Operators
|
||||
|
@ -174,21 +141,22 @@ extension Interval {
|
|||
///
|
||||
/// 2.hours - 1.hour == 1.hour
|
||||
public static func - (lhs: Interval, rhs: Interval) -> Interval {
|
||||
return lhs.subtracting(rhs)
|
||||
return lhs.adding(rhs.negated)
|
||||
}
|
||||
|
||||
/// Adds two intervals and stores the result in the first interval.
|
||||
/// Adds two intervals and stores the result in the left interval.
|
||||
public static func += (lhs: inout Interval, rhs: Interval) {
|
||||
lhs = lhs.adding(rhs)
|
||||
}
|
||||
|
||||
/// Returns the additive inverse of the specified interval.
|
||||
public prefix static func - (interval: Interval) -> Interval {
|
||||
return interval.opposite
|
||||
return interval.negated
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sugars
|
||||
|
||||
extension Interval {
|
||||
|
||||
/// Creates an interval from the given number of seconds.
|
||||
|
@ -245,6 +213,7 @@ public protocol IntervalConvertible {
|
|||
|
||||
extension Int: IntervalConvertible {
|
||||
|
||||
/// Creates an interval from this amount of nanoseconds.
|
||||
public var nanoseconds: Interval {
|
||||
return Interval(nanoseconds: Double(self))
|
||||
}
|
||||
|
@ -252,6 +221,7 @@ extension Int: IntervalConvertible {
|
|||
|
||||
extension Double: IntervalConvertible {
|
||||
|
||||
/// Creates an interval from this amount of nanoseconds.
|
||||
public var nanoseconds: Interval {
|
||||
return Interval(nanoseconds: self)
|
||||
}
|
||||
|
@ -259,80 +229,96 @@ extension Double: IntervalConvertible {
|
|||
|
||||
extension IntervalConvertible {
|
||||
|
||||
// Alias for `nanoseconds`.
|
||||
public var nanosecond: Interval {
|
||||
return nanoseconds
|
||||
}
|
||||
|
||||
// Alias for `microseconds`.
|
||||
public var microsecond: Interval {
|
||||
return microseconds
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of microseconds.
|
||||
public var microseconds: Interval {
|
||||
return nanoseconds * pow(10, 3)
|
||||
}
|
||||
|
||||
/// Alias for `milliseconds`.
|
||||
public var millisecond: Interval {
|
||||
return milliseconds
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of milliseconds.
|
||||
public var milliseconds: Interval {
|
||||
return nanoseconds * pow(10, 6)
|
||||
}
|
||||
|
||||
/// Alias for `second`.
|
||||
public var second: Interval {
|
||||
return seconds
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of seconds.
|
||||
public var seconds: Interval {
|
||||
return nanoseconds * pow(10, 9)
|
||||
}
|
||||
|
||||
/// Alias for `minute`.
|
||||
public var minute: Interval {
|
||||
return minutes
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of minutes.
|
||||
public var minutes: Interval {
|
||||
return seconds * 60
|
||||
}
|
||||
|
||||
/// Alias for `hours`.
|
||||
public var hour: Interval {
|
||||
return hours
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of hours.
|
||||
public var hours: Interval {
|
||||
return minutes * 60
|
||||
}
|
||||
|
||||
/// Alias for `days`.
|
||||
public var day: Interval {
|
||||
return days
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of days.
|
||||
public var days: Interval {
|
||||
return hours * 24
|
||||
}
|
||||
|
||||
/// Alias for `weeks`.
|
||||
public var week: Interval {
|
||||
return weeks
|
||||
}
|
||||
|
||||
/// Creates an interval from this amount of weeks.
|
||||
public var weeks: Interval {
|
||||
return days * 7
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Date
|
||||
|
||||
extension Date {
|
||||
|
||||
/// The interval between this date and the current date and time.
|
||||
///
|
||||
/// If this date is earlier than now, this interval will be negative.
|
||||
/// If this date is earlier than now, the interval will be negative.
|
||||
public var intervalSinceNow: Interval {
|
||||
return timeIntervalSinceNow.seconds
|
||||
}
|
||||
|
||||
/// Returns the interval between this date and the given date.
|
||||
///
|
||||
/// If this date is earlier than the given date, this interval will be negative.
|
||||
/// If this date is earlier than the given date, the interval will be negative.
|
||||
public func interval(since date: Date) -> Interval {
|
||||
return timeIntervalSince(date).seconds
|
||||
}
|
||||
|
@ -342,18 +328,19 @@ extension Date {
|
|||
return addingTimeInterval(interval.asSeconds())
|
||||
}
|
||||
|
||||
/// Returns a date with an interval added to it.
|
||||
/// Returns a new date by adding an interval to the date.
|
||||
public static func + (lhs: Date, rhs: Interval) -> Date {
|
||||
return lhs.adding(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DispatchSourceTimer
|
||||
|
||||
extension DispatchSourceTimer {
|
||||
|
||||
/// Schedule this timer later.
|
||||
func schedule(after timeout: Interval) {
|
||||
guard !timeout.isNegative else { return }
|
||||
if timeout.isNegative { return }
|
||||
let ns = timeout.nanoseconds.clampedToInt()
|
||||
schedule(wallDeadline: .now() + DispatchTimeInterval.nanoseconds(ns))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
/// `Monthday` represents a day of a month.
|
||||
/// `Monthday` represents the combination of a month and day-of-month.
|
||||
public enum Monthday {
|
||||
|
||||
case january(Int)
|
||||
|
@ -27,9 +27,9 @@ public enum Monthday {
|
|||
|
||||
case december(Int)
|
||||
|
||||
/// Returns a dateComponenets of the monthday, using gregorian calender and
|
||||
/// Returns a dateComponenets of this monthday, using gregorian calender and
|
||||
/// current time zone.
|
||||
public func toDateComponents() -> DateComponents {
|
||||
public func asDateComponents() -> DateComponents {
|
||||
var month, day: Int
|
||||
switch self {
|
||||
case .january(let n): month = 1; day = n
|
||||
|
@ -49,16 +49,15 @@ public enum Monthday {
|
|||
calendar: Calendar.gregorian,
|
||||
timeZone: TimeZone.current,
|
||||
month: month,
|
||||
day: day
|
||||
)
|
||||
day: day)
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
|
||||
/// Returns a boolean value indicating whether today is the monthday.
|
||||
/// Returns a Boolean value indicating whether this date is the monthday.
|
||||
public func `is`(_ monthday: Monthday) -> Bool {
|
||||
let components = monthday.toDateComponents()
|
||||
let components = monthday.asDateComponents()
|
||||
|
||||
let m = Calendar.gregorian.component(.month, from: self)
|
||||
let d = Calendar.gregorian.component(.day, from: self)
|
||||
|
@ -72,7 +71,7 @@ extension Monthday: CustomStringConvertible {
|
|||
///
|
||||
/// "Monthday: May 1st"
|
||||
public var description: String {
|
||||
let components = toDateComponents()
|
||||
let components = asDateComponents()
|
||||
|
||||
let m = components.month!
|
||||
let d = components.day!
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
|
||||
/// `Period` represents a period of time defined in terms of fields.
|
||||
/// Type used to represents a date-based amount of time in the ISO-8601 calendar system,
|
||||
/// such as '2 years, 3 months and 4 days'.
|
||||
///
|
||||
/// It's a little different from `Interval`:
|
||||
///
|
||||
|
@ -51,104 +52,113 @@ public struct Period {
|
|||
/// Period.registerQuantifier("fifty", for: 15)
|
||||
/// let period = Period("fifty minutes")
|
||||
public static func registerQuantifier(_ word: String, for number: Int) {
|
||||
quantifiers.write { $0[word] = number }
|
||||
quantifiers.writeVoid { $0[word] = number }
|
||||
}
|
||||
|
||||
/// Creates a period from a natural expression.
|
||||
/// Initializes a period from a natural expression.
|
||||
///
|
||||
/// Period("one second") => Period(seconds: 1)
|
||||
/// Period("two hours and ten minutes") => Period(hours: 2, minutes: 10)
|
||||
/// Period("1 year, 2 months and 3 days") => Period(years: 1, months: 2, days: 3)
|
||||
/// Period("one second") -> Period(seconds: 1)
|
||||
/// Period("two hours and ten minutes") -> Period(hours: 2, minutes: 10)
|
||||
/// Period("1 year, 2 months and 3 days") -> Period(years: 1, months: 2, days: 3)
|
||||
public init?(_ string: String) {
|
||||
var period = string
|
||||
var str = string
|
||||
for (word, number) in Period.quantifiers.read({ $0 }) {
|
||||
period = period.replacingOccurrences(of: word, with: "\(number)")
|
||||
str = str.replacingOccurrences(of: word, with: "\(number)")
|
||||
}
|
||||
guard let regex = try? NSRegularExpression(pattern: "( and |, )") else {
|
||||
guard let regexp = try? NSRegularExpression(pattern: "( and |, )") else {
|
||||
return nil
|
||||
}
|
||||
period = regex.stringByReplacingMatches(in: period, range: NSRange(location: 0, length: period.count), withTemplate: "$")
|
||||
str = regexp.stringByReplacingMatches(in: str, range: NSRange(location: 0, length: str.count), withTemplate: "$")
|
||||
|
||||
var period = 0.year
|
||||
for pair in str.split(separator: "$").map({ $0.split(separator: " ") }) {
|
||||
guard
|
||||
pair.count == 2,
|
||||
let number = Int(pair[0])
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result = 0.year
|
||||
for pair in period.split(separator: "$").map({ $0.split(separator: " ") }) {
|
||||
guard pair.count == 2 else { return nil }
|
||||
guard let number = Int(pair[0]) else { return nil }
|
||||
var unit = String(pair[1])
|
||||
if unit.last == "s" { unit.removeLast() }
|
||||
switch unit {
|
||||
case "year": result = result + number.years
|
||||
case "month": result = result + number.months
|
||||
case "day": result = result + number.days
|
||||
case "week": result = result + (number * 7).days
|
||||
case "hour": result = result + number.hours
|
||||
case "minute": result = result + number.minutes
|
||||
case "second": result = result + number.second
|
||||
case "nanosecond": result = result + number.nanosecond
|
||||
case "year": period = period + number.years
|
||||
case "month": period = period + number.months
|
||||
case "day": period = period + number.days
|
||||
case "week": period = period + (number * 7).days
|
||||
case "hour": period = period + number.hours
|
||||
case "minute": period = period + number.minutes
|
||||
case "second": period = period + number.second
|
||||
case "nanosecond": period = period + number.nanosecond
|
||||
default: break
|
||||
}
|
||||
}
|
||||
self = result.tidied(to: .day)
|
||||
self = period
|
||||
}
|
||||
|
||||
/// Returns a new period by adding a period to this period.
|
||||
/// Returns a new period by adding the given period to this period.
|
||||
public func adding(_ other: Period) -> Period {
|
||||
return Period(years: years.clampedAdding(other.years),
|
||||
months: months.clampedAdding(other.months),
|
||||
days: days.clampedAdding(other.days),
|
||||
hours: hours.clampedAdding(other.hours),
|
||||
minutes: minutes.clampedAdding(other.minutes),
|
||||
seconds: seconds.clampedAdding(other.seconds),
|
||||
nanoseconds: nanoseconds.clampedAdding(other.nanoseconds))
|
||||
return Period(
|
||||
years: years.clampedAdding(other.years),
|
||||
months: months.clampedAdding(other.months),
|
||||
days: days.clampedAdding(other.days),
|
||||
hours: hours.clampedAdding(other.hours),
|
||||
minutes: minutes.clampedAdding(other.minutes),
|
||||
seconds: seconds.clampedAdding(other.seconds),
|
||||
nanoseconds: nanoseconds.clampedAdding(other.nanoseconds))
|
||||
}
|
||||
|
||||
/// Returns a new period by adding an interval to this period.
|
||||
/// Returns a new period by adding an interval to the period.
|
||||
public func adding(_ interval: Interval) -> Period {
|
||||
return Period(
|
||||
years: years, months: months, days: days,
|
||||
hours: hours, minutes: minutes, seconds: seconds,
|
||||
nanoseconds: nanoseconds.clampedAdding(interval.nanoseconds.clampedToInt())
|
||||
).tidied(to: .day)
|
||||
nanoseconds: nanoseconds.clampedAdding(interval.nanoseconds.clampedToInt()))
|
||||
}
|
||||
|
||||
/// Adds two periods and produces their sum.
|
||||
/// Returns a new period by adding the right period to the left period.
|
||||
///
|
||||
/// Period(days: 1) + Period(days: 1) -> Period(days: 2)
|
||||
public static func + (lhs: Period, rhs: Period) -> Period {
|
||||
return lhs.adding(rhs)
|
||||
}
|
||||
|
||||
/// Returns a period with an interval added to it.
|
||||
/// Returns a new period by adding an interval to the period.
|
||||
public static func + (lhs: Period, rhs: Interval) -> Period {
|
||||
return lhs.adding(rhs)
|
||||
}
|
||||
|
||||
/// Type to be used as tidying parameter.
|
||||
public enum Unit {
|
||||
/// Represents the tidy level.
|
||||
public enum TideLevel {
|
||||
case day, hour, minute, second, nanosecond
|
||||
}
|
||||
|
||||
/// Returns a tidied period.
|
||||
/// Returns the tidied period.
|
||||
///
|
||||
/// Period(hours: 25).tidied(to .day) => Period(days: 1, hours: 1)
|
||||
public func tidied(to unit: Unit) -> Period {
|
||||
public func tidied(to level: TideLevel) -> Period {
|
||||
var period = self
|
||||
if case .nanosecond = unit { return period }
|
||||
|
||||
if case .nanosecond = level { return period }
|
||||
|
||||
if period.nanoseconds.magnitude >= UInt(1.second.nanoseconds) {
|
||||
period.seconds += period.nanoseconds / Int(1.second.nanoseconds)
|
||||
period.nanoseconds %= Int(1.second.nanoseconds)
|
||||
}
|
||||
if case .second = level { return period }
|
||||
|
||||
if case .second = unit { return period }
|
||||
if period.seconds.magnitude >= 60 {
|
||||
period.minutes += period.seconds / 60
|
||||
period.seconds %= 60
|
||||
}
|
||||
if case .minute = level { return period }
|
||||
|
||||
if case .minute = unit { return period }
|
||||
if period.minutes.magnitude >= 60 {
|
||||
period.hours += period.minutes / 60
|
||||
period.minutes %= 60
|
||||
}
|
||||
if case .hour = level { return period }
|
||||
|
||||
if case .hour = unit { return period }
|
||||
if period.hours.magnitude >= 24 {
|
||||
period.days += period.hours / 24
|
||||
period.hours %= 24
|
||||
|
@ -156,9 +166,9 @@ public struct Period {
|
|||
return period
|
||||
}
|
||||
|
||||
/// Returns a dateComponenets of the period, using gregorian calender and
|
||||
/// Returns a dateComponenets of this period, using gregorian calender and
|
||||
/// current time zone.
|
||||
public func toDateComponents() -> DateComponents {
|
||||
public func asDateComponents() -> DateComponents {
|
||||
return DateComponents(year: years, month: months, day: days,
|
||||
hour: hours, minute: minutes, second: seconds,
|
||||
nanosecond: nanoseconds)
|
||||
|
@ -169,10 +179,10 @@ extension Date {
|
|||
|
||||
/// Returns a new date by adding a period to this date.
|
||||
public func adding(_ period: Period) -> Date {
|
||||
return Calendar.gregorian.date(byAdding: period.toDateComponents(), to: self) ?? .distantFuture
|
||||
return Calendar.gregorian.date(byAdding: period.asDateComponents(), to: self) ?? .distantFuture
|
||||
}
|
||||
|
||||
/// Returns a date with a period added to it.
|
||||
/// Returns a new date by adding a period to this date.
|
||||
public static func + (lhs: Date, rhs: Period) -> Date {
|
||||
return lhs.adding(rhs)
|
||||
}
|
||||
|
@ -180,18 +190,22 @@ extension Date {
|
|||
|
||||
extension Int {
|
||||
|
||||
/// Creates a period from this amount of years.
|
||||
public var years: Period {
|
||||
return Period(years: self)
|
||||
}
|
||||
|
||||
/// Alias for `years`.
|
||||
public var year: Period {
|
||||
return years
|
||||
}
|
||||
|
||||
/// Creates a period from this amount of month.
|
||||
public var months: Period {
|
||||
return Period(months: self)
|
||||
}
|
||||
|
||||
/// Alias for `month`.
|
||||
public var month: Period {
|
||||
return months
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ extension Plan {
|
|||
var last: Date!
|
||||
return AnyIterator {
|
||||
last = last ?? Date()
|
||||
guard let next = calendar.date(byAdding: period.toDateComponents(),
|
||||
guard let next = calendar.date(byAdding: period.asDateComponents(),
|
||||
to: last) else {
|
||||
return nil
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ extension Plan {
|
|||
} else if Date().is(weekday) {
|
||||
date = Date().startOfToday
|
||||
} else {
|
||||
let components = weekday.toDateComponents()
|
||||
let components = weekday.asDateComponents()
|
||||
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
|
||||
}
|
||||
return date
|
||||
|
@ -450,7 +450,7 @@ extension Plan {
|
|||
} else if Date().is(monthday) {
|
||||
date = Date().startOfToday
|
||||
} else {
|
||||
let components = monthday.toDateComponents()
|
||||
let components = monthday.asDateComponents()
|
||||
date = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .strict)
|
||||
}
|
||||
return date
|
||||
|
|
|
@ -94,7 +94,7 @@ open class Task {
|
|||
}
|
||||
|
||||
private func scheduleNext() {
|
||||
_mutex.withLock {
|
||||
_mutex.withLockVoid {
|
||||
let now = Date()
|
||||
var estimated = _timeline.estimatedNextExecution ?? now
|
||||
repeat {
|
||||
|
@ -155,7 +155,7 @@ open class Task {
|
|||
|
||||
/// Reschedules this task with the new plan.
|
||||
public func reschedule(_ new: Plan) {
|
||||
_mutex.withLock {
|
||||
_mutex.withLockVoid {
|
||||
_iterator = new.makeIterator()
|
||||
}
|
||||
scheduleNext()
|
||||
|
@ -170,7 +170,7 @@ open class Task {
|
|||
|
||||
/// Suspends this task.
|
||||
public func suspend() {
|
||||
_mutex.withLock {
|
||||
_mutex.withLockVoid {
|
||||
if _suspensions < UInt64.max {
|
||||
_timer.suspend()
|
||||
_suspensions += 1
|
||||
|
@ -180,7 +180,7 @@ open class Task {
|
|||
|
||||
/// Resumes this task.
|
||||
public func resume() {
|
||||
_mutex.withLock {
|
||||
_mutex.withLockVoid {
|
||||
if _suspensions > 0 {
|
||||
_timer.resume()
|
||||
_suspensions -= 1
|
||||
|
@ -190,7 +190,7 @@ open class Task {
|
|||
|
||||
/// Cancels this task.
|
||||
public func cancel() {
|
||||
_mutex.withLock {
|
||||
_mutex.withLockVoid {
|
||||
_timer.cancel()
|
||||
}
|
||||
TaskCenter.default.remove(self)
|
||||
|
@ -234,7 +234,9 @@ open class Task {
|
|||
/// - Returns: `true` if set successfully, `false` if not.
|
||||
@discardableResult
|
||||
public func setLifetime(_ interval: Interval) -> Bool {
|
||||
guard restOfLifetime.isPositive else { return false }
|
||||
if restOfLifetime.isNegative {
|
||||
return false
|
||||
}
|
||||
|
||||
_mutex.lock()
|
||||
let age = Date().interval(since: _timeline.initialization)
|
||||
|
@ -258,10 +260,10 @@ open class Task {
|
|||
@discardableResult
|
||||
public func addLifetime(_ interval: Interval) -> Bool {
|
||||
var rest = restOfLifetime
|
||||
guard rest.isPositive else { return false }
|
||||
if rest.isNegative { return false }
|
||||
rest += interval
|
||||
guard rest.isPositive else { return false }
|
||||
_mutex.withLock {
|
||||
if rest.isNegative { return false }
|
||||
_mutex.withLockVoid {
|
||||
_lifetime += interval
|
||||
_lifetimeTimer.schedule(after: rest)
|
||||
}
|
||||
|
@ -276,7 +278,7 @@ open class Task {
|
|||
/// - Returns: `true` if set successfully, `false` if not.
|
||||
@discardableResult
|
||||
public func subtractLifetime(_ interval: Interval) -> Bool {
|
||||
return addLifetime(interval.opposite)
|
||||
return addLifetime(interval.negated)
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
@ -298,15 +300,15 @@ open class Task {
|
|||
|
||||
/// Removes action by key from this task.
|
||||
public func removeAction(byKey key: ActionKey) {
|
||||
_mutex.withLock {
|
||||
_ = _onElapseActions.delete(key.bagKey)
|
||||
_mutex.withLockVoid {
|
||||
_ = _onElapseActions.removeValue(for: key.bagKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all actions from this task.
|
||||
public func removeAllActions() {
|
||||
_mutex.withLock {
|
||||
_onElapseActions.clear()
|
||||
_mutex.withLockVoid {
|
||||
_onElapseActions.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ open class TaskCenter {
|
|||
|
||||
task.taskCenterMutex.unlock()
|
||||
|
||||
mutex.withLock {
|
||||
mutex.withLockVoid {
|
||||
let box = TaskBox(task)
|
||||
tagMap[box] = []
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ open class TaskCenter {
|
|||
|
||||
task.taskCenterMutex.unlock()
|
||||
|
||||
mutex.withLock {
|
||||
mutex.withLockVoid {
|
||||
let box = TaskBox(task)
|
||||
if let tags = self.tagMap[box] {
|
||||
for tag in tags {
|
||||
|
@ -99,7 +99,7 @@ open class TaskCenter {
|
|||
open func addTags(_ tags: [String], to task: Task) {
|
||||
guard task.taskCenter === self else { return }
|
||||
|
||||
mutex.withLock {
|
||||
mutex.withLockVoid {
|
||||
let box = TaskBox(task)
|
||||
if tagMap[box] == nil {
|
||||
tagMap[box] = []
|
||||
|
@ -127,7 +127,7 @@ open class TaskCenter {
|
|||
open func removeTags(_ tags: [String], from task: Task) {
|
||||
guard task.taskCenter === self else { return }
|
||||
|
||||
mutex.withLock {
|
||||
mutex.withLockVoid {
|
||||
let box = TaskBox(task)
|
||||
for tag in tags {
|
||||
tagMap[box]?.remove(tag)
|
||||
|
@ -170,7 +170,7 @@ open class TaskCenter {
|
|||
|
||||
/// Removes all tasks in this center.
|
||||
open func clear() {
|
||||
mutex.withLock {
|
||||
mutex.withLockVoid {
|
||||
tagMap = [:]
|
||||
taskMap = [:]
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ public struct Time {
|
|||
/// Nanosecond of second.
|
||||
public let nanosecond: Int
|
||||
|
||||
/// Creates a time with `hour`, `minute`, `second` and `nanosecond` fields.
|
||||
///
|
||||
/// If any parameter is illegal, return nil.
|
||||
/// Initializes a time with `hour`, `minute`, `second` and `nanosecond`.
|
||||
///
|
||||
/// Time(hour: 11, minute: 11) => "11:11:00.000"
|
||||
/// Time(hour: 25) => nil
|
||||
|
@ -36,34 +34,29 @@ public struct Time {
|
|||
self.nanosecond = nanosecond
|
||||
}
|
||||
|
||||
/// Creates a time with a string.
|
||||
/// Initializes a time with a string.
|
||||
///
|
||||
/// If the parameter is illegal, return nil.
|
||||
///
|
||||
/// Time("11") == Time(hour: 11)
|
||||
/// Time("11:12") == Time(hour: 11, minute: 12)
|
||||
/// Time("11:12:13") == Time(hour: 11, minute: 12, second: 13)
|
||||
/// Time("11:12:13.123") == Time(hour: 11, minute: 12, second: 13, nanosecond: 123000000)
|
||||
/// Time("11") -> Time(hour: 11)
|
||||
/// Time("11:12") -> Time(hour: 11, minute: 12)
|
||||
/// Time("11:12:13") -> Time(hour: 11, minute: 12, second: 13)
|
||||
/// Time("11:12:13.123") -> Time(hour: 11, minute: 12, second: 13, nanosecond: 123000000)
|
||||
///
|
||||
/// Time("-1.0") == nil
|
||||
///
|
||||
/// Any of the previous examples can have a period suffix("am", "AM", "pm", "PM"),
|
||||
/// separated by a space.
|
||||
///
|
||||
/// Time("11 pm") == Time(hour: 23)
|
||||
/// Time("11:12:13 PM") == Time(hour: 23, minute: 12, second: 13)
|
||||
public init?(_ string: String) {
|
||||
let pattern = "^(\\d{1,2})(:(\\d{1,2})(:(\\d{1,2})(.(\\d{1,3}))?)?)?( (am|AM|pm|PM))?$"
|
||||
|
||||
guard let regexp = try? NSRegularExpression(pattern: pattern, options: []) else { return nil }
|
||||
guard let result = regexp.matches(in: string, options: [], range: NSRange(location: 0, length: string.count)).first else { return nil }
|
||||
guard let matches = regexp.matches(in: string, options: [], range: NSRange(location: 0, length: string.count)).first else { return nil }
|
||||
|
||||
var hasAM = false
|
||||
var hasPM = false
|
||||
var values: [Int] = []
|
||||
|
||||
for i in 0..<result.numberOfRanges {
|
||||
let range = result.range(at: i)
|
||||
for i in 0..<matches.numberOfRanges {
|
||||
let range = matches.range(at: i)
|
||||
if range.length == 0 { continue }
|
||||
let captured = NSString(string: string).substring(with: range)
|
||||
hasAM = ["am", "AM"].contains(captured)
|
||||
|
@ -94,7 +87,7 @@ public struct Time {
|
|||
|
||||
/// Returns a dateComponenets of the time, using gregorian calender and
|
||||
/// current time zone.
|
||||
public func toDateComponents() -> DateComponents {
|
||||
public func asDateComponents() -> DateComponents {
|
||||
return DateComponents(calendar: Calendar.gregorian,
|
||||
timeZone: TimeZone.current,
|
||||
hour: hour,
|
||||
|
|
|
@ -7,18 +7,17 @@ public enum Weekday: Int {
|
|||
|
||||
/// Returns dateComponenets of the weekday, using gregorian calender and
|
||||
/// current time zone.
|
||||
public func toDateComponents() -> DateComponents {
|
||||
public func asDateComponents() -> DateComponents {
|
||||
return DateComponents(
|
||||
calendar: Calendar.gregorian,
|
||||
timeZone: TimeZone.current,
|
||||
weekday: rawValue
|
||||
)
|
||||
weekday: rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
|
||||
/// Returns a boolean value indicating whether this day is the weekday.
|
||||
/// Returns a Boolean value indicating whether this date is the weekday.
|
||||
public func `is`(_ weekday: Weekday) -> Bool {
|
||||
return Calendar.gregorian.component(.weekday, from: self) == weekday.rawValue
|
||||
}
|
||||
|
|
|
@ -3,29 +3,42 @@ import XCTest
|
|||
|
||||
final class AtomicTests: XCTestCase {
|
||||
|
||||
func testSnapshot() {
|
||||
let i = Atomic<Int>(1)
|
||||
XCTAssertEqual(i.snapshot(), 1)
|
||||
}
|
||||
|
||||
func testRead() {
|
||||
let i = Atomic<Int>(1)
|
||||
i.read {
|
||||
XCTAssertEqual($0, 1)
|
||||
}
|
||||
let val = i.read { $0 }
|
||||
XCTAssertEqual(val, 1)
|
||||
}
|
||||
|
||||
func testReadVoid() {
|
||||
let i = Atomic<Int>(1)
|
||||
var val = 0
|
||||
i.read { val = $0 }
|
||||
XCTAssertEqual(val, 1)
|
||||
}
|
||||
|
||||
func testWrite() {
|
||||
let i = Atomic<Int>(1)
|
||||
let val = i.write { v -> Int in
|
||||
v += 1
|
||||
return v
|
||||
}
|
||||
XCTAssertEqual(i.read { $0 }, val)
|
||||
}
|
||||
|
||||
func testWriteVoid() {
|
||||
let i = Atomic<Int>(1)
|
||||
var val = 0
|
||||
i.write {
|
||||
$0 += 1
|
||||
val = $0
|
||||
}
|
||||
XCTAssertEqual(i.snapshot(), 2)
|
||||
XCTAssertEqual(i.read { $0 }, val)
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testSnapshot", testSnapshot),
|
||||
("testRead", testRead),
|
||||
("testWrite", testWrite)
|
||||
("testReadVoid", testReadVoid),
|
||||
("testWrite", testWrite),
|
||||
("testWriteVoid", testWriteVoid),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -15,66 +15,80 @@ final class BagTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testAppend() {
|
||||
var cabinet = Bag<Fn>()
|
||||
cabinet.append { 1 }
|
||||
cabinet.append { 2 }
|
||||
var bag = Bag<Fn>()
|
||||
bag.append { 1 }
|
||||
bag.append { 2 }
|
||||
|
||||
XCTAssertEqual(cabinet.count, 2)
|
||||
XCTAssertEqual(bag.count, 2)
|
||||
}
|
||||
|
||||
func testGet() {
|
||||
var cabinet = Bag<Fn>()
|
||||
let k1 = cabinet.append { 1 }
|
||||
let k2 = cabinet.append { 2 }
|
||||
func testValueForKey() {
|
||||
var bag = Bag<Fn>()
|
||||
let k1 = bag.append { 1 }
|
||||
let k2 = bag.append { 2 }
|
||||
|
||||
guard
|
||||
let fn1 = cabinet.get(k1),
|
||||
let fn2 = cabinet.get(k2)
|
||||
else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(fn1(), 1)
|
||||
XCTAssertEqual(fn2(), 2)
|
||||
}
|
||||
|
||||
func testDelete() {
|
||||
var cabinet = Bag<Fn>()
|
||||
|
||||
let k1 = cabinet.append { 1 }
|
||||
let k2 = cabinet.append { 2 }
|
||||
|
||||
XCTAssertEqual(cabinet.count, 2)
|
||||
|
||||
let fn1 = cabinet.delete(k1)
|
||||
let fn1 = bag.value(for: k1)
|
||||
XCTAssertNotNil(fn1)
|
||||
|
||||
let fn2 = cabinet.delete(k2)
|
||||
let fn2 = bag.value(for: k2)
|
||||
XCTAssertNotNil(fn2)
|
||||
|
||||
XCTAssertEqual(cabinet.count, 0)
|
||||
guard let _fn1 = fn1, let _fn2 = fn2 else { return }
|
||||
|
||||
XCTAssertEqual(_fn1(), 1)
|
||||
XCTAssertEqual(_fn2(), 2)
|
||||
}
|
||||
|
||||
func testClear() {
|
||||
var cabinet = Bag<Fn>()
|
||||
func testRemoveValueForKey() {
|
||||
var bag = Bag<Fn>()
|
||||
|
||||
cabinet.append { 1 }
|
||||
cabinet.append { 2 }
|
||||
let k1 = bag.append { 1 }
|
||||
let k2 = bag.append { 2 }
|
||||
|
||||
XCTAssertEqual(cabinet.count, 2)
|
||||
let fn1 = bag.removeValue(for: k1)
|
||||
XCTAssertNotNil(fn1)
|
||||
|
||||
cabinet.clear()
|
||||
XCTAssertEqual(cabinet.count, 0)
|
||||
let fn2 = bag.removeValue(for: k2)
|
||||
XCTAssertNotNil(fn2)
|
||||
|
||||
guard let _fn1 = fn1, let _fn2 = fn2 else { return }
|
||||
|
||||
XCTAssertEqual(_fn1(), 1)
|
||||
XCTAssertEqual(_fn2(), 2)
|
||||
}
|
||||
|
||||
func testCount() {
|
||||
var bag = Bag<Fn>()
|
||||
|
||||
let k1 = bag.append { 1 }
|
||||
let k2 = bag.append { 2 }
|
||||
|
||||
XCTAssertEqual(bag.count, 2)
|
||||
|
||||
bag.removeValue(for: k1)
|
||||
bag.removeValue(for: k2)
|
||||
|
||||
XCTAssertEqual(bag.count, 0)
|
||||
}
|
||||
|
||||
func testRemoveAll() {
|
||||
var bag = Bag<Fn>()
|
||||
|
||||
bag.append { 1 }
|
||||
bag.append { 2 }
|
||||
|
||||
bag.removeAll()
|
||||
XCTAssertEqual(bag.count, 0)
|
||||
}
|
||||
|
||||
func testSequence() {
|
||||
var cabinet = Bag<Fn>()
|
||||
cabinet.append { 0 }
|
||||
cabinet.append { 1 }
|
||||
cabinet.append { 2 }
|
||||
var bag = Bag<Fn>()
|
||||
bag.append { 0 }
|
||||
bag.append { 1 }
|
||||
bag.append { 2 }
|
||||
|
||||
var i = 0
|
||||
for fn in cabinet {
|
||||
for fn in bag {
|
||||
XCTAssertEqual(fn(), i)
|
||||
i += 1
|
||||
}
|
||||
|
@ -83,9 +97,10 @@ final class BagTests: XCTestCase {
|
|||
static var allTests = [
|
||||
("testBagKey", testBagKey),
|
||||
("testAppend", testAppend),
|
||||
("testGet", testGet),
|
||||
("testDelete", testDelete),
|
||||
("testClear", testClear),
|
||||
("testValueForKey", testValueForKey),
|
||||
("testRemoveValueForKey", testRemoveValueForKey),
|
||||
("testCount", testCount),
|
||||
("testRemoveAll", testRemoveAll),
|
||||
("testSequence", testSequence)
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,9 +6,8 @@ final class DateTimeTests: XCTestCase {
|
|||
func testInterval() {
|
||||
|
||||
XCTAssertTrue((-1).second.isNegative)
|
||||
XCTAssertTrue(1.second.isPositive)
|
||||
XCTAssertEqual(1.1.second.magnitude, 1.1.second.nanoseconds)
|
||||
XCTAssertEqual(1.second.opposite, (-1).second)
|
||||
XCTAssertEqual(1.1.second.abs, 1.1.second)
|
||||
XCTAssertEqual(1.second.negated, (-1).second)
|
||||
|
||||
XCTAssertEqual(7.day.hashValue, 1.week.hashValue)
|
||||
XCTAssertEqual(7.day, 1.week)
|
||||
|
@ -21,10 +20,6 @@ final class DateTimeTests: XCTestCase {
|
|||
|
||||
XCTAssertTrue(1.1.second.isLonger(than: 1.0.second))
|
||||
XCTAssertTrue(3.days.isShorter(than: 1.week))
|
||||
XCTAssertEqual(Interval.longest(1.hour, 1.day, 1.week), 1.week)
|
||||
XCTAssertEqual(Interval.longest([]), nil)
|
||||
XCTAssertEqual(Interval.shortest(1.hour, 59.minutes, 2999.seconds), 2999.seconds)
|
||||
XCTAssertEqual(Interval.shortest([]), nil)
|
||||
|
||||
XCTAssertEqual(1.second * 60, 1.minute)
|
||||
XCTAssertEqual(59.minutes + 60.seconds, 1.hour)
|
||||
|
@ -55,18 +50,18 @@ final class DateTimeTests: XCTestCase {
|
|||
let d = Date(year: 2019, month: 1, day: 1)
|
||||
XCTAssertTrue(d.is(.january(1)))
|
||||
|
||||
XCTAssertEqual(Monthday.january(1).toDateComponents().month, 1)
|
||||
XCTAssertEqual(Monthday.february(1).toDateComponents().month, 2)
|
||||
XCTAssertEqual(Monthday.march(1).toDateComponents().month, 3)
|
||||
XCTAssertEqual(Monthday.april(1).toDateComponents().month, 4)
|
||||
XCTAssertEqual(Monthday.may(1).toDateComponents().month, 5)
|
||||
XCTAssertEqual(Monthday.june(1).toDateComponents().month, 6)
|
||||
XCTAssertEqual(Monthday.july(1).toDateComponents().month, 7)
|
||||
XCTAssertEqual(Monthday.august(1).toDateComponents().month, 8)
|
||||
XCTAssertEqual(Monthday.september(1).toDateComponents().month, 9)
|
||||
XCTAssertEqual(Monthday.october(1).toDateComponents().month, 10)
|
||||
XCTAssertEqual(Monthday.november(1).toDateComponents().month, 11)
|
||||
XCTAssertEqual(Monthday.december(1).toDateComponents().month, 12)
|
||||
XCTAssertEqual(Monthday.january(1).asDateComponents().month, 1)
|
||||
XCTAssertEqual(Monthday.february(1).asDateComponents().month, 2)
|
||||
XCTAssertEqual(Monthday.march(1).asDateComponents().month, 3)
|
||||
XCTAssertEqual(Monthday.april(1).asDateComponents().month, 4)
|
||||
XCTAssertEqual(Monthday.may(1).asDateComponents().month, 5)
|
||||
XCTAssertEqual(Monthday.june(1).asDateComponents().month, 6)
|
||||
XCTAssertEqual(Monthday.july(1).asDateComponents().month, 7)
|
||||
XCTAssertEqual(Monthday.august(1).asDateComponents().month, 8)
|
||||
XCTAssertEqual(Monthday.september(1).asDateComponents().month, 9)
|
||||
XCTAssertEqual(Monthday.october(1).asDateComponents().month, 10)
|
||||
XCTAssertEqual(Monthday.november(1).asDateComponents().month, 11)
|
||||
XCTAssertEqual(Monthday.december(1).asDateComponents().month, 12)
|
||||
}
|
||||
|
||||
func testPeriod() {
|
||||
|
@ -113,7 +108,7 @@ final class DateTimeTests: XCTestCase {
|
|||
XCTAssertTrue(i.isAlmostEqual(to: (0.456.second.nanoseconds).nanoseconds, leeway: 0.001.seconds))
|
||||
}
|
||||
|
||||
let components = t1?.toDateComponents()
|
||||
let components = t1?.asDateComponents()
|
||||
XCTAssertEqual(components?.hour, 11)
|
||||
XCTAssertEqual(components?.minute, 12)
|
||||
XCTAssertEqual(components?.second, 13)
|
||||
|
@ -140,7 +135,7 @@ final class DateTimeTests: XCTestCase {
|
|||
let d = Date(year: 2019, month: 1, day: 1)
|
||||
XCTAssertTrue(d.is(.tuesday))
|
||||
|
||||
XCTAssertEqual(Weekday.monday.toDateComponents().weekday!, 2)
|
||||
XCTAssertEqual(Weekday.monday.asDateComponents().weekday!, 2)
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
|
|
|
@ -5,9 +5,9 @@ import XCTest
|
|||
|
||||
final class DeinitObserverTests: XCTestCase {
|
||||
|
||||
func testObserver() {
|
||||
func testObserve() {
|
||||
var i = 0
|
||||
var fn = {
|
||||
let fn = {
|
||||
let obj = NSObject()
|
||||
DeinitObserver.observe(obj) {
|
||||
i += 1
|
||||
|
@ -15,8 +15,11 @@ final class DeinitObserverTests: XCTestCase {
|
|||
}
|
||||
fn()
|
||||
XCTAssertEqual(i, 1)
|
||||
}
|
||||
|
||||
fn = {
|
||||
func testCancel() {
|
||||
var i = 0
|
||||
let fn = {
|
||||
let obj = NSObject()
|
||||
let observer = DeinitObserver.observe(obj) {
|
||||
i += 1
|
||||
|
@ -24,11 +27,12 @@ final class DeinitObserverTests: XCTestCase {
|
|||
observer.cancel()
|
||||
}
|
||||
fn()
|
||||
XCTAssertEqual(i, 1)
|
||||
XCTAssertEqual(i, 0)
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("testObserver", testObserver)
|
||||
("testObserve", testObserve),
|
||||
("testCancel", testCancel)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,6 @@ final class ExtensionsTests: XCTestCase {
|
|||
XCTAssertEqual(i.clampedAdding(1), Int.max)
|
||||
}
|
||||
|
||||
func testClampedSubtracting() {
|
||||
let i = Int.min
|
||||
XCTAssertEqual(i.clampedSubtracting(1), Int.min)
|
||||
}
|
||||
|
||||
func testStartOfToday() {
|
||||
let components = Date().startOfToday.dateComponents
|
||||
guard
|
||||
|
@ -39,7 +34,6 @@ final class ExtensionsTests: XCTestCase {
|
|||
static var allTests = [
|
||||
("testClampedToInt", testClampedToInt),
|
||||
("testClampedAdding", testClampedAdding),
|
||||
("testClampedSubtracting", testClampedSubtracting),
|
||||
("testStartOfToday", testStartOfToday)
|
||||
]
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ extension Date {
|
|||
extension Interval {
|
||||
|
||||
func isAlmostEqual(to interval: Interval, leeway: Interval) -> Bool {
|
||||
return (interval - self).magnitude <= leeway.magnitude
|
||||
return (interval - self).abs <= leeway.abs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@ final class PlanTests: XCTestCase {
|
|||
let intervals = [1.second, 2.hours, 3.days, 4.weeks]
|
||||
let s0 = Plan.of(intervals[0], intervals[1], intervals[2], intervals[3])
|
||||
XCTAssertTrue(s0.makeIterator().isAlmostEqual(to: intervals, leeway: leeway))
|
||||
let s1 = Plan.from(intervals)
|
||||
XCTAssertTrue(s1.makeIterator().isAlmostEqual(to: intervals, leeway: leeway))
|
||||
|
||||
let d0 = Date() + intervals[0]
|
||||
let d1 = d0 + intervals[1]
|
||||
|
@ -42,15 +40,15 @@ final class PlanTests: XCTestCase {
|
|||
func testConcat() {
|
||||
let s0: [Interval] = [1.second, 2.minutes, 3.hours]
|
||||
let s1: [Interval] = [4.days, 5.weeks]
|
||||
let s3 = Plan.from(s0).concat(Plan.from(s1))
|
||||
let s4 = Plan.from(s0 + s1)
|
||||
let s3 = Plan.of(s0).concat(Plan.of(s1))
|
||||
let s4 = Plan.of(s0 + s1)
|
||||
XCTAssertTrue(s3.isAlmostEqual(to: s4, leeway: leeway))
|
||||
}
|
||||
|
||||
func testMerge() {
|
||||
let intervals0: [Interval] = [1.second, 2.minutes, 1.hour]
|
||||
let intervals1: [Interval] = [2.seconds, 1.minutes, 1.seconds]
|
||||
let scheudle0 = Plan.from(intervals0).merge(Plan.from(intervals1))
|
||||
let scheudle0 = Plan.of(intervals0).merge(Plan.of(intervals1))
|
||||
let scheudle1 = Plan.of(1.second, 1.second, 1.minutes, 1.seconds, 58.seconds, 1.hour)
|
||||
XCTAssertTrue(scheudle0.isAlmostEqual(to: scheudle1, leeway: leeway))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue