Say I have a model:
Mymodel(models.Model):
endtimestamp = models.DateTimeField(null=True)
I need a query to get all Mymodel objects with endstimestamp between today's midnight and yesterday midnight.
what have I tried:
today = datetime.datetime.today()
todays_midnigh = datetime.datetime.fromtimestamp(
today.strftime('%Y-%m-%d 00:00:00.0')
)
yesterday_midnight = todays_midnight - datetime.timedelta(days=1)
objs = Mymodel.objects.filter(endtimestamp__range(
yesterday_midnight, todays_midnight)
)
But this line todays_midnigh = datetime.datetime.fromtimestamp(today.strftime('%Y-%m-%d 00:00:00.0')) does not work, and I know there must be a much pythonic and clear way of achieving this.
assumming this from datetime import datetime as dt, do that:
today = dt.today()
todays_midnigh = dt.combine(today,dt.max.time())
or
todays_midnigh = dt.combine(today,dt.min.time())
as appropriate
Related
My model as these fields:
date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
I would like to annotate the queryset with start_datetime and end_datetime, like so:
class SessionQuerySet(models.QuerySet):
def with_datetimes(self):
return self.annotate(
start_datetime=ExpressionWrapper(
F('date') + F('start_time'),
output_field=models.DateTimeField()
),
end_datetime=ExpressionWrapper(
F('date') + F('end_time'),
output_field=models.DateTimeField()
),
)
However, the output field in the query results in a naive datetime:
>>> Session.objects.with_datetimes()[0].start_datetime
<<< datetime.datetime(2021, 9, 20, 17, 0)
I would like the dates to be localized within the query.
I tried wrapping the above expressions in django.db.models.functions.Cast(), with output_field=DateTimeField(), but it casts to UTC and not the local timezone.
Essentially what I need is the equivalent of the Postgres at time zone feature to convert a naive time to localtime. Is there a way to do that in Django?
Yes. You can use any Postgres function by writing a custom django database function.
Here is a custom django database function for the equivalent of the Postgres at time zone.
Django 4.0
from django.db.models import ExpressionWrapper, F, Func
from django.db import models
class AtTimeZone(Func):
function = 'AT TIME ZONE'
template = "%(expressions)s %(function)s '%(timezone)s'"
class SessionQuerySet(models.QuerySet):
def with_datetimes(self):
return self.annotate(
start_datetime=ExpressionWrapper(
F('date') + F('start_time'),
output_field=models.DateTimeField()
),
end_datetime=ExpressionWrapper(
F('date') + F('end_time'),
output_field=models.DateTimeField()
),
start_local_datetime=AtTimeZone(F('start_datetime', timezone='Europe/Berlin')
)
The above is for a model with the initial fields of: date, start_time, and end_time.
Here are the docs regarding django's custom database functions. https://docs.djangoproject.com/en/4.0/ref/models/expressions/#func-expressions.
As of the start of 2022, the docs don't provide many examples on how to create custom database functions. This should help.
I think you can use the library pytz (https://pypi.org/project/pytz/) to localize the naive datetime.
from datetime import datetime
from pytz import timezone
tz = timezone('Europe/Amsterdam')
naive_dt = datetime(2021, 9, 20, 17, 0)
localized_dt = tz.localize(naive_dt)
print(localized_dt)
I created a form with a DateField and a TimeField. When printing these from the routes.py I get these example values:
TimeField: 17:30:00
DateField: 2021-07-12
How can I turn those values into a datetime object, which I can submit to my Postgres Database? The required object format is DateTime. The Postgres Table is set up as TIMESTAMPTZ. I tried replace() to add the time to the DateField Data. But that does not work. I am new to datetimes and such, so please excuse my ignorance. How do the Timezones work? because I probably need to add the timezone somehow for the TIMESTAMPTZ, right?
Below is the minimal code for the functioning
forms.py
class CreateEvent(Form):
dt = DateField('DateTimePicker')
start_time = TimeField('Start')
submit = SubmitField("Submit")
models.py
class Events(db.Model):
__tablename__='events'
eid = db.Column(db.Integer, primary_key = True)
event_day = db.Column(db.DateTime, nullable=False)
start_time = db.Column(db.DateTime, nullable=False)
def __init__(self, event_day, start_time):
self.event_day = event_day
self.start_time = start_time
routes.py
if request.method == 'POST':
if form.validate() == False:
return render_template('create.html', form=form)
else:
event_day = form.dt.data
start_time = form.start_time.data
print(start_time)
print(event_day)
start_time = event_day.replace(time=start_time)
newevent = Events(event_day, start_time)
db.session.add(newevent)
db.session.commit()
return "Success"
Just in case, here is the Postgres Create Statement for the table:
CREATE TABLE events (
eid serial PRIMARY KEY NOT NULL,
event_day TIMESTAMPTZ NOT NULL,
start_time TIMESTAMPTZ NOT NULL,
);
pip install dateutil:
https://dateutil.readthedocs.io/en/stable/
then:
from dateutil.parser import parse
TimeField = "17:30:00"
DateField = "2021-07-12"
parse(DateField + ' ' + TimeField)
datetime.datetime(2021, 7, 12, 17, 30)
You probably don't need to add a timezone, Postgres will use the timezone setting for the server. Postgres does not actually store the timezone in a timestamptz field. It just uses the timezone setting to rotate the value to a UTC time for storage.
Not tested but as an example, and you may have to install pytz from PIP (if not already present on your system). Python knows so-called naive datetime objects (that have no timezone context), and time zone-aware datetime objects.
So the goal is to build a string with the date and the time, then parse it into a datetime object. Next, we add the desired time zone (in this example it is assumed US/Pacific so change as appropriate).
import datetime
import pytz
TimeField = "17:30:00"
DateField = "2021-07-12"
current_tz = pytz.timezone('US/Pacific')
# parse string into naive datetime object
dt_naive = datetime.datetime.strptime(f"{DateField} {TimeField}", "%Y-%m-%d %H:%M:%S")
# convert to time zone-aware datetime
dt_aware = current_tz.localize(dt_naive)
How can I get the local date and time in Django?
I tried this solution. But it works only for the date now.
from django.utils import timezone
now = timezone.localtime(timezone.now()) #OK
date = timezone.localtime(timezone.date()) #Error
time = timezone.localtime(timezone.time()) #Error
Preferably in the same format as for datetime
now = datetime.datetime.now() #'2020-04-28 17:57:34.120383'
date = datetime.datetime.now().date() #'2020-04-28
time = datetime.datetime.now().time() #18:12:08.987472
Extract the date and time from the local instant object.
from django.utils import timezone
now = timezone.localtime(timezone.now())
date = now.date()
time = now.time()
I am trying to all the record for a certain day with the following:
entered_at = request.session['entered_at']
entered_at = datetime.strptime(entered_at, "%m-%d-%Y")
day_start = entered_at.replace(hour=00, minute=00)
day_end = entered_at.replace(hour=23, minute=59)
entries = Entry.objects.filter(
customer=customer,
entered_at__lt=day_end,
entered_at__gte=day_start
)
When I do this I get the following warning in my console:
DateTimeField received a naive datetime while time zone support is
active.
I know I can add something like: , day_start = entered_at.replace(hour=00, minute=00, tzinfo=<UTC>)
however, this will not make the range from midnight to 11:59pm relative to the users timezone if I use UTC.
How can I express a full day relative to the users timezone?
I believe you want something like this, using the pytz library. See How to make an unaware datetime timezone aware in python:
import pytz
entered_at = request.session['entered_at']
entered_at = datetime.strptime(entered_at, "%m-%d-%Y")
day_start = entered_at.replace(hour=00, minute=00)
day_end = entered_at.replace(hour=23, minute=59)
timezone = pytz.timezone("America/Los_Angeles")
day_start = timezone.localize(day_start)
day_end = timezone.localize(day_end)
entries = Entry.objects.filter(customer=customer,
entered_at__lt=day_end,
entered_at__gte=day_start)
How does one set the date on the CalendarDatePicker. i.e. it defaults to current date and I want to display it with another date which I will set from my controller.
I am displaying the CalendarDatePicker widget in a TableForm from tw.form. I have looked at this for a few hours and can't work out how to do this so any pointers greatly appreciated.
import tw.forms as twf
form = twf.TableForm('dateSel', action='changeDate', children=[
twf.CalendarDatePicker('StartDate', date_format = "%d/%m/%Y"),
twf.CalendarDatePicker('EndDate', date_format = "%d/%m/%Y" )
])
I don't have a copy of twforms laying around, but based on their sample code, it looks like you might want to do something like:
from datetime import datetime
start = twf.CalendarDatePicker('StartDate', date_format = "%d/%m/%Y")
start.default = datetime.now() # or any valid datetime object
end = twf.CalendarDatePicker('EndDate', date_format = "%d/%m/%Y" )
start.default = datetime.now() # or any valid datetime object
form = twf.TableForm('dateSel', action='changeDate', children=[start, end])