<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jason R Tibbetts</title>
    <description>The latest articles on DEV Community by Jason R Tibbetts (@jrtibbetts).</description>
    <link>https://dev.to/jrtibbetts</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F20977%2F21f85a4c-bc2c-4b03-b185-413688fa9858.jpeg</url>
      <title>DEV Community: Jason R Tibbetts</title>
      <link>https://dev.to/jrtibbetts</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jrtibbetts"/>
    <language>en</language>
    <item>
      <title>How Not to Attempt an April Fool’s Day Prank</title>
      <dc:creator>Jason R Tibbetts</dc:creator>
      <pubDate>Mon, 01 Apr 2019 19:25:48 +0000</pubDate>
      <link>https://dev.to/jrtibbetts/how-not-to-attempt-an-april-fool-s-day-prank-3d5f</link>
      <guid>https://dev.to/jrtibbetts/how-not-to-attempt-an-april-fool-s-day-prank-3d5f</guid>
      <description>

&lt;p&gt;In light of &lt;a href="https://dev.to/devteam/comic-sans-is-now-the-default-reading-font-2n6g"&gt;Ben’s post&lt;/a&gt; about an April Fool’s Day prank gone wrong, it reminded me of the biggest failed developer prank I’ve ever experienced. I once worked on a mission-critical application for a government agency. It was classified at a very high level. I was one of the developers who was cleared to view classified data, but others on the team weren’t, so they worked with dummy data.&lt;/p&gt;

&lt;p&gt;One morning, I got a frantic call from the project manager begging me to get to the office as soon as possible because some time overnight, the application looked broken. Not only was the &lt;em&gt;data&lt;/em&gt; corrupted, but so were &lt;em&gt;all&lt;/em&gt; of the UI elements. It was completely unusable. Even worse, some of the data &lt;em&gt;in the database&lt;/em&gt; (several gigabytes’ worth) was also corrupted.&lt;/p&gt;

&lt;p&gt;I got to the office and took a look. My project lead was shaking with anxiety. There was a &lt;em&gt;pattern&lt;/em&gt; to the corrupted data, and I it took me about 10 seconds to figure out what was wrong. “What’s today’s date?” I asked the lead.&lt;/p&gt;

&lt;p&gt;“April 1st. Why?”&lt;/p&gt;

&lt;p&gt;I told him to take another look.&lt;/p&gt;

&lt;p&gt;“What? I just see gibberish.”&lt;/p&gt;

&lt;p&gt;“It’s all &lt;em&gt;backwards&lt;/em&gt;,” I said. Sure enough, &lt;em&gt;every&lt;/em&gt; string was reversed. The leads anguish turned to anger.&lt;/p&gt;

&lt;p&gt;The next step was to figure out &lt;em&gt;how&lt;/em&gt;, and then, &lt;em&gt;who&lt;/em&gt; did it. I figured out the latter pretty quickly, and it was the last person I’d expected to do anything remotely humorous--a 25-year veteran of the group. He didn't have a clearance, so he never saw the corrupted data.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;how&lt;/em&gt; was the most interesting part. The developer had somehow hooked into the Java Swing UI to reverse the strings, but then those changes to the editable fields were auto-saved. He had to issue a hotfix for both the app and the database, without being able to access them to apply the changes himself, and I’m sure that he was more pissed about having to walk me through the fixes than he was contrite about having messed things up so badly. In the end, all the data was restored, and the episode lasted, at most, a couple of hours.&lt;/p&gt;

&lt;p&gt;So, Dear Reader, please think twice, or maybe three times, before pulling off a stunt like this at work, especially on production, mission-critical apps!&lt;/p&gt;


</description>
      <category>aprilfools</category>
      <category>prank</category>
      <category>humor</category>
      <category>disaster</category>
    </item>
    <item>
      <title>Decoding Formatted JSON Dates in Swift</title>
      <dc:creator>Jason R Tibbetts</dc:creator>
      <pubDate>Fri, 08 Feb 2019 15:34:34 +0000</pubDate>
      <link>https://dev.to/jrtibbetts/decoding-formatted-json-dates-in-swift-2odb</link>
      <guid>https://dev.to/jrtibbetts/decoding-formatted-json-dates-in-swift-2odb</guid>
      <description>&lt;p&gt;If you've done any Web, mobile, or desktop programming in the past decade (and would you even be here if you hadn't?), you've used JSON--the &lt;em&gt;lingua franca&lt;/em&gt; for serializing data across devices and platforms. JSON's popularity lies in its simplicity: it supports only a handful of data types, like strings, numerics, Booleans, arrays, and dictionaries, and is generally easy for humans to read. But for strongly-typed languages like Swift, this simplicity could lead to suboptimal parsing strategies. Xcode 9's Swift SDK introduced an elegant way to handle parsing, and that was with the &lt;code&gt;Codable&lt;/code&gt; protocol. When you declare a &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;struct&lt;/code&gt;, or &lt;code&gt;enum&lt;/code&gt; as &lt;code&gt;Codable&lt;/code&gt;, the Swift compiler implicitly generates three things: &lt;code&gt;init(from: Decoder)&lt;/code&gt;, &lt;code&gt;encode(to: Encoder)&lt;/code&gt;, and a &lt;code&gt;CodingKey&lt;/code&gt; enumeration, which maps JSON key values to the names of your &lt;code&gt;Codable&lt;/code&gt;'s properties. To make your data type &lt;code&gt;Codable&lt;/code&gt;, you simply declare that it implements &lt;code&gt;Codable&lt;/code&gt;, and ensure that all of its properties are themselves &lt;code&gt;Codable&lt;/code&gt; or are one of the supported types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;String&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Float&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Date&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;arrays of &lt;code&gt;Codable&lt;/code&gt; types&lt;/li&gt;
&lt;li&gt;dictionaries of &lt;code&gt;Codable&lt;/code&gt; types&lt;/li&gt;
&lt;li&gt;optionals of all of the above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, the &lt;code&gt;JSONDecoder&lt;/code&gt; can parse &lt;code&gt;Date&lt;/code&gt;s only in their raw form--a &lt;code&gt;TimeInterval&lt;/code&gt; (=&lt;code&gt;Double&lt;/code&gt;) of their milliseconds since the &lt;em&gt;reference date&lt;/em&gt;: 12:00 a.m. UTC&lt;sup&gt;&lt;small&gt;2&lt;/small&gt;&lt;/sup&gt; on January 1st, 2000. This article's focus is on supporting custom &lt;code&gt;Date&lt;/code&gt; formats, and the journey I took from naïve implementation to full understanding.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sunrise, Sunset
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz4ddh3kyysb3x42gfh7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmz4ddh3kyysb3x42gfh7.jpg" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://youtu.be/03rzUoyq9K0?t=124" rel="noopener noreferrer"&gt;"Sunrise, Sunset" from &lt;em&gt;The Fiddler on the Roof&lt;/em&gt; (1971)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've recently been working with some REST APIs for looking up sunrise and sunset times for a given latitude and longitude. One of them, &lt;a href="https://api.sunrise-sunset.org" rel="noopener noreferrer"&gt;Sunrise-Sunset.org&lt;/a&gt;, returns the following JSON when called with &lt;code&gt;https://api.sunrise-sunset.org/json?lat=41.948437&amp;amp;lng=-87.655334&amp;amp;date=2019-02-14&lt;/code&gt;&lt;sup&gt;&lt;small&gt;3&lt;/small&gt;&lt;/sup&gt; (omitting some lines for clarity):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "results":
    {
        "sunrise":"12:59:22 PM",
        "sunset":"11:09:45 PM"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I created the following &lt;code&gt;Codable&lt;/code&gt; structures, which I'll call&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #1: The Naïve Approach, aka How It &lt;em&gt;Should&lt;/em&gt; Work
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public struct SunriseSunsetResponse: Codable {
    public var results: SunriseSunset
}

public struct SunriseSunset: Codable {
    public var sunrise: Date
    public var sunset: Date
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;SunriseSunset&lt;/code&gt; is a nested element that represents the value of the &lt;code&gt;"results"&lt;/code&gt; element, and both it and the &lt;code&gt;SunriseSunsetResponse&lt;/code&gt; have to implement &lt;code&gt;Codable&lt;/code&gt;. Now I'll create an instance of &lt;code&gt;SunriseSunsetResponse&lt;/code&gt; by calling the API and parsing it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if let url = URL(string: "https://api.sunrise-sunset.org/json?lat=41.948437&amp;amp;lng=-87.655334&amp;amp;date=2019-02-04") {
    do {
        let responseData = try Data(contentsOf: url)
        let response = try JSONDecoder().decode(SunriseSunsetResponse.self, from: responseData)
        let sunriseSunset = response.results
        print("Daylight duration (in seconds): \(sunriseSunset.sunset.timeIntervalSince(sunriseSunset.sunrise))")
    } catch {
        print(error)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And...it fails. The error tells says&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typeMismatch(Swift.Double, Swift.DecodingError.Context(
    codingPath: [CodingKeys(stringValue: "results", intValue: nil),
                 CodingKeys(stringValue: "sunrise", intValue: nil)],
    debugDescription: "Expected to decode Double but found a string/data instead.",
    underlyingError: nil))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Did you spot the problem? &lt;code&gt;sunrise&lt;/code&gt; and &lt;code&gt;sunset&lt;/code&gt; properties are &lt;code&gt;Date&lt;/code&gt;s, but the parser found the strings &lt;code&gt;"12:59:22 PM"&lt;/code&gt; and &lt;code&gt;"11:09:45 PM"&lt;/code&gt; instead. How can I fix this, without manipulating the JSON data in some way? Here's what I did:&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #2: Storing the dates as &lt;code&gt;String&lt;/code&gt;s
&lt;/h2&gt;

&lt;p&gt;Let's refactor &lt;code&gt;SunriseSunset&lt;/code&gt; to expect date &lt;em&gt;strings&lt;/em&gt;, like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public struct SunriseSunset: Codable {
    public var sunrise: String
    public var sunset: String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This shifts the responsibility for parsing the strings into &lt;code&gt;Date&lt;/code&gt;s to the caller. The downside is that if the &lt;code&gt;SunriseSunset&lt;/code&gt; object is used in multiple places, you may wind up with many identical parsing calls. Even if you create a single &lt;code&gt;DateFormatter&lt;/code&gt; instance and used it in multiple places, you'd still wind up violating the dreaded DRY (&lt;strong&gt;D&lt;/strong&gt;on't &lt;strong&gt;R&lt;/strong&gt;epeat &lt;strong&gt;Y&lt;/strong&gt;ourself) principle. There must be a better way. Let's try&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #3: Keep the &lt;code&gt;String&lt;/code&gt; properties, and add corresponding computed &lt;code&gt;Date&lt;/code&gt; properties
&lt;/h2&gt;

&lt;p&gt;I want to simplify how my &lt;code&gt;SunriseSunset&lt;/code&gt; gets used, so why not make &lt;em&gt;it&lt;/em&gt; responsible for parsing the dates itself? I'll add a &lt;code&gt;DateFormatter&lt;/code&gt; property, plus &lt;em&gt;computed&lt;/em&gt; &lt;code&gt;Date&lt;/code&gt; properties. This sounds better than the naïve, &lt;code&gt;String&lt;/code&gt;-based approach, even though the &lt;code&gt;Date&lt;/code&gt; versions should be &lt;code&gt;Optional&lt;/code&gt;, because that's what &lt;code&gt;DateFormatter.date(from:)&lt;/code&gt; returns:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public struct SunriseSunset: Codable {
    private var dateFormatter: DateFormatter

    public var sunrise: String

    public var sunriseDate: Date? {
        return dateFormatter.date(from: sunrise)
    }

    public var sunset: String

    public var sunsetDate: Date? {
        return dateFormatter.date(from: sunset)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is starting to look kind of ugly, but it should suit my purposes. But now it won't compile! Remember that &lt;em&gt;every&lt;/em&gt; property of a &lt;code&gt;Codable&lt;/code&gt; type that you want to convert to &amp;amp; from JSON must be a type that itself implements &lt;code&gt;Codable&lt;/code&gt;, and &lt;code&gt;DateFormatter&lt;/code&gt; does not. There &lt;em&gt;is&lt;/em&gt; a workaround for this, which we'll call&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #4: Using custom &lt;code&gt;CodingKeys&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This way is to define a enumeration of &lt;code&gt;CodingKeys&lt;/code&gt; for your &lt;code&gt;Codable&lt;/code&gt; type. The &lt;code&gt;CodingKeys&lt;/code&gt; enumeration &lt;em&gt;must&lt;/em&gt; having a raw type of &lt;code&gt;String&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;CodingKey&lt;/code&gt; (note the singular), in that order. The Swift compiler generates this for you from your properties' names &lt;em&gt;for free&lt;/em&gt;, but if your property names don't &lt;em&gt;exactly&lt;/em&gt; match the JSON data, or, as in this case, you don't want &lt;em&gt;all&lt;/em&gt; of your properties to be parsed from JSON data, you must add your own. So now we'll try:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public struct SunriseSunset: Codable {
    private var dateFormatter: DateFormatter

    public var sunrise: String

    public var sunriseDate: Date? {
        return dateFormatter.date(from: sunrise)
    }

    public var sunset: String

    public var sunsetDate: Date? {
        return dateFormatter.date(from: sunset)
    }

    private enum CodingKeys: String, CodingKey {
        case sunrise
        case sunriseDate
        case sunset
        case sunsetDate
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that I've omitted &lt;code&gt;dateFormatter&lt;/code&gt; from the custom keys. But again, this won't compile. The compiler barfs numerous errors, the most important of which are:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: type 'SunriseSunset4' does not conform to protocol 'Decodable'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;public struct SunriseSunset4: Codable {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;note: protocol requires initializer 'init(from:)' with type 'Decodable'
public init(from decoder: Decoder) throws

error: type 'SunriseSunset4' does not conform to protocol 'Encodable'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;public struct SunriseSunset4: Codable {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;note: protocol requires function 'encode(to:)' with type 'Encodable'
public func encode(to encoder: Encoder) throws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What these are telling you (not very clearly, IMHO) is that if you have properties that should &lt;em&gt;not&lt;/em&gt; be handled by the &lt;code&gt;JSONDecoder&lt;/code&gt;/&lt;code&gt;JSONEncoder&lt;/code&gt;, then &lt;em&gt;you&lt;/em&gt; have to supply a custom initializer and encoding function. &lt;a href="https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types" rel="noopener noreferrer"&gt;Apple's documentation&lt;/a&gt; &lt;em&gt;really&lt;/em&gt; doesn't help much. It says,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Omit properties from the `CodingKeys` enumeration if they won't be present
&amp;gt; when decoding instances, or if certain properties shouldn't be included in an
&amp;gt; encoded representation. A property omitted from `CodingKeys` needs a default
&amp;gt; value in order for its containing type to receive automatic conformance to
&amp;gt; `Decodable` or `Codable`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This &lt;em&gt;sounds&lt;/em&gt; like you should be able to assign &lt;code&gt;dateFormatter&lt;/code&gt; a &lt;code&gt;DateFormatter&lt;/code&gt; instance when it's declared, like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; let dateFormatter = DateFormatter()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;but you can't. The only way is to implement the initializer and encoding function. If you're thinking to yourself that this really defeats the purpose of using &lt;code&gt;Codable&lt;/code&gt; in the first place, which is to let the Swift compiler generate the &lt;code&gt;CodingKeys&lt;/code&gt;, initializer, and encoding functions, then you're completely correct. "There &lt;em&gt;must&lt;/em&gt; be a better way!" I said. And there is! It's &lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #5: &lt;code&gt;dateDecodingStrategy&lt;/code&gt; with a custom &lt;code&gt;DateFormatter&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;JSONDecoder&lt;/code&gt; has a property called &lt;code&gt;dateDecodingStrategy&lt;/code&gt;, of type &lt;code&gt;JSONDecoder.DateDecodingStrategy&lt;/code&gt;, which allows you to change how dates are parsed. This an &lt;code&gt;enum&lt;/code&gt; with 6 cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deferredToDate&lt;/code&gt; (&lt;em&gt;default&lt;/em&gt;): This treats &lt;code&gt;Date&lt;/code&gt;s as &lt;code&gt;Double&lt;/code&gt; values that indicate the date's number of milliseconds since the reference date (see above)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iso8601&lt;/code&gt;: The &lt;a href="https://xkcd.com/1179/" rel="noopener noreferrer"&gt;best way to format dates&lt;/a&gt;, e.g. &lt;code&gt;"2019-02-04T12:59:22+00:00"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;formatted(DateFormatter)&lt;/code&gt;: Allows you to use custom &lt;code&gt;DateFormatter&lt;/code&gt; instance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;custom(@escaping (Decoder) -&amp;gt; Date)&lt;/code&gt;: Allows you to specify a custom block for parsing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;millisecondsSince1970&lt;/code&gt;: Like the default &lt;code&gt;deferredToDate&lt;/code&gt; option, but calculates dates from the beginning of the &lt;a href="https://en.wikipedia.org/wiki/Unix_time" rel="noopener noreferrer"&gt;Unix epoch&lt;/a&gt; (i.e. January 1st, 1970)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secondsSince1970&lt;/code&gt;: Like &lt;code&gt;millisecondsSince1970&lt;/code&gt;, but in seconds, not milliseconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thankfully, &lt;code&gt;"12:59:22 PM"&lt;/code&gt; happens to be an exact match for &lt;code&gt;DateFormatter.TimeStyle.medium&lt;/code&gt;, so I'll configure my decoder accordingly:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .medium
decoder.dateDecodingStrategy = .formatted(dateFormatter)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now it prints the answer I expected:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Daylight duration (in seconds): 36623.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I'm done, right? Not quite. Examining the returned date further, I find that although the &lt;em&gt;time&lt;/em&gt; is what I expected, the &lt;em&gt;date&lt;/em&gt; is January 1st, 2000! That doesn't do me much good if I want to calculate how many seconds have elapsed since sunrise &lt;em&gt;today&lt;/em&gt;! Now I have to normalize the returned times to today's date, and that's a little trickier. One way is to get the interval from the reference date to midnight today, and add that to the parsed time.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let midnightThen = Calendar.current.startOfDay(for: sunriseSunset.sunrise)
let millisecondsToSunrise = sunriseSunset.sunrise.timeIntervalSince(midnightThen)

let midnightToday = Calendar.current.startOfDay(for: Date())
let normalizedSunrise = midnightToday.addingTimeInterval(millisecondsToSunrise)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, there's no way to do this kind of transformation simply by using a custom &lt;code&gt;DateFormatter&lt;/code&gt; instance, so we're back to the original problem of duplicating time-normalization calls throughout my app. Well, it turns out that there &lt;em&gt;is&lt;/em&gt; a &lt;code&gt;dateDecodingStrategy&lt;/code&gt; that can do this, and that's&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach #6: Using a custom &lt;code&gt;dateDecodingStrategy&lt;/code&gt; block
&lt;/h2&gt;

&lt;p&gt;One of the &lt;code&gt;JSONDecoder.DateDecodingStrategy&lt;/code&gt; enum cases is &lt;code&gt;custom&lt;/code&gt;, which takes an associated block that gets a &lt;code&gt;JSONDecoder&lt;/code&gt; instance and returns a &lt;code&gt;Date&lt;/code&gt;. So let's put the previous date-manipulation code into that block, like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dateDecodingStrategy = .custom({ (decoder) -&amp;gt; Date in
    // Parse the date using a custom `DateFormatter`
    let container = try decoder.singleValueContainer()
    let dateString = try container.decode(String.self)
    let date = self.dateFormatter.date(from: dateString)

    let midnightThen = Calendar.current.startOfDay(for: date)
    let millisecondsFromMidnight = date.timeIntervalSince(midnightThen)

    let midnightToday = Calendar.current.startOfDay(for: Date())
    let normalizedDate = midnightToday.addingTimeInterval(millisecondsFromMidnight)

    return normalizedDate
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note the first three statements in the block. The first two show how to get a single &lt;code&gt;String&lt;/code&gt; value from &lt;code&gt;decoder&lt;/code&gt;. But what is this &lt;code&gt;decoder&lt;/code&gt; instance? It's of type &lt;code&gt;Decoder&lt;/code&gt; (&lt;em&gt;not&lt;/em&gt; &lt;code&gt;JSONDecoder&lt;/code&gt;!), and it holds a single element--in this case, a &lt;code&gt;JSON&lt;/code&gt; value string. (If your JSON contains an array or dictionary of &lt;code&gt;Date&lt;/code&gt;s that need to be manipulated, you would change the container and decoded types accordingly.)&lt;/p&gt;

&lt;p&gt;Is that it? Are we done? Not &lt;em&gt;quite&lt;/em&gt;. Note that this custom decoding strategy still needs a &lt;code&gt;DateFormatter&lt;/code&gt; instance. &lt;code&gt;DateFormatter&lt;/code&gt; instances are expensive to create, so I'll create one and assign it to a property of the class that sets up this &lt;code&gt;dateDecodingStrategy&lt;/code&gt;. To keep things relatively self-contained, I subclassed &lt;code&gt;JSONDecoder&lt;/code&gt;, like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class NormalizingDecoder: JSONDecoder {

    /// The formatter for date strings returned by `sunrise-sunset.org`.
    /// These are in the `.medium` time style, like `"7:27:02 AM"` and
    /// `"12:16:28 PM"`.
    let dateFormatter: DateFormatter
    let calendar = Calendar.current

    override init() {
        super.init()
        dateFormatter = DateFormatter()
        dateFormatter.timeStyle = .medium
        keyDecodingStrategy = .convertFromSnakeCase
        dateDecodingStrategy = .custom { (decoder) -&amp;gt; Date in
            let container = try decoder.singleValueContainer()
            let dateString = try container.decode(String.self)
            let date = self.dateFormatter.date(from: dateString)

            if let date = date {
                let midnightThen = calendar.startOfDay(for: date)
                let millisecondsFromMidnight = date.timeIntervalSince(midnightThen)

                let today = Date()
                let midnightToday = calendar.startOfDay(for: today)
                let normalizedDate = midnightToday.addingTimeInterval(millisecondsFromMidnight)

                return normalizedDate
            } else {
                throw DecodingError.dataCorruptedError(in: container,
                                                       debugDescription:
                    "Date values must be formatted like \"7:27:02 AM\" " +
                    "or \"12:16:28 PM\".")
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Using this custom &lt;code&gt;JSONDecoder&lt;/code&gt;, our &lt;code&gt;Codable&lt;/code&gt; can once again look like what we wanted in Approach #1, namely&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public struct SunriseSunsetResponse: Codable {
    public var results: SunriseSunset
}

public struct SunriseSunset: Codable {
    public var sunrise: Date
    public var sunset: Date
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.amuniversal.com%2Fa200ff809c43012f2fe400163e41dd5b" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.amuniversal.com%2Fa200ff809c43012f2fe400163e41dd5b" alt="Nerdvana, according to Dilbert"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dilbert.com/strip/1991-09-28" rel="noopener noreferrer"&gt;Dilbert achieves Nerdvana&lt;/a&gt;&lt;br&gt;
With this approach, you can do even more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adjust times for time zone offsets&lt;/li&gt;
&lt;li&gt;Handle dates that may be in one of several acceptable formats&lt;/li&gt;
&lt;li&gt;Handle arrays and dictionaries of formatted &lt;code&gt;Date&lt;/code&gt;s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've made it this far, thank you for reading! This is my first public technical writeup, despite being a professional developer since 1996.&lt;/p&gt;

&lt;h6&gt;
  
  
  Footnotes
&lt;/h6&gt;

&lt;p&gt;&lt;sup&gt;&lt;small&gt;1&lt;/small&gt;&lt;/sup&gt; Technically speaking, &lt;code&gt;Codable&lt;/code&gt; is a &lt;code&gt;typealias&lt;/code&gt; of &lt;code&gt;Encodable&lt;/code&gt; and &lt;code&gt;Decodable&lt;/code&gt;.&lt;br&gt;
&lt;sup&gt;&lt;small&gt;2&lt;/small&gt;&lt;/sup&gt; &lt;strong&gt;C&lt;/strong&gt;oordinated &lt;strong&gt;U&lt;/strong&gt;niversal &lt;strong&gt;T&lt;/strong&gt;ime, better known as Greenwich Mean Time (GMT).&lt;br&gt;
&lt;sup&gt;&lt;small&gt;3&lt;/small&gt;&lt;/sup&gt; If you're a &lt;em&gt;Blues Brothers&lt;/em&gt; fan, you may recognize these as the coordinates for Elwood Blues's address.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fla58cniru0zs33179ybz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fla58cniru0zs33179ybz.jpg" alt="That's Wrigley Field!"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>codable</category>
      <category>json</category>
      <category>dates</category>
    </item>
  </channel>
</rss>
