parseapi 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ff073cfaba8eb3f7afa9550ddc3e69aa9ac77c48ebc082393489eedc465139a
4
- data.tar.gz: e09d1d59dde43a30422d4e2b5f1ce011fa30952bddbe570c95acc07b26ce366e
3
+ metadata.gz: 73f1b424549c76d9c2b85d8571a47531254b144d53fc62e58e0637c2992e53e6
4
+ data.tar.gz: e421ec5da88ddec8b7dbbce5d1f43ecee0ba68a5fa6c8e6721fbc2b7678aa196
5
5
  SHA512:
6
- metadata.gz: d36e99b464e96c9b6b9a33038f5aafad856606be7a394ee7d6df0b66afd1aa584ee0b7a45e116deb9fb281a3198fa063e1f652242ef6987cc353b427c00df9d5
7
- data.tar.gz: 1e894cfdf2410f80eca23d6b7dae2b4652cf417a32737ea907ca9bd5243192b98f11ebb230aa10f448bce90b8eac6160fbbc13536fb571c4e04f3b7df78d105b
6
+ metadata.gz: 25878269f2677245170b84be1d2b2ad0f19f923509daab15674e94bfa372bd6b2c64c946941a9560aa018fe41eb4e88987ff0d9e459baf7118f50a397e69c982
7
+ data.tar.gz: 2922d33c672fa469cddf839f8f06ad0081b44c16ef1c6313891fd3906b9c3a441f153324cf6723272cbb2003e715a80079a555bab0c9de311f85c3dd6291b375
data/README.md CHANGED
@@ -13,7 +13,7 @@ Get a key at [parseapi.com](https://parseapi.com). The client also reads `PARSEA
13
13
 
14
14
  ## API versions
15
15
 
16
- Version 1.4.0 explicitly selects the API contract supported by this SDK. It sends `Parse-Version: 2.0.0` on every lookup so responses match the API contract supported by the package. Your key and the team's saved default stay the same.
16
+ Version 1.6.0 explicitly selects the API contract supported by this SDK. It sends `Parse-Version: 2.0.0` on every lookup so responses match the API contract supported by the package. Your key and the team's saved default stay the same.
17
17
 
18
18
  Upgrade the dependency in staging, review the [release notes](https://parseapi.com/docs/releases), and test the application before deploying the same code and dependency version to production. Commit your dependency lockfile so the tested package travels with your deployment. Future major SDK upgrades can select a newer API contract.
19
19
 
@@ -138,7 +138,50 @@ The next call keeps its usual default unless it also supplies `lang`. Existing `
138
138
 
139
139
  ## Time
140
140
 
141
- `time` returns local ISO `at` with its UTC offset and integer Unix seconds in `unix`. The core `offset` preserves exact precision. Optional `deep.offset_seconds` gives the numeric offset, while `deep.offset_minutes` gives whole minutes. Historical offsets and ISO times can include offset seconds. Omitted `at` means now. With `to`, an offsetless `at` is source wall time. Otherwise it is UTC. Include an offset for repeated local times around a clock change. Current time and conversion use pooled requests on every plan. Coordinate clock fields can be null when the timezone is unknown. Existing `timezone` methods remain supported.
141
+ `time` returns local ISO `at` with its UTC offset and integer Unix seconds in `unix`. The core `offset` preserves exact precision. Optional `deep.offset_seconds` gives the numeric offset, while `deep.offset_minutes` gives whole minutes. Historical offsets and ISO times can include offset seconds. Omitted `at` means now. With `to` or `targets`, an offsetless `at` is source wall time. Otherwise it is UTC. Include an offset for repeated local times around a clock change. Current time and conversion use pooled requests on every plan. Coordinate clock fields can be null when the timezone is unknown. Existing `timezone` methods remain supported.
142
+
143
+ For an offsetless `at` with `to` or `targets`, choose how to handle a clock change with `disambiguation`. It applies to named-zone and coordinate Time calls.
144
+
145
+ | Value | Repeated time | Skipped time |
146
+ | --- | --- | --- |
147
+ | `compatible` (default) | Earlier occurrence | Shift forward by the clock change |
148
+ | `earlier` | Earlier occurrence | Shift backward by the clock change |
149
+ | `later` | Later occurrence | Shift forward by the clock change |
150
+ | `reject` | `400 ambiguous_time` | `400 nonexistent_time` |
151
+
152
+ An explicit UTC offset selects an instant directly. For example, `2026-11-01T01:30:00-04:00` and `2026-11-01T01:30:00-05:00` identify the two New York occurrences. A valid `disambiguation` value has no effect on explicit instants, current-time requests or lookups without `to` or `targets`. For user-entered appointment times, start with `reject`. Handle `ambiguous_time` or `nonexistent_time` by collecting an explicit offset or an earlier/later choice from the user. Other malformed input still uses `invalid_request`.
153
+
154
+ ```ruby
155
+ result = parse.time('America/New_York', at: '2026-11-01T01:30:00',
156
+ to: 'UTC', disambiguation: 'later')
157
+ puts result['to']['at'] # 2026-11-01T06:30:00+00:00
158
+ ```
159
+
160
+ Canonical Time `deep` includes the pinned rule edition in `deep.timezone_database_version` and source-wall resolution in `deep.resolution`. Resolution records `kind` (`unique`, `overlap` or `gap`), the selected policy, signed `adjustment_seconds`, and chronological alternatives with exact `at`, Unix seconds and UTC offset. Unique times have an empty alternatives list. Explicit instants, current time and lookups without conversion have null resolution. Destination detail stays compact.
161
+
162
+ Search serving IANA IDs by city or region, or omit the query to list all (Go and Rust use an empty string). Discovery returns `timezone_database_version` and sorted `timezones`. No search matches returns `timezones: []`.
163
+
164
+ Pass `targets` to convert one instant to 1-10 zones in a single pooled request. The native list preserves order and duplicates. Use `targets` instead of `to`. The response adds `targets`, with optional detail inside each target. Unknown source coordinates return `targets: null`. An unknown destination rejects the whole request with `not_found`. Omission keeps the original response shape.
165
+
166
+ ```ruby
167
+ zones = parse.time_zones('New York')
168
+ result = parse.time('UTC', at: '2026-09-24T12:00:00Z',
169
+ targets: ['America/New_York', 'Asia/Tokyo'])
170
+ p zones['timezones']
171
+ p result['targets']
172
+ ```
173
+
174
+ ### Location inputs and timezone filters
175
+
176
+ `parse.time(iata: 'JFK', deep: true)` and `parse.time_zones(country: 'US', dst: false, observes_dst: true, details: true)`.
177
+
178
+ Choose one explicit location input: IP, exact city name or stable city ID, country, IATA airport, ICAO airport, port UN/LOCODE, or address. Country and state can narrow a city or address. State requires country. Address lookup requires US country context and a strict address-point match. Port lookup covers the reviewed port subset, not every assigned UN/LOCODE. IP lookup always uses the supplied IP.
179
+
180
+ Location calls add `location` with `status`, `candidates`, `truncated`, `source` and the typed input. Check `status` before using the clock: ambiguous or missing locations retain null time fields. Candidate coordinates and IDs can also be null. A country with multiple timezones does not silently choose one. Named-zone and coordinate calls retain their existing signatures.
181
+
182
+ Timezone discovery accepts country, IANA area, exact signed offset, abbreviation, DST-at-instant and observes-DST-during-year filters. `at` selects the common instant, `sort` selects timezone or offset order, and `details` adds `zones` rows plus the evaluation `at`. The default `timezones` list stays compact. False DST filters are sent explicitly. An abbreviation returns candidate zones rather than choosing one. Observes-DST uses the UTC calendar year containing `at`.
183
+
184
+ Source deep adds `standard_offset`, `standard_offset_seconds`, signed `dst_offset_seconds` and `season`. Seasonal adjustments can be negative. `season` describes the current DST-flag interval, or the next within 400 days, with actual before/after transition facts and signed `change_seconds`. Unknown boundaries remain null. These fields are optional and nullable, and destination deep stays compact.
142
185
 
143
186
  ## Measurements
144
187
 
@@ -283,13 +283,19 @@ module ParseAPI
283
283
  get("/name/#{seg(name)}", country: country, deep: deep, name_locale: name_locale)
284
284
  end
285
285
 
286
- # Current local time, UTC by default. With to, offsetless at is source wall time.
287
- def time(timezone = nil, at: nil, to: nil, deep: false, lang: nil)
288
- get(timezone.nil? ? '/time' : "/time/#{seg(timezone)}", at: at, to: to, deep: deep, lang: lang)
286
+ # Current local time, UTC by default. With to or targets, offsetless at is source wall time.
287
+ def time(timezone = nil, at: nil, to: nil, deep: false, lang: nil, disambiguation: nil, targets: nil, ip: nil, city: nil, country: nil, state: nil, iata: nil, icao: nil, unlocode: nil, address: nil)
288
+ raise ArgumentError, 'Time source must be an IANA timezone ID. Use timezone discovery to list IDs.' if timezone && %w[zones help].include?(timezone.strip.downcase)
289
+ source = time_source(timezone, ip: ip, city: city, country: country, state: state, iata: iata, icao: icao, unlocode: unlocode, address: address)
290
+ get(timezone.nil? ? '/time' : "/time/#{seg(timezone)}", **source, at: at, to: to, deep: deep, lang: lang, disambiguation: disambiguation, targets: time_targets(targets, to))
289
291
  end
290
292
 
291
- def time_at(lat, lon, at: nil, to: nil, deep: false, lang: nil)
292
- get('/time', lat: lat, lon: lon, at: at, to: to, deep: deep, lang: lang)
293
+ def time_at(lat, lon, at: nil, to: nil, deep: false, lang: nil, disambiguation: nil, targets: nil)
294
+ get('/time', lat: lat, lon: lon, at: at, to: to, deep: deep, lang: lang, disambiguation: disambiguation, targets: time_targets(targets, to))
295
+ end
296
+
297
+ def time_zones(query = nil, country: nil, area: nil, offset: nil, abbreviation: nil, dst: nil, observes_dst: nil, at: nil, details: false, sort: nil)
298
+ get('/time/zones', q: query, country: country, area: area, offset: offset, abbreviation: abbreviation, dst: dst.nil? ? nil : dst.to_s, observes_dst: observes_dst.nil? ? nil : observes_dst.to_s, at: at, details: details, sort: sort)
293
299
  end
294
300
 
295
301
  def timezone(id, at: nil, to: nil, deep: false, lang: nil)
@@ -355,6 +361,26 @@ module ParseAPI
355
361
 
356
362
  private
357
363
 
364
+ def time_source(timezone, **values)
365
+ primary = values.values_at(:ip, :city, :iata, :icao, :unlocode, :address).compact
366
+ if values.values.any? { |v| !v.nil? && (!v.is_a?(String) || v.strip.empty?) } ||
367
+ (!timezone.nil? && values.values.any? { |v| !v.nil? }) || primary.length > 1 ||
368
+ (!values[:country].nil? && !primary.empty? && values[:city].nil? && values[:address].nil?) ||
369
+ (!values[:state].nil? && ((values[:city].nil? && values[:address].nil?) || values[:country].nil?)) ||
370
+ (!values[:address].nil? && values[:country].nil?)
371
+ raise ArgumentError, 'Pass one Time source, using country only with city or address and state only with city or address and country.'
372
+ end
373
+ values
374
+ end
375
+
376
+ def time_targets(targets, to)
377
+ return nil if targets.nil?
378
+ unless to.nil? && targets.is_a?(Array) && (1..10).cover?(targets.length) && targets.all? { |zone| zone.is_a?(String) && !zone.strip.empty? && !zone.include?(',') }
379
+ raise ArgumentError, 'Time targets requires 1 to 10 timezone IDs and cannot be combined with to.'
380
+ end
381
+ targets.join(',')
382
+ end
383
+
358
384
  def seg(value)
359
385
  URI.encode_www_form_component(value.to_s).gsub('+', '%20')
360
386
  end
@@ -1,3 +1,3 @@
1
1
  module ParseAPI
2
- VERSION = '1.4.0'.freeze
2
+ VERSION = '1.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parseapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ParseAPI