Rails Activesupport Time Parsing

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



Leave a reply



Submit