Skip to content

Commit

Permalink
Merge pull request #415 from jaywardell/add_timezone_parameter_to_Site
Browse files Browse the repository at this point in the history
Add timezone parameter to Site protocol
  • Loading branch information
JPToroDev authored Jan 30, 2025
2 parents e1d71f4 + 73fc858 commit 1d2ffd1
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 15 deletions.
4 changes: 2 additions & 2 deletions Sources/Ignite/Extensions/Date-RFC822.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import Foundation

extension Date {
/// Converts `Date` objects to RFC-822 format, which is used by RSS.
public var asRFC822: String {
public func asRFC822(timeZone: TimeZone? = nil) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.timeZone = timeZone ?? TimeZone(secondsFromGMT: 0)
return formatter.string(from: self)
}
}
7 changes: 7 additions & 0 deletions Sources/Ignite/Framework/Site.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ public protocol Site: Sendable {
/// The base URL for your site, e.g. https://www.example.com
var url: URL { get }

/// the time zone used to do date output for your site. Defaults to GMT
/// (nil is equivalent to GMT)
var timeZone: TimeZone? { get }

/// Choose whether to use a local version of Bootstrap, a remote version,
/// or none at all
var useDefaultBootstrapURLs: BootstrapOptions { get }
Expand Down Expand Up @@ -149,6 +153,9 @@ public extension Site {
/// English as default language.
var language: Language { .english }

/// Uses gmt as the default
var timeZone: TimeZone? { .gmt }

/// Uses the default light theme based on Bootstrap.
var lightTheme: (any Theme)? { DefaultLightTheme() }

Expand Down
14 changes: 7 additions & 7 deletions Sources/Ignite/Publishing/FeedGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ struct FeedGenerator {
.prefix(site.feedConfiguration.contentCount)
.map { item in
var itemXML = """
<item>\
<guid isPermaLink="true">\(item.path(in: site))</guid>\
<title>\(item.title)</title>\
<link>\(item.path(in: site))</link>\
<description><![CDATA[\(item.description)]]></description>\
<pubDate>\(item.date.asRFC822)</pubDate>
"""
<item>\
<guid isPermaLink="true">\(item.path(in: site))</guid>\
<title>\(item.title)</title>\
<link>\(item.path(in: site))</link>\
<description><![CDATA[\(item.description)]]></description>\
<pubDate>\(item.date.asRFC822(timeZone: site.timeZone))</pubDate>
"""

let authorName = item.author ?? site.author

Expand Down
111 changes: 109 additions & 2 deletions Tests/IgniteTesting/Extensions/Date-RFC822.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import Testing
@testable import Ignite

/// Tests for the `Date-RFC822` extension.
///
/// NOTE: These tests only test against dates in the Unix Epoch
/// (since January 1, 1970, see https://en.wikipedia.org/wiki/Unix_time)
/// Testing time zone output against historical times (times before standardized time zones)
/// is tricky and beyond the scope of what this project needs.
@Suite("Date-RFC822 Tests")
@MainActor
struct DateRFC822Tests {
Expand All @@ -20,7 +25,7 @@ struct DateRFC822Tests {
let expected: String
}

@Test("Test Against Known Output", arguments: [
@Test("Test Against Known Output for Default Time Zone", arguments: [
Instance(input: Date(timeIntervalSince1970: 60228332501.13208), expected: "Wed, 24 Jul 3878 04:21:41 +0000"),
Instance(input: Date(timeIntervalSince1970: 27871740518.22975), expected: "Fri, 21 Mar 2853 14:08:38 +0000"),
Instance(input: Date(timeIntervalSince1970: -3284356034.069809), expected: "Sun, 03 Dec 1865 14:52:45 +0000"),
Expand All @@ -33,6 +38,108 @@ struct DateRFC822Tests {
Instance(input: Date(timeIntervalSince1970: -37373736994.632614), expected: "Tue, 30 Aug 0785 14:23:25 +0000")
])
func outputs_expected_result(instance: Instance) async throws {
#expect(instance.input.asRFC822 == instance.expected)
#expect(instance.input.asRFC822() == instance.expected)
}

@Test("Test Against Known Output for Greenwich Mean Time", arguments: [
Instance(input: Date(timeIntervalSince1970: 20012346618.957466), expected: "Fri, 02 Mar 2604 09:10:18 +0000"),
Instance(input: Date(timeIntervalSince1970: 56076958399.89086), expected: "Tue, 03 Jan 3747 20:53:19 +0000"),
Instance(input: Date(timeIntervalSince1970: 43889947931.30432), expected: "Sat, 25 Oct 3360 12:12:11 +0000"),
Instance(input: Date(timeIntervalSince1970: 60401587537.13003), expected: "Sat, 19 Jan 3884 10:45:37 +0000"),
Instance(input: Date(timeIntervalSince1970: 2887257381.52073), expected: "Wed, 29 Jun 2061 07:56:21 +0000"),
Instance(input: Date(timeIntervalSince1970: 15764928045.389473), expected: "Sat, 27 Jul 2469 10:40:45 +0000"),
Instance(input: Date(timeIntervalSince1970: 30573435574.337566), expected: "Sat, 01 Nov 2938 04:59:34 +0000"),
Instance(input: Date(timeIntervalSince1970: 2818825684.6154914), expected: "Tue, 29 Apr 2059 07:08:04 +0000"),
Instance(input: Date(timeIntervalSince1970: 9199677333.36627), expected: "Thu, 11 Jul 2261 17:55:33 +0000"),
Instance(input: Date(timeIntervalSince1970: 53706378711.11124), expected: "Fri, 20 Nov 3671 14:31:51 +0000")
])
func outputs_expected_result_for_greenwich_mean_time(instance: Instance) async throws {
// GMT
let timezone = TimeZone(abbreviation: "GMT")
#expect(instance.input.asRFC822(timeZone: timezone) == instance.expected)
}

@Test("Test Against Known Output for New York Time", arguments: [
Instance(input: Date(timeIntervalSince1970: 20012346618.957466), expected: "Fri, 02 Mar 2604 04:10:18 -0500"),
Instance(input: Date(timeIntervalSince1970: 56076958399.89086), expected: "Tue, 03 Jan 3747 15:53:19 -0500"),
Instance(input: Date(timeIntervalSince1970: 43889947931.30432), expected: "Sat, 25 Oct 3360 08:12:11 -0400"),
Instance(input: Date(timeIntervalSince1970: 60401587537.13003), expected: "Sat, 19 Jan 3884 05:45:37 -0500"),
Instance(input: Date(timeIntervalSince1970: 2887257381.52073), expected: "Wed, 29 Jun 2061 03:56:21 -0400"),
Instance(input: Date(timeIntervalSince1970: 15764928045.389473), expected: "Sat, 27 Jul 2469 06:40:45 -0400"),
Instance(input: Date(timeIntervalSince1970: 30573435574.337566), expected: "Sat, 01 Nov 2938 00:59:34 -0400"),
Instance(input: Date(timeIntervalSince1970: 2818825684.6154914), expected: "Tue, 29 Apr 2059 03:08:04 -0400"),
Instance(input: Date(timeIntervalSince1970: 9199677333.36627), expected: "Thu, 11 Jul 2261 13:55:33 -0400"),
Instance(input: Date(timeIntervalSince1970: 53706378711.11124), expected: "Fri, 20 Nov 3671 09:31:51 -0500")
])
func outputs_expected_result_for_new_york_time(instance: Instance) async throws {
// EDT - America/New_York
// western hemisphere
// 5 hours behind GMT
let timezone = TimeZone(abbreviation: "EDT")
#expect(instance.input.asRFC822(timeZone: timezone) == instance.expected)
}

@Test("Test Against Known Output for America/St Johns Time", arguments: [
Instance(input: Date(timeIntervalSince1970: 20012346618.957466), expected: "Fri, 02 Mar 2604 05:40:18 -0330"),
Instance(input: Date(timeIntervalSince1970: 56076958399.89086), expected: "Tue, 03 Jan 3747 17:23:19 -0330"),
Instance(input: Date(timeIntervalSince1970: 43889947931.30432), expected: "Sat, 25 Oct 3360 09:42:11 -0230"),
Instance(input: Date(timeIntervalSince1970: 60401587537.13003), expected: "Sat, 19 Jan 3884 07:15:37 -0330"),
Instance(input: Date(timeIntervalSince1970: 2887257381.52073), expected: "Wed, 29 Jun 2061 05:26:21 -0230"),
Instance(input: Date(timeIntervalSince1970: 15764928045.389473), expected: "Sat, 27 Jul 2469 08:10:45 -0230"),
Instance(input: Date(timeIntervalSince1970: 30573435574.337566), expected: "Sat, 01 Nov 2938 02:29:34 -0230"),
Instance(input: Date(timeIntervalSince1970: 2818825684.6154914), expected: "Tue, 29 Apr 2059 04:38:04 -0230"),
Instance(input: Date(timeIntervalSince1970: 9199677333.36627), expected: "Thu, 11 Jul 2261 15:25:33 -0230"),
Instance(input: Date(timeIntervalSince1970: 53706378711.11124), expected: "Fri, 20 Nov 3671 11:01:51 -0330")
])
func outputs_expected_result_for_st_johns_time(instance: Instance) async throws {

// NDT - America/St_Johns
// western hemisphere
// 3.5 hours behind GMT
let timezone = TimeZone(abbreviation: "NDT")
#expect(instance.input.asRFC822(timeZone: timezone) == instance.expected)
}

@Test("Test Against Known Output for Asia/Jakarta Time", arguments: [
Instance(input: Date(timeIntervalSince1970: 20012346618.957466), expected: "Fri, 02 Mar 2604 16:10:18 +0700"),
Instance(input: Date(timeIntervalSince1970: 56076958399.89086), expected: "Wed, 04 Jan 3747 03:53:19 +0700"),
Instance(input: Date(timeIntervalSince1970: 43889947931.30432), expected: "Sat, 25 Oct 3360 19:12:11 +0700"),
Instance(input: Date(timeIntervalSince1970: 60401587537.13003), expected: "Sat, 19 Jan 3884 17:45:37 +0700"),
Instance(input: Date(timeIntervalSince1970: 2887257381.52073), expected: "Wed, 29 Jun 2061 14:56:21 +0700"),
Instance(input: Date(timeIntervalSince1970: 15764928045.389473), expected: "Sat, 27 Jul 2469 17:40:45 +0700"),
Instance(input: Date(timeIntervalSince1970: 30573435574.337566), expected: "Sat, 01 Nov 2938 11:59:34 +0700"),
Instance(input: Date(timeIntervalSince1970: 2818825684.6154914), expected: "Tue, 29 Apr 2059 14:08:04 +0700"),
Instance(input: Date(timeIntervalSince1970: 9199677333.36627), expected: "Fri, 12 Jul 2261 00:55:33 +0700"),
Instance(input: Date(timeIntervalSince1970: 53706378711.11124), expected: "Fri, 20 Nov 3671 21:31:51 +0700")
])
func outputs_expected_result_for_jakarta_time(instance: Instance) async throws {

// WIT - Asia/Jakarta
// eastern hemisphere
// 7 hours before GMT
let timezone = TimeZone(abbreviation: "WIT")
#expect(instance.input.asRFC822(timeZone: timezone) == instance.expected)
}

@Test("Test Against Known Output for Asia/Kolkata Time", arguments: [
Instance(input: Date(timeIntervalSince1970: 20012346618.957466), expected: "Fri, 02 Mar 2604 14:40:18 +0530"),
Instance(input: Date(timeIntervalSince1970: 56076958399.89086), expected: "Wed, 04 Jan 3747 02:23:19 +0530"),
Instance(input: Date(timeIntervalSince1970: 43889947931.30432), expected: "Sat, 25 Oct 3360 17:42:11 +0530"),
Instance(input: Date(timeIntervalSince1970: 60401587537.13003), expected: "Sat, 19 Jan 3884 16:15:37 +0530"),
Instance(input: Date(timeIntervalSince1970: 2887257381.52073), expected: "Wed, 29 Jun 2061 13:26:21 +0530"),
Instance(input: Date(timeIntervalSince1970: 15764928045.389473), expected: "Sat, 27 Jul 2469 16:10:45 +0530"),
Instance(input: Date(timeIntervalSince1970: 30573435574.337566), expected: "Sat, 01 Nov 2938 10:29:34 +0530"),
Instance(input: Date(timeIntervalSince1970: 2818825684.6154914), expected: "Tue, 29 Apr 2059 12:38:04 +0530"),
Instance(input: Date(timeIntervalSince1970: 9199677333.36627), expected: "Thu, 11 Jul 2261 23:25:33 +0530"),
Instance(input: Date(timeIntervalSince1970: 53706378711.11124), expected: "Fri, 20 Nov 3671 20:01:51 +0530")
])
func outputs_expected_result_for_kolkata_time(instance: Instance) async throws {

// IST - Asia/Kolkata
// eastern hemisphere
// 5.5 hours before GMT
let timezone = TimeZone(abbreviation: "IST")
#expect(instance.input.asRFC822(timeZone: timezone) == instance.expected)
}

}
14 changes: 10 additions & 4 deletions Tests/IgniteTesting/Publishing/FeedGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@ import Testing
@Suite("FeedGenerator Tests")
@MainActor
struct FeedGeneratorTests {
@Test("Test generateFeed")
func generateFeed() async throws {
let site = TestSite()

static let sites: [any Site] = [
TestSite(),
TestSite().withTimeZone(.init(abbreviation: "GMT")!),
TestSite().withTimeZone(.init(abbreviation: "EST")!),
]

@Test("Test generateFeed", arguments: await sites)
func generateFeed(for site: any Site) async throws {
let feedHref = site.url.appending(path: site.feedConfiguration.path).absoluteString
var exampleContent = Content()
exampleContent.title = "Example Title"
Expand Down Expand Up @@ -51,7 +57,7 @@ struct FeedGeneratorTests {
<title>\(exampleContent.title)</title>\
<link>\(exampleContent.path(in: site))</link>\
<description><![CDATA[\(exampleContent.description)]]></description>\
<pubDate>\(exampleContent.date.asRFC822)</pubDate>\
<pubDate>\(exampleContent.date.asRFC822(timeZone: site.timeZone))</pubDate>\
</item>\
</channel>\
</rss>
Expand Down
9 changes: 9 additions & 0 deletions Tests/IgniteTesting/TestSite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ struct TestSite: Site {

var builtInIconsEnabled: BootstrapOptions = .localBootstrap

var timeZone: TimeZone? = nil

var homePage = TestLayout()
var layout = EmptyLayout()
var feedConfiguration = FeedConfiguration(
mode: .descriptionOnly,
contentCount: 20,
image: .init(url: "path/to/image.png", width: 100, height: 100)
)

func withTimeZone(_ timeZone: TimeZone) -> TestSite {
var site = self
site.timeZone = timeZone

return site
}
}

/// An example page used in tests.
Expand Down

0 comments on commit 1d2ffd1

Please sign in to comment.