There is a useful Google API available to developers who register with the Google Console and obtain an API key. For a chosen country, this API returns a list of dates which are public holidays.
However, there are problems with the default output, e.g
- a single public holiday may in some cases have an original date and an observed (if, for example, the original date is not possible e.g. is on a weekend). The API returns both dates as separate items.
- multiple holidays can are given with different dates but identical names
- a single public holiday date can be duplicated, with some instances including an extra "holiday" suffix in their name
- holidays with identical names but from multiple years (both previous, current and future years) are given
The method for extracting a list of OBSERVED public holidays in Japan, in Japanese language is presented here for iOS/Swift
URL
https://www.googleapis.com/calendar/v3/calendars/{LANGUAGE}.{COUNTRY}%23holiday%40group.v.calendar.google.com/events?key={API_KEY}
where
- {LANGUAGE} is the requested language, e.g: ja, en
- {COUNTRY} is the requested country, e.g: japanese, uk, french
- {API_KEY} is the API key from your developer account
Define a custom date handler object
This object helps to compare dates extracted from the API
struct PublicHoliday{
// some holiday names are provided with an "observed" suffix - so we define a base name (without this suffix) to more easily compare dates
let baseName: String
let name: String
let start: String
let end: String
let observedKeyword: String = "observed"
// let dates with the "observed" keyword take priority (override) for a specific holiday date
var isOverride: Bool {
get {
return self.name.contains(observedKeyword)
}
}
init(name: String, start: String, end: String) {
self.name = name
self.start = start
self.end = end
let components = name.split(separator: " ")
if let last = components.last, last == observedKeyword {
self.baseName = components.dropLast().joined(separator: " ")
}
else {
self.baseName = name
}
}
init(date: String) {
self.init(name: "", start: date, end: date)
}
}
Parse Response in Swift
The following parse function extracts only observed public holidays for the current as an array of Public Holiday objects
class GoogleCalendarAPIParser {
static func process(json: [String: Any]) -> [PublicHoliday] {
guard let items = json["items"] as? JSONArray,
0 < items.count else {
return []
}
// extract all dates
var dates: [PublicHoliday] = items.compactMap { item in
guard
let name= item["summary"] as? String,
let start = item["start"] as? [String: Any],
let end = item["start"] as? [String: Any],
let startDate = start["date"] as? String,
let endDate = end["date"] as? String
else {
Swift.print("unable to parse dates", item)
return nil
}
return PublicHoliday(
name: name,
start: startDate,
end: endDate
)
}
// keep only dates from the current year
let currentYear = String(Calendar.current.component(.year, from: Date()))
dates = dates.filter { $0.startDate.split("-")[0] == currentYear }
// keep dates that are overrides (the "observed" date) and dates which are not overriden by other dates
dates = dates.filter { date in
let isOverride = date.isOverride
let hasNoOverride = dates.filter {
$0.baseName == date.baseName && $0.isOverride
}.isEmpty
return isOverride || hasNoOverride
}
return dates
}
}