Never dealt with timezones before. Haven't heard pleasant things.
I am pulling Tweets from a Twitter account to post on an application. I am using the following line of code to display the date as follows: November 26th, 2015.
<%= Date::MONTHNAMES[tweet.created_at.month] %> <%= tweet.created_at.day.ordinalize %>, <%= tweet.created_at.year %>
However, the time is displayed as +0600 further into time than my current location. I am located in Canada/US Central which is -0600. All my local records are correct (both in time and timezone), but I assume this is different because I am pulling it from a non-local source.
2015-11-27 03:07:04 +0000 is the date displayed from the created_at property of the tweet.
I am wanting to change the tweet's time so that is correct for my timezone at minimum, if not too hard to do I'd prefer it to be compatible with all timezones.
You can use in_time_zone method to convert the time to your desired timezone:
created_at = '2015-11-27 03:07:04 +0000'
created_at.in_time_zone('Central Time (US & Canada)')
# => Thu, 26 Nov 2015 21:07:04 CST -06:00
ActiveRecord keeps track of time zones and stores all times as UTC in the database. In you're code you're retrieving that time from the database, but using a very non-standard way to output it which is bypassing ActiveRecord's time zone conversion.
you can try this
tweet_time = tweet.created_at.in_time_zone
tweet_time.strftime("#{tweet_time.day.ordinalize} %B, %Y")
to get the time in your server's timezone, which can be configured in config/application.rb
have a look here and here for more information on working with time zones
In order to get each user to see the time in their own zone you need to capture each user's timezone, then can do this in your action controller
around_filter :user_time_zone, :if => :current_user
def user_time_zone(&block)
Time.use_zone(current_user.time_zone, &block)
end
Related
I have a Rails 5 app with default Timezone settings (UTC).
I have a record that was created on Thu, 08 Aug 2019 02:12:56 UTC +00:00 but the app is being accessed by someone located at Central Time which means the record was created for them on Wed, 07 Aug 2019 20:12:56 CST -06:00; The app provides a filter and this user is trying to retrieve records from 08/07/2019 and is expecting that this record would be part f the resulting set, date filter is being passed as a Date object.
I have tried several forms to convert this Date object into a form that when passed down to the query it successfully returns the expected results. Note: filter.end_date is an instance of Date with value "Wed, 07 Aug 2019"
Query:
Record.where("records.created_at BETWEEN ? AND ?", filter.start_date.beginning_of_day, filter.end_date.end_of_day)
SQL:
Record Load (4.2ms) SELECT "records".* FROM "records" WHERE (records.created_at BETWEEN '2019-08-07 00:00:00' AND '2019-08-07 23:59:59.999999')
The only way that I have achieved this is when I set Timezone to central time and then pass the query:
This is working
Time.use_zone("Central America") { Record.where("records.created_at BETWEEN ? AND ?", date_filter.end_date.beginning_of_day, date_filter.end_date.end_of_day) }
SQL:
Record Load (4.2ms) SELECT "records".* FROM "records" WHERE (records.created_at BETWEEN '2019-08-07 06:00:00' AND '2019-08-08 05:59:59.999999')
I am wondering if there is a way to avoid the necessity to set a Timezone per user within the app and just do the correct conversion when passing down the date provided in the filter.
Unfortunately there isn't a simple way to ignore the timezone.
If you are getting the users timezone on sign up you could add something like Time.zone(current_user.timezone) if current_user into a before action callback or some other initial startup sequence. The has the disadvantage of making the value mostly static, ie if they are traveling and are in a different timezone your results will still be for your "home" timezone unless you add that as an option in their profile etc.
Alternatively you could set the current timezone dynamically using javascript:
window.addEventListener("submit", function(event) { // listen to all submit events
var form = event.target.closest('form');
var timezoneInput = form.querySelector('input[name=timezone]');
if (timezoneInput) {
timezoneInput.value = moment.tz.guess(); // assuming you are using a time library
}
});
And handle that in your controller:
def index
Time.use_zone(params[:timezone]) do
#records = Record.where("records.created_at BETWEEN ? AND ?", date_filter.end_date.beginning_of_day, date_filter.end_date.end_of_day)
end
end
For a database solution (which is commendable since you use timestamp with time zone), just set the timezone parameter for the database session to the current time zone of the user. Then everything should work as expected.
Whether or not you should store user's timezone in the DB depends on how often you will need to show times in a specific timezone. Best practice is to set your application to use the time zone where most of your users are. If you have not already, you probably want to set this application.rb for example:
config.time_zone = 'Eastern Time (US & Canada)'
For complete list you can run rake time:zones:all
Now make sure you always use zone. Anywhere in your application you will want to replace Time.now with Time.zone.now and Date.today with Date.current or better Time.zone.now.to_date.
If in your case you just want to filter records timestamps (i.e. created_at) I'm guessing you use some kind of datetime picker on the front end in a form etc. There is likely an option to add the users's browser's time offset. Here's an example of using jQuery datetimepicker. The additional format option is O
$(function() {
$("#datetimepicker").datetimepicker({
timepicker: true,
format: 'm/d/Y H:i O',
maxDate: '0'
});
})
Now you will have the datetime with timezone offset value which you can use in your database query. You'll be able to do something like this rough example.
start_time = Time.zone.parse(params[:start_time].to_s)
end_time = Time.zone.parse(params[:end_time].to_s)
Record.where('created_at > ? and created_at < ?', start_time, end_time)
I would also recommend checking out this Railscast, old but relevant.
I've looked at a few StackOverflow questions but none of them seem to crack the case.
My time for example is :
2012-04-19 08:42:00 +0200
This is what is inputed through the form. But everything is displayed relative to what timezone it is in. So because the computer works in UTC, this time after it is saved comes out as :
Wed, 18 Apr 2012 23:42:00 PDT -07:00
How do I keep the same time, but just change the zone?
I think the method Time.use_zone might help you. In the app I'm working one we wanted times to be interpreted according to the current user's time zone. Our User class was given a time_zone attribute, which is just a valid ActiveSupport::TimeZone string, and we created an around_filter as follows:
class ApplicationController < ActionController::Base
around_filter :activate_user_time_zone
def activate_user_time_zone
return yield unless current_user # nothing to do if no user logged in
return yield unless current_user.time_zone && ActiveSupport::TimeZone[current_user.time_zone] # nothing to do if no user zone or zone is incorrect
Time.use_zone(ActiveSupport::TimeZone[current_user.time_zone]) do
yield
end
end
end
Basically, this will execute the code in the controller action as if it was in the current user's time zone.
I have same requirement. Please check this and this link also
Alternatively
"2012-04-19 08:42:00 +0200".to_time.strftime("%c").to_datetime
I am using rails 3.0.5 and I have created_at and updated_at stored in UTC. Now I want to display the created_at time in users' timezone. I believe it is possible to pick user's timezone from the browser and then convert time to user's timezone.
I am sure rails will have a gem/plugin to take care of something like this. Is there something?
Rails by default converts every date into UTC before storing the value into the database. This means that, regardless the server timezone, you always have UTC dates in your database.
In order to convert the dates into your user's timezone you have basically two possibilities:
server-side approach
client-side approach
Server-side approach
If your site allows registration, you can store the user timezone as user preference. In the user table, store the user timezone. Then create a custom helper you can use to format any date/time into the proper timezone using the in_time_zone method.
> t = Time.current
# => Mon, 23 Dec 2013 18:25:55 UTC +00:00
> t.zone
# => "UTC"
> t.in_time_zone("CET")
# => Mon, 23 Dec 2013 19:25:55 CET +01:00
Your helper may looks like
def format_time(time, timezone)
time.in_time_zone(timezone)
end
I normally also like to output a standard format, using the I18n.l helper
def format_time(time, timezone)
I18n.l time.to_time.in_time_zone(timezone), format: :long
end
Client-side approach
If your site has no registration or you don't want to ask your users for their timezone or you simply want to use the user system timezone, then you can use JavaScript.
My suggestion is to create a custom helper that will print out every time in a proper way so that you can create a generic JavaScript function to convert the values.
def format_time(time, timezone)
time = time.to_time
content_tag(:span, I18n.l(time, format: :long), data: { timezone: timezone, time: time.iso8601 })
end
Now, create a JavaScript function that is performed on DOM load and that will select all the HTML tags with data-time attribute. Loop them and update the value inside the span tag with the proper time in the given timezone.
A simple jQuery example would be
$(function() {
$("span[data-time]").each(function() {
// get the value from data-time and format according to data-timezone
// write the content back into the span tag
});
});
I'm not posting the full code here, since there are plenty of JavaScript time formatters available with a simple search. Here's a few possible solutions
Convert date to another timezone in JavaScript
Convert date in local timezone using javascript
There is a nice gem by Basecamp called local_time for client side rendering - https://github.com/basecamp/local_time. It's great for applications where user is not signed in and it's caching friendly.
You can add this to your application controller to convert all times to the User's timezone:
class ApplicationController < ActionController::Base
around_filter :user_time_zone, :if => :current_user
def user_time_zone(&block)
Time.use_zone(current_user.timezone_name, &block)
end
end
You just have to capture the user's timezone
Assuming that the value you want displayed is coming from the database, :ie started_at and is (as is the default) stored in UTC.
If you have the user's timezone as an offset you can also localize the time by doing:
started_at.in_time_zone(-2)
=> Mon, 24 Feb 2014 23:07:56 GST -02:00
Which then can be munged in all sorts of way to get the parts you want:
started_at.in_time_zone(-2).yesterday
=> Sun, 23 Feb 2014 23:07:56 GST -02:00
started_at.in_time_zone(-2) + 3.days
=> Thu, 27 Feb 2014 23:07:56 GST -02:00
http://api.rubyonrails.org/classes/Time.html#method-c-use_zone
This is what you're looking for :
Time.use_zone(#user.timezone) do
blah blah blah
end
If you'd like to convert your date to a specific Timezone:
deadline.in_time_zone(time_zone)
Here deadline is a date.
In addition, you can find Universal time through your local machine Timezone plus local time and vice verse, like in Karachi - +05:00, you can simply add it to value in Universal time to find time in your time zone or get Universal time from your local time by subtraction of Timezone difference (05:00 in our case) from your local time
My jquery is a rusty, so it took me a little while to figure out how to implement the client-side approach of the accepted answer above.
Here's my solution:
HTML:
<span data-time="<%= last_message_at %>"> </span>
Jquery/Javascript:
<script type="text/javascript">
$(document).ready($(function() {
$("span[data-time]").each(function() {
var timestamp = $(this).attr("data-time");
var localeTimestamp = new Date(timestamp).toLocaleString();
$(this).html(localeTimestamp);
});
}));
</script>
You might want to take a look at these links in addition to the one Ben posted:
http://railscasts.com/episodes/106-time-zones-in-rails-2-1
I have the need to capture a time and time zone from users of a rails 2.3.8 app, but have been unable to think of a clean solution to create and parse the selections.
Ideally I would have a drop-down menus for the following:
hour (1-12)
minute (0-59)
AM/PM
Time Zone
Is there a gem/plugin that accomplishes what I am looking for? Will I need to store both the time and time zone in the database? What is the best strategy for storage?
I'll eventually need to spit these values out in UTC, but a user should be able to go back and review the time in the correct time zone.
You can get time zone selects with the appropriate methods:
time_zone_options_for_select
time_zone_select
Similarly, there's date_select for dates.
Storage:
If the timezone is specific to the user and doesn't change, then store their time zone in their user record and set it when you load the current_user. Rails will convert times to/from UTC and always store UTC in the database and do the automatic convert to that default timezone for you (including daylight savings!). Easiest way to do it.
use_zone(zone) lets you override the default zone for a block, so you can accept a form value and set it with that function and set your value in that block.
UPDATE: I wrote up some Rails timezone examples as a blog entry.
I personally used jQuery to change the display only:
ampm = ["12 AM","01 AM","02 AM","03 AM","04 AM","05 AM","06 AM","07 AM","08 AM","09 AM","10 AM","11 AM","12 PM","01 PM","12 PM","01 PM","02 PM","01 PM","02 PM","03 PM","04 PM","05 PM","06 PM","07 PM","08 PM","09 PM","10 PM","11 PM"]
j("#game_start_time_4i option").each(function(index,value){
j(value).html(ampm[index]);
});
Rails:
<%= datetime_select('game', 'start_time') %>
I've been pulling my hair out trying to work with Time in Rails. Basically I need to set all time output (core as well as ActiveSupport) to the server's local time -- no GMT, no UTC, etc. I've seen various posts relating to Time, but they usually involve someone's need to set it for each user. Mine isn't nearly as complex, I simply want consistency when I use any Time object. (I'd also appreciate not receiving errors every 3 seconds telling me that I can't convert a Fixnum (or some other type) to string -- it's Ruby, just do it!)
I also seem to be getting drastically different times for Time.new vs the ActiveSupport 1.second.ago. Anyway, does anyone have any quality suggestions as regards working with Time in Rails?
If you just want Time objects to be consistent, then why not stick with UTC? I just tried Time.new and 1.second.ago using script/console and I get the same output (give or take a second for typing the command). How are you doing it?
Somewhere in your initializers, define the format(s) that you want to use.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => '%m/%d/%Y %H:%M')
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:my_special_format => '%H:%M %p')
Then when you want to print a Time object, it works like the following example. Notice that the Time object in my console is already aware of my time zone. I'm not performing any magical transformations here.
>> t = Time.now
=> Wed Jul 15 18:47:33 -0500 2009
>> t.to_s
=> "07/15/2009 18:47"
>> t.to_s(:my_special_format)
=> "18:47 PM"
Calling Time#to_s uses the :default format, or you can pass in the name of the format you'd rather use like I did with :my_special_format.
You can see the various options for formatting a Time object here.
If u don't want to store each user time setting, the only solution is to use javascript time system because it work on user client time. For example i have an application that each time user try it, the app will create some example data with each data have a initial date value "today". At first time, it confuse me a lot because my host server is in australia and lot of user is on western part, so sometime the initial date value is not "today", it said "yesterday" because of different time region.
After a couple day of headache i finally take decision to JUST use javascript time system and include it in the link, so when user click the "try now" link it will also include today date value.
<% javascript_tag do -%>
var today = new Date();
$("trynow").href = "<%= new_invitation_path %>?today=" + today.toLocaleString();
<% end -%>
Add the following to config/environment.rb to handle time correctly and consistently all the time within the context of Rails. It's important to know that it will store your times to the database in UTC -- but this is what you want -- all the conversion is done automatically.
config.time_zone = 'Pacific Time (US & Canada)'
You can run rake time:zones:local from your Rails root directory to get a list of valid time zone strings in your area.
A quick addition to the DATE_FORMAT solution posted above. Your format can be a string, in which case it works as noted above by calling strftime, but you can also define the format as a lambda:
CoreExtensions::Time::Conversions::DATE_FORMATS.merge! :my_complex_format => lambda {|time|
# your code goes here
}