Timezone Aware Views

Hi,

Im trying to setup a page which should show the time to match the timezone of the user.
I have setup a field in my user model to set the time zone.

If i print out the time in the user time zone within my view its shows the time in to correct zone, but when rendered in the template its back to the server time.

In my settings.py

TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True

I have this timezone filter

@register.filter(name='to_user_timezone')
def to_user_timezone(value, user_timezone):
    """
    Convert a datetime to the user's timezone
    """
    if value is None:
        return ''
    
    # Ensure the datetime is timezone-aware
    if timezone.is_naive(value):
        value = timezone.make_aware(value, timezone.utc)
    
    # Convert to user's timezone
    return value.astimezone(user_timezone)

I have a context_processor

def user_timezone(request):
    """
    Add user timezone to template context if user is authenticated
    """
    if request.user.is_authenticated:
        try:
            timezone = pytz.timezone(request.user.timezone)
        except pytz.exceptions.UnknownTimeZoneError:
            timezone = pytz.UTC
    else:
        timezone = pytz.UTC
    
    return {
        'user_timezone': timezone
    } 

In my view i have

post.scheduled_release = post.scheduled_release.astimezone(user_timezone)
 print(f'post.scheduled_release (thread {thread_id})', post.scheduled_release)

And the output of this does show scheduled_release time as the correct timezone based on my timezone, but as soon as i render this in my template its not converting.

{% if forloop.last and post.scheduled_release %}
    <div class="schedule-badge scheduled">
        <box-icon name='time' color='rgb(29, 155, 240)'></box-icon>
            <span>Scheduled for {{ post.scheduled_release|to_user_timezone:user_timezone|date:"M d, Y h:i A" }}</span>
     </div>
{% elif forloop.last %}

I just can’t see why the time between the view and what is rendering in the template is different?

It’s kind of hard to debug without additional info. You should add prints and check all the date values before and after converting in your template filter, view and template itself.

Another thing, if you are already sending the correctly converted user_timezone to the template, why not just use it instead of using the template filter?

I wondering here on why you’re trying to implement a feature that already exists on Django?
Such functionality can be achieved using the django.utils.timezone module.

I suggest that you turn this into a middleware, that will use the activate function

It would look something like:

from django.utils import timezone

class UserTimezoneMiddleware:
  def __init__(self, get_response):
    self.get_response = get_response

  def __call__(self, request):
    if not request.user or not request.user.is_authenticated:
      # Do nothing
      return self.get_response(request)

    timezone.activate(request.user.timezone)
    response = self.get_response(request)
    timezone.deactivate()
    return response

The above example is based on this section of the documentation: Selecting the current time zone

Then on your templates you can use the built-in localization tags and filters to get the value on the users timezone.

I suggest that you read the entire page of the documentation of the links that I’ve sent (It’s the same page for both of the links).

Hope that helps.

2 Likes