import datetime
import math
import doctest
import typing
from .durations import Duration
# ========================================================
# ========================================================
# === DATE
# ========================================================
# ========================================================
[docs]class Date:
""" Class to convert dates for astronomy
*Date formats are:*
| now = Now. e.g. "now"
| jd = Julian day. e.g. 24504527.45678
| iso = ISO 8601. e.g. 2018-02-28T12:34:55.23
| sql = ISO 8601. e.g. 2018-02-28 12:34:55.23
| ymdhms = Calendar. e.g. 2018 2 28 12 34 55.23
| equinox = Equinox. e.g. J2000,0
| digits = Pure digits e.g. 20180228123455.23
*Usage:*
First, instanciate an object from the class:
::
date = Date()
Second, assign a date in any date format:
::
date.date("2018-02-28T12:34:55")
Third, get the converted date:
::
jd = date.jd()
date = date.date()
iso = date.iso()
ymdhms = date.ymdhms()
equinox = date.equinox()
*Informations:*
All dates are in UTC.
Some other useful methods:
::
help(Date)
Date().infos("doctest")
Date().infos("doc_methods")
"""
# ========================================================
# === attributs
# ========================================================
_B1850 = 2396758.203
_B1900 = 2415020.3135
_B1950 = 2433282.4235
_B1975 = 2442413.478
_B2000 = 2451544.533
_B2025 = 2460675.588
_B2050 = 2469806.643
_B2100 = 2488068.753
_J1900 = 2415020.0000
_J1950 = 2433282.5000
_J2000 = 2451545.0000
_J2050 = 2469807.5000
_J2100 = 2488070.0000
# ========================================================
# === internal methods
# ========================================================
def _init(self,date="") -> None:
""" Initialize internal attributes.
:param date: date is a date in any supported format (cf. help(Date))
:type date: any
:returns: None
:rtype: None
:Example:
>>> objdate = Date()
>>> objdate._init()
"""
self._init_date = date
self._init_dateformat = 0
self._computed_jd = 0
self._computed_iso = 0
self._computed_iso_nb_subdigit = 3
self._computed_iso_letter = 'T'
self._computed_ymdhms = 0
self._computed_digits = 0
self._computed_digits_nb_subdigit = 3
self._computed_equinox = 0
self._computed_equinox_year_type = "J"
self._computed_equinox_nb_subdigit = 1
self._jd = 0
self._iso = 0
self._ymdhms = 0
self._equinox = 0
def _is_number(self,s) -> bool:
""" Return True if the string is a number else return False.
:param s: A string to test
:type s: string
:returns: True is the string can be concerted into a float
:rtype: bool
:Example:
>>> objdate = Date()
>>> objdate._is_number("3e5")
True
>>> objdate._is_number("3a5")
False
"""
try:
float(s)
return True
except ValueError:
pass
try:
import unicodedata
unicodedata.numeric(s)
return True
except (TypeError, ValueError):
pass
return False
def _duration2day(self, duration) -> float:
""" Return a duration in day unit from a duration in dhms format.
:param duration: A string formated as "2d7h23m12.5s"
:type duration: string
:returns: duration expressed in day and fraction of day
:rtype: float
:Example:
>>> objdate = Date()
>>> objdate._duration2day("2d7h23m12.5s")
2.3077835648148146
"""
duration = str(duration)
duration = duration.upper()
cars = "+-.E0123456789"
units = "DHMS"
dur = 0
k1 = -1
k2 = -1
k = 0
div = 0
for car in duration:
if car in cars:
if k1 == -1:
k1 = k
k2 = k
elif car in units:
if (k1>=0 and k2>=0):
div = 0
if car=='D':
div = 1.
elif car=='H':
div = 24.
elif car=='M':
div = 1440.
elif car=='S':
div = 86400.
if div>0:
n = float(duration[k1:k2+1])
dur += n/div
k1 = -1
k2 = -1
k += 1
if (div==0 and dur==0 and k1>=0 and k2>=k1):
n = float(duration[k1:k2+1])
dur += n
return dur
def _date_compare(self, date, operator):
""" Comparaison of dates for various operators.
:param date: A date in any supported format (cf. help(Date))
:type date: Date()
:param operator : Operator such as == != > >= < <=
:type operator : string
:returns: The logic result of the comparison.
:rtype: bool
:Example:
>>> objdate1 = Date()
>>> objdate2 = Date()
>>> objdate1.date("2018 02 28"); objdate2.date("2018 02 27"); objdate1._date_compare(objdate2,">")
'2018 02 28'
'2018 02 27'
True
.. note:: Does not account for the modulo.
"""
if self._computed_jd == 0:
self.jd()
if date._computed_jd == 0:
date.jd()
res = False
if (self._computed_jd == 1) and (date._computed_jd == 1):
toeval = str(self._jd)+" "+operator+" "+str(date._jd)
res = eval(toeval)
return res
# ========================================================
# === date methods
# ========================================================
[docs] def date_date2jd(self,date) -> typing.Tuple[int, float]:
""" Compute a julian day from any date format
:param date: A string formated as "2d7h23m12.5s"
:type date: string
:returns: A tuple of init_dateformat, julian day. init_dateformat: The identified format of the input:
* 0 = Error, format not known
* 1 = Now
* 2 = Equinox
* 3 = Sql
* 4 = Calendar
* 5 = ISO 8601
* 6 = Julian day
* 7 = Modified Julian day
* 8 = Digits
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_date2jd("2018-02-28T12:34:55.234")
(5, 2458178.0242503937)
.. note:: Prefer using objdate.date() followed by objdate.jd().
"""
# --- First we do not process if date is ever a Date object
if (isinstance(date, Date)):
return date
# --- Decode the date
jd = 0
init_dateformat = 0
str_date = str(date).upper()
str_split = str_date.split(" ")
str_nsplit = len(str_split)
if str_date == "NOW":
# style NOW
utc_datetime = datetime.datetime.utcnow()
day = utc_datetime.day + ((((utc_datetime.microsecond*1e-6) + utc_datetime.second)/60. + utc_datetime.minute)/60. + utc_datetime.hour)/24.
error, jd = self.date_ymd2jd(utc_datetime.year, utc_datetime.month, day)
init_dateformat = 1
elif (str_date[0] is "B") or (str_date[0] is "J"):
# style J2000,0
error, jd = self.date_equinox2jd(str_date)
init_dateformat = 2
elif (str_nsplit is 2):
# style SQL 2018-01-02 23:02:45.456
iso_date = str_split[0] + "T" + str_split[1]
error, jd = self.date_iso2jd(iso_date)
init_dateformat = 3
elif (str_nsplit is 3):
# style calenday 2018 01 02.4567
str_split = [ float(x) for x in str_split]
error, jd = self.date_ymd2jd(str_split[0],str_split[1],str_split[2])
init_dateformat = 4
elif (str_nsplit is 4):
# style calenday 2018 01 02 05.324
str_split = [ float(x) for x in str_split]
error, jd = self.date_ymdhms2jd(str_split[0],str_split[1],str_split[2],str_split[3])
init_dateformat = 4
elif (str_nsplit is 5):
# style calenday 2018 01 02 05 12.1809
str_split = [ float(x) for x in str_split]
error, jd = self.date_ymdhms2jd(str_split[0],str_split[1],str_split[2],str_split[3],str_split[4])
init_dateformat = 4
elif (str_nsplit is 6):
# style calenday 2018 01 02 05 12 56.789
str_split = [ float(x) for x in str_split]
error, jd = self.date_ymdhms2jd(str_split[0],str_split[1],str_split[2],str_split[3],str_split[4],str_split[5])
init_dateformat = 4
elif (str_nsplit is 1) and (str_date.find("T") > 6):
# style ISO 2018-01-02T23:02:45.456
error, jd = self.date_iso2jd(str_date)
init_dateformat = 5
elif (self._is_number(str_date) is True):
# style is julian day or MJD
# MJD = JD - 2400000.5 (SAO in 1957)
# digits as 20180316
jd = float(str_date)
init_dateformat = 6
if (math.log10(jd) > 7.2):
# --- digits
error, jd = self.date_digits2jd(str_date)
init_dateformat = 8
elif (math.log10(jd) < 5):
# --- MJD
jd += 2400000.5
init_dateformat = 7
return init_dateformat, jd
[docs] def date_iso2jd(self,string) -> tuple:
""" Compute a julian day from a ISO8601 date
:param string: A string formated in ISO 8601
:type string: string
:returns: A tuple of error, julian day. error = 0 means no error
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_iso2jd("2018-02-28T12:34:55.234")
(0, 2458178.0242503937)
First integer is an error code. 0 = no problem.
.. note:: Prefer using objdate.date() followed by objdate.jd().
"""
error = 0
k1 = string.find("-",1)
k2 = string.find("-",k1+1)
kt = string.find("T",k2+1)
if (kt==-1):
kt = string.find("T",k2+1)
k3 = string.find(":",kt+1)
k4 = string.find(":",k3+1)
d=1
hh=0
mm=0
ss=0.0
if (k2 > -1):
y = int(string[0:k1])
m = int(string[k1+1:k2])
if (kt == -1):
d = float(string[k2+1:])
else:
d = int(string[k2+1:kt])
if (k3 > -1):
hh = int(string[kt+1:k3])
d += hh/24.
if (k4 == -1):
mm = float(string[k3+1:])
d += mm/1440.
else:
mm = int(string[k3+1:k4])
d += mm/1440.
ss = float(string[k4+1:])
d += ss/86400.
error, jd = self.date_ymd2jd(y,m,d)
else:
# pb format
error = 1
return error, jd
[docs] def date_jd2iso(self, jd, nb_subdigit=3, letter='T') -> tuple:
""" Compute a ISO8601 date from a julian day
:param jd: A julian day
:type jd: float
:param nb_subdigit: The number of digits returned after the seconds.
:type nb_subdigit: int
:param letter: The letter to separe date end time. If letter is "" then the output format sticks all digits without any characters :-T. So the format is no longer ISO but useful for a pure digit code.
:type letter: int
:returns: A tuple of error, string of a date formatted into ISO8601. error = 0 means no error.
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_jd2iso(2458178.0242503937)
(0, '2018-02-28T12:34:55.234')
.. note:: Prefer using objdate.date() followed by objdate.iso()
"""
error, y, m, d, hh, mm, ss = self.date_jd2ymdhms(jd)
nb_prefixdigit = nb_subdigit+3
if (nb_subdigit < 0):
nb_subdigit = 0
if (nb_subdigit == 0):
nb_prefixdigit -= 1
if (letter==''):
fstring = "{:04d}{:02d}{:02d}{}{:02d}{:02d}{:0"+str(nb_prefixdigit)+"."+str(nb_subdigit)+"f}"
else:
fstring = "{:04d}-{:02d}-{:02d}{}{:02d}:{:02d}:{:0"+str(nb_prefixdigit)+"."+str(nb_subdigit)+"f}"
res = fstring.format(y, m, d, letter, hh, mm, ss)
return error, res
[docs] def date_equinox2jd(self,string):
""" Compute a julian day from a equinoxal date
:param string: date string is a string in Equinox format (cf. help(Date))
:type string: string
:returns: A tuple of error, julian day. error = 0 means no error
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_equinox2jd("J2025,0")
(0, 2460676.25)
.. note:: Prefer using objdate.date() followed by objdate.jd()
"""
error = 0
t = string[0]
s = string[1:].replace(',','.')
a = float(s)
jd = 0
if t is "J":
if (a == 1900):
jd = self._J1900
elif (a == 1950):
jd = self._J1950
elif (a == 2000):
jd = self._J2000
elif (a == 2050):
jd = self._J2050
elif (a == 2100):
jd = self._J2100
else:
jd = 2451545.0+(a-2000.0)*365.25;
elif t is "B":
if (a == 1850):
jd = self._B1850
elif (a == 1900):
jd = self._B1900
elif (a == 1950):
jd = self._B1950
elif (a == 1975):
jd = self._B1975
elif (a == 2000):
jd = self._B2000
elif (a == 2025):
jd = self._B2025
elif (a == 2050):
jd = self._B2050
elif (a == 2100):
jd = self._B2100
else:
error = 2
return error,jd
[docs] def date_digits2jd(self,string):
""" Compute a julian day from a date with only digits
:param string: A string formated in digits
:type string: string
:returns: A tuple of error, julian day. error = 0 means no error
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_iso2jd("2018-02-28T12:34:55.234")
(0, 2458178.0242503937)
First integer is an error code. 0 = no problem.
.. note:: Prefer using objdate.date() followed by objdate.digits().
"""
error = 0
k1 = string.find("-",1)
k2 = string.find("-",k1+1)
kt = string.find("T",k2+1)
if (kt==-1):
kt = string.find("T",k2+1)
k3 = string.find(":",kt+1)
k4 = string.find(":",k3+1)
d=1
hh=0
mm=0
ss=0.0
if (k2 > -1):
y = int(string[0:k1])
m = int(string[k1+1:k2])
if (kt == -1):
d = float(string[k2+1:])
else:
d = int(string[k2+1:kt])
if (k3 > -1):
hh = int(string[kt+1:k3])
d += hh/24.
if (k4 == -1):
mm = float(string[k3+1:])
d += mm/1440.
else:
mm = int(string[k3+1:k4])
d += mm/1440.
ss = float(string[k4+1:])
d += ss/86400.
error, jd = self.date_ymd2jd(y,m,d)
else:
# pb format
error = 1
return error, jd
[docs] def date_jd2digits(self, jd, nb_subdigit=3):
""" Compute a date only with digits from a julian day
:param jd: A julian day
:type jd: float
:param nb_subdigit: The number of digits returned after the seconds.
:type nb_subdigit: int
:returns: A tuple of error, string of a date formatted into ISO8601. error = 0 means no error.
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_jd2iso(2458178.0242503937)
(0, '2018-02-28T12:34:55.234')
.. note:: Prefer using objdate.date() followed by objdate.digits()
"""
return self.date_jd2iso(jd,nb_subdigit,"")
[docs] def date_jd2equinox(self,jd,year_type="",nb_subdigit=1):
""" Compute an equinoxal date from a julian day
:param jd: A julian day
:type jd: float
:param year_type: "B" (Bessel) or "J" (Julian) or "" for automatic choice
:type year_type: string
:param nb_subdigit: The number of digits returned after the year.
:type nb_subdigit: int
:returns: A tuple of error, string of a date formatted into ISO8601. error = 0 means no error.
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_jd2equinox(2458178.0242503937)
(0, 'J2018.2')
.. note:: Prefer using objdate.date() followed by objdate.equinox()
"""
error = 0
eps=1e-3
if (year_type==""):
year_type="J"
if ((math.fabs(jd-2415020.3135)<eps) or (math.fabs(jd-2433282.4235)<eps)):
year_type="B"
if (year_type=="J"):
# julian
a=(jd-2451545.0)/365.25+2000.0
chaine0="J"
if (year_type=="B"):
# besselian
a = 1900.0 + (jd - 2415020.31352) / 365.242198781
chaine0 = "B"
if (nb_subdigit>0):
fstring = "{:."+str(nb_subdigit)+"f}"
equinox = chaine0 + fstring.format(a)
else:
equinox = chaine0 + "{:.0f}".format(a)
return error, equinox
[docs] def date_ymdhms2jd(self, y:int, m:int, d:int, hh:int=0, mm:int=0, ss:float=0) -> tuple:
""" Compute a julian day from a calendar date
:param y: Year
:type y: int
:param m: Month
:type m: int
:param d: Day
:type d: int
:param hh: Hour
:type hh: int
:param mm: Minutes
:type mm: int
:param ss: Seconds
:type ss: float
:returns: A tuple of error, float corresponding to the julian day. error = 0 means no error.
:rtype: tuple(int, float)
:Example:
>>> objdate = Date()
>>> objdate.date_ymdhms2jd(2017,3,12,0,23,12.34)
(0, 2457824.516115046)
First integer is an error code. 0 = no problem.
:Related topics:
Prefer using objdate.date() followed by objdate.jd()
"""
d += ( hh + ( mm + ss/60.) /60.) /24.;
error, jd = self.date_ymd2jd(y, m, d)
return error, jd
[docs] def date_ymd2jd(self,year, month, day):
""" Compute a julian day from a calendar date
:param year: Year
:type year: int
:param month: Month
:type month: int
:param day: Day and fraction of the day
:type day: float
:Example:
>>> objdate = Date()
>>> objdate.date_ymd2jd(2017,3,12.64232)
(0, 2457825.14232)
First integer is an error code. 0 = no problem.
:Related topics:
Prefer using objdate.date() followed by objdate.jd()
"""
error = 0
a=year;
m=month;
j=day;
if m <= 2:
a=a-1
m=m+12
aa=math.floor(a/100)
bb=2-aa+math.floor(aa/4)
jd=math.floor(365.25*(a+4716))+math.floor(30.6001*(m+1))+bb-1524.5
jd=jd+j;
if (jd<2299160.5) :
jd=math.floor(365.25*(a+4716))+math.floor(30.6001*(m+1))-1524.5;
jd=jd+j
return error,jd
[docs] def date_jd2ymd(self,jd):
""" Compute a calendar date from a julian day
:param jd: Julian day
:type jd: float
:Example:
>>> objdate = Date()
>>> objdate.date_jd2ymd(2457825.14232)
(0, 2017, 3, 12.64232000010088)
First integer is an error code. 0 = no problem. Following items are:
* year = year
* month = month
* day = day and fraction of day
:Related topics:
Prefer using objdate.date() followed by objdate.ymdhms()
"""
error=0;
jd+=.5
z=math.floor(jd)
f=jd-z
if (z<2299161.):
a=z
else:
alpha=math.floor((z-1867216.25)/36524.25)
a=z+1+alpha-math.floor(alpha/4)
b=a+1524
c=math.floor(((b-122.1)/365.25))
d=math.floor(365.25*c)
e=math.floor((b-d)/30.6001)
d=b-d-math.floor(30.6001*e)+f
if e<14:
m = (int)(e-1)
else:
m = (int)(e-13)
if m>2:
y = (int)(c-4716)
else:
y = (int)(c-4715)
return error, y, m, d
[docs] def date_jd2ymdhms(self,jd):
""" Compute a calendar date from a julian day
:param jd: Julian day
:type jd: float
:Example:
>>> objdate = Date()
>>> objdate.date_jd2ymdhms(2457824.516115046)
(0, 2017, 3, 12, 0, 23, 12.339983582496643)
First integer is an error code. 0 = no problem. Following items are:
* y = year
* m = month
* d = day
* hh = hour
* mm = minutes
* ss = seconds
:Related topics:
Prefer using objdate.date() followed by objdate.ymdhms()
"""
error, y, m, day = self.date_jd2ymd(jd)
d = int(math.floor(day))
hh = 0
mm = 0
ss = 0
if (error==0):
r = (day-d)*24
hh = int(math.floor(r))
r = (r-hh)*60
mm = int(math.floor(r))
ss = (r-mm)*60
return error, y, m, d, hh, mm, ss
# ========================================================
# === get/set methods
# ========================================================
[docs] def date(self, date=""):
""" Set the input date in any format
:param date: date is a date in any supported format (cf. help(Date))
:type date: any
:returns: The input date.
:rtype: string
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
.. note:: After using objdate.date() get conversions with methods as objdate.jd() or objdate.iso().
"""
if date != "":
if isinstance(date, str) == True:
dt = date.upper()
else:
dt = ""
if (date != self._init_date) or (dt == "NOW"):
self._init(date)
return self._init_date
[docs] def jd(self):
""" Get the date in julian day format
:returns: The julian day.
:rtype: float
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
>>> objdate.jd()
2458178.0242503937
.. note:: Before use objdate.date() to set the input date.
"""
if (self._computed_jd == 0):
init_dateformat, jd = self.date_date2jd(self._init_date)
if (init_dateformat > 0):
self._init_dateformat = init_dateformat
self._computed_jd = 1
self._jd = jd
return self._jd
return -1
return self._jd
[docs] def iso(self, nb_subdigit=3,letter='T'):
""" Get the date in ISO 8601 format
:param nb_subdigit: The number of digits returned after the seconds.
:type nb_subdigit: int
:param letter: The letter to separe date end time. If letter is "" then the output format sticks all digits without any characters :-T. So the format is no longer ISO but useful for a pure digit code.
:type letter: int
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
>>> objdate.iso(2)
'2018-02-28T12:34:55.23'
:Related topics:
Before use objdate.date() to set the input date.
"""
if (self._computed_iso == 0) or (self._computed_iso_nb_subdigit != nb_subdigit) or (self._computed_iso_letter != letter):
if (self._computed_jd == 0):
self.jd()
if (self._init_dateformat > 0):
error, iso = self.date_jd2iso(self._jd, nb_subdigit, letter)
if error==0:
self._computed_iso = 1
self._iso = iso
self._computed_iso_nb_subdigit = nb_subdigit
self._computed_iso_letter = letter
return self._iso
return -1
return self._iso
[docs] def ymdhms(self):
""" Get the date in ymdhms format
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
>>> objdate.ymdhms()
[2018, 2, 28, 12, 34, 55.23401856422424]
:Related topics:
Before use objdate.date() to set the input date.
"""
if (self._computed_ymdhms == 0):
if (self._computed_jd == 0):
self.jd()
if (self._init_dateformat > 0):
error, *ymdhms = self.date_jd2ymdhms(self._jd)
if error==0:
self._computed_ymdhms = 1
self._ymdhms = ymdhms
return self._ymdhms
return -1
return self._ymdhms
[docs] def digits(self, nb_subdigit=3):
""" Get the date in digits format
:param nb_subdigit: The number of digits returned after the seconds.
:type nb_subdigit: int
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
>>> objdate.iso(2)
'2018-02-28T12:34:55.23'
:Related topics:
Before use objdate.date() to set the input date.
"""
if (self._computed_digits == 0) or (self._computed_digits_nb_subdigit != nb_subdigit):
if (self._computed_jd == 0):
self.jd()
if (self._init_dateformat > 0):
error, digits = self.date_jd2digits(self._jd, nb_subdigit)
if error==0:
self._computed_digits = 1
self._digits = digits
self._computed_digits_nb_subdigit = nb_subdigit
return self._digits
return -1
return self._digits
[docs] def equinox(self, year_type="J", nb_subdigit=1):
""" Get the date in equinox format
:param year_type: "B" (Bessel) or "J" (Julian) or "" for automatic choice
:type year_type: string
:param nb_subdigit: The number of digits returned after the year.
:type nb_subdigit: int
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234")
'2018-02-28T12:34:55.234'
>>> objdate.equinox()
'J2018.2'
:Related topics:
Before use objdate.date() to set the input date.
"""
if (self._computed_equinox == 0) or (self._computed_equinox_year_type != year_type) or (self._computed_equinox_nb_subdigit != nb_subdigit):
if (self._computed_jd == 0):
self.jd()
if (self._init_dateformat > 0):
error, equinox = self.date_jd2equinox(self._jd,year_type,nb_subdigit)
if error==0:
self._computed_equinox = 1
self._equinox = equinox
self._computed_equinox_year_type = year_type
self._computed_equinox_nb_subdigit = nb_subdigit
return self._equinox
return -1
return self._equinox
# ========================================================
# === debug methods
# ========================================================
[docs] def infos(self, action):
""" To get informations about this class
:param action: A command to run a debug action (see examples).
:type action: string
:Example:
::
Date().infos("doctest")
Date().infos("doc_methods")
Date().infos("internal_attributes")
Date().infos("public_methods")
"""
if (action == "doc_methods"):
publics = [x for x in dir(self) if x[0]!="_"]
for public in publics:
varname = "{}".format(public)
if (callable(getattr(self,varname))==True):
print("\n{:=^40}".format(" method "+varname+" "))
t = "Date()."+varname+".__doc__"
tt =eval(t)
print(tt)
if (action == "doctest"):
if __name__ == "__main__":
print("\n{:~^40}".format("doctest"))
#doctest.testmod(verbose=True, extraglobs={'objdate': Date()})
doctest.testmod(verbose=True)
if (action == "internal_attributes"):
internals = [x for x in dir(self) if x[0]=="_" and x[1]!="_"]
for internal in internals:
varname = "{}".format(internal)
#if (hasattr(self,varname)==True):
if (callable(getattr(self,varname))==False):
print(varname + "=" + str(getattr(self,varname)))
if (action == "public_methods"):
publics = [x for x in dir(self) if x[0]!="_"]
for public in publics:
varname = "{}".format(public)
if (callable(getattr(self,varname))==True):
print(varname)
# ========================================================
# === special methods
# ========================================================
def __init__(self, date=""):
""" Object initialization where date is the input in any format
:param date: A input date in any format.
:type date: string
date is a date in any supported format (cf. help(Date))
"""
self._init(date)
[docs] def __add__(self, duration):
""" Add a duration to a date
:param duration: Duration in dhms format (e.g 3d20h5m3s).
:type duration: string
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234") ; date = objdate + "12m45s" ; date.iso()
'2018-02-28T12:34:55.234'
'2018-02-28T12:47:40.234'
"""
if self._computed_jd == 0:
self.jd()
if self._computed_jd == 1:
jd = self._jd
else:
return Date()
duration = Duration(duration)
day = duration.day()
jd += day
return Date(jd)
[docs] def __radd__(self, duration):
""" Right addition a duration to a date
"""
return self + duration
[docs] def __iadd__(self, duration):
""" Add a duration to a date
"""
return self + duration
[docs] def __sub__(self, object_or_duration):
""" Subtract a duration to a date (returns an object date) or Compute the duration between two date objects.
:param object_or_duration: Duration in dhms format (e.g 3d20h5m3s)
:type object_or_duration: Date object
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234") ; date = objdate - "12m45s" ; date.iso()
'2018-02-28T12:34:55.234'
'2018-02-28T12:22:10.234'
>>> objdate.date("2018-02-28T12:34:55.234") ; objdate2 = Date("2018-02-25T12:34:55.234") ; days = objdate - objdate2 ; print(days)
'2018-02-28T12:34:55.234'
3.0
"""
res = 0
if self._computed_jd == 0:
self.jd()
if isinstance(object_or_duration, Date) == True:
date = object_or_duration
if date._computed_jd == 0:
date.jd()
if (self._computed_jd == 1) and (date._computed_jd == 1):
res = self._jd - date._jd
else:
duration = object_or_duration
if self._computed_jd == 1:
jd = self._jd
else:
return Date()
duration = Duration(duration)
day = duration.day()
jd -= day
return Date(jd)
return res
[docs] def __rsub__(self, date):
""" Right subtraction only for a date to another date
"""
if isinstance(date, Date) == True:
return self - date
else:
return date
[docs] def __isub__(self, duration):
""" Subtract a duration to a date
:param duration: Duration in dhms format (e.g 3d20h5m3s)
:type duration: str
:Example:
>>> objdate = Date()
>>> objdate.date("2018-02-28T12:34:55.234") ; objdate -= "12m45s" ; objdate.iso()
'2018-02-28T12:34:55.234'
'2018-02-28T12:22:10.234'
"""
return self - duration
[docs] def __eq__(self, date):
""" Comparaison of dates. Return True if dates are defined and equals.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 28"); objdate2 = Date("2018 02 28"); objdate == objdate2
'2018 02 28'
True
"""
return self._date_compare( date, "==")
[docs] def __ne__(self, date):
""" Comparaison of dates. Return True if dates are defined and not equals.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 28"); objdate2 = Date("2018 02 28"); objdate != objdate2
'2018 02 28'
False
"""
return self._date_compare( date, "!=")
[docs] def __gt__(self, date):
""" Comparaison of dates: date1 > date 2. Return True if dates are defined and date1 > date 2.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 28"); objdate2 = Date("2018 02 27"); objdate > objdate2
'2018 02 28'
True
"""
return self._date_compare( date, ">")
[docs] def __ge__(self, date):
""" Comparaison of dates: date1 >= date 2. Return True if dates are defined and date1 >= date 2.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 28"); objdate2 = Date("2018 02 27"); objdate >= objdate2
'2018 02 28'
True
"""
return self._date_compare( date, ">=")
[docs] def __lt__(self, date):
""" Comparaison of dates: date1 < date 2. Return True if dates are defined and date1 < date 2.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 26"); objdate2 = Date("2018 02 27"); objdate < objdate2
'2018 02 26'
True
"""
return self._date_compare( date, "<")
[docs] def __le__(self, date):
""" Comparaison of dates: date1 <= date 2. Return True if dates are defined and date1 <= date 2.
:param date: An object instancied on Date
:type date: Date
:Example:
>>> objdate = Date()
>>> objdate.date("2018 02 26"); objdate2 = Date("2018 02 27"); objdate <= objdate2
'2018 02 26'
True
"""
return self._date_compare( date, "<=")
# ========================================================
# ========================================================
# ========================================================