9

I'm trying to make a proper date handling setup for my web app. I have a model that looks something like this

class Entity(models.Model):
    name = models.CharField(max_length=255)
    date = models.DateTimeField()

User can send a request to my DRF endpoint /api/v1/entity/ to get a list of such entities. Now there is a requirement that user should be able to request all Entity objects for a single day, which is determined by the date parameter. Dates are stored in UTC in the database while users of this app are not in a UTC timezone.

User can create an entity with the following date 2018-06-19T01:00:00+02:00, which is stored as 2018-06-18T23:00:00Z in the database. Now if I try to list all entities user has created for 2018-06-19 nothing's returned, but filtering by 2018-06-18 returns one entry.

This is the code setup I'm using: http://127.0.0.1:8000/api/v1/entity/?date=2018-06-18.

def get_queryset(self):
    user = self.request.user
    entities = Entity.objects.filter(owner=user)
    date = self.request.query_params.get('date')
    if date:
        entities = entities.filter(date__date=date)
    return entities

So in this case the appropriate date range would be 2018-06-18T23:00:00Z - 2018-06-19T23:00:00Z. What's the correct approach to fetch all entities for a single day (or a date range) in user's timezone?

Eugene
  • 9,868
  • 4
  • 33
  • 54
  • did you tried [this solution](https://stackoverflow.com/a/20379589/8283848) – JPG Jun 19 '18 at 17:13
  • @JerinPeterGeorge Thanks, but the solution you've linked is basically the next step I'll need to do. Before I can use a range lookup, I need to somehow construct that range relative to user's timezone though, which is what this question is about. – Eugene Jun 19 '18 at 20:14

2 Answers2

1

WhIf I understood correctly, You have to convert the local DateTime to equivalent UTC time and query to the DB. So I defined a simple timeconversion function below


import pytz
from datetime import datetime
def convert_to_UTC(local_tz,dt_1,dt_2):
    """
    local_tz : any possible timezone which supports pytz lib (https://stackoverflow.com/questions/13866926/is-there-a-list-of-pytz-timezones)
    dt_1 and dt_2 : local datetime in string in this format ->> '%Y-%m-%dT%H:%M:%S'

    return a list as ->> [utc_equivalent_of_dt_1_in_string,utc_equivalent_of_dt_2_in_string]
    """
    format='%Y-%m-%dT%H:%M:%S'
    pytz_local_tz = pytz.timezone(local_time_zone)
    dt_obj_from_date_time = datetime.strptime(dt_1,format)
    dt_obj_to_date_time = datetime.strptime(dt_2,format)
    return [pytz_local_tz.localize(dt_obj_from_date_time).astimezone(tz=pytz.utc).strftime(format),
            pytz_local_tz.localize(dt_obj_to_date_time).astimezone(tz=pytz.utc).strftime(format)]

To utilize this fuction, change your get_queryset() method as below,


def get_queryset(self):
    user = self.request.user
    entities = Entity.objects.filter(owner=user)
    date_from = self.request.query_params.get('date_from')
    date_to = self.request.query_params.get('date_to')

    if date_from and date_to:
        entities = entities.filter(date__range=convert_to_UTC('Asia/Kolkata', date_from, date_to))
    return entities

hence the url will be like, /api/v1/entity/?date_from=2018-06-18T23:00:00&date_to=2018-06-19T23:00:00

JPG
  • 69,639
  • 12
  • 88
  • 162
  • Thanks for your answer. `'Asia/Kolkata'` - is there a way to get user's local timezone dynamically or should I just design my APIs around sending this information? Are there any best practices? I don't want the client application to do any calculations on start and end dates, so in the end it should probably work like this: `/api/v1/entity/?date=2018-06-18&tz=+02:00`. – Eugene Jun 20 '18 at 09:46
  • As far as I knew, you have to detect the `timezone` from the client (angular/react etc). – JPG Jun 20 '18 at 10:14
  • In my opinion, If you could detect the timezone from the client and do the timezone conversion, that's the better thing – JPG Jun 20 '18 at 10:14
0
# simplified functions

def localize(usertimzone, date, type):
    """
    type = 'date', 'time', 'datetime'
    """
    from dateutil import parser
    date = parser.parse(date)
    if not date.tzinfo:
        # "if the POST request like `2018-06-19T01:00:00`"
        usertimzone = pytz.timezone(usertimzone)
        date = usertimzone.localize(date)

    # "if the POST request like `2018-06-19T01:00:00+02:00`" then just do this
    utc_date = date.astimezone(pytz.utc)
    return utc_date
#usages example
    def post(self, request, *args, **kwargs):
        alert_date = request.data.get('alert_date')
        if alert_date:
            request.data['alert_date'] = localize(request.user.timezone, alert_date, 'datetime')
ali al-karaawi
  • 782
  • 4
  • 18