<commit_before>import hashlib
import pytz

from datetime import datetime
from django.contrib.auth import get_user_model
from django.db.models import ObjectDoesNotExist
from django.utils import timezone


def tzdt(*args, **kwargs):
    """Return a timezone-aware datetime object."""
    tz = kwargs.pop("tz", timezone.utc)
    dt = datetime(*args)
    return timezone.make_aware(dt, timezone=tz)


def to_utc(dt):
    """Convert the given naive/aware datetime to UTC."""
    if dt and timezone.is_aware(dt):
        dt = dt.astimezone(timezone.utc)
    elif dt:
        dt = timezone.make_aware(dt, timezone.utc)
    return dt


def to_localtime(dt, user):
    """Given a datetime object, convert it to the user's localtime.

    * dt - either a naive or aware datetime object. If naive, the user's
      timezone will be applied, making it aware. If aware, the datetime object
      will be converted to the user's timezone.
    * user - a User instance. They must have a UserProfile with a set timezone
      for this to work properly.

    """
    if dt and user.userprofile.timezone:
        tz = pytz.timezone(user.userprofile.timezone)
        if timezone.is_naive(dt):
            dt = timezone.make_aware(dt, timezone=tz)
        else:
            dt = dt.astimezone(tz)
    return dt


def local_now(user):
    """Return a `now` time in the user's local timezone."""
    now = timezone.now()
    return to_localtime(now, user)


def local_day_range(user, dt=None):
    """Return a tuple of the form (start, end), containing datetime objects
    in utc time that represents the range for the user's full day.

    * user: a User instance. The User whom we're considering.
    * dt: (optional). If given, this is the datetime around which the range<commit_msg>heavily cache access to user timezones<commit_after>import hashlib
import pytz

from datetime import datetime
from django.core.cache import cache
from django.contrib.auth import get_user_model
from django.db.models import ObjectDoesNotExist
from django.utils import timezone


def user_timezone(user, timeout=3600):
    """Return the user's timezone (as specified on their UserProfile).

    This function is heavily cached, because accessing `user.userprofile.timezone`
    spawns a database query, and when this is done for each item in a list
    of serialized objects, it may result in 100s of unnessary queries.

    Returns a string containing the user's timezone, e.g. "America/Chicago"

    """
    key = "usertz-{}".format(user.id)
    tz = cache.get(key)
    if tz is None:
        tz = user.userprofile.timezone
        cache.set(key, tz, timeout)
    return tz


def tzdt(*args, **kwargs):
    """Return a timezone-aware datetime object."""
    tz = kwargs.pop("tz", timezone.utc)
    dt = datetime(*args)
    return timezone.make_aware(dt, timezone=tz)


def to_utc(dt):
    """Convert the given naive/aware datetime to UTC."""
    if dt and timezone.is_aware(dt):
        dt = dt.astimezone(timezone.utc)
    elif dt:
        dt = timezone.make_aware(dt, timezone.utc)
    return dt


def to_localtime(dt, user):
    """Given a datetime object, convert it to the user's localtime.

    * dt - either a naive or aware datetime object. If naive, the user's
      timezone will be applied, making it aware. If aware, the datetime object
      will be converted to the user's timezone.
    * user - a User instance. They must have a UserProfile with a set timezone
      for this to work properly.

    """
    tz = user_timezone(user)
    if dt and tz:
        tz = pytz.timezone(tz)
        if timezone.is_naive(dt):
            dt = timezone.make_aware(dt, timezone=tz)
        else:
            dt = dt.astimezone(tz)
    return dt


def local_now(user):
    """Return a `now` time in the user's local timezone."""
    now = timezone.now()
    return to_localtime(now, user)


def local_day_range(user, dt=None):
    """Return a tuple of the form (start, end), containing datetime objects
    in utc time that represents the range for the user's full day.

    * user: a User instance. The User whom we're considering.
    * dt: (optional). If given, this is the datetime around which the range
{'fraction_non_alphanumeric': 0.09103977000479156, 'fraction_numerical': 0.0016770483948251077, 'mean_word_length': 3.061284046692607, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3499659', 'n_tokens_mistral': 1301, 'n_tokens_neox': 1241, 'n_words': 478}