Skip to main content

Time Zone Utilities for Django Models

Project description

Latest Version Test Status Coverage Status Code Health Supported Python versions License Development Status

django-timezone-utils adds automatic time zone conversions and support utilities to Django.

Please note that this project is currently marked as a development status of Beta. Suggestions, constructive criticism, and feedback are certainly welcomed and appreciated.

Installation

django-timezone-utils works with Django 1.4, 1.5, 1.6 and 1.7.

To install it, simply:

$ pip install django-timezone-utils

Then add timezone_utils to your settings.INSTALLED_APPS:

INSTALLED_APPS = (
    ...
    'timezone_utils',
)

Example Usage

Imagine you have the models Location and LocationReportingPeriod:

from datetime import datetime
from django.db import models
from django.utils.translation import ugettext_lazy as _
from timezone_utils.fields import LinkedTZDateTimeField, TimeZoneField
from timezone_utils.choices import PRETTY_ALL_TIMEZONES_CHOICES

class Location(models.Model):
    company = models.ForeignKey(
        verbose_name='company',
        to='app_label.Company',
        related_name='locations',
    )
    name = models.CharField(
        verbose_name=_('name'),
        max_length=128,
    )
    timezone = TimeZoneField(
        verbose_name=_('timezone'),
        max_length=64,
        choices=PRETTY_ALL_TIMEZONES_CHOICES,
    )

    created = LinkedTZDateTimeField(
        verbose_name=_('created'),
        auto_now=True,
    )
    modified = LinkedTZDateTimeField(
        verbose_name=_('modified'),
        auto_now_add=True,
    )


def _get_reporting_period_timzone(obj):
    """Called as `obj` being the LocationReportingPeriodInstance.

    Note:
        populate_from=lambda: instance: instance.location.timezone is not valid.

    """
    return obj.location.timezone


class LocationReportingPeriod(models.Model)
    location = models.ForeignKey(
        verbose_name=_('location'),
        to='app_label.Location',
        related_name='reporting_periods',
    )
    start = LinkedTZDateTimeField(
        verbose_name=_('start'),
        # populate_from can also be a string value, provided that the string value
        #   is a field on the same model
        populate_from=_get_reporting_period_timzone,
        # Time override must be a datetime.time instance
        time_override=datetime.min.time(),
    )
    end = LinkedTZDateTimeField(
        verbose_name=_('end'),
        populate_from=_get_reporting_period_timzone,
        # Time override must be a datetime.time instance
        time_override=datetime.max.time(),
    )

    created = LinkedTZDateTimeField(
        verbose_name=_('created'),
        auto_now=True,
    )
    modified = LinkedTZDateTimeField(
        verbose_name=_('modified'),
        auto_now_add=True,
    )

    class Meta:
        ordering = ('location', '-start')

In the above code example, if we set the value of Location.timezone to US/Eastern, each time a LocationReportingPeriod is saved, it will save the LocationReportingPeriod.start as the date 12:00AM in US/Eastern time zone, and the LocationReportingPeriod.end as 11:59:59.9999999PM in the US/Eastern time zone.

So assuming the date was 2015-01-01, we would be saving the following values to the database:

# LocationReportingPeriod.start
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)

# LocationReportingPeriod.end
datetime.datetime(2015, 1, 1, 23, 59, 59, 999999, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)

For each location, let’s say that the client wants to see the start and end of the reporting period in that location’s time zone. One thing to remember is that just because you saved the LocationReportingPeriod start/end dates as a particular time zone, it does not mean that they will come that way from the database. For example, if your application’s settings.TIME_ZONE = 'UTC', you would get back:

print(period.start)
datetime.datetime(2015, 1, 1, 5, 0, tzinfo=<UTC>)

print(period.end)
datetime.datetime(2015, 1, 2, 4, 59, 59, 999999, tzinfo=<UTC>)

Here is how we would handle the displaying conversions from view to template:

# views.py:
# Django
from django.views.generic import ListView

# App
from app_label.models import LocationReportingPeriod


class LocationReportingPeriodListView(ListView):
    model = LocationReportingPeriod
    template_name = 'app_label/period_list.html'

    def get_queryset(self):
        """Retrieve the queryset and perform select_related on `location` since
        we will be using it in the template.

        """
        return super(
            LocationReportingPeriodListView,
            self
        ).get_queryset().select_related(
            'location'
        )
{% load tz %}
{% load i18n %}

{% block content %}
    <table>
        <thead>
            <tr>
                <th>{% trans "Location" %}</th>
                <th>{% trans "Start" %}</th>
                <th>{% trans "End" %}</th>
            </tr>
        </thead>
        <tdata>
            {% for period in object_list %}
                {# Activate the timezone for each location #}
                {% timezone period.location.timezone %}
                    <tr>
                        <td>{{ period.location.name }}</td>
                        <td>{{ period.start }}</td>
                        <td>{{ period.end }}</td>
                    </tr>
                {% endtimezone %}
            {% empty %}
                <tr>
                    <td colspan=3>{% trans "No periods to display." %}</td>
                </tr>
            {% endfor %}
        </tdata>
    </table>
{% endblock content %}

Inspiration

On multiple occasions, I have had the need to store time zone information to the one model, then base another model’s datetime on that time zone. If you have ever had to deal with this, you will know how complicated this can be.

I created these fields to ease the process of manipulating times based on another field’s or models timezone choice. Instead of having to remember to use Model.clean_fields, we can now create the models with the validation built into the model field.

Contributors

Changelog

  • 0.3 Code cleanup.

  • 0.2 Multiple bug fixes based on testing.

  • 0.1 Initial release.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-timezone-utils-0.4.tar.gz (7.8 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page