Use timezone aware methods (#893)

* Use timezone aware datetime methods

* Use timezone aware date parse for GitHub issue tag

* Introduce a bit of chaos programming using Zonebie

Zonebie uses a random timezone to run tests, it's a really good way to see if the code is timezone dependent or not.

* Convert GitHub issue date as UTC
This commit is contained in:
rhymes 2018-10-18 22:26:29 +02:00 committed by Mac Siri
parent e9753dbe99
commit 1b60ee5009
44 changed files with 90 additions and 87 deletions

View file

@ -624,7 +624,7 @@ Rails/Date:
Description: >-
Checks the correct usage of date aware methods,
such as Date.today, Date.current etc.
Enabled: false
Enabled: true
Rails/FindBy:
Description: 'Prefer find_by over where.first.'

View file

@ -148,5 +148,6 @@ group :test do
gem "test-prof", "~> 0.7"
gem "timecop", "~> 0.9"
gem "webmock", "~> 3.4"
gem "zonebie", "~> 0.6.1"
end
# rubocop:enable LineLength

View file

@ -914,6 +914,7 @@ GEM
xpath (3.1.0)
nokogiri (~> 1.8)
yajl-ruby (1.4.0)
zonebie (0.6.1)
PLATFORMS
ruby
@ -1047,6 +1048,7 @@ DEPENDENCIES
webmock (~> 3.4)
webpacker (~> 3.5)
webpush (~> 0.3)
zonebie (~> 0.6.1)
RUBY VERSION
ruby 2.5.1p57

View file

@ -24,7 +24,7 @@ module Api
end
def less_than_one_day_old?(user)
range = (Time.now.beginning_of_day - 1.day)..(Time.now)
range = 1.day.ago.beginning_of_day..Time.current
user_identity_age = user.github_created_at ||
user.twitter_created_at || 8.days.ago
# last one is a fallback in case both are nil

View file

@ -45,7 +45,7 @@ class ArticlesController < ApplicationController
@article = if @tag.present? && @user&.editor_version == "v2"
authorize Article
Article.new(body_markdown: "", cached_tag_list: @tag.name,
processed_html: "")
processed_html: "")
elsif @tag&.submission_template.present? && @user
authorize Article
Article.new(body_markdown: @tag.submission_template_customized(@user.name),
@ -104,7 +104,7 @@ class ArticlesController < ApplicationController
@article.tag_list = []
@article.main_image = nil
edited_at_date = if @article.user == current_user && @article.published
Time.now
Time.current
else
@article.edited_at
end

View file

@ -3,7 +3,7 @@ class EmailSubscriptionsController < ApplicationController
def unsubscribe
verified_params = Rails.application.message_verifier(:unsubscribe).verify(params[:ut])
if verified_params[:expires_at] > Time.now
if verified_params[:expires_at] > Time.current
user = User.find(verified_params[:user_id])
user.update(verified_params[:email_type] => false)
@email_type = preferred_email_name(verified_params[:email_type])

View file

@ -40,11 +40,13 @@ class GiveawaysController < ApplicationController
render :edit
return
end
now = Time.current
@user.onboarding_package_requested_again = true if @user.onboarding_package_requested
@user.onboarding_package_requested = true
@user.onboarding_package_form_submmitted_at = Time.now.to_datetime
@user.personal_data_updated_at = Time.now.to_datetime
@user.shipping_validated_at = Time.now.to_datetime if user_params[:shipping_validated] == "1"
@user.onboarding_package_form_submmitted_at = now
@user.personal_data_updated_at = now
@user.shipping_validated_at = now if user_params[:shipping_validated] == "1"
if @user.save!
format.html { redirect_to "/freestickers/edit" }
format.json { render :show, status: :ok, location: @user }

View file

@ -52,7 +52,7 @@ class Internal::ArticlesController < Internal::ApplicationController
where(published: true).
or(Article.where(published_from_feed: true)).
where(featured: true).
where("featured_number > ?", Time.now.to_i).
where("featured_number > ?", Time.current.to_i).
includes(:user).
includes(:buffer_updates).
limited_columns_internal_select.

View file

@ -12,7 +12,7 @@ class Internal::DogfoodController < Internal::ApplicationController
user_ids = @team_members.map(&:id)
@comment_totals_this_week = Comment.
users_with_number_of_comments(user_ids, Date.today.beginning_of_week)
users_with_number_of_comments(user_ids, Time.zone.today.beginning_of_week)
@comment_totals_24_hours = Comment.
users_with_number_of_comments(user_ids, 24.hours.ago)

View file

@ -47,7 +47,7 @@ class MessagesController < ApplicationController
username: new_message.user.username,
profile_image_url: ProfileImage.new(new_message.user).get(90),
message: new_message.message_html,
timestamp: Time.now,
timestamp: Time.current,
color: new_message.preferred_user_color,
reception_method: "pushed"
}.to_json

View file

@ -4,7 +4,7 @@ class RegistrationsController < Devise::RegistrationsController
def new
if user_signed_in?
redirect_to "/dashboard?signed-in-already&t=#{Time.now.to_i}"
redirect_to "/dashboard?signed-in-already&t=#{Time.current.to_i}"
return
end
super

View file

@ -11,18 +11,18 @@ class Bufferizer
BufferUpdate.buff!(article.id, twitter_buffer_text, tag.buffer_profile_id_code, "twitter", tag.id)
end
end
article.update(last_buffered: Time.now)
article.update(last_buffered: Time.current)
end
def main_teet!
BufferUpdate.buff!(article.id, twitter_buffer_text, ApplicationConfig["BUFFER_TWITTER_ID"], "twitter", nil)
article.update(last_buffered: Time.now)
article.update(last_buffered: Time.current)
end
def facebook_post!
BufferUpdate.buff!(article.id, fb_buffer_text, ApplicationConfig["BUFFER_FACEBOOK_ID"], "facebook")
BufferUpdate.buff!(article.id, fb_buffer_text, ApplicationConfig["BUFFER_LINKEDIN_ID"], "linkedin")
article.update(facebook_last_buffered: Time.now)
article.update(facebook_last_buffered: Time.current)
end
private

View file

@ -12,13 +12,13 @@ class CacheBuster
end
def bust_comment(commentable, username)
if commentable.featured_number.to_i > (Time.now.to_i - 5.hours.to_i)
if commentable.featured_number.to_i > 5.hours.ago.to_i
bust("/")
bust("/?i=i")
bust("?i=i")
end
if commentable.decorate.cached_tag_list_array.include?("discuss") &&
commentable.featured_number.to_i > (Time.now.to_i - 35.hours.to_i)
commentable.featured_number.to_i > 35.hours.ago.to_i
bust("/")
bust("/?i=i")
bust("?i=i")
@ -58,7 +58,7 @@ class CacheBuster
end
def bust_home_pages(article)
if article.featured_number.to_i > Time.now.to_i
if article.featured_number.to_i > Time.current.to_i
bust("/")
bust("?i=i")
end

View file

@ -12,7 +12,7 @@ class EmailLogic
end
def analyze
@last_email_sent_at = get_last_digest_email_user_recieved
@last_email_sent_at = get_last_digest_email_user_received
@open_percentage = get_open_rate
@days_until_next_email = get_days_until_next_email
@ready_to_receive_email = get_user_readiness
@ -75,11 +75,11 @@ class EmailLogic
def get_user_readiness
return true unless @last_email_sent_at
# Has it been atleast x days since @user receive an email?
(Time.now.utc - @last_email_sent_at) >= @days_until_next_email.days.to_i
# Has it been at least x days since @user received an email?
Time.current - @last_email_sent_at >= @days_until_next_email.days.to_i
end
def get_last_digest_email_user_recieved
def get_last_digest_email_user_received
@user.email_messages.where(mailer: "DigestMailer#digest_email").last&.sent_at
end

View file

@ -19,7 +19,7 @@ class GithubTag
username = content_json[:user][:login]
user_html_url = content_json[:user][:html_url]
user_avatar_url = content_json[:user][:avatar_url]
date = Date.parse(content_json[:created_at].to_s).strftime("%b %d, %Y")
date = Time.zone.parse(content_json[:created_at].to_s).utc.strftime("%b %d, %Y")
date_link = content_json[:html_url]
title = generate_title
html = "" \

View file

@ -7,7 +7,7 @@ class ApplicationMailer < ActionMailer::Base
Rails.application.message_verifier(:unsubscribe).generate(
user_id: id,
email_type: email_type.to_sym,
expires_at: Time.now + 31.days,
expires_at: 31.days.from_now,
)
end
end

View file

@ -28,7 +28,7 @@ class Article < ApplicationRecord
url: { allow_blank: true, no_local: true, schemes: ["https", "http"] },
uniqueness: { allow_blank: true }
# validates :description, length: { in: 10..170, if: :published? }
validates :body_markdown, uniqueness: { scope: [:user_id, :title] }
validates :body_markdown, uniqueness: { scope: %i[user_id title] }
validate :validate_tag
validate :validate_video
validates :video_state, inclusion: { in: %w(PROGRESSING COMPLETED) }, allow_nil: true
@ -288,15 +288,13 @@ class Article < ApplicationRecord
end
def evaluate_markdown
begin
fixed_body_markdown = MarkdownFixer.fix_all(body_markdown || "")
parsed = FrontMatterParser::Parser.new(:md).call(fixed_body_markdown)
parsed_markdown = MarkdownParser.new(parsed.content)
self.processed_html = parsed_markdown.finalize
evaluate_front_matter(parsed.front_matter)
rescue StandardError => e
errors[:base] << ErrorMessageCleaner.new(e.message).clean
end
fixed_body_markdown = MarkdownFixer.fix_all(body_markdown || "")
parsed = FrontMatterParser::Parser.new(:md).call(fixed_body_markdown)
parsed_markdown = MarkdownParser.new(parsed.content)
self.processed_html = parsed_markdown.finalize
evaluate_front_matter(parsed.front_matter)
rescue StandardError => e
errors[:base] << ErrorMessageCleaner.new(e.message).clean
end
def has_frontmatter?
@ -331,7 +329,7 @@ class Article < ApplicationRecord
def readable_publish_date
relevant_date = crossposted_at.present? ? crossposted_at : published_at
if relevant_date && relevant_date.year == Time.now.year
if relevant_date && relevant_date.year == Time.current.year
relevant_date&.strftime("%b %e")
else
relevant_date&.strftime("%b %e '%y")
@ -395,12 +393,11 @@ class Article < ApplicationRecord
end
def parsed_date(date)
today_date = Time.now.to_datetime
return published_at || today_date unless date
given_date = date.to_datetime
now = Time.current
return published_at || now unless date
error_msg = "must be entered in DD/MM/YYYY format with current or past date"
return errors.add(:date_time, error_msg) if given_date > today_date
given_date
return errors.add(:date_time, error_msg) if date > now
date
end
def validate_tag
@ -448,18 +445,18 @@ class Article < ApplicationRecord
def set_published_date
if published && published_at.blank?
self.published_at = Time.now
self.published_at = Time.current
user.delay.resave_articles # tack-on functionality HACK
organization&.delay&.resave_articles # tack-on functionality HACK
end
end
def set_featured_number
self.featured_number = Time.now.to_i if featured_number.blank? && published
self.featured_number = Time.current.to_i if featured_number.blank? && published
end
def set_crossposted_at
self.crossposted_at = Time.now if published && crossposted_at.blank? && published_from_feed
self.crossposted_at = Time.current if published && crossposted_at.blank? && published_from_feed
end
def set_last_comment_at

View file

@ -42,7 +42,7 @@ class BadgeAchievement < ApplicationRecord
end
def activity_target
"badge_#{Time.now}"
"badge_#{Time.current}"
end
def remove_from_feed

View file

@ -171,7 +171,7 @@ class Comment < ApplicationRecord
end
def activity_target
"comment_#{Time.now}"
"comment_#{Time.current}"
end
def remove_from_feed
@ -203,7 +203,7 @@ class Comment < ApplicationRecord
end
def readable_publish_date
if created_at.year == Time.now.year
if created_at.year == Time.current.year
created_at.strftime("%b %e")
else
created_at.strftime("%b %e '%y")

View file

@ -12,12 +12,12 @@ class Event < ApplicationRecord
before_validation :evaluate_markdown
scope :in_the_future_and_published, -> {
where("starts_at > ?", Time.now).
where("starts_at > ?", Time.current).
where(published: true)
}
scope :in_the_past_and_published, -> {
where("starts_at < ?", Time.now).
where("starts_at < ?", Time.current).
where(published: true)
}

View file

@ -59,7 +59,7 @@ class Mention < ApplicationRecord
end
def activity_target
"mention_#{Time.now}"
"mention_#{Time.current}"
end
def remove_from_feed

View file

@ -64,7 +64,7 @@ class Notification < ApplicationRecord
end
def activity_target
"#{notifiable_type.downcase}_#{Time.now}"
"#{notifiable_type.downcase}_#{Time.current.utc}"
end
def activity_notify

View file

@ -95,7 +95,7 @@ class Tweet < ApplicationRecord
tweet.extended_entities_serialized = tweeted.attrs[:extended_entities]
tweet.full_fetched_object_serialized = tweeted.attrs
tweet.tweeted_at = tweeted.attrs[:created_at]
tweet.last_fetched_at = Time.now
tweet.last_fetched_at = Time.current
tweet.user_is_verified = tweeted.user.verified?
tweet.is_quote_status = tweeted.attrs[:is_quote_status]
tweet.save!

View file

@ -298,7 +298,7 @@ class User < ApplicationRecord
end
def scholar
valid_pass = workshop_expiration.nil? || workshop_expiration > Time.now
valid_pass = workshop_expiration.nil? || workshop_expiration > Time.current
has_role?(:workshop_pass) && valid_pass
end
@ -550,11 +550,11 @@ class User < ApplicationRecord
def mentorship_status_update
if mentor_description_changed? || offering_mentorship_changed?
self.mentor_form_updated_at = Time.now
self.mentor_form_updated_at = Time.current
end
if mentee_description_changed? || seeking_mentorship_changed?
self.mentee_form_updated_at = Time.now
self.mentee_form_updated_at = Time.current
end
end
end

View file

@ -106,9 +106,9 @@ class AuthorizationService
def account_less_than_a_week_old?(user, logged_in_identity)
user_identity_age = user.github_created_at ||
user.twitter_created_at ||
Time.parse(logged_in_identity.auth_data_dump.extra.raw_info.created_at)
Time.zone.parse(logged_in_identity.auth_data_dump.extra.raw_info.created_at)
# last one is a fallback in case both are nil
range = (Time.now.beginning_of_day - 1.week)..(Time.now)
range = 1.week.ago.beginning_of_day..Time.current
range.cover?(user_identity_age)
end

View file

@ -13,7 +13,7 @@ class MembershipService
true if create_subscription &&
assign_membership_role &&
user.update(monthly_dues: monthly_dues,
membership_started_at: Time.now,
membership_started_at: Time.current,
email_membership_newsletter: true,
stripe_id_code: customer.id) &&
send_welcome_email

View file

@ -12,7 +12,7 @@ class ModerationService
notifiable_type: object.class.name,
action: "Moderation",
)
moderator.update_column(:last_moderation_notification, Time.now)
moderator.update_column(:last_moderation_notification, Time.current)
end
end
handle_asynchronously :send_moderation_notification

View file

@ -5,7 +5,7 @@
<div class="field-unit__field" style="width: 2em;">
<%= f.check_box field.attribute %>
</div>
<%= f.date_field "workshop_expiration", value: (Time.now + 1.year).strftime("%Y-%m-%d"), style: "width: 25%;" %>
<%= f.date_field "workshop_expiration", value: 1.year.from_now.strftime("%Y-%m-%d"), style: "width: 25%;" %>
<div style="margin-left: 1em;">
<%= f.label "Send Scholarship Email?" %>
</div>

View file

@ -1,9 +1,9 @@
<%= f.label :cover_image %>:
<%= f.label :cover_image %>:
<%= f.file_field :cover_image %>
<br>
<img src="<%= event.profile_image_url %>" style="width: 25%;">
<br>
<%= f.label :profile_image %> (for live notification):
<%= f.label :profile_image %> (for live notification):
<%= f.file_field :profile_image %>
<br>
<%= f.label :title %>
@ -16,10 +16,10 @@
<%= f.select :category, ['AMA', 'Workshop', 'Talk', 'Town Hall'], required: true %>
<br>
<%= f.label :starts_at %>
<%= f.datetime_select :starts_at, required: true, include_blank: true, start_year: Date.today.year, end_year: Date.today.year + 2 %> UTC Time Only (4 hours ahead of eastern time)
<%= f.datetime_select :starts_at, required: true, include_blank: true, start_year: Time.current.year, end_year: Time.current.year + 2 %> UTC Time Only (4 hours ahead of eastern time)
<br>
<%= f.label :ends_at %>
<%= f.datetime_select :ends_at, required: true, include_blank: true, start_year: Date.today.year, end_year: Date.today.year + 2 %> UTC Time Only (4 hours ahead of eastern time)
<%= f.datetime_select :ends_at, required: true, include_blank: true, start_year: Time.current.year, end_year: Time.current.year + 2 %> UTC Time Only (4 hours ahead of eastern time)
<br>
<%= f.label :location_name %>
<%= f.text_field :location_name, required: true %>

View file

@ -11,7 +11,7 @@
}
</style>
<% else %>
<meta name="last-updated" content="<%= Time.now %>">
<meta name="last-updated" content="<%= Time.current %>">
<meta name="user-signed-in" content="<%= user_signed_in? %>">
<meta name="algolia-public-id" content="<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>">
<meta name="algolia-public-key" content="<%= ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY %>">

View file

@ -1,4 +1,4 @@
<!-- cached at: <%= Time.now %> -->
<!-- cached at: <%= Time.current %> -->
<h3>Billing Details</h3>
<% if current_user.monthly_dues == 0 %>
<div class="default-billing-message">

View file

@ -34,7 +34,7 @@ User.clear_index!
email_comment_notifications: false,
email_follower_notifications: false,
email: Faker::Internet.email(name, "+"),
confirmed_at: Time.now,
confirmed_at: Time.current,
password: "password",
)

View file

@ -40,7 +40,7 @@ task renew_hired_articles: :environment do
each do |article|
if article.automatically_renew
article.featured_number = Time.now.to_i
article.featured_number = Time.current.to_i
else
article.approved = false
article.body_markdown = article.body_markdown.gsub(
@ -67,7 +67,7 @@ task github_repo_fetch_all: :environment do
end
task send_email_digest: :environment do
return if Time.now.wday < 3
return if Time.current.wday < 3
EmailDigest.send_periodic_digest_email
end

View file

@ -2,8 +2,8 @@ FactoryBot.define do
factory :event do
title { Faker::Company.bs }
description_markdown { Faker::Hipster.paragraph(2) }
starts_at { Time.now }
ends_at { Time.now + 3660 }
starts_at { Time.current }
ends_at { 3660.seconds.from_now }
category { "AMA" }
end
end

View file

@ -17,7 +17,7 @@ FactoryBot.define do
github_username { generate :github_username }
summary { Faker::Lorem.paragraph[0..rand(190)] }
website_url { Faker::Internet.url }
confirmed_at { Time.now }
confirmed_at { Time.current }
saw_onboarding { true }
signup_cta_variant { "navbar_basic" }
email_digest_periodic { false }

View file

@ -19,8 +19,8 @@ describe "Admin creates new event" do
def create_and_publish_event
fill_in("Title", with: "Workshop Title")
select_date_and_time(Date.today.year.to_s, "December", "30", "15", "30", "starts_at")
select_date_and_time(Date.today.year.to_s, "December", "30", "16", "30", "ends_at")
select_date_and_time(Time.current.year.to_s, "December", "30", "15", "30", "starts_at")
select_date_and_time(Time.current.year.to_s, "December", "30", "16", "30", "ends_at")
check("event[published]")
click_button("Create Event")
end

View file

@ -41,7 +41,7 @@ RSpec.describe EmailLogic do
create_list(:article, 3, user_id: author.id, positive_reactions_count: 20)
10.times do
Ahoy::Message.create(mailer: "DigestMailer#digest_email",
user_id: user.id, sent_at: Time.now.utc)
user_id: user.id, sent_at: Time.current.utc)
end
end
@ -55,15 +55,15 @@ RSpec.describe EmailLogic do
before do
10.times do
Ahoy::Message.create(mailer: "DigestMailer#digest_email", user_id: user.id,
sent_at: Time.now.utc, opened_at: Time.now.utc)
sent_at: Time.current.utc, opened_at: Time.current.utc)
author = create(:user)
user.follow(author)
create_list(:article, 3, user_id: author.id, positive_reactions_count: 40)
end
end
it "evaluates that user is ready to recieve an email" do
Timecop.freeze(Date.today + 3) do
it "evaluates that user is ready to receive an email" do
Timecop.freeze(3.days.from_now) do
h = described_class.new(user).analyze
expect(h.should_receive_email?).to eq(true)
end

View file

@ -30,14 +30,14 @@ RSpec.describe RateLimitChecker do
it ".limit_by_email_recipient_address returns true if too many published articles at once" do
10.times do
EmailMessage.create(to: user.email, sent_at: Time.now)
EmailMessage.create(to: user.email, sent_at: Time.current)
end
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(true)
end
it ".limit_by_email_recipient_address returns false if published articles comment" do
2.times do
EmailMessage.create(to: user.email, sent_at: Time.now)
EmailMessage.create(to: user.email, sent_at: Time.current)
end
expect(described_class.new.limit_by_email_recipient_address(user.email)).to eq(false)
end

View file

@ -14,7 +14,7 @@ RSpec.describe Event, type: :model do
end
it "rejects ends times that are earlier than start times" do
event.ends_at = Time.now - 50000
event.ends_at = 14.hours.ago
expect(event).not_to be_valid
end

View file

@ -49,7 +49,7 @@ RSpec.describe GithubRepo, type: :model do
it "updates all repo" do
old_updated_at = repo.updated_at
Timecop.freeze(Date.today + 3) do
Timecop.freeze(3.days.from_now) do
described_class.update_to_latest
expect(old_updated_at).not_to eq(GithubRepo.find(repo.id).updated_at)
end

View file

@ -7,7 +7,7 @@ RSpec.describe "EmailSubscriptions", type: :request do
Rails.application.message_verifier(:unsubscribe).generate(
user_id: user_id,
email_type: :email_mention_notifications,
expires_at: Time.now + 31.days,
expires_at: 31.days.from_now,
)
end
@ -28,9 +28,9 @@ RSpec.describe "EmailSubscriptions", type: :request do
to raise_error(ActionController::RoutingError)
end
it "won't work if it's past expireation date" do
it "won't work if it's past expiration date" do
token = generate_token(user.id)
Timecop.freeze(Date.today + 32) do
Timecop.freeze(32.days.from_now) do
get email_subscriptions_unsubscribe_url(ut: token)
expect(response).to render_template("invalid_token")
end

View file

@ -15,7 +15,7 @@ RSpec.describe "Registrations", type: :request do
it "redirects to /dashboard" do
login_as user
get "/enter"
is_expected.to redirect_to("/dashboard?signed-in-already&t=#{Time.now.to_i}")
is_expected.to redirect_to("/dashboard?signed-in-already&t=#{Time.current.to_i}")
end
end
end

View file

@ -67,14 +67,14 @@ RSpec.describe UserRoleService do
end
it "adds workshop_expiration date with valid params" do
expiration_date = Time.now + 1.year
expiration_date = 1.year.from_now
described_class.new(user, admin.id).
send(:new_roles?, scholar: "1", workshop_expiration: expiration_date)
expect(user.workshop_expiration).to eq(expiration_date)
end
it "doesn't add a workshop_expiration date if scholar is not checked" do
expiration_date = Time.now + 1.year
expiration_date = 1.year.from_now
described_class.new(user, admin.id).
send(:new_roles?, scholar: "0", workshop_expiration: expiration_date)
expect(user.workshop_expiration).to eq(nil)

View file

@ -1,4 +1,5 @@
require "simplecov"
require "zonebie/rspec"
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.