Flask: save object with timestamp - flask

I am doing flask app with flask-sqlalchemy.
I need to save object to db with timestamp provided by client.
For example:
2017-03-03T11:30:00+04:00
I need to save this timestamp with +4 time zone. Model declaration looks like this:
class Appointment(db.Model):
__tablename__ = 'appointments'
id = Column(db.Integer, primary_key=True)
start_time = Column(db.DateTime(timezone=True), nullable=False)
So here I have start_time attribute. I try to save my appointment object like this:
new_appointment = Appointment()
# at this line I convert string time to python datetime
appointment_time = datetime.strptime(string_datetime[:len(string_datetime) - 3] + string_datetime[len(string_datetime) - 2:],
'%Y-%m-%dT%H:%M:%S%z')
new_appointment.start_time = appointment_time
db.session.add(new_appointment)
db.session.commit()
When I check my db, my object is saved with my servers timezone (in this case as +6). I also tried to save without timezone (setting timezone=False), but again timestamp is saved as +6, but without indication of timezone. How do I force my object being saved with provided timzone?

Use pyTZ module
Take UTC time and convert it to desired timezone
from datetime import datetime
from pytz import *
eastern = timezone('US/Eastern')
loc_dt = eastern.localize(datetime(2017, 10, 3, 7, 27, 0))
print loc_dt
Also, you can go through this interesting article detailing problems with the datetime timezone handling

Related

Get list of users when it is midnight in their timezone

I need to send an email to my users when it is midnight in their timezone. I think my approach is fine, but I am having an issue with the Django F() function in my query.
Here is the query:
target_offset = 0 - timezone.now().hour
users_to_email = User.objects.exclude(
timezone__isnull=True
).annotate(
tz_offset=Value(
int (datetime.now(F('timezone')).strftime('%z')) / 100,
models.IntegerField()
)
).filter(
tz_offset=target_offset
)
I get this error:
int (datetime.now(F('timezone')).strftime('%z')) / 100,
File "/Users/leeloftiss/Desktop/daisy_coder/website/djangoEnv/lib/python3.8/site-packages/freezegun/api.py", line 386, in now
result = tz.fromutc(now.replace(tzinfo=tz)) + cls._tz_offset()
AttributeError: 'F' object has no attribute 'fromutc'
If I just call F('timezone') outside of the datetime.now() function, I get the actual value from the users TIMEZONE column.
So I guess I just need to know how I can get that value in this case where I need it in the datetime.now() function.
Note: I am using SQL, not Postgres
Django will filter at the database side, so using datetime.now(F('timezone')) will not work. What you can do is "prepare" the list of timezones with a given offset:
from pytz import all_timezones, timezone
from datetime import datetime, timedelta
from django.utils.timezone import now
now = datetime.utcnow()
day = timedelta(days=1)
target_offset = timedelta(hours=now.hour) % day
timezones = {
tz
for tz in all_timezones
if timezone(tz).utcoffset(now) % day == target_offset
}
This will generate a set timezones that contains the names of all timezones with the given offset. Next we can filter the users for that timezone:
users_to_email = User.objects.filter(
timezone__in=timezones
)

How to get timezone.today as default value?

I would like to set my DateField's default value to today's date in UTC timezone.
If I put datetime.now(timezone.utc).date(), it will be a fixed value that is called when the server restarts.
If I put timezone.now, it will return datetime object, not day object.
How can I get the today's date in UTC timezone? A function that is callable (without parentheses at the end)
Because if I put timezone.now().date, won't it be called when the server is restarted only, not every time the object is created/updated?
You seem to have mostly solved your own problem. Simple create "a function that is callable".
from datetime import datetime
def today_utc():
return datetime.utcnow().date()
class MyModel(models.Model):
my_field = models.DateField(default=today_utc)
In your settings.py file you must set this var:
USE_TZ = True
Now in your code if you want to get the correct utc now date you do so:
from django.utils import timezone
today = timezone.now().date()
If you have field in your model is DateField, example: my_field do so:
my_field = models.DateFiel(default=date.today)
If you have field called my_field set DateTimeField do so:
my_field = models.DateTimeField(default=timezone.now)
And read the doc

How to write the right time to DB according my timezone?

When I save dates in my database Django shows message about succesfull adding with the right time but in fact in the databese time is different
models.py:
from datetime import datetime
from django.db import models
class Teg1(models.Model):
created_at = models.DateTimeField(default=datetime.now, null=True, blank=True, editable=False)
num = models.FloatField(default=0.0, null=True, blank=True)
def __str__(self):
return str(self.num) + " || " + str(self.created_at)
settings.py
TIME_ZONE = 'Asia/Novosibirsk'
USE_TZ = True
The first sentence of Django's time zone documentation explains what you're seeing:
When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
So the database value is in UTC. The str() value is also in UTC, since you've manually converted the UTC datetime to a string without changing the timezone. The value interpreted by the form and displayed by the template is in your local time, since templates convert DateTimeFields to the current timezone.
If you want the str() value to use the local timezone you can use Django's localtime() function:
from django.utils.timezone import localtime
class Teg1(models.Model):
...
def __str__(self):
return str(self.num) + " || " + str(localtime(self.created_at))
If i'm not mistaken, you must be in Russia which is 7 hours ahead of UTC. So, the server that you use must be using the UTC time which in my opinion is a good thing.
I personally prefer to save times in UTC time in the data base and then convert them to the local time in the front end.
from django.utils import timezone
from datetime import datetime
teg1 = Teg1(created_at=datetime.now(tz=timezone.utc)
teg1.save()
However, if you want to save the datetime in your local time, you can use:
from datetime import datetime
import pytz
novosibirsk = pytz.timezone("Asia/Novosibirsk")
now = datetime.now(novosibirsk)
teg1 = Teg1(created_at=now)
teg1.save()
Have in mind that in your admin interface, you might see the time and date based on the timezone you select in your settings.py. However, the data saved in the database is still in UTC time.
instead of using
from datetime import datetime
class Teg1(models.Model):
created_at = models.DateTimeField(default=datetime.now)
use (this will use timezone that you have set in settings.py)
from django.utils import timezone
class Teg1(models.Model):
created_at = models.DateTimeField(default=timezone.localtime())

How to filter Django queryset by non field values

I need to query a model by their local time.
send_reminder_query = PersonModel.objects.filter('Is after 7pm thier localtime')
The person will have a zipcode field and I have a function to get the timezone by their zipcode. I'd also be willing to write a migration script to populate the timezone on the Person model but I still don't see how to make the query.
send_reminder_query = PersonModel.objects.filter(tzinfo='...', ?)
I was able to solve the problem by pre processing the data to store the person's timezone. Then using pytz I do this.
from django.utils import timezone
import pytz
valid_timezones = []
for tz in list_of_timezones:
local_time = now().astimezone(pytz.timezone(tz))
if 19 < local_time.hour < 20:
valid_timezones.append(tz)
reminders = Person.objects.filter(timezone__in=valid_timezones)

How to create a datetime aware for Django in UTC

I have configured my timezone in settings, as America/Guatemala and I have some datetime fields in my models, I'm using default=timezone.now but it is not saving my local hour, which is UTC-6:00, is saving it as UTC-00:00. I can't change that because now there is some important data stored in the database in that way.
I have problems retrieving the data in a queryset, I send a string in request.POST like this:
date='1/09/2016'
And I have tried this to configure my date for the query:
f=date.split('/')
if len(f)>1:
initialdate=datetime.datetime(int(f[2]),int(f[1]),int(f[0]),0,0,0,tzinfo=pytz.UTC)
finaldate=datetime.datetime(int(f[2]),int(f[1]),int(f[0]),23,59,59,tzinfo=pytz.UTC)
And this is my queryset:
sale=Sale.objects.filter(Q(pk=int(cod))|Q(sale_date__range=(initialdate,finaldate)))
But because of the 6 hours of difference between my saved data and my local date and time, if I store a Sale at 6:01pm of my local time, the saved data is stored as 00:01am of tomorrow. If i want to check all the sales that I made today, it doesn't show me the sales after 6pm, because they are saved in a different date.
I have another queries where I send two differents dates, and I use the same code, I just add time 0,0,0 to the first date, and 23,59,59 to the second date, but I have the same problem.
My question is, how can I add those six hours to the dates that I use as parameters? I need the datetime to be aware, and I can't change nothing in my model, I can change only the parameters that I'm sending to the queryset.
I am also have the question. and what I am did is that:
the whole day's date range is: 00:00:00 - 23:59:59, and this is the local datetime, but the datetime in datebase has beed transformed to utc, so I just sub the local datetime 6 hour, and you can do so;
import datetime
f=date.split('/')
if len(f)>1:
initialdate=datetime.datetime(int(f[2]),int(f[1]),int(f[0]),0,0,0,tzinfo=pytz.UTC)
finaldate=datetime.datetime(int(f[2]),int(f[1]),int(f[0]),23,59,59,tzinfo=pytz.UTC)
initialdate = initialdate - datetime.timedelta(hours=6)
finaldate = finaldate - datetime.timedelta(hours=6)
import pytz
from django.utils import timezone
initialdate = datetime.datetime.combine(date, datetime.time.min.replace(tzinfo=timezone.UTC())).astimezone(pytz.timezone('America/Guatemala'))
finaldate = datetime.datetime.combine(date, datetime.time.max.replace(tzinfo=timezone.UTC())))).astimezone(pytz.timezone('America/Guatemala'))
Edited to make date aware
To simply change the date six hours back you can use datetime's timedelta.
To change sale_date of objects within your query you simply need to do this:
import datetime
import pytz
for s in sale:
sale_date = sale_date - datetime.timedelta(hours=6)
sale_date = sale_date.replace(tzinfo=pytz.timezone("America/Guatemala"))
s.save()
To change sale_date of all Sale objects:
import datetime
import pytz
all_sales = Sale.objects.all()
for sale in all_sales:
sale_date = sale_date - datetime.timedelta(hours=6)
sale_date = sale_date.replace(tzinfo=pytz.timezone("America/Guatemala"))
sale.save()
Also, to parse strings containing time information use strptime:
import datetime
date='1/09/2016'
parsed_date = datetime.datetime.strptime(date, '%w/%m%Y')
More info on it here