I do have a postgres DB which contains a timestamp. This timestamp follow the ISO standard and looks like:
2017-08-28 20:14:45.684926+00
I have used ActiveRecord to access the DB from Ruby/Sinatra environment but the ts returned is ISO8601.
Is there a way to force the formatting to ISO and not ISO8601 ?
I would like to avoid parsing the data once received.
Right now, I am using the command:
class ApiResponse < ActiveRecord::Base
class << self
def send_query(user, params)
limit = params.include?('limit') ? params['limit'] : 50
where_params = {
userid: user,
allowed_intent: true
}
ApiResponse.select([:id, :ts, :userid, :intent, :response])
.where(where_params)
.order(ts: :desc)
.limit(limit).to_a
end
end
The ApiResponse class is used to access the DB through ActiveRecord and it works fine. but the format is not correct. it shows :
2017-08-29T05:58:44.488Z
instead of something like
2017-08-28 20:14:45.684926+00
This format ISO is the one in the db
Any idea how to get the timestamp ts correctly formated as I expect inside the ActiveRecord call?
I think there are some misunderstandings here:
Postgres uses ISO 8601 for output (the so called ISO, they are not different standards) https://www.postgresql.org/docs/9.6/static/datatype-datetime.html
ActiveRecord, I assume, returns an instance of DateTime class or something similar, and not a string in that format.
What you see is only a format used for displaying; you can use strftime to display the data in another format.
Related
Python3.7 :: Eve:
Looking for a way to format datetime for a domain field instead of setting a global datetime format?
I am trying to store yyyy-mm-dd format but I don't want to change how the _created and _update work. Am I better off just storing the string and handling date conversion as part of the front end render?
--edit--
Would it be expensive to use a validator like so?
import datetime
from dateutil.parser import parse
from eve.io.mongo import Validator
class MyValidator(Validator):
"""
Extend / override the built-in validation rules
"""
def _validate_is_yyyymmdd(self, is_yyyymmdd, field, value):
"""datetime format yyyy-mm-dd"""
print(is_yyyymmdd, field, value)
print(datetime.datetime.strptime(value, r'%Y-%m-%d'))
print(is_yyyymmdd and datetime.datetime.strptime(value, r'%Y-%m-%d'))
try:
if is_yyyymmdd and datetime.datetime.strptime(value, r'%Y-%m-%d'):
return
except:
self._error(field, "Value is not valid yyyy-mm-dd")
volumes.py
volumes = {
'schema':{
'record_date':{
'type':'string',
'is_yyyymmdd':True,
},
'volume_gallons':{'type':'float'},
}
SOLVED - update
DATE_FORMAT = r"%Y-%m-%dT%H:%M:%S.%f%Z%z"
Using the new date format the payload can be submitted with a timezone adjustment which is then stored in mongo as UTC.
{
"record_date":"2019-04-06T15:49:12.012UTC+0500",
"group":"horizontal",
"program_year":2016
}
python script to help convert to utc from a given time
from datetime import datetime
from dateutil import tz
from dateutil.parser import parse
def main():
"""
modified solution found here: https://stackoverflow.com/questions/4770297/convert-utc-datetime-string-to-local-datetime
"""
# set the time zones to convert from and to
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
from_zone = tz.gettz('America/Denver')
to_zone = tz.tzutc()
# This is the format SQL Server outputs date time
datetime_str = "2019-03-21 02:37:21"
# local = datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
local = parse(datetime_str)
# Tell the datetime object that it's in local time zone since
# datetime objects are 'naive' by default
local = local.replace(tzinfo=from_zone)
# Convert time zone
utc = local.astimezone(to_zone)
print(utc, local)
if __name__ == "__main__":
main()
It's generally a good idea to leave the database field in its most agnostic format.
Create a method to handle the conversion details.
If you're annoyed by the prospect of typing out the full date/time conversion every time you need to have a date output, you could create a method in your object which handles the conversion in the way you like.
That way, you can name it something easy to remember and save yourself the hassle of remembering the exact notation of the date / time format function.
You might even create a super-class for your objects, and add the method there, so that it would be inherited by all the objects which you'd like to have this behavior available.
So if you have BlogObject class as a super-class, and BlogPost inherits from BlogObject, and you're accessing a standard field which exists in all those objects, such as Date Created or Date Modified
class BlogObject(BaseClassName):
def pretty_create_dt():
self.beatify_date(self.update_dt)
def beautify_date(date):
#[your format code]
#then have the other class(es) inherit the method:
class BlogPost(BlogObject):
def get_xmas_date_before(days_before_xmas):
date_x_before_christmas = self.beautify_date(self.xmas_dt - days_before_xmas)
#pseudo-code-ish, just to get the point across
That way, when you call a function from your template, it's already formatted by the Model or Controller. You should avoid doing this sort of thing in the View, because it's bad MVC practice, especially for something you plan to utilize application-wide.
The reason that this is the generally-accepted pattern is that
it reduces repetitive code processing which is prone to human error
it is less work-intensive for future development
It maintains the "separation on duty" inherent in MVC framework
For example, if date format policy were to change, perhaps due to internationalization, then you'd want a solution which could be modified in one location (Model or Controller super-class), rather than in 1,000 view instances
I need to check if a DateTime is in a valid ISO8601 format.
Like: #iso8601?
I checked if ruby has a specific method but I haven't found any.
Currently I'm using date.iso8601 == date to check this.
Is there a good way to do this?
EDIT
Explaining my environment, and changing the scope of the question.
So, my project will use the js api FullCalendar, that's why i need a iso8601 string format. And I wondered what it's better or the correct way, save the date in the database in the correct format, or let the ActiveRecord do their job and manipulate it on just when I require the time information.
I dont' quite understand your question. I am assuming that you want to check a DateTime string if it's a valid ISO8601 format date string or not. Correct me if I am wrong.
You can use the class methods Time.iso8601 and Date.iso8601. In order to use them, you need to require the 'date' and 'time' library from standard library. One caveat is, as you can see from the name, they are not predicate method (without ?), instead they raise an ArgumentError with "invalid date" message if the wrong string is being input. So, you need to add a begin rescue block. eg.
require 'time'
t = Time.now
time_string = t.rfc2822 # intentionally set to wrong format string
begin
Time.iso8601(time_string)
puts "Yeah, correct string"
rescue ArgumentError => e
puts e
puts "Nah, wrong string"
end
This is more verbose than your "date.iso8601 == date". But I am posting it because don't understand how your method works. To me date.iso8601 == date would always be false. Am I wrong?
UPDATE
As an answer for your updated question, it's best you can just store the DateTime normally in the database and call iso8601 method to get the ISO8601 string. For creating DateTime, you can just use Time.iso8601 to parse the input string into DateTime object.
Ruby 3.1:
begin
invalid_date = '1970-01-32'
Date.iso8601(invalid_date)
rescue Date::Error
puts 'The date is invalid'
end
Rails 6.1:
Given the attribute has the type of date in the underlying database, you will not be able to set it to the invalid value. The following test (written in RSpec) passes:
specify 'Rails ignores invalid date' do
invalid_date = '1970-01-32'
invoice = Invoice.new
invoice.date = invalid_date
expect(invoice.date).to be nil
end
P.S. It's not fully clear whether you needed to make it work with Rails or without so there are solutions for both cases.
In my database I have a datetime stored as text as:
2013-12-14T02:35:00-07:00
I want to determine if this time is before the current time. I can do this in Ruby as follows:
if DateTime.parse('2013-12-14T02:35:00-07:00') < Time.now.to_datetime
#do something
end
This provides the expected results. However, I am accessing my database like this:
#list = Alert.where("time_alert < ? ", Time.now.to_datetime)
which is not working as time_alert is being stored as text and the comparison is off. I am not sure how to parse time_alert into a DateTime object like in the condition.
I have a problem with timezones and a postgresql db (Rails 3.0.4, PostgreSQL 9.0). I'm using a custom scope, in which I append some conditions, do joins etc.pp.
The problem is, that Rails don't convert the times to my local timezone.
Here is the code of the scope:
scope :with_activities_within_range_in_week, lambda{ |latMin, lngMin, latMax, lngMax, date|
select("date_trunc('day', activities.starting_at) as date,
count(activities.id) as value
") \
.within(latMin, lngMin, latMax, lngMax) \
.joins(:activities) \
.merge(Activity.in_week(date)) \
.group("date") \
.order("date")
}
The within method checks for ranges, the Activity.in_week scope returns this:
where("activities.starting_at >= ? AND activities.starting_at < ?", start, stop)
And in the select statement, I want to trunc the starting_at field to day.
I'm getting the following output for the date field:
2011-04-07 18:48:32
2011-04-02 14:07:20
2011-04-02 14:06:49
Unfortunately it doesn't include the timezone. When I try to access my model through the "normal" Rails functions, it works:
puts Activity.first.starting_at
-> 2011-04-15 06:47:55 +0200
What I'm doing wrong? Hope someone can help!
thx,
tux
Your database is storing your timestamps in UTC (as it should). ActiveRecord is making timezone adjustments when it knows that it has a timestamp; so, when you say this:
puts Activity.first.starting_at
AR knows that starting_at is a timestamp so it instantiates the timestamp as an ActiveSupport::TimeWithZone instance and that class applies the timezone adjustment. But, when you say this:
select("date_trunc('day', activities.starting_at) as date ...
AR isn't going to parse the SQL to figure out that date_trunc will return a timestamp, AR doesn't even know what date_trunc means. AR will just see a string coming out of the database and it will hand it to you without interpretation. You are free to feed that string to ActiveSupport::TimeWithZone (or your favorite time handling class) yourself: there's nothing wrong with telling AR things that it does not and cannot know on its own.
Rails is clever but it isn't magic.
I'm using rails 3 and I want to change the default date format of created_at and updated_at date when I save them into the db.
Default date format is %Y-%m-%d %H:%M:%S, but
I would like it to be %Y%m%d%H%M%S
Where should I change the format? I'm trying to create a time_formats.rb in the initializer folder.
Here is content:
class TimeFormats
Time::DATE_FORMATS[:db] = "%Y%m%d%H%M%S"
Time::DATE_FORMATS[:default] = "%Y%m%d%H%M%S"
end
This does not work. Is there someone who can help me? Thank you.
The time and date formats that you define in the initializers apply only when converting them to strings in Ruby. The formats you've defined would be used like Time.now.to_s(:default).
I don't recommend (nor am I aware of a way how) to change how dates are stored in the database. You should let the database store them as it does by default, then change how they are formatted in the views using .to_s(:format) as defined in the initializers.
Normally the database stores the timestamps as a specific timedate datatype rather than a formatted string. If you just want default timestamp printing style to be different, you might try overriding the created_at and updated_at methods:
def updated_at
super.strftime("%Y%m%d%H%M%S")
end