Rails: How to parse date-time string into a specific time zone
%Z
is the correct way to specify a Time zone name. Have you tried the following ?
date_and_time = '%m-%d-%Y %H:%M:%S %Z'
DateTime.strptime("04-15-2010 10:00:00 Central Time (US & Canada)",date_and_time)
How to parse an instance of ActiveSupport::TimeWithZone to get date, weekday, month and year?
checkout ruby
Date, Time and strftime
Date.today
=>Thu, 16 May 2019
Date.today.strftime('%A')
=> "Thursday" # you week name
Date.today.strftime('%B')
=> "May" # month name
Date.today.strftime('%d %A %b %Y')
=> "16 Thursday May 2019"
10.days.ago.strftime('%-d %b %Y')
=> "6 May 2019" # gives date without 0
Which should I always parse date times with? DateTime, Time, Time.zone?
Go with Time.zone.parse if you just want to write into ActiveRecord.
DateTime should be avoided. If you're handling dates, you should use Date.parse instead.
Beyond that, it depends on whether the input comes with timezone information, what the current timezone is set to, and whether you want timezones in your data.
Time.zone.parse will return an ActiveSupport::TimeWithZone, defaulting to UTC.
> Time.zone.parse("12:30")
=> Thu, 10 May 2012 12:30:00 UTC +00:00
Time.parse will return a Time, with a zone if it's specified in the input, or the local TZ.
> Time.parse("12:30")
=> 2012-05-09 12:30:00 -0700
For a more detailed explanation of Ruby time comparisons and precision, read this blog post:
http://blog.solanolabs.com/rails-time-comparisons-devil-details-etc/
How ActiveSupport TimeWithZone retains values?
Ruby's -
(subtraction) method from the Time
class is used for this as you can see looking at the Rails source code for TimeWithZone.
def -(other)
if other.acts_like?(:time)
to_time - other.to_time
elsif duration_of_variable_length?(other)
method_missing(:-, other)
else
result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
result.in_time_zone(time_zone)
end
end
As per the Ruby docs it returns a float.
Difference — Returns a difference in seconds as a Float between time
and other_time, or subtracts the given number of seconds in numeric
from time.
How do I get Ruby to parse time as if it were in a different time zone?
You could just append the UTC timezone name to the string before parsing it:
require 'time'
s = "11/23/10 23:29:57"
Time.parse(s) # => Tue Nov 23 23:29:57 -0800 2010
s += " UTC"
Time.parse(s) # => Tue Nov 23 23:29:57 UTC 2010
Rails - A better way to parse the time with a timezone?
Were you looking for a specific timezone of the current local one?
# Current zone
1.9.3p194> Time.zone.parse('2012-12-25T00:00:00+09:00')
=> Mon, 24 Dec 2012 15:00:00 UTC +00:00
Console was set at UTC for above but will work for whatever you have configured
# Specific timezone
1.9.3p194> Time.find_zone('Wellington').parse('2012-12-25T00:00:00+09:00')
=> Tue, 25 Dec 2012 04:00:00 NZDT +13:00
I notice you're trying to pass +9 so as an example
1.9.3p194> Time.zone = 'Tokyo'
=> "Tokyo"
1.9.3p194> Time.zone.parse('2012-12-25T00:00:00+09:00')
=> Tue, 25 Dec 2012 00:00:00 JST +09:00
Gives you the right result.
Parsing a TimeZone name string that includes GMT offset hours
ShopifyAPI::Shop.current
returns properties documented here. Yes, timezone
is one of them, but it is intended to be a display name, not something you should parse.
Instead, use the iana_timezone
property, which will give you the IANA time zone identifier, such as America/Los_Angeles
. These are compatible with Rails, as well as Ruby's tzinfo gem, and also are used in many other platforms.
irb> ShopifyAPI::Shop.current.timezone
=> "(GMT-08:00) Pacific Time (US & Canada)"
irb> ShopifyAPI::Shop.current.iana_timezone
=> "America/Los_Angeles"
If you want to get a Rails time zone from there, you can use the MAPPING
constant defined in ActiveSupport::TimeZone
. Though I'd avoid it if possible, for the reasons mentioned in the timezone tag wiki in the "Rails Time Zone Identifiers" section at the bottom.
ActiveSupport::Duration incorrectly calculates interval?
ActiveSupport::Duration calculates its value using the following constants and algorithm (I have added the explanation on what it's doing below but here is a link to the source). As you can see below, the SECONDS_PER_YEAR
constant is the average number of seconds in the gregorian calendar (which is then used to define SECONDS_PER_MONTH
). It is because of this, "average definition" of SECONDS_PER_YEAR and SECONDS_PER_MONTH that you are getting the unexpected hours, minutes and seconds. It is defined as an average because a month and year is not a standard fixed amount of time.
SECONDS_PER_MINUTE = 60
SECONDS_PER_HOUR = 3600
SECONDS_PER_DAY = 86400
SECONDS_PER_WEEK = 604800
SECONDS_PER_MONTH = 2629746 # This is 1/12 of a Gregorian year
SECONDS_PER_YEAR = 31556952 # The length of a Gregorian year = 365.2425 days
# You pass ActiveSupport::Duration the number of seconds (b-a) = 9255600.0 seconds
remainder_seconds = 9255600.0
# Figure out how many years fit into the seconds using integer division.
years = (remainder_seconds/SECONDS_PER_YEAR).to_i # => 0
# Subtract the amount of years from the remaining_seconds
remainder_seconds -= years * SECONDS_PER_YEAR # => 9255600.0
months = (remainder_seconds/SECONDS_PER_MONTH).to_i # => 3
remainder_seconds -= months * SECONDS_PER_MONTH # => 1366362.0
weeks = (remainder_seconds/SECONDS_PER_WEEK).to_i # => 2
remainder_seconds -= weeks * SECONDS_PER_WEEK # => 156762.0
days = (remainder_seconds/SECONDS_PER_DAY).to_i # => 1
remainder_seconds -= days * SECONDS_PER_DAY # => 70362.0
hours = (remainder_seconds/SECONDS_PER_HOUR).to_i # => 19
remainder_seconds -= hours * SECONDS_PER_HOUR # => 1962.0
minutes = (remainder_seconds/SECONDS_PER_MINUTE).to_i # => 32
remainder_seconds -= minutes * SECONDS_PER_MINUTE # => 42
seconds = remainder_seconds # => 42
puts "#{years} years, #{months} months, #{weeks} weeks, #{days} days, #{hours} hours, #{minutes} minutes, #{seconds} seconds"
# 0 years, 3 months, 2 weeks, 1 days, 19 hours, 32 minutes, 42.0 seconds
To avoid the issue you are having, I would suggest to just represent the time in week, days, hours, minutes and seconds (basically anything excluding month & year).
The number of seconds in a month is complicated if you don't use an average since you will need to account for 28, 29, 30 and 31 days for each separate month. Similarly, for the year, you will need to account for leap/non-leap if you don't use the average.
I am not sure of any gems around which do this for you, however I can provide a function which can help you calculate the duration in days, hours, minutes and seconds below.
def duration_in_whms(seconds)
parts_and_seconds_in_part = {:weeks => 604800, :days => 86400, :hours => 3600, :minutes => 60}
result = {}
remainder = seconds
parts_and_seconds_in_part.each do |k, v|
result[k] = (remainder/v).to_i
remainder -= result[k]*v
end
result.merge(seconds: remainder)
end
duration_in_whms(9255600) => # {:weeks=>15, :days=>2, :hours=>3, :minutes=>0, :seconds=>0.0}
Related Topics
Sum the Value of Array in Hash
How to Use Bundler with Offline .Gem File
How to Simulate Java-Like Annotations in Ruby
How to Install the Ruby Ri Documentation
How to Install Jekyll on Osx 10.11
Ruby: How to Convert a String to Boolean
Simple Way of Turning Off Observers During Rake Task
How to Ensure That Ruby Uses an Openssl Not Vulnerable to Heartbleed
How to Decompress Gzip String in Ruby
How to Change All the Keys of a Hash by a New Set of Given Keys
Devise - How to Change Setting So That Email Addresses Don't Need to Be Unique