diff --git a/gracedb/buildVOEvent.py b/gracedb/buildVOEvent.py index c62b04f6d3134d3c0eb11bf6a9933b5e0ed2639c..9e3e3bdbc91eabd83825e429918d53b6605ac921 100755 --- a/gracedb/buildVOEvent.py +++ b/gracedb/buildVOEvent.py @@ -21,7 +21,7 @@ from VOEventLib.VOEvent import Time, TimeInstant # XXX ER2.utils. utils is in project directory. ugh. from utils import gpsToUtc -from datetime import datetime +from django.utils import timezone from django.conf import settings from django.core.urlresolvers import reverse from models import CoincInspiralEvent, MultiBurstEvent @@ -104,7 +104,7 @@ def buildVOEvent(event, serial_number, voevent_type, request=None, skymap_filena a.add_contactName("LIGO Scientific Collaboration and Virgo Collaboration") #a.add_contactEmail("postmaster@ligo.org") w.set_Author(a) - w.set_Date(datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")) + w.set_Date(timezone.now().strftime("%Y-%m-%dT%H:%M:%S")) v.set_Who(w) ############ Why ############################ diff --git a/gracedb/management/commands/create_test_perms_fixtures.py b/gracedb/management/commands/create_test_perms_fixtures.py index 08f6ef28397f44c5c49963730b621c2ce45deb0d..398df14f1c815f73603cd4580e90203c45cafd08 100644 --- a/gracedb/management/commands/create_test_perms_fixtures.py +++ b/gracedb/management/commands/create_test_perms_fixtures.py @@ -1,7 +1,6 @@ import os import json from StringIO import StringIO -from datetime import datetime from gracedb.models import GrbEvent, Tag, Event from gracedb.models import MultiBurstEvent @@ -13,6 +12,8 @@ from django.core.management import call_command from django.core.management.base import NoArgsCommand from django.conf import settings +from django.utils import timezone + #------------------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------------------ # Parameters @@ -143,7 +144,7 @@ def get_user_field_dicts(user_info): user_dict['user_permissions'] = [] user_dict['password'] = 'X' - now = datetime.now().isoformat().split('.')[0] + now = timezone.now().isoformat().split('.')[0] user_dict['last_login'] = now user_dict['date_joined'] = now diff --git a/gracedb/management/commands/make_histograms.py b/gracedb/management/commands/make_histograms.py index 057e6348ec12e56e122a3f5333cc2ca2887a437f..d36307de4797a0b8832553c8eb35605abed20ec1 100644 --- a/gracedb/management/commands/make_histograms.py +++ b/gracedb/management/commands/make_histograms.py @@ -10,8 +10,8 @@ import numpy from gracedb.models import Event, Pipeline import os -from datetime import datetime, timedelta - +from datetime import timedelta +from django.utils import timezone DEST_DIR = settings.LATENCY_REPORT_DEST_DIR MAX_X = settings.LATENCY_MAXIMUM_CHARTED @@ -27,7 +27,7 @@ class Command(NoArgsCommand): def handle_noargs(self, **options): - now = datetime.now() + now = timezone.now() start_day = now - timedelta(1) start_week = now - timedelta(7) @@ -71,7 +71,7 @@ class Command(NoArgsCommand): def writeIndex(notes, fname): - createdDate = str(datetime.now()) + createdDate = str(timezone.now()) maxx = MAX_X table = '<table border="1" bgcolor="white">' diff --git a/gracedb/models.py b/gracedb/models.py index c2343ddf8f12149e7fb13154d1832fdf96da0b60..d328d5761f0c5014d6f6ce572caf1fec3ad03a96 100644 --- a/gracedb/models.py +++ b/gracedb/models.py @@ -26,7 +26,8 @@ import json from utils import posixToGpsTime from django.conf import settings -import pytz, time +import pytz +import calendar from cStringIO import StringIO from hashlib import sha1 @@ -175,7 +176,8 @@ class Event(models.Model): dt = self.created if not dt.tzinfo: dt = SERVER_TZ.localize(dt) - posix_time = time.mktime(dt.timetuple()) + dt = dt.astimezone(pytz.utc) + posix_time = calendar.timegm(dt.timetuple()) gps_time = int(posixToGpsTime(posix_time)) return gps_time - self.gpstime diff --git a/gracedb/nltime.py b/gracedb/nltime.py index b6ce811d085da39bb04f0a51404f52d777da17fe..0cbcc7338d86dce803530caa5f612c5b403c2427 100755 --- a/gracedb/nltime.py +++ b/gracedb/nltime.py @@ -6,6 +6,12 @@ from datetime import datetime, timedelta from pyparsing import * import calendar +from django.utils import timezone +import pytz + +# Note, since the 'now' comes from django.utils.timezone, it will be in UTC. +# We should therefore localize all of the datetime objects generated here to +# UTC. # string conversion parse actions def convertToTimedelta(toks): @@ -25,7 +31,7 @@ def convertToTimedelta(toks): toks["timeOffset"] = td def convertToDay(toks): - now = datetime.now() + now = timezone.now() if "wkdayRef" in toks: todaynum = now.weekday() daynames = [n.lower() for n in calendar.day_name] @@ -34,23 +40,23 @@ def convertToDay(toks): daydiff = (nameddaynum + 7 - todaynum) % 7 else: daydiff = -((todaynum + 7 - nameddaynum) % 7) - toks["absTime"] = datetime(now.year, now.month, now.day)+timedelta(daydiff) + toks["absTime"] = pytz.utc.localize(datetime(now.year, now.month, now.day)+timedelta(daydiff)) else: name = toks.name.lower() toks["absTime"] = { "now" : now, - "today" : datetime(now.year, now.month, now.day), - "yesterday" : datetime(now.year, now.month, now.day)+timedelta(-1), - "tomorrow" : datetime(now.year, now.month, now.day)+timedelta(+1), + "today" : pytz.utc.localize(datetime(now.year, now.month, now.day)), + "yesterday" : pytz.utc.localize(datetime(now.year, now.month, now.day)+timedelta(-1)), + "tomorrow" : pytz.utc.localize(datetime(now.year, now.month, now.day)+timedelta(+1)), }[name] def convertToAbsTime(toks): - now = datetime.now() + now = timezone.now() if "dayRef" in toks: day = toks.dayRef.absTime - day = datetime(day.year, day.month, day.day) + day = pytz.utc.localize(datetime(day.year, day.month, day.day)) else: - day = datetime(now.year, now.month, now.day) + day = pytz.utc.localize(datetime(now.year, now.month, now.day)) if "timeOfDay" in toks: if isinstance(toks.timeOfDay,basestring): timeOfDay = { @@ -78,7 +84,7 @@ def calculateTime(toks): if toks.absTime: absTime = toks.absTime else: - absTime = datetime.now() + absTime = timezone.now() if toks.timeOffset: absTime += toks.timeOffset toks["calculatedTime"] = absTime @@ -181,7 +187,7 @@ if __name__ == "__main__": 2009/12/22 12:13:14""".splitlines() for t in tests: - print t, "(relative to %s)" % datetime.now() + print t, "(relative to %s)" % timezone.now() res = nlTimeExpression.parseString(t) if "calculatedTime" in res: print res.calculatedTime diff --git a/gracedb/query.py b/gracedb/query.py index 5749f7515092bcad5c7c4c98364aef0b4169fbae..f5c00fe9ea38c7a5195951e0148c177ce6b9c0d5 100644 --- a/gracedb/query.py +++ b/gracedb/query.py @@ -20,6 +20,7 @@ import datetime import models from django.db.models import Q from django.db.models.query import QuerySet +import pytz from pyparsing import \ Word, nums, Literal, CaselessLiteral, delimitedList, Suppress, QuotedString, \ @@ -170,7 +171,7 @@ nltimeRange = nltime + Suppress("..") + nltime def doTime(tok): x = datetime.datetime(*(map(int, tok))) - return x + return pytz.utc.localize(x) dash = Suppress('-') colon = Suppress(':') diff --git a/gracedb/reports.py b/gracedb/reports.py index effd01e7bcf9ea0ab7009a0ad512a393e4908277..9725cc5e4b6e67d6088d1fc0d1272e70649bd27b 100644 --- a/gracedb/reports.py +++ b/gracedb/reports.py @@ -24,9 +24,10 @@ import matplotlib.pyplot as plot import StringIO import base64 import sys -import time -from datetime import datetime, timedelta +import calendar +from datetime import timedelta from utils import posixToGpsTime +from django.utils import timezone @internal_user_required def histo(request): @@ -76,7 +77,7 @@ def histo(request): def rate_data(): # XXX there is a better way -- should be using group_by or something. # WAAY too many queries (~300) going on here. - now = datetime.now() + now = timezone.now() day = timedelta(1) ts_min = now - 60 * day @@ -149,11 +150,12 @@ def cbc_report(request, format=""): if request.method == "GET": if "query" not in request.GET: # Use default query. LowMass events from the past week. - t_high = datetime.now() + t_high = timezone.now() dt = timedelta(days=7) t_low = t_high - dt - t_high = posixToGpsTime(time.mktime(t_high.timetuple())) - t_low = posixToGpsTime(time.mktime(t_low.timetuple())) + # Now the times are in UTC. So we can't use mktime to get posix time. + t_high = posixToGpsTime(calendar.timegm(t_high.timetuple())) + t_low = posixToGpsTime(calendar.timegm(t_low.timetuple())) query = 'CBC LowMass %d .. %d' % (t_low, t_high) rawquery = query form = SimpleSearchForm({'query': query}) diff --git a/gracedb/templatetags/flash.py b/gracedb/templatetags/flash.py index 4093b2db2268868268434e6031ac7e098b6f229d..f0c827d3bbec716f8b7098d14dd5d6d39e337cb1 100644 --- a/gracedb/templatetags/flash.py +++ b/gracedb/templatetags/flash.py @@ -34,7 +34,8 @@ django.contrib.sessions.middleware.SessionMiddleware from django import template from django.template import resolve_variable, Context -import datetime +from datetime import timedelta +from django.utils import timezone from django.template.loader import render_to_string from django.contrib.sessions.models import Session from django.conf import settings @@ -59,7 +60,7 @@ def session_clear(session): # Save changes to session if(session.session_key): Session.objects.save(session.session_key, session._session, - datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)) + timezone.now() + timedelta(seconds=settings.SESSION_COOKIE_AGE)) class RunFlashBlockNode(template.Node): diff --git a/gracedb/templatetags/timeutil.py b/gracedb/templatetags/timeutil.py index 3eaee57bdd904bd6b91fb6b972878dee564ffc78..c2c9248c9b8a7acc1158fcbb1afca9f6a5a7b02d 100644 --- a/gracedb/templatetags/timeutil.py +++ b/gracedb/templatetags/timeutil.py @@ -49,17 +49,20 @@ def get_multitime_value(t, label, autoescape, format): dt = t if not dt.tzinfo: dt = SERVER_TZ.localize(dt) - #dt = dt.astimezone(pytz.utc) + # XXX in order for mktime to give correct results, the time must be + # in the server's timezone. + dt = dt.astimezone(SERVER_TZ) posix_time = time.mktime(dt.timetuple()) gps_time = int(posixToGpsTime(posix_time)) elif isinstance(t, int) or isinstance(t, long): gps_time = t dt = gpsToUtc(t) - posix_time = time.mktime(dt.timetuple()) + # Note: must convert to server timezone before calling mktime + posix_time = time.mktime(dt.astimezone(SERVER_TZ).timetuple()) elif isinstance(t, decimal.Decimal): gps_time = float(t) dt = gpsToUtc(t) - posix_time = time.mktime(dt.timetuple()) + posix_time = time.mktime(dt.astimezone(SERVER_TZ).timetuple()) else: return "N/A" return '<time utc="%s" gps="%s" llo="%s" lho="%s" virgo="%s" jsparsable="%s"%s>%s</time>' % \ @@ -147,6 +150,8 @@ def gpsdate_tz(gpstime, label="utc"): def gpstime(dt): if not dt.tzinfo: dt = SERVER_TZ.localize(dt) + # convert to SERVER_TZ if not already + dt = dt.astimezone(SERVER_TZ) posix_time = time.mktime(dt.timetuple()) gps_time = int(posixToGpsTime(posix_time)) return gps_time @@ -160,17 +165,17 @@ def timeSelections(t): dt = t if not dt.tzinfo: dt = SERVER_TZ.localize(dt) - #dt = dt.astimezone(pytz.utc) + dt = dt.astimezone(SERVER_TZ) posix_time = time.mktime(dt.timetuple()) gps_time = int(posixToGpsTime(posix_time)) elif isinstance(t, int) or isinstance(t, long): gps_time = t dt = gpsToUtc(t) - posix_time = time.mktime(dt.timetuple()) + posix_time = time.mktime(dt.astimezone(SERVER_TZ).timetuple()) elif isinstance(t, decimal.Decimal): gps_time = float(t) dt = gpsToUtc(t) - posix_time = time.mktime(dt.timetuple()) + posix_time = time.mktime(dt.astimezone(SERVER_TZ).timetuple()) else: raise ValueError("time must be type int, long or datetime, not '%s'" % type(t)) diff --git a/gracedb/view_logic.py b/gracedb/view_logic.py index 8c6ebfe1d457b1c40f8631e278c7b024743f6aa7..d529096c4a0e7dee6a2de494c204d0cf8b8b7a9b 100644 --- a/gracedb/view_logic.py +++ b/gracedb/view_logic.py @@ -31,6 +31,7 @@ import datetime #import dateutil from dateutil import parser import logging +import pytz def _createEventFromForm(request, form): saved = False @@ -234,9 +235,14 @@ def get_performance_info(): # Now parse the log file dateformat = '%Y-%m-%dT%H:%M:%S' # ISO format. I think. - # Lookback time is 3 days. - dt_now = datetime.datetime.now() + # Lookback time is 3 days. These are in UTC. + dt_now = timezone.now() dt_min = dt_now + datetime.timedelta(days=-3) + + # Convert to local time + SERVER_TZ = pytz.timezone(settings.TIME_ZONE) + dt_now = dt_now.astimezone(SERVER_TZ) + dt_min = dt_min.astimezone(SERVER_TZ) totals_by_status = {} totals_by_method = {} @@ -245,6 +251,8 @@ def get_performance_info(): datestring = line[0:len('YYYY-MM-DDTHH:MM:SS')] # Check the date to see whether it's fresh enough dt = datetime.datetime.strptime(datestring, dateformat) + # Localize so we can compare with aware datetimes + dt = SERVER_TZ.localize(dt) if dt > dt_min: # Get rid of the datestring and the final colon. line = line[len(datestring)+1:] @@ -539,6 +547,8 @@ def create_emobservation(request, event): try: start_time = parser.parse(start_time) + if not start_time.tzinfo: + start_time = pytz.utc.localize(start_time) except: raise ValueError('Could not parse start time list element %d of %s'%(i, startTimeRealList)) diff --git a/gracedb/view_utils.py b/gracedb/view_utils.py index a8ee6d9b013815450864796a54d4551a220be1ef..1b0f0c9b0855f6e241ff7b10b917c0fe9f96e8f9 100644 --- a/gracedb/view_utils.py +++ b/gracedb/view_utils.py @@ -29,8 +29,8 @@ GRACEDB_DATA_DIR = settings.GRACEDB_DATA_DIR import json import pytz -from datetime import datetime -from time import mktime +import time +import calendar SERVER_TZ = pytz.timezone(settings.TIME_ZONE) def timeToUTC(dt): @@ -483,12 +483,13 @@ def skymapViewerEMObservationToDict(emo, request=None): for t in startTimeList: time_count += 1 # timetuple throws away the microsecond for some reason - avg_time_s += mktime(t.timetuple()) + float(t.microsecond)/1e6 + # Note: the datetimes in the startTimeList are in UTC. + avg_time_s += calendar.timegm(t.timetuple()) + float(t.microsecond)/1e6 if time_count > 0: avg_time_s /= time_count - avg_time = datetime.fromtimestamp(avg_time_s) + avg_time = time.gmtime(avg_time_s) avg_time_string = avg_time.strftime("%a %b %d %H:%M:%S UTC %Y") return { diff --git a/ligoauth/middleware/auth.py b/ligoauth/middleware/auth.py index 47c524a2e40ee47e180b3e007bafd37b4946ef19..dcfe60a607a3344532cfd1c2a4661d50e4786802 100644 --- a/ligoauth/middleware/auth.py +++ b/ligoauth/middleware/auth.py @@ -14,7 +14,7 @@ from django.http import HttpResponse, HttpResponseForbidden proxyPattern = re.compile(r'^(.*?)(/CN=\d+)*$') -from datetime import datetime +from django.utils import timezone from base64 import b64decode import json @@ -153,7 +153,7 @@ class LigoAuthMiddleware: # actually use 'date_joined' for it's intended purpose. # check: is now greater than date_joined + time_delta? if user: - if datetime.now() > user.date_joined + settings.PASSWORD_EXPIRATION_TIME: + if timezone.now() > user.date_joined + settings.PASSWORD_EXPIRATION_TIME: msg = "Your password has expired. Please log in and request another." return HttpResponseForbidden(json.dumps({'error': msg})) diff --git a/settings/default.py b/settings/default.py index 2bff96c7cbf48c79a9e4d361ee03d15195c4437b..f07e76166e5ebf54d888135ec6c8b961e5b6df84 100644 --- a/settings/default.py +++ b/settings/default.py @@ -1,5 +1,13 @@ from settings_secret import * +USE_TZ = True + +import warnings +warnings.filterwarnings( + 'error', r"DateTimeField .* received a naive datetime", + RuntimeWarning, r'django\.db\.models\.fields') + + # Suitable for production ALLOWED_HOSTS = ['*'] diff --git a/templates/gracedb/query_help_frag.html b/templates/gracedb/query_help_frag.html index 756ffc37ffad7645a6cb983cc792db3b616121f7..e4ef40965a5af7627b6c46a4dc7d342967199e6f 100644 --- a/templates/gracedb/query_help_frag.html +++ b/templates/gracedb/query_help_frag.html @@ -24,7 +24,7 @@ <h4>By Creation Time</h4> Creation time may be indicated by an exact time or a range. Date/times are - in the format <code>2009-10-20 13:00:00</code>. If the time is omitted, it + in the format <code>2009-10-20 13:00:00</code> (must be UTC). If the time is omitted, it is assumed to be <code>00:00:00</code>. Dates may also consist of certain variants of English-like phrases. The <code>created:</code> keyword is (generally) optional. diff --git a/userprofile/views.py b/userprofile/views.py index fa07e9a16c7b6b61f942fc79940cdaac2c45511c..023a41426af19d7d8b36ea96ddf5d22dfc960e8a 100644 --- a/userprofile/views.py +++ b/userprofile/views.py @@ -15,7 +15,7 @@ from forms import ContactForm, triggerFormFactory from gracedb.permission_utils import internal_user_required, lvem_user_required -from datetime import datetime +from django.utils import timezone from gracedb.query import labelQuery from gracedb.models import Label @@ -38,7 +38,7 @@ def managePassword(request): password = User.objects.make_random_password(length=20) d['password'] = password request.user.set_password(password) - request.user.date_joined = datetime.now() + request.user.date_joined = timezone.now() request.user.save() return render_to_response('profile/manage_password.html', d,