Source code for exoctk.contam_visibility.time_extensionsx

"""Module containing library functions for time manipulation.
Standard for time representation in this project is fractional days.
Dates are represented as modified Julian dates (mjd).
An mjd gives the number of days since midnight on November 17, 1858.
"""
import string
from math import ceil, floor

# Constant for converting Julian dates to modified Julian dates
MJD_BASELINE = 2400000.5


[docs]def is_leap_year(year): """Returns True if the year is a leap year, False otherwise. Parameters ---------- year: int The year to check. Returns ------- bool Is the year a leap year? """ return (((year % 4 == 0) and ((year % 100 > 0) or (year % 400 == 0))))
[docs]def days_in_year(year): """Returns the number of days in a year. Parameters ---------- year: int The year to search. Returns ------- days : int The number of days that year. """ days = 365 if (is_leap_year(year)): days += 1 return(days)
[docs]def leap_years(year1, year2): """Returns the number of leap years between year1 and year2, non-inclusive. year1 and year2 must be integers, with year2 > year1 Parameters ---------- year1: int The start year. year2: int The end year. Returns ------- int The number of leap years between year1 and year2. """ # Find next years after year1 that are divisible by 4, 100, and 400 next_div4 = int(4 * ceil(year1/4.0)) next_div100 = int(100 * ceil(year1/100.0)) next_div400 = int(400 * ceil(year1/400.0)) # Now compute number of years between year1 and year2 that are # evenly divisible by 4, 100, 400 div4_years = int(ceil((year2 - next_div4)/4.0)) div100_years = int(ceil((year2 - next_div100)/100.0)) div400_years = int(ceil((year2 - next_div400)/400.0)) # Leap years are years divisible by 4, except for years # divisible by 100 that are not divisible by 400 return(div4_years - (div100_years - div400_years))
[docs]def integer_days(time): """Takes a time in fractional days and returns integer component. Parameters ---------- time: float The float time. Returns ------- int The integer time. """ # If time is negative, integer days is a larger negative number return(int(floor(time)))
[docs]def seconds_into_day(time): """Takes a time in fractional days and returns number of seconds since the start of the current day. Parameters ---------- time: float The time as a float. Returns ------- int The day's duration in seconds. """ return(int(round(86400.0 * (time % 1))))
[docs]def days_to_seconds(days): """Takes a time in fractional days and converts it into integer seconds. Parameters ---------- days: float The number of days as a float. Returns ------- int The number of seconds in as many days. """ return(int(round(86400 * days)))
[docs]def seconds_to_days(seconds): """Takes a time in integer seconds and converts it into fractional days. Parameters ---------- seconds: int The number of seconds. Returns ------- float The number of days as a float. """ return(seconds / 86400.0)
[docs]def round_to_second(time): """Rounds a time in days to the nearest second. Parameters ---------- time: int The number of days as a float. Returns ------- float The number of seconds in as many days. """ return(round(time * 86400)/86400.0)
[docs]def display_time(time, force_hours=False): """Returns a string representation of a time specified in fractional days. Parameters ---------- time: float The time as a float. force_hours: bool Force the hour calculation. Returns ------- str The time as a string. """ # round to nearest second before extracting fields time = round_to_second(time) # if time is negative, print a minus sign and display absolute value if (time < 0): neg_string = '-' time = abs(time) else: neg_string = '' days = integer_days(time) day_string = hour_string = min_string = '' secs_within_day = seconds_into_day(time) hours_within_day = int(secs_within_day / 3600) secs_within_hour = secs_within_day % 3600 mins_within_hour = int(secs_within_hour / 60) secs_within_min = secs_within_hour % 60 # Unless force_hours is specified, only print a field if it or a # higher field is nonzero. Fill with leading zeros. # The force_hours option is useful because Excel can get confused # when reading short time strings. if (days != 0): day_string = '%s:' % ((str(days)).zfill(3)) if ((days != 0) or (hours_within_day > 0) or force_hours): hour_string = '%s:' % ((str(hours_within_day)).zfill(2)) if ((days != 0) or (secs_within_day >= 60) or force_hours): min_string = '%s:' % ((str(mins_within_hour)).zfill(2)) if ((days == 0) and (hours_within_day == 0) and (mins_within_hour == 0)): # avoid zero fill when there are only seconds sec_string = '%s' % ((str(secs_within_min))) else: sec_string = '%s' % ((str(secs_within_min)).zfill(2)) return(neg_string + day_string + hour_string + min_string + sec_string)
[docs]def time_from_string(time_string): """Takes a string of the form ddd:hh:mm:ss and converts it to fractional days. All subfields above seconds are optional and may be omitted if the subfield and all higher-order ones are zero. Parameters ---------- time_string: str The time as a string. Returns ------- float The fractional days. """ # extract fields fields = (string.split(time_string, ':')) seconds = int(fields[-1]) num_fields = len(fields) # default to zero if not provided minutes = hours = days = 0 if (num_fields > 1): minutes = int(fields[-2]) if (num_fields > 2): hours = int(fields[-3]) if (num_fields > 3): days = int(fields[-4]) total_seconds = seconds + 60 * minutes + 3600 * hours + 86400 * days return(seconds_to_days(total_seconds))
[docs]def display_date(mjd): """Returns a string representation of the date represented by a modified Julian date. Parameters ---------- mjd: float The modified julian day. Returns ------- str The MJD as a string. """ # adjust to number of days since Dec. 31, 1857 int_days = int(floor(321.0 + mjd)) # seconds_in_day = seconds_into_day(mjd) fractional_day = mjd % 1 # First compute year and day without allowing for leap years, then adjust year = 1858 + int_days/365 day_of_year = int_days % 365 - leap_years(1858, year) # handle case where leap year adjustment has made day negative while (day_of_year < 1): year -= 1 day_of_year = day_of_year + days_in_year(year) year_string = '%s:' % (year) return(year_string + display_time(day_of_year + fractional_day))
[docs]def compute_mjd(year, day_of_year, hour, minute, second): """Computes a modified Julian date from a date specified as a year, day of year, hour, minute, and second. Arguments should be integers. Parameters ---------- year: int The year. day_of_year: int The day. hour: int The hour. minute: int The minute. second: int The second. Returns ------- float The modified julian day. """ fractional_days = (hour * 3600 + minute * 60 + second)/86400.0 mjd_years = year - 1859 num_leaps = leap_years(1858, year) # number of leap years since 1858 # Add 45 days from Nov. 17 to end of 1858 return((365*mjd_years)+num_leaps+45+(day_of_year-1)+fractional_days)
[docs]def mjd_from_string(time_string): """Takes a string of the form yyyy.ddd:hh:mm:ss and returns an mjd. Parameters ---------- time_string: str The MJD as a string. Returns ------- float The modified julian day. """ years = int(time_string[0:4]) days = int(time_string[5:8]) hours = int(time_string[9:11]) minutes = int(time_string[12:14]) seconds = int(time_string[15:17]) return(compute_mjd(years, days, hours, minutes, seconds))
[docs]def mjd_to_jd(mjd): """Converts a modified Julian date to a true Julian date. Parameters ---------- mjd: float The modified julian day. Returns ------- float The true Julian day. """ return(MJD_BASELINE + mjd)
[docs]def jd_to_mjd(jd): """Converts a Julian date to a modified Julian date. Parameters ---------- jd: float The true Julian day. Returns ------- float The modified Julian day. """ return (jd - MJD_BASELINE)
[docs]class Interval(object): """Class to represent a simple temporal interval. """ def __init__(self, start, end): """Constructor for an interval. Parameters ---------- start: float The start time. end: float The end time. """ self.start = start self.end = end def __str__(self): """Returns a string representation of the interval.""" return('Interval: start: %s, end: %s' % (display_date(self.start), display_date(self.end)))
[docs] def start_time(self): """Returns the start of the interval.""" return(self.start)
[docs] def end_time(self): """Returns the end of the interval.""" return(self.end)
[docs] def duration(self): """Returns the duration of an interval in fractional days.""" return(self.end_time() - self.start_time())
[docs] def temporal_relationship(self, time): """Returns the temporal relationship between an interval and an absolute time. Returns 'before' if the interval ends at or before the time, 'after' if the interval begins at or after the time, 'includes' if the time occurs during the interval. Parameters ---------- time: float The time. Returns ------- rel : str The temporal relationship. """ if (self.end_time() <= time): rel = 'before' elif (self.start_time() >= time): rel = 'after' else: rel = 'includes' return(rel)
[docs]class FlexibleInterval(Interval): """Class to represent an interval with flexibility on when it can start and end. """ def __init__(self, est, lst, let): """Constructor for a FlexibileInterval. Parameters ---------- est: float Earliest start time (mjd). lst: float Latest start time (mjd). let: float Latest end time (mjd). """ self.est = est self.lst = lst self.let = let def __str__(self): """Returns a string representation of the FlexibleInterval.""" txt = (display_date(self.est), display_date(self.lst), display_date(self.let)) return('FlexibleInterval: EST: %s, LST: %s, LET: %s' % txt)
[docs] def start_time(self): """Returns the start of the FlexibleInterval.""" return(self.est)
[docs] def end_time(self): """Returns the end of the FlexibleInterval.""" return(self.let)
[docs] def flexibility(self): """Returns the flexibility of the FlexibleInterval, in fractional days.""" return(self.lst - self.est)
[docs] def maximum_duration(self): """Returns the maximum duration of the FlexibleInterval, in fractional days.""" return(self.let - self.lst)