How to show user timezone in Rails 4 app? - ruby-on-rails

I want to display visiting user time and corresponding zone. For instance if a record was created at 12 noon GMT. If someone visiting in GMT+5 timezone then should show 5:00PM. Is it possible natively in Rails? I set config.active_record.default_timezone = :local but it shows info in UTC
Running rake timezones:local return more than one entry:
* UTC +05:00 *
Ekaterinburg
Islamabad
Karachi
Tashkent
I belong to Karachi

I am not sure if this can be done using just Ruby on Rails. I had to use JavaScript to change the corresponding time to client's local-timezone, which in my guess can not be done by server side code. This is what I'd done in my implementation -
First create a helper method:
def show_created_time(record)
time = record.created_at.strftime('%Y-%m-%dT%H:%M:%S')
content_tag(:span, time, "data-timer" => time, :class => 'record_created_time')
end
NOTE: Creating such helper method is not really mandatory. You can just create an HTML element(span, p, div, etc.), which has data-timer attribute with the time formatted as mentioned above.
Then call helper method:
<%= show_created_time(record) %>
Now create a JavaScript file, for example let's call it timer.js:
$(document).ready(function() {
$("[data-timer]").each(function() {
var cTime = $(this).attr('data-timer');
// createdTime for record in db:
var createdTime = new Date(cTime);
// Return the timezone difference between UTC and User Local Time
// var date = new Date();
var userTimeZoneDiff = createdTime.getTimezoneOffset();
// Since there are 60,000 milliseconds in a minute
var MS_PER_MINUTE = 60000;
// Record final created_at will depend on the final subtracted date as:
var recordCreatedDateTime = new Date(createdTime - userTimeZoneDiff * MS_PER_MINUTE);
$(this).text(recordCreatedDateTime);
});
});
Make sure you include this js in your application.js:
//= require timer.js
This is the project where I'd implemented it. Adding it here as a reference.

Related

Getting datetime as a string in current timezone

In the MySQL DB: '2020-04-19 22:00:00'(UTC). That's also what my endpoint returns since I set the connection option dataStrings:true.
On the client, after I fetch date:
const timezone = moment.tz.guess();
const convertedDate = moment(date)
.tz(timezone)
.format();
convertedDate then equals to "2020-04-19T22:00:00+02:00" (I'm in the UTC+2 zone).
I would like to get it in the format "2020-04-20T00:00:00" instead. How can I do that?
It looks like moment(date) believes your incoming date value is in local time, not UTC. So, your timezone conversion to local time changes nothing. You can tell moment it's UTC, like this:
const timezone = moment.tz.guess();
const convertedDate = moment.utc(date)
.tz(timezone)
.format();
You do not need moment-timezone for this. With Moment by itself you can use the utc function when parsing, and the local function to convert to the user's local time zone before formatting.
moment.utc('2020-04-19 22:00:00').local().format()
//=> "2020-04-20T00:00:00+02:00"
Also, the Moment team recommends using Moment for existing projects only. For new development, we recommend using Luxon instead:
luxon.DateTime.fromSQL('2020-04-19 22:00:00', {zone: 'utc'}).toLocal().toISO()
//=> "2020-04-20T00:00:00.000+02:00"

Rails/React flatpickr momentjs time conversion

I have a rails/react app (just one app) in which a user is allowed to schedule a meeting using Flatpickr .
I am passing down a datetime column called "scheduled_for" so that I can use it in my react component.
This is what my "componentDidMount()" looks like:
componentDidMount = () => {
new Flatpickr(this.refs.scheduledFor, {
minDate: new Date(),
enableTime: true,
altInput: true,
altFormat: "F j, Y h:i K",
onChange: function(dateObject) { console.log(dateObject) }
});
}
There is a "scheduled_for_future" validation method in my Meeting model to prevent the meeting from being scheduled in the past.
##app/models/meeting.rb
validate :scheduled_for_future
def scheduled_for_future
if scheduled_for.present? && scheduled_for < Time.zone.now
errors.add(:scheduled_for, "Must be in future")
end
end
I want a user to be able to pick a date & time in their local time zone and have it be saved as UTC (the Heroku default).
Everything works fine on local dev but if I try to pick a time & date in production, say for example 10 minutes from now, I get the "Must be in the future" error. (this obviously occurs because my Timezone is PT and 10 minutes from now is in the past according to the server's time)
It feels like this should be simple to fix. For the sake of UX I want the client to be able to pick the time in their own time zone and have convert to UTC before saving, but just can't figure it out.
I'm not very experienced with momentjs or flatpickr so it's likely that I'm missing something very important.
Please let me know if you need any more info/ something doesn't make sense.
thanks a million
You can use moment to format the datetime on the client side to include the timezone offset. currDate, in your case would be the datetime selected in your Flatpickr calendar.
var currDate = new Date();
console.log("Current Date: " + moment(currDate).format("YYYY-MM-DD HH:mm:ssZ"));
// Returns ...
Current Date: 2017-02-25 09:38:02-05:00
Then you can pass that to up rails, as a string, and convert it to UTC before persisting in the database
2.3.1 :003 > client_date = "2017-02-25 09:38:02-05:00"
=> "2017-02-25 09:38:02-05:00"
2.3.1 :004 > utc_date = Time.zone.parse(client_date).utc
=> 2017-02-25 14:38:02 UTC

Save task with a specific date and timezone

It's a rails project. I have a form where the user can schedule tasks. For the moment I get the timezone of the browser with :
var currentTime = new Date();
var timezone_offset = currentTime.getTimezoneOffset()
And then I use it in my controller. But the problem is :
How can I save the date to be scheduled to the right moment?
I thought the answer will be to parse the date from my form and then change the offset but it doesn't work:
> time = Time.parse("2015/11/30 12:00")
=> 2015-11-30 12:00:00 +0100
> time.change(offset: '+02:00')
=> 2015-11-30 12:00:00 +0100
Maybe the answer is to create a Time.new?
My heroku server is on UTC. Some users on CET.
You would be better off getting your form to submit the time in UTC format.
Using something like moment.js (http://momentjs.com/) you can get their local time in the browser and display the date/time in their local format, but submit the UTC counterpart to the server when submitting your form.

Automatic save after select

I noticed that invoking service method with simple select makes MyDomain.class Date field update in DB (clears time). However enclosing the method with #Transactional(readOnly = true) doesn't update the date value.
Why the value is saved into DB?
Here is the service method
#Transactional(readOnly = true)
Date getDate()
{
Date date = null
date = MyDomain.executeQuery("select min(s.startDate) from MyDomain s where ....)[0]
print "Result: " + date
}
The object will only get updated in the database if it is somehow changed after it brought into the hibernate session (selected) and before the the session is closed (usually at the end of the method). Take a look at your object, and see how it might be modified in some way that you did not intend.

Saving and Querying a Date in GORM Grails

I have a database table TableA, which has a column 'theDate' for which the datatype in the database is DATE.
When I save a java.util.Date to 'theDate' through GORM it appears to save just the date value when I look at the data in the table by just executing select * from TableA.
However, when I run a query such as:
select * from TableA where theDate = :myDate
No results are found, but if I run something like;
select * from TableA where theDate <= :myDate
I do get results.
So it's like the Time is relevant.
My question is how do I save a Date and query for a Date ignoring the Time completely and just matching on an exact Date only?
Thanks.
note: I have also tried using sql.Date and util.Calendar but to no success.
clearTime()
You can use clearTime() before saving and before comparing to zero out the time fields:
// zero the time when saving
new MyDomain(theDate: new Date().clearTime()).save()
// zero the target time before comparing
def now = new Date().clearTime()
MyDomain.findAll('SELECT * FROM MyDomain WHERE theDate = :myDate', [myDate: now])
joda-time plugin
An alternative would be to install the joda-time plugin and use the LocalDate type (which only holds date information, no times) instead of Date. For what it's worth, I don't think I've worked on a project with dates without using the Joda plugin. It's completely worth it.
If you have date saved without clearing you could retrieve it using range, as Jordan H. wrote but in more simple way.
def getResults(Date date) {
def from = date.clearTime()
def to = from + 1
def results = MyDomain.findAll("from MyDomain where dateCreated between :start and :stop" ,[start:from,stop:to])
}
Your question may be a duplicate. See Convert datetime in to date. But if anyone has more recent information, that would be great.
If that doesn't help, you can hack it the way I might, with a BETWEEN restriction, e.g.
def today = new Date()
def ymdFmt = new java.text.SimpleDateFormat("yyyy-MM-dd")
def dateYmd = ymdFmt.format(today)
def dateTimeFormat = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
def startDate = dateTimeFormat.parse("${dateYmd} 00:00:00");
def endDate = dateTimeFormat.parse("${dateYmd} 23:59:59");
MyDomain.findAll("from MyDomain where dateCreated between ? and ?", [startDate, endDate])
It's definitely not pretty, but it may get you where you're going.
I figured it out.
I used DateGroovyMethods.clearTime to clear the time value before saving.
You can use the DB type date not datetime , in the filed type

Resources