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
Related
What is the best way with Rails to have a “time” attribute (selected by the user) which is supposed to always be displayed as the same “static” time value?
(Meaning: It should always show the same time, for example “14:00”, completely independently of any user’s time zone and/or DST value.)
Until now, I have tried the following setup:
In the MySQL database, I use a field of the type time (i.e. with the format: 14:00:00)
In the Rails view, I use the helper time_select (because it’s really handy)
However, it seams that with this approach, Rails’ ActiveRecord will treat this time value as a full-blown Ruby Time object, and therefor convert the value (14:00:00) to the default time zone (usually set to ‘UTC’) for storage and then convert it back to the user’s time zone, for the view. And if I’m not mistaken, this also means that the fluctuating DST value will make the displayed time value fluctuate throughout the year (and the same happens if the user moves to another time zone).
So what is the best way to manage a simple “static” time attribute with Rails?
If you don't want any time related functionality, why not save it as an string field. Since from your question description its evident that functionalities such as timezone doesn't effect your use case, so just make it a normal VARCHAR(8) and save the value as a string and parse it such as Time.now.strftime("%H:%M:%S") before saving it to the database, you can also write this logic inside your ActiveRecrd model class
def static_time=(value)
super(value.strftime("%H:%M:%S"))
end
you can somewhere in the code say model_object.static_time=Time.now and this will automatically parse it, if you want to get the time as a ruby object retaining the format you can simply do it defining a custom getter.
def static_time
current_time = Time.now
time_keys = [:hour, :min, :sec]
current_time.change(**Hash[time_keys.zip(super.split(":"))])
end
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.
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'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
I've run into a spot of bother with date formats in our Rails application.
I have a date field in our view which I want to be formatted as dd/mm/yy. This is how the user will expect to enter their dates, and the datepicker control uses this format.
However, Active Record seems to be expecting mm/dd/yy.
If I enter 01/03/2010, this gets put in as 03 January 2010.
If I enter 25/03/2010, this gets put in a null.
How do I get ActiveRecord to expect Her Majesties date format?
Rails' DateTime tries to detect the formatting automatically. It will detect the following formats: mm/dd/yy or dd-mm-yy or yyyy-mm-dd or yyyy/mm/dd. You could monkey-patch DateTime.parse, but I would rather move this issue to the View of your application.
I always recommend to use yyyy-mm-dd [hh:mm:ss] as a string representation for a date. Check the documentation of your DatePicker if it supports multiple date-formats.
The jQuery date-picker for example has this covered with dateFormat (for the data that is sent to the server, set this to yyyy-mm-dd) as well as altFormat (for the input the user sees, set this to dd/mm/yyyy).
Add a file called rails_defaults.rb to config\initializers directory; with following lines:
Date::DATE_FORMATS[:default] = '%d/%m/%Y'
Time::DATE_FORMATS[:default]= '%d/%m/%Y %H:%M:%S'
Restart the server and you are good to go.
class Date
class << self
def _parse_with_us_format(date, *args)
if date =~ %r{^(\d+)/(\d+)/(\d+)$}
_parse_without_us_format("#{$3.length == 2 ? "20#{$3}" : $3}-#{$1}-#{$2}", *args)
else
_parse_without_us_format(date, *args)
end
end
alias_method_chain :_parse, :us_format
end
end