Skip to content

Commit

Permalink
Merge branch 'feature/4.1.1' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
malcommac committed Apr 6, 2017
2 parents 7892236 + 73d2898 commit 0be0c82
Show file tree
Hide file tree
Showing 23 changed files with 576 additions and 1,127 deletions.
77 changes: 63 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

## CHANGELOG

* Version **[4.1.1](#411)**
* Version **[4.1.0](#410)**
* Version **[4.0.14](#4014)**
* Version **[4.0.13](#4013)**
Expand All @@ -22,6 +23,23 @@
* Version **[4.0.2](#402)**
* Version **[4.0.0](#400)**

<a name="411" />

## SwiftDate 4.1.1
---
- **Release Date**: 2017/04/06
- **Zipped Version**: [Download 4.1.1](https://github.com/malcommac/SwiftDate/releases/tag/4.1.1)

#### New Features
- [#408](https://github.com/malcommac/SwiftDate/pull/408) `Date` and `DateInRegion` has now two functions to round a date to certain interval (expressed in `seconds` or `minutes`). `DateInRegion` exposes `roundAt()` which directly modify the date itself; `Date`, as immutable object, exposes the same feature in `roundedAt()` function (which return a new `Date` instances).
- [#]() Added Daylight Saving Time functions: `.isDST` in `DateInRegion` (it returns true if the date uses daylight saving time); `.DSTOffset` in `DateInRegion` (it returns the value (in seconds) of the daylight saving time offset of the represented date), `. nextDSTTransitionDate` in `DateInRegion` (it returns the next daylight saving time transition after currently represented date when expressed); `nextDSTTransitionDate()` func in `Date` (it returns the next dst transition date when receiver is expressed in a particular timezone); `DSTOffset()` func in `Date` (it returns the offset of daylight saving time when receiver is expressed in a particular timezone); `isDST()` func in `Date` (it returns if receiver date uses daylight saving time when expressed in a particular timezone).

#### Fixes
- [#](https://github.com/malcommac/SwiftDate/pull/) Several fixes while parsing less common `ISO8601` formats (weeks only w/wo implicit year/month/day).
- [#](https://github.com/malcommac/SwiftDate/pull/) Parsing an `ISO8601` string now results in a `DateInRegion` which contains the correct timezone defined (`Date` instances are now converted automatically to be time-independent).
- [#]() Updated Unit Tests to become compatible with the new APIs changes. All tests now passes.
- [#409](https://github.com/malcommac/SwiftDate/pull/409) Fixed an issue converting a difference between two dates when its more than 24 hours, but less than 48 hours (incorrectly the colloquial values returns 'yesterday')
- [#]() Single framework target in XCode Project (iOS, macOS, tvOS and watchOS)

<a name="410" />

Expand All @@ -45,25 +63,45 @@ YYYY-MM-DD
YYYY-MM
YYYY
YY //century
//Implied century: YY is 00-99
```

IMPLIED CENTURY: YY is 00-99

```
YYMMDD
YY-MM-DD
-YYMM
-YY-MM
-YY
//Implied year
```

IMPLIED YEAR

```
--MMDD
--MM-DD
--MM
//Implied year and month
```

IMPLIED YEAR AND MONTH

```
---DD
//Ordinal dates: DDD is the number of the day in the year (1-366)
```

ORDINAL DATES: DDD IS THE NUMBER OF THE DAY IN THE YEAR (1-366)

```
YYYYDDD
YYYY-DDD
YYDDD
YY-DDD
-DDD
//Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
```

WEEK-BASED DATES: ww IS THE NUMBER OF THE WEEK, AND d IS THE NUMBER (1-7) OF THE DAY IN THE WEEK

```
yyyyWwwd
yyyy-Www-d
yyyyWww
Expand All @@ -72,31 +110,42 @@ yyWwwd
yy-Www-d
yyWww
yy-Www
//Year of the implied decade
```

YEAR OF THE IMPLIED DECADE

```
-yWwwd
-y-Www-d
-yWww
-y-Www
//Week and day of implied year
```

WEEK AND DAY OF IMPLIED YEAR

```
-Wwwd
-Www-d
//Week only of implied year
```

WEEK ONLY OF IMPLIED YEAR

```
-Www
//Day only of implied week
-W-d
```

DAY ONLY OF IMPLIED WEEK

```
-W-d
```

#### Fixes
- [#405](https://github.com/malcommac/SwiftDate/pull/405) Fixed some translation issues in Swedish (thanks to @deville)
- [#368](https://github.com/malcommac/SwiftDate/pull/368) Deprecated `at(unitsWithValues dict: [Calendar.Component : Int])` in `Date` and `DateInRegion` and replaced with functional `at(values: [Calendar.Component : Int], keep: Set<Calendar.Component>)`
- [#392](https://github.com/malcommac/SwiftDate/pull/392) Fixed an issue with report negative interval when making operation with dates `a` and `b` where `a - b < 0 iff a < b`.
- [#397](https://github.com/malcommac/SwiftDate/pull/397) Fixed an issue with `colloquial` func which report wrong difference of `1 day` when two dates are distant < 24h but in two different days.





<a name="4014" />

## SwiftDate 4.0.14
Expand Down
17 changes: 17 additions & 0 deletions Sources/SwiftDate/Commons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@

import Foundation

public enum IntervalRoundingType {
case round
case ceil
case floor
}

public enum IntervalType {
case seconds(_: Int)
case minutes(_: Int)

internal var seconds: TimeInterval {
switch self {
case .seconds(let secs): return TimeInterval(secs)
case .minutes(let mins): return TimeInterval(mins * 60)
}
}
}

/// This define the weekdays
///
Expand Down
12 changes: 4 additions & 8 deletions Sources/SwiftDate/Date+Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,9 @@ public extension Date {
/// - parameter minute: the minute value
/// - parameter second: the second value
///
/// - throws: throw a `FailedToCalculate` exception of the new date cannot be evaluated.
///
/// - returns: a new `Date` object calculated at given time
public func atTime(hour: Int, minute: Int, second: Int) throws -> Date {
return try self.inDateDefaultRegion().atTime(hour: hour, minute: minute, second: second).absoluteDate
public func atTime(hour: Int, minute: Int, second: Int) -> Date? {
return self.inDateDefaultRegion().atTime(hour: hour, minute: minute, second: second)?.absoluteDate
}

/// Create a new instance calculated by setting a specific component of a given date to a given value, while trying to keep lower
Expand All @@ -343,11 +341,9 @@ public extension Date {
/// - parameter unit: The unit to set with the given value
/// - parameter value: The value to set for the given calendar unit.
///
/// - throws: throw a `FailedToCalculate` exception of the new date cannot be evaluated.
///
/// - returns: a new `Date` object calculated at given unit value
public func at(unit: Calendar.Component, value: Int) throws -> Date {
return try self.inDateDefaultRegion().at(unit: unit, value: value).absoluteDate
public func at(unit: Calendar.Component, value: Int) -> Date? {
return self.inDateDefaultRegion().at(unit: unit, value: value)?.absoluteDate
}

/// Create a new instance calculated by setting a list of components of a given date to given values (components
Expand Down
49 changes: 49 additions & 0 deletions Sources/SwiftDate/Date+Math.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,55 @@ public extension Date {
return dates
}

/// Adjust time of the date by rounding to the next `value` interval.
/// Interval can be `seconds` or `minutes` and you can specify the type of rounding function to use.
///
/// - Parameters:
/// - value: value to round
/// - type: type of rounding
public func roundedAt(_ value: IntervalType, type: IntervalRoundingType = .ceil) -> Date {
var roundedInterval: TimeInterval = 0
let seconds = value.seconds
switch type {
case .round:
roundedInterval = (self.timeIntervalSinceReferenceDate / seconds).rounded() * seconds
case .ceil:
roundedInterval = ceil(self.timeIntervalSinceReferenceDate / seconds) * seconds
case .floor:
roundedInterval = floor(self.timeIntervalSinceReferenceDate / seconds) * seconds
}
return Date(timeIntervalSinceReferenceDate: roundedInterval)
}

/// Returns a boolean value that indicates whether the represented absolute date uses daylight saving time when
/// expressed in passed timezone.
///
/// - Parameter tzName: destination timezone
/// - Returns: `true` if date uses DST when represented in given timezone, `false` otherwise
public func isDST(in tzName: TimeZoneName) -> Bool {
return tzName.timeZone.isDaylightSavingTime(for: self)
}

/// The current daylight saving time offset of the represented date when expressed in passed timezone.
///
/// - Parameter tzName: destination timezone
/// - Returns: interval of DST expressed in seconds
public func DSTOffset(in tzName: TimeZoneName) -> TimeInterval {
return tzName.timeZone.daylightSavingTimeOffset(for: self)
}

/// The date of the next daylight saving time transition after currently represented date when expressed
/// in given timezone.
///
/// - Parameter tzName: destination timezone
/// - Returns: next transition date
public func nextDSTTransitionDate(in tzName: TimeZoneName) -> Date? {
guard let next_date = tzName.timeZone.nextDaylightSavingTimeTransition(after: self) else {
return nil
}
return next_date
}

}

// MARK: - Sum of Dates and Date & Components
Expand Down
37 changes: 27 additions & 10 deletions Sources/SwiftDate/DateInRegion+Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -486,12 +486,12 @@ extension DateInRegion {
/// - parameter minute: the minute value
/// - parameter second: the second value
///
/// - throws: throw a `FailedToCalculate` exception of the new date cannot be evaluated.
///
/// - returns: a new `DateInRegion` object calculated at given time
public func atTime(hour: Int, minute: Int, second: Int) throws -> DateInRegion {
guard let newDate = self.region.calendar.date(bySettingHour: hour, minute: minute, second: second, of: self.absoluteDate) else {
throw DateError.FailedToCalculate
/// - returns: a new `DateInRegion` object calculated at given time; `nil` if date cannot be evaluated
public func atTime(hour: Int, minute: Int, second: Int) -> DateInRegion? {
let calendar = self.region.calendar
let absoluteDate = self.absoluteDate
guard let newDate = calendar.date(bySettingHour: hour, minute: minute, second: second, of: absoluteDate) else {
return nil
}
return DateInRegion(absoluteDate: newDate, in: self.region)
}
Expand All @@ -503,12 +503,10 @@ extension DateInRegion {
/// - parameter unit: The unit to set with the given value
/// - parameter value: The value to set for the given calendar unit.
///
/// - throws: throw a `FailedToCalculate` exception of the new date cannot be evaluated.
///
/// - returns: a new `DateInRegion` object calculated at given unit value
public func at(unit: Calendar.Component, value: Int) throws -> DateInRegion {
public func at(unit: Calendar.Component, value: Int) -> DateInRegion? {
guard let newDate = self.region.calendar.date(bySetting: unit, value: value, of: self.absoluteDate) else {
throw DateError.FailedToCalculate
return nil
}
return DateInRegion(absoluteDate: newDate, in: self.region)
}
Expand Down Expand Up @@ -583,6 +581,25 @@ extension DateInRegion {
public static func earliestDate(_ list: [DateInRegion]) -> DateInRegion {
return list.earliestDate
}

/// Returns a boolean value that indicates whether the represented date uses daylight saving time.
public var isDST: Bool {
return self.region.timeZone.isDaylightSavingTime(for: self.absoluteDate)
}

/// The current daylight saving time offset of the represented date.
public var DSTOffset: TimeInterval {
return self.region.timeZone.daylightSavingTimeOffset(for: self.absoluteDate)
}

/// The date of the next daylight saving time transition after currently represented date.
/// Date is reported in the same timezone of the receiver.
public var nextDSTTransitionDate: DateInRegion? {
guard let next_transition = self.region.timeZone.nextDaylightSavingTimeTransition(after: self.absoluteDate) else {
return nil
}
return DateInRegion(absoluteDate: next_transition, in: self.region)
}
}

public extension Array where Element: DateInRegion {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftDate/DateInRegion+Formatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ public extension DateInRegion {
return self.formatters.dateFormatter(format: format).string(from: self.absoluteDate)
case .iso8601(let options):
let formatter = self.formatters.isoFormatter()
return formatter.string(from: self.absoluteDate, options: options)
return formatter.string(from: self.absoluteDate, tz: self.region.timeZone, options: options)
case .iso8601Auto:
let formatter = self.formatters.isoFormatter()
return formatter.string(from: self.absoluteDate, options: [.withInternetDateTime])
return formatter.string(from: self.absoluteDate, tz: self.region.timeZone, options: [.withInternetDateTime])
case .rss(let isAltRSS):
let format = (isAltRSS ? "d MMM yyyy HH:mm:ss ZZZ" : "EEE, d MMM yyyy HH:mm:ss ZZZ")
return self.formatters.dateFormatter(format: format).string(from: self.absoluteDate)
Expand Down
23 changes: 22 additions & 1 deletion Sources/SwiftDate/DateInRegion+Math.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import Foundation
// MARK: - DateInRegion Private Extension

extension DateInRegion {


/// Return a `DateComponent` object from a given set of `Calendar.Component` object with associated values and a specific region
///
Expand Down Expand Up @@ -91,6 +90,28 @@ extension DateInRegion {
return dates
}


/// Adjust time of the date by rounding to the next `value` interval.
/// Interval can be `seconds` or `minutes` and you can specify the type of rounding function to use.
///
/// - Parameters:
/// - value: value to round
/// - type: type of rounding
public func roundAt(_ value: IntervalType, type: IntervalRoundingType = .ceil) {
var roundedInterval: TimeInterval = 0
let rounded_abs_date = self.absoluteDate
let seconds = value.seconds
switch type {
case .round:
roundedInterval = (rounded_abs_date.timeIntervalSinceReferenceDate / seconds).rounded() * seconds
case .ceil:
roundedInterval = ceil(rounded_abs_date.timeIntervalSinceReferenceDate / seconds) * seconds
case .floor:
roundedInterval = floor(rounded_abs_date.timeIntervalSinceReferenceDate / seconds) * seconds
}
self.absoluteDate = Date(timeIntervalSinceReferenceDate: roundedInterval)
}

}

// MARK: - DateInRegion Support for math operation
Expand Down
22 changes: 7 additions & 15 deletions Sources/SwiftDate/DateInRegion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class DateInRegion: CustomStringConvertible {
public fileprivate(set) var region: Region

/// Absolute date represented outside the `region`
public fileprivate(set) var absoluteDate: Date
public internal(set) var absoluteDate: Date

/// This is a reference to use formatters
public fileprivate(set) var formatters: Formatters
Expand Down Expand Up @@ -231,7 +231,7 @@ public class DateInRegion: CustomStringConvertible {
/// - parameter string: string with date to parse
/// - parameter format: format in which the date is expressed (see `DateFormat`)
/// - parameter region: region in which the date should be expressed (if nil `Region.Local()` will be used instead)
/// When `.iso8601` or `.iso8601Auto` is used, `region` parameter is ignored (timezone is set automatically by reading the string.
/// When `.iso8601` or `.iso8601Auto` is used, `region.timezone' is ignored (timezone is set automatically by reading the string; Region `calendar` and `locale` are used)
/// - returns: a new DateInRegion from given string
public init?(string: String, format: DateFormat, fromRegion region: Region? = nil) {
var srcRegion = region ?? Region.Local()
Expand All @@ -248,19 +248,11 @@ public class DateInRegion: CustomStringConvertible {
}
self.absoluteDate = date
case .iso8601(_), .iso8601Auto:
do {
let configuration = ISO8601Configuration(calendar: srcRegion.calendar)
guard let date = try ISO8601Parser(string, config: configuration).parsedDate else {
return nil
}
self.absoluteDate = date
if srcRegion != Region.GMT() { // region is ignored
print("Region is read from the string when ISO8601 parser is used")
}
srcRegion = Region.GMT()
} catch {
return nil
}
let configuration = ISO8601Configuration(calendar: srcRegion.calendar)
guard let parser = ISO8601Parser(string, config: configuration), let date = parser.parsedDate, let tz = parser.parsedTimeZone else { return nil }
//guard let date = parser.parsedDate, let tz = parser.parsedTimeZone else { return nil }
self.absoluteDate = date
srcRegion = Region(tz: tz, cal: srcRegion.calendar, loc: srcRegion.locale)
case .extended:
let format = "eee dd-MMM-yyyy GG HH:mm:ss.SSS zzz"
guard let date = self.formatters.dateFormatter(format: format).date(from: string) else {
Expand Down
Loading

0 comments on commit 0be0c82

Please sign in to comment.