From 3684ad20423e78f9049fd96adf5320c93d463695 Mon Sep 17 00:00:00 2001 From: End Nightshade Date: Sun, 22 Feb 2026 14:47:38 -0700 Subject: [PATCH 1/4] feat(slack): set timezone from browser or country when provisioning Slack users via SCIM Prefer the user's browser-detected IANA timezone (stored on IdentitySession) when creating a Slack user via SCIM. If no browser timezone is available, fall back to a best-guess timezone for the user's country using TZInfo. --- app/services/scim_service.rb | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/app/services/scim_service.rb b/app/services/scim_service.rb index c262269d..74fd93df 100644 --- a/app/services/scim_service.rb +++ b/app/services/scim_service.rb @@ -286,6 +286,13 @@ def build_user_payload(identity:, username:, user_type:) } } + # Set timezone based on user's browser + tz = timezone_for_identity(identity) + if tz.present? + payload[:timezone] = tz + Rails.logger.info "Setting Slack timezone to #{tz} for #{identity.primary_email}" + end + # Add guest extension for multi-channel guests if user_type == :multi_channel_guest payload[:schemas] << "urn:ietf:params:scim:schemas:extension:slack:guest:2.0:User" @@ -294,5 +301,36 @@ def build_user_payload(identity:, username:, user_type:) payload end + + + def timezone_for_identity(identity) + # Prefer browser-detected timezone from the user's most recent session + session_tz = identity.sessions.order(created_at: :desc).pick(:timezone) + if session_tz.present? + begin + TZInfo::Timezone.get(session_tz) + return session_tz + rescue TZInfo::InvalidTimezoneIdentifier + Rails.logger.warn "Invalid session timezone '#{session_tz}' for identity #{identity.id}" + end + end + + # Fall back to country-based timezone + country_code = identity.country + if country_code.present? + begin + country = TZInfo::Country.get(country_code) + zone_id = country.zone_identifiers.first + return zone_id if zone_id.present? + rescue TZInfo::InvalidCountryCode + Rails.logger.warn "Invalid country code '#{country_code}' for timezone lookup" + end + end + + nil + rescue => e + Rails.logger.warn "Failed to determine timezone for identity #{identity.id}: #{e.message}" + nil + end end end From a9bef9d0ff15ff949f5db30d637014008450f36d Mon Sep 17 00:00:00 2001 From: End Nightshade Date: Sun, 22 Feb 2026 14:53:43 -0700 Subject: [PATCH 2/4] style(rubocop): fix space inside array literal in analytics_service --- app/services/analytics_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/analytics_service.rb b/app/services/analytics_service.rb index b7c358d8..48a5305b 100644 --- a/app/services/analytics_service.rb +++ b/app/services/analytics_service.rb @@ -134,7 +134,7 @@ def promotion_breakdown totals.map do |scenario, total| prom = promoted[scenario] || 0 rate = total > 0 ? ((prom.to_f / total) * 100).round(1) : 0 - [scenario || "default", { total: total, promoted: prom, rate: rate }] + [ scenario || "default", { total: total, promoted: prom, rate: rate } ] end.sort_by { |_, v| -v[:total] }.to_h end From a02f122c2d885056cd50b3252dbb58b16b2241f1 Mon Sep 17 00:00:00 2001 From: End Nightshade Date: Sun, 22 Feb 2026 14:57:00 -0700 Subject: [PATCH 3/4] fix(slack): convert country enum to string for TZInfo lookup --- app/services/scim_service.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/services/scim_service.rb b/app/services/scim_service.rb index 74fd93df..c05adc79 100644 --- a/app/services/scim_service.rb +++ b/app/services/scim_service.rb @@ -315,17 +315,17 @@ def timezone_for_identity(identity) end end - # Fall back to country-based timezone - country_code = identity.country - if country_code.present? - begin - country = TZInfo::Country.get(country_code) - zone_id = country.zone_identifiers.first - return zone_id if zone_id.present? - rescue TZInfo::InvalidCountryCode - Rails.logger.warn "Invalid country code '#{country_code}' for timezone lookup" - end - end + # Fall back to country-based timezone + country_code = identity.country + if country_code.present? + begin + country = TZInfo::Country.get(country_code.to_s) + zone_id = country.zone_identifiers.first + return zone_id if zone_id.present? + rescue TZInfo::InvalidCountryCode + Rails.logger.warn "Invalid country code '#{country_code}' for timezone lookup" + end + end nil rescue => e From 20ffa8d1e4e44f935b094bf5f0b1af161896ed55 Mon Sep 17 00:00:00 2001 From: End Nightshade Date: Sun, 22 Feb 2026 15:03:56 -0700 Subject: [PATCH 4/4] style(jobs): fix array-bracket spacing and add final newline --- app/jobs/identity/reap_aged_out_users_job.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/jobs/identity/reap_aged_out_users_job.rb b/app/jobs/identity/reap_aged_out_users_job.rb index f58d0c57..a9a5be3a 100644 --- a/app/jobs/identity/reap_aged_out_users_job.rb +++ b/app/jobs/identity/reap_aged_out_users_job.rb @@ -2,7 +2,7 @@ class Identity::ReapAgedOutUsersJob < ApplicationJob queue_as :default def perform - aged_out = Identity.where(ysws_eligible: true, hq_override: [false, nil]) + aged_out = Identity.where(ysws_eligible: true, hq_override: [ false, nil ]) .where("birthday <= ?", 19.years.ago.to_date) reaped_count = 0 @@ -15,4 +15,4 @@ def perform Rails.logger.info "ReapAgedOutUsersJob: marked #{reaped_count} #{"user".pluralize reaped_count} as alumni and ineligible" end -end \ No newline at end of file +end