gam7 7.20.4__py3-none-any.whl → 7.21.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

gam/__init__.py CHANGED
@@ -25,17 +25,15 @@ https://github.com/GAM-team/GAM/wiki
25
25
  """
26
26
 
27
27
  __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
28
- __version__ = '7.20.04'
28
+ __version__ = '7.21.01'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
32
32
  import base64
33
- import calendar as calendarlib
34
33
  import codecs
35
34
  import collections
36
35
  import configparser
37
36
  import csv
38
- import datetime
39
37
  from email.charset import add_charset, QP
40
38
  from email.generator import Generator
41
39
  from email.header import decode_header, Header
@@ -109,7 +107,7 @@ from cryptography.x509.oid import NameOID
109
107
  if not getattr(sys, 'frozen', False):
110
108
  sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
111
109
 
112
- from dateutil.relativedelta import relativedelta
110
+ import arrow
113
111
 
114
112
  from pathvalidate import sanitize_filename, sanitize_filepath
115
113
 
@@ -143,7 +141,6 @@ from gamlib import glgapi as GAPI
143
141
  from gamlib import glgdata as GDATA
144
142
  from gamlib import glglobals as GM
145
143
  from gamlib import glindent
146
- from gamlib import gliso8601 as iso8601
147
144
  from gamlib import glmsgs as Msg
148
145
  from gamlib import glskus as SKU
149
146
  from gamlib import gluprop as UProp
@@ -162,7 +159,7 @@ def ISOformatTimeStamp(timestamp):
162
159
  return timestamp.isoformat('T', 'seconds')
163
160
 
164
161
  def currentISOformatTimeStamp(timespec='milliseconds'):
165
- return datetime.datetime.now(GC.Values[GC.TIMEZONE]).isoformat('T', timespec)
162
+ return arrow.now(GC.Values[GC.TIMEZONE]).isoformat('T', timespec)
166
163
 
167
164
  Act = glaction.GamAction()
168
165
  Cmd = glclargs.GamCLArgs()
@@ -212,6 +209,7 @@ ONE_GIGA_10_BYTES = 1000000000
212
209
  ONE_KILO_BYTES = 1024
213
210
  ONE_MEGA_BYTES = 1048576
214
211
  ONE_GIGA_BYTES = 1073741824
212
+ DAYS_OF_WEEK = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
215
213
  SECONDS_PER_MINUTE = 60
216
214
  SECONDS_PER_HOUR = 3600
217
215
  SECONDS_PER_DAY = 86400
@@ -1842,13 +1840,13 @@ def getStringWithCRsNLsOrFile():
1842
1840
  return (unescapeCRsNLs(getString(Cmd.OB_STRING, minLen=0)), UTF8, False)
1843
1841
 
1844
1842
  def todaysDate():
1845
- return datetime.datetime(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
1846
- tzinfo=GC.Values[GC.TIMEZONE])
1843
+ return arrow.Arrow(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
1844
+ tzinfo=GC.Values[GC.TIMEZONE])
1847
1845
 
1848
1846
  def todaysTime():
1849
- return datetime.datetime(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
1850
- GM.Globals[GM.DATETIME_NOW].hour, GM.Globals[GM.DATETIME_NOW].minute,
1851
- tzinfo=GC.Values[GC.TIMEZONE])
1847
+ return arrow.Arrow(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
1848
+ GM.Globals[GM.DATETIME_NOW].hour, GM.Globals[GM.DATETIME_NOW].minute,
1849
+ tzinfo=GC.Values[GC.TIMEZONE])
1852
1850
 
1853
1851
  def getDelta(argstr, pattern):
1854
1852
  if argstr == 'NOW':
@@ -1861,22 +1859,22 @@ def getDelta(argstr, pattern):
1861
1859
  sign = tg.group(1)
1862
1860
  delta = int(tg.group(2))
1863
1861
  unit = tg.group(3)
1864
- if unit == 'y':
1865
- deltaTime = datetime.timedelta(days=delta*365)
1866
- elif unit == 'w':
1867
- deltaTime = datetime.timedelta(weeks=delta)
1868
- elif unit == 'd':
1869
- deltaTime = datetime.timedelta(days=delta)
1870
- elif unit == 'h':
1871
- deltaTime = datetime.timedelta(hours=delta)
1872
- elif unit == 'm':
1873
- deltaTime = datetime.timedelta(minutes=delta)
1862
+ if sign == '-':
1863
+ delta = -delta
1874
1864
  baseTime = todaysDate()
1875
1865
  if unit in {'h', 'm'}:
1876
- baseTime = baseTime+datetime.timedelta(hours=GM.Globals[GM.DATETIME_NOW].hour, minutes=GM.Globals[GM.DATETIME_NOW].minute)
1877
- if sign == '-':
1878
- return baseTime-deltaTime
1879
- return baseTime+deltaTime
1866
+ baseTime = baseTime.shift(hours=GM.Globals[GM.DATETIME_NOW].hour, minutes=GM.Globals[GM.DATETIME_NOW].minute)
1867
+ if unit == 'y':
1868
+ return baseTime.shift(days=delta*365)
1869
+ if unit == 'w':
1870
+ return baseTime.shift(weeks=delta)
1871
+ if unit == 'd':
1872
+ return baseTime.shift(days=delta)
1873
+ if unit == 'h':
1874
+ return baseTime.shift(hours=delta)
1875
+ if unit == 'm':
1876
+ return baseTime.shift(minutes=delta)
1877
+ return baseTime
1880
1878
 
1881
1879
  DELTA_DATE_PATTERN = re.compile(r'^([+-])(\d+)([dwy])$')
1882
1880
  DELTA_DATE_FORMAT_REQUIRED = '(+|-)<Number>(d|w|y)'
@@ -1915,7 +1913,7 @@ def getYYYYMMDD(minLen=1, returnTimeStamp=False, returnDateTime=False, alternate
1915
1913
  elif argstr == 'NEVER':
1916
1914
  argstr = NEVER_DATE
1917
1915
  try:
1918
- dateTime = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
1916
+ dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
1919
1917
  Cmd.Advance()
1920
1918
  if returnTimeStamp:
1921
1919
  return time.mktime(dateTime.timetuple())*1000
@@ -1937,7 +1935,7 @@ def getHHMM():
1937
1935
  argstr = Cmd.Current().strip().upper()
1938
1936
  if argstr:
1939
1937
  try:
1940
- datetime.datetime.strptime(argstr, HHMM_FORMAT)
1938
+ arrow.Arrow.strptime(argstr, HHMM_FORMAT)
1941
1939
  Cmd.Advance()
1942
1940
  return argstr
1943
1941
  except ValueError:
@@ -1957,7 +1955,7 @@ def getYYYYMMDD_HHMM():
1957
1955
  argstr = NEVER_DATETIME
1958
1956
  argstr = argstr.replace('T', ' ')
1959
1957
  try:
1960
- datetime.datetime.strptime(argstr, YYYYMMDD_HHMM_FORMAT)
1958
+ arrow.Arrow.strptime(argstr, YYYYMMDD_HHMM_FORMAT)
1961
1959
  Cmd.Advance()
1962
1960
  return argstr
1963
1961
  except ValueError:
@@ -1976,10 +1974,10 @@ def getDateOrDeltaFromNow(returnDateTime=False):
1976
1974
  argstr = 'TODAY'
1977
1975
  argDate = getDeltaDate(argstr)
1978
1976
  elif argstr == 'NEVER':
1979
- argDate = datetime.datetime.strptime(NEVER_DATE, YYYYMMDD_FORMAT)
1977
+ argDate = arrow.Arrow.strptime(NEVER_DATE, YYYYMMDD_FORMAT)
1980
1978
  elif YYYYMMDD_PATTERN.match(argstr):
1981
1979
  try:
1982
- argDate = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
1980
+ argDate = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
1983
1981
  except ValueError:
1984
1982
  invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
1985
1983
  else:
@@ -1987,12 +1985,12 @@ def getDateOrDeltaFromNow(returnDateTime=False):
1987
1985
  Cmd.Advance()
1988
1986
  if not returnDateTime:
1989
1987
  return argDate.strftime(YYYYMMDD_FORMAT)
1990
- return (datetime.datetime(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE]),
1988
+ return (arrow.Arrow(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE]),
1991
1989
  GC.Values[GC.TIMEZONE], argDate.strftime(YYYYMMDD_FORMAT))
1992
1990
  missingArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
1993
1991
 
1994
1992
  YYYYMMDDTHHMMSS_FORMAT_REQUIRED = 'yyyy-mm-ddThh:mm:ss[.fff](Z|(+|-(hh:mm)))'
1995
- TIMEZONE_FORMAT_REQUIRED = 'Z|(+|-(hh:mm))'
1993
+ TIMEZONE_FORMAT_REQUIRED = 'utc|z|local|(+|-(hh:mm))|<ValidTimezoneName>'
1996
1994
 
1997
1995
  def getTimeOrDeltaFromNow(returnDateTime=False):
1998
1996
  if Cmd.ArgumentsRemaining():
@@ -2004,7 +2002,7 @@ def getTimeOrDeltaFromNow(returnDateTime=False):
2004
2002
  argstr = NEVER_TIME
2005
2003
  elif YYYYMMDD_PATTERN.match(argstr):
2006
2004
  try:
2007
- dateTime = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
2005
+ dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
2008
2006
  except ValueError:
2009
2007
  invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
2010
2008
  try:
@@ -2012,12 +2010,12 @@ def getTimeOrDeltaFromNow(returnDateTime=False):
2012
2010
  except OverflowError:
2013
2011
  pass
2014
2012
  try:
2015
- fullDateTime = iso8601.parse_date(argstr)
2013
+ fullDateTime = arrow.get(argstr)
2016
2014
  Cmd.Advance()
2017
2015
  if not returnDateTime:
2018
2016
  return argstr.replace(' ', 'T')
2019
2017
  return (fullDateTime, fullDateTime.tzinfo, argstr.replace(' ', 'T'))
2020
- except (iso8601.ParseError, OverflowError):
2018
+ except (arrow.parser.ParserError, OverflowError):
2021
2019
  pass
2022
2020
  invalidArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
2023
2021
  missingArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
@@ -2030,19 +2028,19 @@ def getRowFilterDateOrDeltaFromNow(argstr):
2030
2028
  deltaDate = getDelta(argstr, DELTA_DATE_PATTERN)
2031
2029
  if deltaDate is None:
2032
2030
  return (False, DELTA_DATE_FORMAT_REQUIRED)
2033
- argstr = ISOformatTimeStamp(deltaDate.replace(tzinfo=iso8601.UTC))
2031
+ argstr = ISOformatTimeStamp(deltaDate.replace(tzinfo='UTC'))
2034
2032
  elif argstr == 'NEVER' or YYYYMMDD_PATTERN.match(argstr):
2035
2033
  if argstr == 'NEVER':
2036
2034
  argstr = NEVER_DATE
2037
2035
  try:
2038
- dateTime = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
2036
+ dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
2039
2037
  except ValueError:
2040
2038
  return (False, YYYYMMDD_FORMAT_REQUIRED)
2041
- argstr = ISOformatTimeStamp(dateTime.replace(tzinfo=iso8601.UTC))
2039
+ argstr = ISOformatTimeStamp(dateTime.replace(tzinfo='UTC'))
2042
2040
  try:
2043
- iso8601.parse_date(argstr)
2041
+ arrow.get(argstr)
2044
2042
  return (True, argstr.replace(' ', 'T'))
2045
- except (iso8601.ParseError, OverflowError):
2043
+ except (arrow.parser.ParserError, OverflowError):
2046
2044
  return (False, YYYYMMDD_FORMAT_REQUIRED)
2047
2045
 
2048
2046
  def getRowFilterTimeOrDeltaFromNow(argstr):
@@ -2056,14 +2054,14 @@ def getRowFilterTimeOrDeltaFromNow(argstr):
2056
2054
  argstr = NEVER_TIME
2057
2055
  elif YYYYMMDD_PATTERN.match(argstr):
2058
2056
  try:
2059
- dateTime = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
2057
+ dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
2060
2058
  except ValueError:
2061
2059
  return (False, YYYYMMDD_FORMAT_REQUIRED)
2062
2060
  argstr = ISOformatTimeStamp(dateTime.replace(tzinfo=GC.Values[GC.TIMEZONE]))
2063
2061
  try:
2064
- iso8601.parse_date(argstr)
2062
+ arrow.get(argstr)
2065
2063
  return (True, argstr.replace(' ', 'T'))
2066
- except (iso8601.ParseError, OverflowError):
2064
+ except (arrow.parser.ParserError, OverflowError):
2067
2065
  return (False, YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
2068
2066
 
2069
2067
  def mapQueryRelativeTimes(query, keywords):
@@ -2096,9 +2094,9 @@ class StartEndTime():
2096
2094
  self.endDateTime, _, self.endTime = self._getValueOrDeltaFromNow(True)
2097
2095
  elif myarg == 'yesterday':
2098
2096
  currDate = todaysDate()
2099
- self.startDateTime = currDate+datetime.timedelta(days=-1)
2097
+ self.startDateTime = currDate.shift(days=-1)
2100
2098
  self.startTime = ISOformatTimeStamp(self.startDateTime)
2101
- self.endDateTime = currDate+datetime.timedelta(seconds=-1)
2099
+ self.endDateTime = currDate.shift(seconds=-1)
2102
2100
  self.endTime = ISOformatTimeStamp(self.endDateTime)
2103
2101
  elif myarg == 'today':
2104
2102
  currDate = todaysDate()
@@ -2113,12 +2111,12 @@ class StartEndTime():
2113
2111
  else:
2114
2112
  firstMonth = getInteger(minVal=1, maxVal=6)
2115
2113
  currDate = todaysDate()
2116
- self.startDateTime = currDate+relativedelta(months=-firstMonth, day=1, hour=0, minute=0, second=0, microsecond=0)
2114
+ self.startDateTime = currDate.shift(months=-firstMonth, day=1, hour=0, minute=0, second=0, microsecond=0)
2117
2115
  self.startTime = ISOformatTimeStamp(self.startDateTime)
2118
2116
  if myarg == 'thismonth':
2119
2117
  self.endDateTime = todaysTime()
2120
2118
  else:
2121
- self.endDateTime = currDate+relativedelta(day=1, hour=23, minute=59, second=59, microsecond=0)+relativedelta(days=-1)
2119
+ self.endDateTime = currDate.shift(day=1, hour=23, minute=59, second=59, microsecond=0).shift(days=-1)
2122
2120
  self.endTime = ISOformatTimeStamp(self.endDateTime)
2123
2121
  if self.startDateTime and self.endDateTime and self.endDateTime < self.startDateTime:
2124
2122
  Cmd.Backup()
@@ -2334,7 +2332,7 @@ def formatLocalTime(dateTimeStr):
2334
2332
  if dateTimeStr in {NEVER_TIME, NEVER_TIME_NOMS}:
2335
2333
  return GC.Values[GC.NEVER_TIME]
2336
2334
  try:
2337
- timestamp = iso8601.parse_date(dateTimeStr)
2335
+ timestamp = arrow.get(dateTimeStr)
2338
2336
  if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
2339
2337
  if GM.Globals[GM.CONVERT_TO_LOCAL_TIME]:
2340
2338
  return ISOformatTimeStamp(timestamp.astimezone(GC.Values[GC.TIMEZONE]))
@@ -2342,27 +2340,27 @@ def formatLocalTime(dateTimeStr):
2342
2340
  if GM.Globals[GM.CONVERT_TO_LOCAL_TIME]:
2343
2341
  return timestamp.astimezone(GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2344
2342
  return timestamp.strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2345
- except (iso8601.ParseError, OverflowError):
2343
+ except (arrow.parser.ParserError, OverflowError):
2346
2344
  return dateTimeStr
2347
2345
 
2348
2346
  def formatLocalSecondsTimestamp(timestamp):
2349
2347
  if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
2350
- return ISOformatTimeStamp(datetime.datetime.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]))
2351
- return datetime.datetime.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2348
+ return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]))
2349
+ return arrow.Arrow.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2352
2350
 
2353
2351
  def formatLocalTimestamp(timestamp):
2354
2352
  if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
2355
- return ISOformatTimeStamp(datetime.datetime.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]))
2356
- return datetime.datetime.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2353
+ return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]))
2354
+ return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
2357
2355
 
2358
2356
  def formatLocalTimestampUTC(timestamp):
2359
- return ISOformatTimeStamp(datetime.datetime.fromtimestamp(int(timestamp)//1000, iso8601.UTC))
2357
+ return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp)//1000, 'UTC'))
2360
2358
 
2361
2359
  def formatLocalDatestamp(timestamp):
2362
2360
  try:
2363
2361
  if not GC.Values[GC.OUTPUT_DATEFORMAT]:
2364
- return datetime.datetime.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(YYYYMMDD_FORMAT)
2365
- return datetime.datetime.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_DATEFORMAT])
2362
+ return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(YYYYMMDD_FORMAT)
2363
+ return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_DATEFORMAT])
2366
2364
  except OverflowError:
2367
2365
  return NEVER_DATE
2368
2366
 
@@ -3706,18 +3704,18 @@ def SetGlobalVariables():
3706
3704
 
3707
3705
  def _getCfgTimezone(sectionName, itemName):
3708
3706
  value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName).lower())
3709
- if value == 'utc':
3707
+ if value in {'utc', 'z'}:
3710
3708
  GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
3711
- return iso8601.UTC
3709
+ return arrow.now('utc').tzinfo
3712
3710
  GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = True
3713
3711
  if value == 'local':
3714
- return iso8601.Local
3712
+ return arrow.now(value).tzinfo
3715
3713
  try:
3716
- return iso8601.parse_timezone_str(value)
3717
- except (iso8601.ParseError, OverflowError):
3714
+ return arrow.now(value).tzinfo
3715
+ except (arrow.parser.ParserError, OverflowError):
3718
3716
  _printValueError(sectionName, itemName, value, f'{Msg.EXPECTED}: {TIMEZONE_FORMAT_REQUIRED}')
3719
3717
  GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
3720
- return iso8601.UTC
3718
+ return arrow.now('utc').tzinfo
3721
3719
 
3722
3720
  def _getCfgDirectory(sectionName, itemName):
3723
3721
  dirPath = os.path.expanduser(_stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName)))
@@ -4043,7 +4041,7 @@ def SetGlobalVariables():
4043
4041
  GC.Values[itemName] = _getCfgDirectory(sectionName, itemName)
4044
4042
  elif varType == GC.TYPE_TIMEZONE:
4045
4043
  GC.Values[itemName] = _getCfgTimezone(sectionName, itemName)
4046
- GM.Globals[GM.DATETIME_NOW] = datetime.datetime.now(GC.Values[GC.TIMEZONE])
4044
+ GM.Globals[GM.DATETIME_NOW] = arrow.now(GC.Values[GC.TIMEZONE])
4047
4045
  # Everything else except row filters
4048
4046
  for itemName, itemEntry in sorted(GC.VAR_INFO.items()):
4049
4047
  varType = itemEntry[GC.VAR_TYPE]
@@ -4389,12 +4387,11 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
4389
4387
  class signjwtJWTCredentials(google.auth.jwt.Credentials):
4390
4388
  ''' Class used for DASA '''
4391
4389
  def _make_jwt(self):
4392
- now = datetime.datetime.utcnow()
4393
- lifetime = datetime.timedelta(seconds=self._token_lifetime)
4394
- expiry = now + lifetime
4390
+ now = arrow.utcnow()
4391
+ expiry = now.shift(seconds=self._token_lifetime)
4395
4392
  payload = {
4396
- "iat": google.auth._helpers.datetime_to_secs(now),
4397
- "exp": google.auth._helpers.datetime_to_secs(expiry),
4393
+ "iat": now.int_timestamp,
4394
+ "exp": expiry.int_timestamp,
4398
4395
  "iss": self._issuer,
4399
4396
  "sub": self._subject,
4400
4397
  }
@@ -4402,7 +4399,7 @@ class signjwtJWTCredentials(google.auth.jwt.Credentials):
4402
4399
  payload["aud"] = self._audience
4403
4400
  payload.update(self._additional_claims)
4404
4401
  jwt = self._signer.sign(payload)
4405
- return jwt, expiry
4402
+ return jwt, expiry.naive
4406
4403
 
4407
4404
  # Some Workforce Identity Federation endpoints such as GitHub Actions
4408
4405
  # only allow TLS 1.2 as of April 2023.
@@ -4415,15 +4412,14 @@ class signjwtCredentials(google.oauth2.service_account.Credentials):
4415
4412
  ''' Class used for DwD '''
4416
4413
 
4417
4414
  def _make_authorization_grant_assertion(self):
4418
- now = datetime.datetime.utcnow()
4419
- lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
4420
- expiry = now + lifetime
4415
+ now = arrow.utcnow()
4416
+ expiry = now.shift(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
4421
4417
  payload = {
4422
- "iat": google.auth._helpers.datetime_to_secs(now),
4423
- "exp": google.auth._helpers.datetime_to_secs(expiry),
4424
- "iss": self._service_account_email,
4425
- "aud": API.GOOGLE_OAUTH2_TOKEN_ENDPOINT,
4426
- "scope": google.auth._helpers.scopes_to_string(self._scopes or ()),
4418
+ "iat": now.int_timestamp,
4419
+ "exp": expiry.int_timestamp,
4420
+ "iss": self._service_account_email,
4421
+ "aud": API.GOOGLE_OAUTH2_TOKEN_ENDPOINT,
4422
+ "scope": google.auth._helpers.scopes_to_string(self._scopes or ()),
4427
4423
  }
4428
4424
  payload.update(self._additional_claims)
4429
4425
  # The subject can be a user email for domain-wide delegation.
@@ -4537,7 +4533,7 @@ def getOauth2TxtCredentials(exitOnError=True, api=None, noDASA=False, refreshOnl
4537
4533
  creds.token = jsonDict['access_token']
4538
4534
  creds._id_token = jsonDict['id_token_jwt']
4539
4535
  GM.Globals[GM.DECODED_ID_TOKEN] = jsonDict['id_token']
4540
- creds.expiry = datetime.datetime.strptime(token_expiry, YYYYMMDDTHHMMSSZ_FORMAT)
4536
+ creds.expiry = arrow.Arrow.strptime(token_expiry, YYYYMMDDTHHMMSSZ_FORMAT, tzinfo='UTC').naive
4541
4537
  return (not noScopes, creds)
4542
4538
  if jsonDict and exitOnError:
4543
4539
  invalidOauth2TxtExit(Msg.INVALID)
@@ -7427,15 +7423,15 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
7427
7423
  def stripTimeFromDateTime(rowDate):
7428
7424
  if YYYYMMDD_PATTERN.match(rowDate):
7429
7425
  try:
7430
- rowTime = datetime.datetime.strptime(rowDate, YYYYMMDD_FORMAT)
7426
+ rowTime = arrow.Arrow.strptime(rowDate, YYYYMMDD_FORMAT)
7431
7427
  except ValueError:
7432
7428
  return None
7433
7429
  else:
7434
7430
  try:
7435
- rowTime = iso8601.parse_date(rowDate)
7436
- except (iso8601.ParseError, OverflowError):
7431
+ rowTime = arrow.get(rowDate)
7432
+ except (arrow.parser.ParserError, OverflowError):
7437
7433
  return None
7438
- return ISOformatTimeStamp(datetime.datetime(rowTime.year, rowTime.month, rowTime.day, tzinfo=iso8601.UTC))
7434
+ return ISOformatTimeStamp(arrow.Arrow(rowTime.year, rowTime.month, rowTime.day, tzinfo='UTC'))
7439
7435
 
7440
7436
  def rowDateTimeFilterMatch(dateMode, op, filterDate):
7441
7437
  def checkMatch(rowDate):
@@ -7497,8 +7493,8 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
7497
7493
  if YYYYMMDD_PATTERN.match(rowDate):
7498
7494
  return None
7499
7495
  try:
7500
- rowTime = iso8601.parse_date(rowDate)
7501
- except (iso8601.ParseError, OverflowError):
7496
+ rowTime = arrow.get(rowDate)
7497
+ except (arrow.parser.ParserError, OverflowError):
7502
7498
  return None
7503
7499
  return f'{rowTime.hour:02d}:{rowTime.minute:02d}'
7504
7500
 
@@ -8699,10 +8695,10 @@ class CSVPrintFile():
8699
8695
  sheetTitle = self.todrive['sheetEntity']['sheetTitle']
8700
8696
  else:
8701
8697
  sheetTitle = self.todrive['sheettitle']
8702
- tdbasetime = tdtime = datetime.datetime.now(GC.Values[GC.TIMEZONE])
8698
+ tdbasetime = tdtime = arrow.now(GC.Values[GC.TIMEZONE])
8703
8699
  if self.todrive['daysoffset'] is not None or self.todrive['hoursoffset'] is not None:
8704
- tdtime = tdbasetime+relativedelta(days=-self.todrive['daysoffset'] if self.todrive['daysoffset'] is not None else 0,
8705
- hours=-self.todrive['hoursoffset'] if self.todrive['hoursoffset'] is not None else 0)
8700
+ tdtime = tdbasetime.shift(days=-self.todrive['daysoffset'] if self.todrive['daysoffset'] is not None else 0,
8701
+ hours=-self.todrive['hoursoffset'] if self.todrive['hoursoffset'] is not None else 0)
8706
8702
  if self.todrive['timestamp']:
8707
8703
  if title:
8708
8704
  title += ' - '
@@ -8712,8 +8708,8 @@ class CSVPrintFile():
8712
8708
  title += tdtime.strftime(self.todrive['timeformat'])
8713
8709
  if self.todrive['sheettimestamp']:
8714
8710
  if self.todrive['sheetdaysoffset'] is not None or self.todrive['sheethoursoffset'] is not None:
8715
- tdtime = tdbasetime+relativedelta(days=-self.todrive['sheetdaysoffset'] if self.todrive['sheetdaysoffset'] is not None else 0,
8716
- hours=-self.todrive['sheethoursoffset'] if self.todrive['sheethoursoffset'] is not None else 0)
8711
+ tdtime = tdbasetime.shift(days=-self.todrive['sheetdaysoffset'] if self.todrive['sheetdaysoffset'] is not None else 0,
8712
+ hours=-self.todrive['sheethoursoffset'] if self.todrive['sheethoursoffset'] is not None else 0)
8717
8713
  if sheetTitle:
8718
8714
  sheetTitle += ' - '
8719
8715
  if not self.todrive['sheettimeformat']:
@@ -9302,7 +9298,7 @@ def getLocalGoogleTimeOffset(testLocation=GOOGLE_TIMECHECK_LOCATION):
9302
9298
  for prot in ['http', 'https']:
9303
9299
  try:
9304
9300
  headerData = httpObj.request(f'{prot}://'+testLocation, 'HEAD')
9305
- googleUTC = datetime.datetime.strptime(headerData[0]['date'], '%a, %d %b %Y %H:%M:%S %Z').replace(tzinfo=iso8601.UTC)
9301
+ googleUTC = arrow.Arrow.strptime(headerData[0]['date'], '%a, %d %b %Y %H:%M:%S %Z', tzinfo='UTC')
9306
9302
  except (httplib2.HttpLib2Error, RuntimeError) as e:
9307
9303
  handleServerError(e)
9308
9304
  except httplib2.socks.HTTPError as e:
@@ -9315,7 +9311,7 @@ def getLocalGoogleTimeOffset(testLocation=GOOGLE_TIMECHECK_LOCATION):
9315
9311
  if prot == 'http':
9316
9312
  continue
9317
9313
  systemErrorExit(NETWORK_ERROR_RC, Msg.INVALID_HTTP_HEADER.format(str(headerData)))
9318
- offset = remainder = int(abs((datetime.datetime.now(iso8601.UTC)-googleUTC).total_seconds()))
9314
+ offset = remainder = int(abs((arrow.utcnow()-googleUTC).total_seconds()))
9319
9315
  if offset < MAX_LOCAL_GOOGLE_TIME_OFFSET and prot == 'http':
9320
9316
  continue
9321
9317
  timeoff = []
@@ -11029,7 +11025,7 @@ class Credentials(google.oauth2.credentials.Credentials):
11029
11025
  expiry = info.get('token_expiry')
11030
11026
  if expiry:
11031
11027
  # Convert the raw expiry to datetime
11032
- expiry = datetime.datetime.strptime(expiry, YYYYMMDDTHHMMSSZ_FORMAT)
11028
+ expiry = arrow.Arrow.strptime(expiry, YYYYMMDDTHHMMSSZ_FORMAT, tzinfo='UTC').naive
11033
11029
  id_token_data = info.get('decoded_id_token')
11034
11030
 
11035
11031
  # Provide backwards compatibility with field names when loading from JSON.
@@ -11293,7 +11289,7 @@ def doOAuthInfo():
11293
11289
  if 'email' in token_info:
11294
11290
  printKeyValueList(['Google Workspace Admin', f'{token_info["email"]}'])
11295
11291
  if 'expires_in' in token_info:
11296
- printKeyValueList(['Expires', ISOformatTimeStamp((datetime.datetime.now()+datetime.timedelta(seconds=token_info['expires_in'])).replace(tzinfo=GC.Values[GC.TIMEZONE]))])
11292
+ printKeyValueList(['Expires', ISOformatTimeStamp(arrow.now(GC.Values[GC.TIMEZONE]).shift(seconds=token_info['expires_in']))])
11297
11293
  if showDetails:
11298
11294
  for k, v in sorted(token_info.items()):
11299
11295
  if k not in ['email', 'expires_in', 'issued_to', 'scope']:
@@ -12409,7 +12405,7 @@ def checkServiceAccount(users):
12409
12405
  throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.NOT_FOUND,
12410
12406
  GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
12411
12407
  name=name, fields='validAfterTime')
12412
- key_created = iso8601.parse_date(key['validAfterTime'])
12408
+ key_created = arrow.get(key['validAfterTime'])
12413
12409
  key_age = todaysTime()-key_created
12414
12410
  printPassFail(Msg.SERVICE_ACCOUNT_PRIVATE_KEY_AGE.format(key_age.days), testWarn if key_age.days > 30 else testPass)
12415
12411
  except GAPI.permissionDenied:
@@ -12664,15 +12660,15 @@ def _generatePrivateKeyAndPublicCert(projectId, clientEmail, name, key_size, b64
12664
12660
  _validate=False)]))
12665
12661
  # Gooogle seems to enforce the not before date strictly. Set the not before
12666
12662
  # date to be UTC two minutes ago which should cover any clock skew.
12667
- now = datetime.datetime.utcnow()
12668
- builder = builder.not_valid_before(now - datetime.timedelta(minutes=2))
12663
+ now = arrow.utcnow()
12664
+ builder = builder.not_valid_before(now.shift(minutes=-2).naive)
12669
12665
  # Google defaults to 12/31/9999 date for end time if there's no
12670
12666
  # policy to restrict key age
12671
12667
  if validityHours:
12672
- expires = now + datetime.timedelta(hours=validityHours) - datetime.timedelta(minutes=2)
12668
+ expires = now.shift(hours=validityHours, minutes=-2).naive
12673
12669
  builder = builder.not_valid_after(expires)
12674
12670
  else:
12675
- builder = builder.not_valid_after(datetime.datetime(9999, 12, 31, 23, 59))
12671
+ builder = builder.not_valid_after(arrow.Arrow(9999, 12, 31, 23, 59).naive)
12676
12672
  builder = builder.serial_number(x509.random_serial_number())
12677
12673
  builder = builder.public_key(public_key)
12678
12674
  builder = builder.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
@@ -13049,7 +13045,7 @@ def _showMailboxMonitorRequestStatus(request, i=0, count=0):
13049
13045
  def doCreateMonitor():
13050
13046
  auditObject, parameters = getAuditParameters(emailAddressRequired=True, requestIdRequired=False, destUserRequired=True)
13051
13047
  #end_date defaults to 30 days in the future...
13052
- end_date = (GM.Globals[GM.DATETIME_NOW]+datetime.timedelta(days=30)).strftime(YYYYMMDD_HHMM_FORMAT)
13048
+ end_date = GM.Globals[GM.DATETIME_NOW].shift(days=30).strftime(YYYYMMDD_HHMM_FORMAT)
13053
13049
  begin_date = None
13054
13050
  incoming_headers_only = outgoing_headers_only = drafts_headers_only = chats_headers_only = False
13055
13051
  drafts = chats = True
@@ -13222,7 +13218,7 @@ def _adjustTryDate(errMsg, numDateChanges, limitDateChanges, prevTryDate):
13222
13218
  else:
13223
13219
  match_date = re.match('End date greater than LastReportedDate.', errMsg)
13224
13220
  if match_date:
13225
- tryDateTime = datetime.datetime.strptime(prevTryDate, YYYYMMDD_FORMAT)-datetime.timedelta(days=1)
13221
+ tryDateTime = arrow.Arrow.strptime(prevTryDate, YYYYMMDD_FORMAT).shift(days=-1)
13226
13222
  tryDate = tryDateTime.strftime(YYYYMMDD_FORMAT)
13227
13223
  if (not match_date) or (numDateChanges > limitDateChanges >= 0):
13228
13224
  printWarningMessage(DATA_NOT_AVALIABLE_RC, errMsg)
@@ -13233,18 +13229,17 @@ def _checkDataRequiredServices(result, tryDate, dataRequiredServices, parameterS
13233
13229
  # -1: Data not available:
13234
13230
  # 0: Backup to earlier date
13235
13231
  # 1: Data available
13236
- oneDay = datetime.timedelta(days=1)
13237
13232
  dataWarnings = result.get('warnings', [])
13238
13233
  usageReports = result.get('usageReports', [])
13239
13234
  # move to day before if we don't have at least one usageReport with parameters
13240
13235
  if not usageReports or not usageReports[0].get('parameters', []):
13241
- tryDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)-oneDay
13236
+ tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
13242
13237
  return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
13243
13238
  for warning in dataWarnings:
13244
13239
  if warning['code'] == 'PARTIAL_DATA_AVAILABLE':
13245
13240
  for app in warning['data']:
13246
13241
  if app['key'] == 'application' and app['value'] != 'docs' and app['value'] in dataRequiredServices:
13247
- tryDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)-oneDay
13242
+ tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
13248
13243
  return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
13249
13244
  elif warning['code'] == 'DATA_NOT_AVAILABLE':
13250
13245
  for app in warning['data']:
@@ -13261,11 +13256,11 @@ def _checkDataRequiredServices(result, tryDate, dataRequiredServices, parameterS
13261
13256
  if not requiredServices:
13262
13257
  break
13263
13258
  else:
13264
- tryDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)-oneDay
13259
+ tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
13265
13260
  return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
13266
13261
  if checkUserEmail:
13267
13262
  if 'entity' not in usageReports[0] or 'userEmail' not in usageReports[0]['entity']:
13268
- tryDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)-oneDay
13263
+ tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
13269
13264
  return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
13270
13265
  return (1, tryDate, usageReports)
13271
13266
 
@@ -13379,11 +13374,13 @@ REPORTS_PARAMETERS_SIMPLE_TYPES = ['intValue', 'boolValue', 'datetimeValue', 'st
13379
13374
  # [(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
13380
13375
  # [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
13381
13376
  # thismonth|(previousmonths <Integer>)]
13377
+ # [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
13382
13378
  # [fields|parameters <String>)]
13383
13379
  # [convertmbtogb]
13384
13380
  # gam report usage customer [todrive <ToDriveAttribute>*]
13385
13381
  # [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
13386
13382
  # thismonth|(previousmonths <Integer>)]
13383
+ # [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
13387
13384
  # [fields|parameters <String>)]
13388
13385
  # [convertmbtogb]
13389
13386
  def doReportUsage():
@@ -13405,8 +13402,8 @@ def doReportUsage():
13405
13402
  invalidArgumentExit(DELTA_DATE_FORMAT_REQUIRED)
13406
13403
  return deltaDate
13407
13404
  try:
13408
- argDate = datetime.datetime.strptime(argstr, YYYYMMDD_FORMAT)
13409
- return datetime.datetime(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE])
13405
+ argDate = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
13406
+ return arrow.Arrow(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE])
13410
13407
  except ValueError:
13411
13408
  Cmd.Backup()
13412
13409
  invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
@@ -13437,7 +13434,6 @@ def doReportUsage():
13437
13434
  startEndTime = StartEndTime('startdate', 'enddate', 'date')
13438
13435
  skipDayNumbers = []
13439
13436
  skipDates = set()
13440
- oneDay = datetime.timedelta(days=1)
13441
13437
  while Cmd.ArgumentsRemaining():
13442
13438
  myarg = getArgument()
13443
13439
  if csvPF and myarg == 'todrive':
@@ -13475,10 +13471,10 @@ def doReportUsage():
13475
13471
  usageErrorExit(Msg.INVALID_DATE_TIME_RANGE.format(myarg, skipEnd, myarg, skipStart))
13476
13472
  while skipStartDate <= skipEndDate:
13477
13473
  skipDates.add(skipStartDate)
13478
- skipStartDate += oneDay
13474
+ skipStartDate = skipStartDate.shift(days=1)
13479
13475
  elif myarg == 'skipdaysofweek':
13480
- skipdaynames = getString(Cmd.OB_STRING).split(',')
13481
- dow = [d.lower() for d in calendarlib.day_abbr]
13476
+ skipdaynames = getString(Cmd.OB_STRING).lower().split(',')
13477
+ dow = [d.lower() for d in DAYS_OF_WEEK]
13482
13478
  skipDayNumbers = [dow.index(d) for d in skipdaynames if d in dow]
13483
13479
  elif userReports and myarg == 'user':
13484
13480
  userKey = getString(Cmd.OB_EMAIL_ADDRESS)
@@ -13497,7 +13493,7 @@ def doReportUsage():
13497
13493
  if startEndTime.endDateTime is None:
13498
13494
  startEndTime.endDateTime = todaysDate()
13499
13495
  if startEndTime.startDateTime is None:
13500
- startEndTime.startDateTime = startEndTime.endDateTime+datetime.timedelta(days=-30)
13496
+ startEndTime.startDateTime = startEndTime.endDateTime.shift(days=-30)
13501
13497
  startDateTime = startEndTime.startDateTime
13502
13498
  startDate = startDateTime.strftime(YYYYMMDD_FORMAT)
13503
13499
  endDateTime = startEndTime.endDateTime
@@ -13530,10 +13526,10 @@ def doReportUsage():
13530
13526
  parameters = ','.join(parameters) if parameters else None
13531
13527
  while startDateTime <= endDateTime:
13532
13528
  if startDateTime.weekday() in skipDayNumbers or startDateTime in skipDates:
13533
- startDateTime += oneDay
13529
+ startDateTime = startDateTime.shift(days=1)
13534
13530
  continue
13535
13531
  useDate = startDateTime.strftime(YYYYMMDD_FORMAT)
13536
- startDateTime += oneDay
13532
+ startDateTime = startDateTime.shift(days=1)
13537
13533
  try:
13538
13534
  for kwarg in kwargs:
13539
13535
  if userReports:
@@ -13724,7 +13720,7 @@ def doReport():
13724
13720
  mg = DISABLED_REASON_TIME_PATTERN.match(item['stringValue'])
13725
13721
  if mg:
13726
13722
  try:
13727
- disabledTime = formatLocalTime(datetime.datetime.strptime(mg.group(1), '%Y/%m/%d-%H:%M:%S').replace(tzinfo=iso8601.UTC).strftime(YYYYMMDDTHHMMSSZ_FORMAT))
13723
+ disabledTime = formatLocalTime(arrow.Arrow.strptime(mg.group(1), '%Y/%m/%d-%H:%M:%S').replace(tzinfo='UTC').strftime(YYYYMMDDTHHMMSSZ_FORMAT))
13728
13724
  row['accounts:disabled_time'] = disabledTime
13729
13725
  csvPF.AddTitles('accounts:disabled_time')
13730
13726
  except ValueError:
@@ -13959,7 +13955,6 @@ def doReport():
13959
13955
  eventCounts = {}
13960
13956
  eventNames = []
13961
13957
  startEndTime = StartEndTime('start', 'end')
13962
- oneDay = datetime.timedelta(days=1)
13963
13958
  filterTimes = {}
13964
13959
  maxActivities = maxEvents = 0
13965
13960
  maxResults = 1000
@@ -14171,7 +14166,7 @@ def doReport():
14171
14166
  if fullData == 0:
14172
14167
  if numDateChanges > limitDateChanges >= 0:
14173
14168
  break
14174
- startDateTime = endDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)
14169
+ startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
14175
14170
  continue
14176
14171
  if not select and userKey == 'all':
14177
14172
  pageMessage = getPageMessageForWhom(forWhom, showDate=tryDate)
@@ -14200,7 +14195,7 @@ def doReport():
14200
14195
  tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
14201
14196
  if not tryDate:
14202
14197
  break
14203
- startDateTime = endDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)
14198
+ startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
14204
14199
  continue
14205
14200
  except GAPI.invalidInput as e:
14206
14201
  systemErrorExit(GOOGLE_API_ERROR_RC, str(e))
@@ -14213,7 +14208,7 @@ def doReport():
14213
14208
  break
14214
14209
  except GAPI.forbidden as e:
14215
14210
  accessErrorExit(None, str(e))
14216
- startDateTime += oneDay
14211
+ startDateTime = startDateTime.shift(days=1)
14217
14212
  if exitUserLoop:
14218
14213
  break
14219
14214
  if user != 'all' and lastDate is None and GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
@@ -14270,7 +14265,7 @@ def doReport():
14270
14265
  if fullData == 0:
14271
14266
  if numDateChanges > limitDateChanges >= 0:
14272
14267
  break
14273
- startDateTime = endDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)
14268
+ startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
14274
14269
  continue
14275
14270
  usage = callGAPIpages(service, 'get', 'usageReports',
14276
14271
  throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.FORBIDDEN],
@@ -14288,13 +14283,13 @@ def doReport():
14288
14283
  tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
14289
14284
  if not tryDate:
14290
14285
  break
14291
- startDateTime = endDateTime = datetime.datetime.strptime(tryDate, YYYYMMDD_FORMAT)
14286
+ startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
14292
14287
  continue
14293
14288
  except GAPI.invalidInput as e:
14294
14289
  systemErrorExit(GOOGLE_API_ERROR_RC, str(e))
14295
14290
  except GAPI.forbidden as e:
14296
14291
  accessErrorExit(None, str(e))
14297
- startDateTime += oneDay
14292
+ startDateTime = startDateTime.shift(days=1)
14298
14293
  csvPF.writeCSVfile(f'Customer Report - {tryDate}')
14299
14294
  else: # activityReports
14300
14295
  csvPF.SetTitles('name')
@@ -14364,8 +14359,8 @@ def doReport():
14364
14359
  eventTime = activity.get('id', {}).get('time', UNKNOWN)
14365
14360
  if eventTime != UNKNOWN:
14366
14361
  try:
14367
- eventTime = iso8601.parse_date(eventTime)
14368
- except (iso8601.ParseError, OverflowError):
14362
+ eventTime = arrow.get(eventTime)
14363
+ except (arrow.parser.ParserError, OverflowError):
14369
14364
  eventTime = UNKNOWN
14370
14365
  if eventTime != UNKNOWN:
14371
14366
  eventDate = eventTime.strftime(YYYYMMDD_FORMAT)
@@ -14389,7 +14384,7 @@ def doReport():
14389
14384
  if val is not None:
14390
14385
  val = int(val)
14391
14386
  if val >= 62135683200:
14392
- event[item['name']] = ISOformatTimeStamp(datetime.datetime.fromtimestamp(val-62135683200, GC.Values[GC.TIMEZONE]))
14387
+ event[item['name']] = ISOformatTimeStamp(arrow.Arrow.fromtimestamp(val-62135683200, GC.Values[GC.TIMEZONE]))
14393
14388
  else:
14394
14389
  event[item['name']] = val
14395
14390
  else:
@@ -23844,7 +23839,7 @@ def _filterActiveTimeRanges(cros, selected, listLimit, startDate, endDate, activ
23844
23839
  activeTimeRanges.reverse()
23845
23840
  i = 0
23846
23841
  for item in activeTimeRanges:
23847
- activityDate = datetime.datetime.strptime(item['date'], YYYYMMDD_FORMAT)
23842
+ activityDate = arrow.Arrow.strptime(item['date'], YYYYMMDD_FORMAT)
23848
23843
  if ((startDate is None) or (activityDate >= startDate)) and ((endDate is None) or (activityDate <= endDate)):
23849
23844
  item['duration'] = formatMilliSeconds(item['activeTime'])
23850
23845
  item['minutes'] = item['activeTime']//60000
@@ -23863,7 +23858,7 @@ def _filterDeviceFiles(cros, selected, listLimit, startTime, endTime):
23863
23858
  filteredItems = []
23864
23859
  i = 0
23865
23860
  for item in cros.get('deviceFiles', []):
23866
- timeValue = iso8601.parse_date(item['createTime'])
23861
+ timeValue = arrow.get(item['createTime'])
23867
23862
  if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
23868
23863
  item['createTime'] = formatLocalTime(item['createTime'])
23869
23864
  filteredItems.append(item)
@@ -23880,7 +23875,7 @@ def _filterCPUStatusReports(cros, selected, listLimit, startTime, endTime):
23880
23875
  filteredItems = []
23881
23876
  i = 0
23882
23877
  for item in cros.get('cpuStatusReports', []):
23883
- timeValue = iso8601.parse_date(item['reportTime'])
23878
+ timeValue = arrow.get(item['reportTime'])
23884
23879
  if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
23885
23880
  item['reportTime'] = formatLocalTime(item['reportTime'])
23886
23881
  for tempInfo in item.get('cpuTemperatureInfo', []):
@@ -23901,7 +23896,7 @@ def _filterSystemRamFreeReports(cros, selected, listLimit, startTime, endTime):
23901
23896
  filteredItems = []
23902
23897
  i = 0
23903
23898
  for item in cros.get('systemRamFreeReports', []):
23904
- timeValue = iso8601.parse_date(item['reportTime'])
23899
+ timeValue = arrow.get(item['reportTime'])
23905
23900
  if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
23906
23901
  item['reportTime'] = formatLocalTime(item['reportTime'])
23907
23902
  item['systemRamFreeInfo'] = ','.join([str(x) for x in item['systemRamFreeInfo']])
@@ -23934,7 +23929,7 @@ def _filterScreenshotFiles(cros, selected, listLimit, startTime, endTime):
23934
23929
  filteredItems = []
23935
23930
  i = 0
23936
23931
  for item in cros.get('screenshotFiles', []):
23937
- timeValue = iso8601.parse_date(item['createTime'])
23932
+ timeValue = arrow.get(item['createTime'])
23938
23933
  if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
23939
23934
  item['createTime'] = formatLocalTime(item['createTime'])
23940
23935
  filteredItems.append(item)
@@ -23971,7 +23966,7 @@ def _computeDVRstorageFreePercentage(cros):
23971
23966
 
23972
23967
  def _getFilterDateTime():
23973
23968
  filterDate = getYYYYMMDD(returnDateTime=True)
23974
- return (filterDate, filterDate.replace(tzinfo=iso8601.UTC))
23969
+ return (filterDate, filterDate.replace(tzinfo='UTC'))
23975
23970
 
23976
23971
  CROS_FIELDS_CHOICE_MAP = {
23977
23972
  'activetimeranges': ['activeTimeRanges.activeTime', 'activeTimeRanges.date'],
@@ -24382,9 +24377,9 @@ def getDeviceFilesEntity():
24382
24377
  else:
24383
24378
  for timeItem in myarg.split(','):
24384
24379
  try:
24385
- timestamp = iso8601.parse_date(timeItem)
24380
+ timestamp = arrow.get(timeItem)
24386
24381
  deviceFilesEntity['list'].append(ISOformatTimeStamp(timestamp.astimezone(GC.Values[GC.TIMEZONE])))
24387
- except (iso8601.ParseError, OverflowError):
24382
+ except (arrow.parser.ParserError, OverflowError):
24388
24383
  Cmd.Backup()
24389
24384
  invalidArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
24390
24385
  return deviceFilesEntity
@@ -24411,14 +24406,14 @@ def _selectDeviceFiles(deviceId, deviceFiles, deviceFilesEntity):
24411
24406
  count = 0
24412
24407
  if deviceFilesEntity['time'][0] == 'before':
24413
24408
  for deviceFile in deviceFiles:
24414
- createTime = iso8601.parse_date(deviceFile['createTime'])
24409
+ createTime = arrow.get(deviceFile['createTime'])
24415
24410
  if createTime >= dateTime:
24416
24411
  break
24417
24412
  count += 1
24418
24413
  return deviceFiles[:count]
24419
24414
  # if deviceFilesEntity['time'][0] == 'after':
24420
24415
  for deviceFile in deviceFiles:
24421
- createTime = iso8601.parse_date(deviceFile['createTime'])
24416
+ createTime = arrow.get(deviceFile['createTime'])
24422
24417
  if createTime >= dateTime:
24423
24418
  break
24424
24419
  count += 1
@@ -24427,14 +24422,14 @@ def _selectDeviceFiles(deviceId, deviceFiles, deviceFilesEntity):
24427
24422
  dateTime = deviceFilesEntity['range'][1]
24428
24423
  spos = 0
24429
24424
  for deviceFile in deviceFiles:
24430
- createTime = iso8601.parse_date(deviceFile['createTime'])
24425
+ createTime = arrow.get(deviceFile['createTime'])
24431
24426
  if createTime >= dateTime:
24432
24427
  break
24433
24428
  spos += 1
24434
24429
  dateTime = deviceFilesEntity['range'][2]
24435
24430
  epos = spos
24436
24431
  for deviceFile in deviceFiles[spos:]:
24437
- createTime = iso8601.parse_date(deviceFile['createTime'])
24432
+ createTime = arrow.get(deviceFile['createTime'])
24438
24433
  if createTime >= dateTime:
24439
24434
  break
24440
24435
  epos += 1
@@ -25217,7 +25212,7 @@ def doInfoPrintShowCrOSTelemetry():
25217
25212
  i = 0
25218
25213
  for item in listItems:
25219
25214
  if 'reportTime' in item:
25220
- timeValue = iso8601.parse_date(item['reportTime'])
25215
+ timeValue = arrow.get(item['reportTime'])
25221
25216
  else:
25222
25217
  timeValue = None
25223
25218
  if (timeValue is None) or (((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime))):
@@ -40058,15 +40053,15 @@ def _getEventDaysOfWeek(event):
40058
40053
  if attr in event:
40059
40054
  if 'date' in event[attr]:
40060
40055
  try:
40061
- dateTime = datetime.datetime.strptime(event[attr]['date'], YYYYMMDD_FORMAT)
40062
- event[attr]['dayOfWeek'] = calendarlib.day_abbr[dateTime.weekday()]
40056
+ dateTime = arrow.Arrow.strptime(event[attr]['date'], YYYYMMDD_FORMAT)
40057
+ event[attr]['dayOfWeek'] = DAYS_OF_WEEK[dateTime.weekday()]
40063
40058
  except ValueError:
40064
40059
  pass
40065
40060
  elif 'dateTime' in event[attr]:
40066
40061
  try:
40067
- dateTime = iso8601.parse_date(event[attr]['dateTime'])
40068
- event[attr]['dayOfWeek'] = calendarlib.day_abbr[dateTime.weekday()]
40069
- except (iso8601.ParseError, OverflowError):
40062
+ dateTime = arrow.get(event[attr]['dateTime'])
40063
+ event[attr]['dayOfWeek'] = DAYS_OF_WEEK[dateTime.weekday()]
40064
+ except (arrow.parser.ParserError, OverflowError):
40070
40065
  pass
40071
40066
 
40072
40067
  def _createCalendarEvents(user, origCal, function, calIds, count, body, parameters):
@@ -46860,7 +46855,7 @@ def doCreateInboundSSOCredential():
46860
46855
  count, Ent.Choose(Ent.INBOUND_SSO_CREDENTIALS, count)))
46861
46856
  if generateKey:
46862
46857
  privKey, pemData = _generatePrivateKeyAndPublicCert('', '', 'GAM', keySize, b64enc_pub=False)
46863
- timestamp = datetime.datetime.now(GC.Values[GC.TIMEZONE]).strftime('%Y%m%d-%I%M%S')
46858
+ timestamp = arrow.now(GC.Values[GC.TIMEZONE]).strftime('%Y%m%d-%I%M%S')
46864
46859
  priv_file = f'privatekey-{timestamp}.pem'
46865
46860
  writeFile(priv_file, privKey)
46866
46861
  writeStdout(Msg.WROTE_PRIVATE_KEY_DATA.format(priv_file))
@@ -47932,8 +47927,8 @@ class CourseAttributes():
47932
47927
  def checkDueDate(self, body):
47933
47928
  if 'dueDate' in body and 'dueTime' in body:
47934
47929
  try:
47935
- return self.currDateTime < datetime.datetime(body['dueDate']['year'], body['dueDate']['month'], body['dueDate']['day'],
47936
- body['dueTime'].get('hours', 0), body['dueTime'].get('minutes', 0), tzinfo=iso8601.UTC)
47930
+ return self.currDateTime < arrow.Arrow(body['dueDate']['year'], body['dueDate']['month'], body['dueDate']['day'],
47931
+ body['dueTime'].get('hours', 0), body['dueTime'].get('minutes', 0), tzinfo='UTC')
47937
47932
  except ValueError:
47938
47933
  pass
47939
47934
  return False
@@ -48115,7 +48110,7 @@ class CourseAttributes():
48115
48110
  entityPerformActionModifierItemValueList([Ent.COURSE, newCourse['id']], Act.MODIFIER_FROM, [Ent.COURSE, self.courseId], i, count)
48116
48111
  Ind.Increment()
48117
48112
  if not self.removeDueDate:
48118
- self.currDateTime = datetime.datetime.now(iso8601.UTC)
48113
+ self.currDateTime = arrow.utcnow()
48119
48114
  self.CopyAttributes(newCourse, i, count)
48120
48115
  if self.csvPF:
48121
48116
  self.csvPF.writeCSVfile('Course Drive File IDs')
@@ -48695,7 +48690,7 @@ def _courseItemPassesFilter(item, courseItemFilter):
48695
48690
  return False
48696
48691
  startTime = courseItemFilter['startTime']
48697
48692
  endTime = courseItemFilter['endTime']
48698
- timeValue = iso8601.parse_date(timeStr)
48693
+ timeValue = arrow.get(timeStr)
48699
48694
  return ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime))
48700
48695
 
48701
48696
  def _gettingCoursesQuery(courseSelectionParameters):
@@ -53326,22 +53321,21 @@ def getStatusEventDateTime(dateType, dateList):
53326
53321
  firstDate = getYYYYMMDD(minLen=1, returnDateTime=True).replace(tzinfo=GC.Values[GC.TIMEZONE])
53327
53322
  if dateType == 'range':
53328
53323
  lastDate = getYYYYMMDD(minLen=1, returnDateTime=True).replace(tzinfo=GC.Values[GC.TIMEZONE])
53329
- deltaDay = datetime.timedelta(days=1)
53330
- deltaWeek = datetime.timedelta(weeks=1)
53331
53324
  if dateType in {'date', 'allday'}:
53332
- dateList.append({'type': 'date', 'first': firstDate, 'last': firstDate+deltaDay,
53333
- 'ulast': firstDate+deltaDay, 'udelta': deltaDay})
53325
+ dateList.append({'type': 'date', 'first': firstDate, 'last': firstDate.shift(days=1),
53326
+ 'ulast': firstDate.shift(days=1), 'udelta': {'days': 1}})
53334
53327
  elif dateType == 'range':
53335
- dateList.append({'type': dateType, 'first': firstDate, 'last': lastDate+deltaDay,
53336
- 'ulast': lastDate, 'udelta': deltaDay})
53328
+ dateList.append({'type': dateType, 'first': firstDate, 'last': lastDate.shift(days=1),
53329
+ 'ulast': lastDate, 'udelta': {'days': 1}})
53337
53330
  elif dateType == 'daily':
53338
53331
  argRepeat = getInteger(minVal=1, maxVal=366)
53339
- dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate+datetime.timedelta(days=argRepeat),
53340
- 'ulast': firstDate+datetime.timedelta(days=argRepeat), 'udelta': deltaDay})
53332
+ dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate.shift(days=argRepeat),
53333
+ 'ulast': firstDate.shift(days=argRepeat), 'udelta': {'days': 1}})
53341
53334
  else: #weekly
53342
53335
  argRepeat = getInteger(minVal=1, maxVal=52)
53343
- dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate+deltaDay, 'pdelta': deltaWeek, 'repeats': argRepeat,
53344
- 'ulast': firstDate+datetime.timedelta(weeks=argRepeat), 'udelta': deltaWeek})
53336
+ dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate.shift(days=1),
53337
+ 'pdelta': {'weeks': 1}, 'repeats': argRepeat,
53338
+ 'ulast': firstDate.shift(weeks=argRepeat), 'udelta': {'weeks': 1}})
53345
53339
 
53346
53340
  def _showCalendarStatusEvent(primaryEmail, calId, eventEntityType, event, k, kcount, FJQC):
53347
53341
  if FJQC.formatJSON:
@@ -53529,7 +53523,7 @@ def createStatusEvent(users, eventType):
53529
53523
  if wlDate['type'] != 'timerange':
53530
53524
  body['start']['date'] = first.strftime(YYYYMMDD_FORMAT)
53531
53525
  kvList[5] = body['start']['date']
53532
- body['end']['date'] = (first+datetime.timedelta(days=1)).strftime(YYYYMMDD_FORMAT)
53526
+ body['end']['date'] = (first.shift(days=1)).strftime(YYYYMMDD_FORMAT)
53533
53527
  else:
53534
53528
  body['start']['dateTime'] = ISOformatTimeStamp(first)
53535
53529
  kvList[5] = body['start']['dateTime']
@@ -53548,7 +53542,7 @@ def createStatusEvent(users, eventType):
53548
53542
  entityActionPerformed(kvList, j, jcount)
53549
53543
  if wlDate['type'] == 'timerange':
53550
53544
  break
53551
- first += wlDate['udelta']
53545
+ first = first.shift(**wlDate['udelta'])
53552
53546
  except (GAPI.forbidden, GAPI.invalid) as e:
53553
53547
  entityActionFailedWarning([Ent.CALENDAR, user], str(e), i, count)
53554
53548
  break
@@ -53749,8 +53743,8 @@ def printShowStatusEvent(users, eventType):
53749
53743
  _getEventDaysOfWeek(event)
53750
53744
  _printCalendarEvent(user, calId, event, csvPF, FJQC)
53751
53745
  if 'pdelta' in wlDate:
53752
- first += wlDate['pdelta']
53753
- last += wlDate['pdelta']
53746
+ first = first.shift(**wlDate['pdelta'])
53747
+ last = last.shift(**wlDate['pdelta'])
53754
53748
  if csvPF:
53755
53749
  csvPF.writeCSVfile(f'Calendar {Ent.Plural(entityType)}')
53756
53750
 
@@ -56254,7 +56248,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
56254
56248
  count = 0
56255
56249
  if revisionsEntity['time'][0] == 'before':
56256
56250
  for revision in results:
56257
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56251
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56258
56252
  if modifiedDateTime >= dateTime:
56259
56253
  break
56260
56254
  revisionIds.append(revision['id'])
@@ -56264,7 +56258,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
56264
56258
  return revisionIds
56265
56259
  # time: after
56266
56260
  for revision in results:
56267
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56261
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56268
56262
  if modifiedDateTime >= dateTime:
56269
56263
  revisionIds.append(revision['id'])
56270
56264
  count += 1
@@ -56276,7 +56270,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
56276
56270
  endDateTime = revisionsEntity['range'][2]
56277
56271
  count = 0
56278
56272
  for revision in results:
56279
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56273
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56280
56274
  if modifiedDateTime >= startDateTime:
56281
56275
  if modifiedDateTime >= endDateTime:
56282
56276
  break
@@ -56486,7 +56480,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
56486
56480
  count = 0
56487
56481
  if revisionsEntity['time'][0] == 'before':
56488
56482
  for revision in results:
56489
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56483
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56490
56484
  if modifiedDateTime >= dateTime:
56491
56485
  break
56492
56486
  count += 1
@@ -56497,7 +56491,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
56497
56491
  return results[:count]
56498
56492
  # time: after
56499
56493
  for revision in results:
56500
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56494
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56501
56495
  if modifiedDateTime >= dateTime:
56502
56496
  break
56503
56497
  count += 1
@@ -56514,7 +56508,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
56514
56508
  count = 0
56515
56509
  selectedResults = []
56516
56510
  for revision in results:
56517
- modifiedDateTime = iso8601.parse_date(revision['modifiedTime'])
56511
+ modifiedDateTime = arrow.get(revision['modifiedTime'])
56518
56512
  if modifiedDateTime >= startDateTime:
56519
56513
  if modifiedDateTime >= endDateTime:
56520
56514
  break
@@ -57052,7 +57046,7 @@ class PermissionMatch():
57052
57046
  break
57053
57047
  elif field in {'expirationstart', 'expirationend'}:
57054
57048
  if 'expirationTime' in permission:
57055
- expirationDateTime = iso8601.parse_date(permission['expirationTime'])
57049
+ expirationDateTime = arrow.get(permission['expirationTime'])
57056
57050
  if field == 'expirationstart':
57057
57051
  if expirationDateTime < value:
57058
57052
  break
@@ -59535,7 +59529,7 @@ def processFilenameReplacements(name, replacements):
59535
59529
  return name
59536
59530
 
59537
59531
  def addTimestampToFilename(parameters, body):
59538
- tdtime = datetime.datetime.now(GC.Values[GC.TIMEZONE])
59532
+ tdtime = arrow.now(GC.Values[GC.TIMEZONE])
59539
59533
  body['name'] += ' - '
59540
59534
  if not parameters[DFA_TIMEFORMAT]:
59541
59535
  body['name'] += ISOformatTimeStamp(tdtime)
@@ -71663,8 +71657,8 @@ def _mapMessageQueryDates(parameters):
71663
71657
  if not mg:
71664
71658
  break
71665
71659
  try:
71666
- dt = datetime.datetime(int(mg.groups()[1]), int(mg.groups()[2]), int(mg.groups()[3]), tzinfo=GC.Values[GC.TIMEZONE])
71667
- query = query[:mg.start(2)]+str(int(datetime.datetime.timestamp(dt)))+query[mg.end(4):]
71660
+ dt = arrow.Arrow(int(mg.groups()[1]), int(mg.groups()[2]), int(mg.groups()[3]), tzinfo=GC.Values[GC.TIMEZONE])
71661
+ query = query[:mg.start(2)]+str(dt.int_timestamp)+query[mg.end(4):]
71668
71662
  except ValueError:
71669
71663
  pass
71670
71664
  pos = mg.end()
@@ -72845,9 +72839,9 @@ def printShowMessagesThreads(users, entityType):
72845
72839
  if pLoc > 0:
72846
72840
  dateTimeValue = dateTimeValue[:pLoc]
72847
72841
  try:
72848
- dateTimeValue = datetime.datetime.strptime(dateTimeValue, RFC2822_TIME_FORMAT)
72842
+ dateTimeValue = arrow.Arrow.strptime(dateTimeValue, RFC2822_TIME_FORMAT)
72849
72843
  if dateHeaderConvertTimezone:
72850
- dateTimeValue = dateTimeValue.astimezone(GC.Values[GC.TIMEZONE])
72844
+ dateTimeValue = dateTimeValue.to(GC.Values[GC.TIMEZONE])
72851
72845
  return dateTimeValue.strftime(dateHeaderFormat)
72852
72846
  except ValueError:
72853
72847
  return headerValue