def test_day_start_end(self): """Tests day_start() and day_end() with time and datetime methods.""" # The "start-of-day" of any UTC time should be that date, but after # forcing the hour:minute:second time to the first second of the day. start_dt_epoch = calendar.timegm(self.day_dt.utctimetuple()) start_st_epoch = calendar.timegm(self.day_st) self.assertEquals(start_dt_epoch, start_st_epoch) # Make the last second of a UTC day, 23:59:59, by forcing hour, minutes, # and seconds to the above limits. THe call to day_start should still # results in 00:00:00. end_dt = self.now_dt.replace(hour=23, minute=59, second=59) end_dt_epoch = calendar.timegm(end_dt.utctimetuple()) end_st = ( self.now_st.tm_year, self.now_st.tm_mon, self.now_st.tm_mday, 23, 59, 59, # Force to 23:59:59, leaving all else unchanged. self.now_st.tm_wday, self.now_st.tm_yday, self.now_st.tm_isdst) end_st_epoch = calendar.timegm(end_st) self.assertEquals(end_dt_epoch, end_st_epoch) self.assertEquals(utc.day_start(start_dt_epoch), start_dt_epoch) self.assertEquals(utc.day_start(self.now_seconds), start_dt_epoch) self.assertEquals(utc.day_start(end_dt_epoch), start_dt_epoch) self.assertEquals(utc.day_end(start_dt_epoch), end_dt_epoch) self.assertEquals(utc.day_end(self.now_seconds), end_dt_epoch) self.assertEquals(utc.day_end(end_dt_epoch), end_dt_epoch)
def test_day_start_end(self): """Tests day_start() and day_end() with time and datetime methods.""" # The "start-of-day" of any UTC time should be that date, but after # forcing the hour:minute:second time to the first second of the day. start_dt_epoch = calendar.timegm(self.day_dt.utctimetuple()) start_st_epoch = calendar.timegm(self.day_st) self.assertEquals(start_dt_epoch, start_st_epoch) # Make the last second of a UTC day, 23:59:59, by forcing hour, minutes, # and seconds to the above limits. THe call to day_start should still # results in 00:00:00. end_dt = self.now_dt.replace(hour=23, minute=59, second=59) end_dt_epoch = calendar.timegm(end_dt.utctimetuple()) end_st = ( self.now_st.tm_year, self.now_st.tm_mon, self.now_st.tm_mday, 23, 59, 59, # Force to 23:59:59, leaving all else unchanged. self.now_st.tm_wday, self.now_st.tm_yday, self.now_st.tm_isdst, ) end_st_epoch = calendar.timegm(end_st) self.assertEquals(end_dt_epoch, end_st_epoch) self.assertEquals(utc.day_start(start_dt_epoch), start_dt_epoch) self.assertEquals(utc.day_start(self.now_seconds), start_dt_epoch) self.assertEquals(utc.day_start(end_dt_epoch), start_dt_epoch) self.assertEquals(utc.day_end(start_dt_epoch), end_dt_epoch) self.assertEquals(utc.day_end(self.now_seconds), end_dt_epoch) self.assertEquals(utc.day_end(end_dt_epoch), end_dt_epoch)
def test_leap_second(self): """Points out that Python does not know when the leap seconds are. This matters because, for example, StudentLifecycleObserver handlers are supplied a datetime.datetime, which includes explicit seconds, like what is obtained from the ISO_8601_DATETIME_FORMAT string ('%Y-%m-%dT%H:%M:%S.%fZ'). The (harmless?) outcome is that events occurring during the leap second (23:59:60) will be added to the next day tallies. """ # 30 Jun 2015 23:59:60 is the most recent leap second, as of this # test. time.strptime() is used here, instead of # datetime.datetime.strptime(), because the latter function does # not understand leap seconds at all, instead complaining with: # ValueError: second must be in 0..59 leap_st = time.strptime("2015-06-30T23:59:60.0Z", schema_transforms.ISO_8601_DATETIME_FORMAT) self.assertEquals(leap_st.tm_year, 2015) self.assertEquals(leap_st.tm_mon, 6) self.assertEquals(leap_st.tm_mday, 30) self.assertEquals(leap_st.tm_hour, 23) self.assertEquals(leap_st.tm_min, 59) self.assertEquals(leap_st.tm_sec, 60) # Not 59, but leap second as 60. leap_epoch = long(calendar.timegm(leap_st)) # 30 Jun 2015 23:59:59 is the last "normal" second in 2015-06-30, # just prior to the leap second. last_dt = datetime.datetime.strptime( "2015-06-30T23:59:59.0Z", schema_transforms.ISO_8601_DATETIME_FORMAT) last_st = last_dt.utctimetuple() self.assertEquals(last_st.tm_year, 2015) self.assertEquals(last_st.tm_mon, 6) self.assertEquals(last_st.tm_mday, 30) self.assertEquals(last_st.tm_hour, 23) self.assertEquals(last_st.tm_min, 59) self.assertEquals(last_st.tm_sec, 59) last_epoch = long(calendar.timegm(last_st)) # According to Posix, "Unix time" (seconds since the 1970-01-01 epoch # also known as a "Posix timestamp") should repeat itself for one # second during a leap second, but the following confirms this not to # be the case. It should not be necessary to add the `+ 1`. self.assertEquals(leap_epoch, last_epoch + 1) # The tangible effect of this is that events occurring during the # actual leap second end up in the tallies for the next day. day_sec = 24 * 60 * 60 self.assertEquals(utc.day_start(leap_epoch), utc.day_start(last_epoch) + day_sec)
def test_to_timestamp(self): """Confirm precedence of seconds, dt, st, or text in to_timestamp().""" # Select now_seconds value, because seconds= was supplied. self.assertEquals( utc.to_timestamp(seconds=self.now_seconds, dt=self.day_dt, st=self.month_st, text=self.year_text), self.now_seconds) # Select day start value, because seconds= was not supplied, and # dt was supplied. self.assertEquals( utc.to_timestamp(dt=self.day_dt, st=self.month_st, text=self.year_text), utc.day_start(self.now_seconds)) # Select month value, because seconds= and dt= were not supplied, and # st was supplied. self.assertEquals( utc.to_timestamp(st=self.month_st, text=self.year_text), calendar.timegm(self.month_dt.utctimetuple())) # Select year value, because seconds=, dt=, and st= were not supplied, # and text was supplied. self.assertEquals(utc.to_timestamp(text=self.year_text), calendar.timegm(self.year_dt.utctimetuple())) # Select new "now" value, because seconds=, dt=, st=, and text= were # all missing. self.assertTrue(utc.to_timestamp() >= self.now_seconds)
def test_to_timestamp(self): """Confirm precedence of seconds, dt, st, or text in to_timestamp().""" # Select now_seconds value, because seconds= was supplied. self.assertEquals( utc.to_timestamp(seconds=self.now_seconds, dt=self.day_dt, st=self.month_st, text=self.year_text), self.now_seconds, ) # Select day start value, because seconds= was not supplied, and # dt was supplied. self.assertEquals( utc.to_timestamp(dt=self.day_dt, st=self.month_st, text=self.year_text), utc.day_start(self.now_seconds) ) # Select month value, because seconds= and dt= were not supplied, and # st was supplied. self.assertEquals( utc.to_timestamp(st=self.month_st, text=self.year_text), calendar.timegm(self.month_dt.utctimetuple()) ) # Select year value, because seconds=, dt=, and st= were not supplied, # and text was supplied. self.assertEquals(utc.to_timestamp(text=self.year_text), calendar.timegm(self.year_dt.utctimetuple())) # Select new "now" value, because seconds=, dt=, st=, and text= were # all missing. self.assertTrue(utc.to_timestamp() >= self.now_seconds)
def test_leap_second(self): """Points out that Python does not know when the leap seconds are. This matters because, for example, StudentLifecycleObserver handlers are supplied a datetime.datetime, which includes explicit seconds, like what is obtained from the ISO_8601_DATETIME_FORMAT string ('%Y-%m-%dT%H:%M:%S.%fZ'). The (harmless?) outcome is that events occurring during the leap second (23:59:60) will be added to the next day tallies. """ # 30 Jun 2015 23:59:60 is the most recent leap second, as of this # test. time.strptime() is used here, instead of # datetime.datetime.strptime(), because the latter function does # not understand leap seconds at all, instead complaining with: # ValueError: second must be in 0..59 leap_st = time.strptime("2015-06-30T23:59:60.0Z", schema_transforms.ISO_8601_DATETIME_FORMAT) self.assertEquals(leap_st.tm_year, 2015) self.assertEquals(leap_st.tm_mon, 6) self.assertEquals(leap_st.tm_mday, 30) self.assertEquals(leap_st.tm_hour, 23) self.assertEquals(leap_st.tm_min, 59) self.assertEquals(leap_st.tm_sec, 60) # Not 59, but leap second as 60. leap_epoch = long(calendar.timegm(leap_st)) # 30 Jun 2015 23:59:59 is the last "normal" second in 2015-06-30, # just prior to the leap second. last_dt = datetime.datetime.strptime("2015-06-30T23:59:59.0Z", schema_transforms.ISO_8601_DATETIME_FORMAT) last_st = last_dt.utctimetuple() self.assertEquals(last_st.tm_year, 2015) self.assertEquals(last_st.tm_mon, 6) self.assertEquals(last_st.tm_mday, 30) self.assertEquals(last_st.tm_hour, 23) self.assertEquals(last_st.tm_min, 59) self.assertEquals(last_st.tm_sec, 59) last_epoch = long(calendar.timegm(last_st)) # According to Posix, "Unix time" (seconds since the 1970-01-01 epoch # also known as a "Posix timestamp") should repeat itself for one # second during a leap second, but the following confirms this not to # be the case. It should not be necessary to add the `+ 1`. self.assertEquals(leap_epoch, last_epoch + 1) # The tangible effect of this is that events occurring during the # actual leap second end up in the tallies for the next day. day_sec = 24 * 60 * 60 self.assertEquals(utc.day_start(leap_epoch), utc.day_start(last_epoch) + day_sec)
def bin(cls, timestamp): """Converts POSIX timestamp to daily counter bin in self.binned dict. Args: timestamp: UTC time, as a POSIX timestamp (seconds since epoch). Returns: The key of the counter bin (which may or may not actually exist) in the self.binned dict associated with the supplied UTC time. """ return utc.day_start(timestamp)
def test_create_announcement_defaults(self): key = self._add_announcement() data = self._get_announcement(key) expected_date = utc.to_text( seconds=utc.day_start(utc.now_as_timestamp())) self.assertEquals(data['date'], expected_date) expected_key = str( db.Key.from_path(announcements.AnnouncementEntity.kind(), 1)) self.assertEquals(data['key'], expected_key) self.assertEquals(data['html'], '') self.assertEquals(data['is_draft'], True) self.assertEquals( data['title'], announcements.AnnouncementsDashboardHandler.DEFAULT_TITLE_TEXT)
def test_create_announcement_defaults(self): key = self._add_announcement() data = self._get_announcement(key) expected_date = utc.to_text( seconds=utc.day_start(utc.now_as_timestamp())) self.assertEquals(data['date'], expected_date) expected_key = str(db.Key.from_path( announcements.AnnouncementEntity.kind(), 1)) self.assertEquals(data['key'], expected_key) self.assertEquals(data['html'], '') self.assertEquals(data['is_draft'], True) self.assertEquals( data['title'], announcements.AnnouncementsDashboardHandler.DEFAULT_TITLE_TEXT)
def reduce(cls, key, values): total = sum(int(value) for value in values) ns_name = namespace_manager.get_namespace() if key == TotalEnrollmentEntity.COUNTING: TotalEnrollmentDAO.set(ns_name, total) yield key, total else: # key is actually a daily 'adds' counter bin seconds since epoch. bin_seconds_since_epoch = long(key) today = utc.day_start(utc.now_as_timestamp()) # Avoid race conditions by not updating today's daily bin (which # is being updated by student lifecycle events). if bin_seconds_since_epoch != today: date_time = utc.timestamp_to_datetime(bin_seconds_since_epoch) EnrollmentsAddedDAO.set(ns_name, date_time, total)
def binned(self): """Returns the binned counters dict (empty if uninitialized counter). Returns: If the counter is initialized (has been set() at least once), a dict containing a single bin containing the total enrollments count is constructed and returned. The single key in the returned dict is the 00:00:00 UTC "start of day" time of last_modified, as seconds since epoch. The single value is the get() count. Otherwise, if the counter is uninitialized, an empty dict is returned (just like the EnrollmentsDTO base class). """ if self.is_empty: return super(TotalEnrollmentDTO, self).binned return { utc.day_start(self.last_modified): self.get(), }