def __init__(self, parse_format=None, utc_mode=False, calendar_mode=None, ref_point_str=None): """Constructor. parse_format -- If specified, parse with the specified format. Otherwise, parse with one of the format strings in self.PARSE_FORMATS. The format should be a string compatible to strptime(3). utc_mode -- If True, parse/print in UTC mode rather than local or other timezones. calendar_mode -- Set calendar mode, for isodatetime.data.Calendar. ref_point_str -- Set the reference time point for operations. If not specified, operations use current date time. """ self.parse_formats = self.PARSE_FORMATS self.custom_parse_format = parse_format self.utc_mode = utc_mode if self.utc_mode: assumed_time_zone = (0, 0) else: assumed_time_zone = None self.set_calendar_mode(calendar_mode) self.time_point_dumper = TimePointDumper() self.time_point_parser = TimePointParser( assumed_time_zone=assumed_time_zone) self.duration_parser = DurationParser() self.ref_point_str = ref_point_str
def _coerce_cycletime_time_zone(value, keys, _): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = _strip_and_unquote(keys, value) if not value: return None try: set_syntax_version(VERSION_NEW, "use of [cylc]cycle point time zone format") except SyntaxVersionError: raise IllegalValueError("cycle point time zone format", keys, value) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, "CCYYMMDDThhmmss") test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError("cycle point time zone format", keys, value) return value
def coerce_cycle_point_format(cls, value, keys): """Coerce to a cycle point format (either CCYYMM... or %Y%m...).""" value = cls.strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if '/' in value: raise IllegalValueError('cycle point format', keys, value) if '%' in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError('cycle point format', keys, value) return value if 'X' in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError('cycle point format', keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError('cycle point format', keys, value) return value
def _coerce_cycletime_time_zone(value, keys, _): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = _strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, "CCYYMMDDThhmmss") test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError("cycle point time zone format", keys, value) return value
def coerce_cycle_point_time_zone(cls, value, keys): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = cls.strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, 'CCYYMMDDThhmmss') test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError( 'cycle point time zone format', keys, value) return value
def _coerce_cycletime_time_zone( value, keys, args ): """Coerce value to a cycle point time zone format - Z, +13, -0800...""" value = _strip_and_unquote( keys, value ) set_syntax_version(VERSION_NEW, "use of [cylc]cycle point time zone format", exc_class=IllegalValueError, exc_args=("cycle point time zone format", keys, value)) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) dumper = TimePointDumper() test_timepoint_string = dumper.dump(test_timepoint, "CCYYMMDDThhmmss") test_timepoint_string += value parser = TimePointParser(allow_only_basic=True) try: parser.parse(test_timepoint_string) except ValueError: raise IllegalValueError("cycle point time zone format", keys, value) return value
def _coerce_cycletime_format( value, keys, args ): """Coerce value to a cycle point format (either CCYYMM... or %Y%m...).""" value = _strip_and_unquote( keys, value ) set_syntax_version(VERSION_NEW, "use of [cylc]cycle point format", exc_class=IllegalValueError, exc_args=("cycle point format", keys, value)) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if "/" in value or ":" in value: raise IllegalValueError("cycle point format", keys, value) if "%" in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value if "X" in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError("cycle point format", keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value
def _coerce_cycletime_format(value, keys, _): """Coerce value to a cycle point format (either CCYYMM... or %Y%m...).""" value = _strip_and_unquote(keys, value) if not value: return None test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if "/" in value: raise IllegalValueError("cycle point format", keys, value) if "%" in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value if "X" in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError("cycle point format", keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value
def init(num_expanded_year_digits=0, custom_dump_format=None, time_zone=None, assume_utc=False, cycling_mode=None): """Initialise suite-setup-specific information.""" SuiteSpecifics.interval_parser = DurationParser() if cycling_mode in Calendar.default().MODES: Calendar.default().set_mode(cycling_mode) if time_zone is None: if assume_utc: time_zone = "Z" time_zone_hours_minutes = (0, 0) else: time_zone = get_local_time_zone_format(reduced_mode=True) time_zone_hours_minutes = get_local_time_zone() else: time_zone_hours_minutes = TimePointDumper().get_time_zone(time_zone) SuiteSpecifics.ASSUMED_TIME_ZONE = time_zone_hours_minutes SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS = num_expanded_year_digits if custom_dump_format is None: if num_expanded_year_digits > 0: SuiteSpecifics.DUMP_FORMAT = EXPANDED_DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = custom_dump_format if u"+X" not in custom_dump_format and num_expanded_year_digits: raise IllegalValueError('cycle point format', ('cylc', 'cycle point format'), SuiteSpecifics.DUMP_FORMAT) SuiteSpecifics.point_parser = TimePointParser( allow_only_basic=False, allow_truncated=True, num_expanded_year_digits=SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS, dump_format=SuiteSpecifics.DUMP_FORMAT, assumed_time_zone=time_zone_hours_minutes) custom_point_parse_function = None if SuiteSpecifics.DUMP_FORMAT == PREV_DATE_TIME_FORMAT: custom_point_parse_function = point_parse SuiteSpecifics.abbrev_util = CylcTimeParser( None, None, num_expanded_year_digits=SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS, dump_format=SuiteSpecifics.DUMP_FORMAT, custom_point_parse_function=custom_point_parse_function, assumed_time_zone=SuiteSpecifics.ASSUMED_TIME_ZONE)
def _coerce_cycletime_format(value, keys, _): """Coerce value to a cycle point format (either CCYYMM... or %Y%m...).""" value = _strip_and_unquote(keys, value) if not value: return None try: set_syntax_version(VERSION_NEW, "use of [cylc]cycle point format") except SyntaxVersionError: raise IllegalValueError("cycle point format", keys, value) test_timepoint = TimePoint(year=2001, month_of_year=3, day_of_month=1, hour_of_day=4, minute_of_hour=30, second_of_minute=54) if "/" in value: raise IllegalValueError("cycle point format", keys, value) if "%" in value: try: TimePointDumper().strftime(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value if "X" in value: for i in range(1, 101): dumper = TimePointDumper(num_expanded_year_digits=i) try: dumper.dump(test_timepoint, value) except ValueError: continue return value raise IllegalValueError("cycle point format", keys, value) dumper = TimePointDumper() try: dumper.dump(test_timepoint, value) except ValueError: raise IllegalValueError("cycle point format", keys, value) return value
def init(num_expanded_year_digits=0, custom_dump_format=None, time_zone=None, assume_utc=False, cycling_mode=None): """Initialise suite-setup-specific information.""" if cycling_mode in Calendar.default().MODES: Calendar.default().set_mode(cycling_mode) if time_zone is None: if assume_utc: time_zone = "Z" time_zone_hours_minutes = (0, 0) else: time_zone = get_local_time_zone_format(TimeZoneFormatMode.reduced) time_zone_hours_minutes = get_local_time_zone() else: time_zone_hours_minutes = TimePointDumper().get_time_zone(time_zone) SuiteSpecifics.ASSUMED_TIME_ZONE = time_zone_hours_minutes SuiteSpecifics.NUM_EXPANDED_YEAR_DIGITS = num_expanded_year_digits if custom_dump_format is None: if num_expanded_year_digits > 0: SuiteSpecifics.DUMP_FORMAT = EXPANDED_DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = DATE_TIME_FORMAT + time_zone else: SuiteSpecifics.DUMP_FORMAT = custom_dump_format if "+X" not in custom_dump_format and num_expanded_year_digits: raise IllegalValueError( 'cycle point format', ('cylc', 'cycle point format'), SuiteSpecifics.DUMP_FORMAT ) SuiteSpecifics.iso8601_parsers = CylcTimeParser.initiate_parsers( dump_format=SuiteSpecifics.DUMP_FORMAT, num_expanded_year_digits=num_expanded_year_digits, assumed_time_zone=SuiteSpecifics.ASSUMED_TIME_ZONE ) (SuiteSpecifics.point_parser, SuiteSpecifics.interval_parser, SuiteSpecifics.recurrence_parser) = SuiteSpecifics.iso8601_parsers SuiteSpecifics.abbrev_util = CylcTimeParser( None, None, SuiteSpecifics.iso8601_parsers )
class RoseDateTimeOperator(object): """A class to parse and print date string with an offset.""" CURRENT_TIME_DUMP_FORMAT = u"CCYY-MM-DDThh:mm:ss+hh:mm" CURRENT_TIME_DUMP_FORMAT_Z = u"CCYY-MM-DDThh:mm:ssZ" NEGATIVE = "-" # strptime formats and their compatibility with the ISO 8601 parser. PARSE_FORMATS = [ ("%a %b %d %H:%M:%S %Y", True), # ctime ("%a %b %d %H:%M:%S %Z %Y", True), # Unix "date" ("%Y-%m-%dT%H:%M:%S", False), # ISO8601, extended ("%Y%m%dT%H%M%S", False), # ISO8601, basic ("%Y%m%d%H", False) # Cylc (current) ] REC_OFFSET = re.compile(r"""\A[\+\-]?(?:\d+[wdhms])+\Z""", re.I) REC_OFFSET_FIND = re.compile(r"""(?P<num>\d+)(?P<unit>[wdhms])""") STR_NOW = "now" STR_REF = "ref" TASK_CYCLE_TIME_ENV = "ROSE_TASK_CYCLE_TIME" UNITS = { "w": "weeks", "d": "days", "h": "hours", "m": "minutes", "s": "seconds" } def __init__(self, parse_format=None, utc_mode=False, calendar_mode=None, ref_point_str=None): """Constructor. parse_format -- If specified, parse with the specified format. Otherwise, parse with one of the format strings in self.PARSE_FORMATS. The format should be a string compatible to strptime(3). utc_mode -- If True, parse/print in UTC mode rather than local or other timezones. calendar_mode -- Set calendar mode, for isodatetime.data.Calendar. ref_point_str -- Set the reference time point for operations. If not specified, operations use current date time. """ self.parse_formats = self.PARSE_FORMATS self.custom_parse_format = parse_format self.utc_mode = utc_mode if self.utc_mode: assumed_time_zone = (0, 0) else: assumed_time_zone = None self.set_calendar_mode(calendar_mode) self.time_point_dumper = TimePointDumper() self.time_point_parser = TimePointParser( assumed_time_zone=assumed_time_zone) self.duration_parser = DurationParser() self.ref_point_str = ref_point_str def date_format(self, print_format, time_point=None): """Reformat time_point according to print_format. time_point -- The time point to format. Otherwise, use ref date time. """ if time_point is None: time_point = self.date_parse()[0] if print_format is None: return str(time_point) if "%" in print_format: try: return time_point.strftime(print_format) except ValueError: return self.get_datetime_strftime(time_point, print_format) return self.time_point_dumper.dump(time_point, print_format) def date_parse(self, time_point_str=None): """Parse time_point_str. Return (t, format) where t is a isodatetime.data.TimePoint object and format is the format that matches time_point_str. time_point_str -- The time point string to parse. Otherwise, use ref time. """ if time_point_str is None or time_point_str == self.STR_REF: time_point_str = self.ref_point_str if time_point_str is None or time_point_str == self.STR_NOW: time_point = get_timepoint_for_now() time_point.set_time_zone_to_local() if self.utc_mode or time_point.get_time_zone_utc(): # is in UTC parse_format = self.CURRENT_TIME_DUMP_FORMAT_Z else: parse_format = self.CURRENT_TIME_DUMP_FORMAT elif self.custom_parse_format is not None: parse_format = self.custom_parse_format time_point = self.strptime(time_point_str, parse_format) else: parse_formats = list(self.parse_formats) time_point = None while parse_formats: parse_format, should_use_datetime = parse_formats.pop(0) try: if should_use_datetime: time_point = self.get_datetime_strptime( time_point_str, parse_format) else: time_point = self.time_point_parser.strptime( time_point_str, parse_format) break except ValueError: pass if time_point is None: time_point = self.time_point_parser.parse(time_point_str, dump_as_parsed=True) parse_format = time_point.dump_format if self.utc_mode: time_point.set_time_zone_to_utc() return time_point, parse_format def date_shift(self, time_point=None, offset=None): """Return a date string with an offset. time_point -- A time point or time point string. Otherwise, use current time. offset -- If specified, it should be a string containing the offset that has the format "[+/-]nU[nU...]" where "n" is an integer, and U is a unit matching a key in self.UNITS. """ if time_point is None: time_point = self.date_parse()[0] # Offset if offset: sign = "+" if offset.startswith("-") or offset.startswith("+"): sign = offset[0] offset = offset[1:] if offset.startswith("P"): # Parse and apply. try: duration = self.duration_parser.parse(offset) except ValueError: raise OffsetValueError(offset) if sign == "-": time_point -= duration else: time_point += duration else: # Backwards compatibility for e.g. "-1h" if not self.is_offset(offset): raise OffsetValueError(offset) for num, unit in self.REC_OFFSET_FIND.findall(offset.lower()): num = int(num) if sign == "-": num = -num key = self.UNITS[unit] time_point += Duration(**{key: num}) return time_point def date_diff(self, time_point_1=None, time_point_2=None): """Return (duration, is_negative) between two TimePoint objects. duration -- is a Duration instance. is_negative -- is a RoseDateTimeOperator.NEGATIVE if time_point_2 is in the past of time_point_1. """ if time_point_2 < time_point_1: return (time_point_1 - time_point_2, self.NEGATIVE) else: return (time_point_2 - time_point_1, "") @classmethod def date_diff_format(cls, print_format, duration, sign): """Format a duration.""" if print_format: delta_lookup = { "y": duration.years, "m": duration.months, "d": duration.days, "h": duration.hours, "M": duration.minutes, "s": duration.seconds } expression = "" for item in print_format: if item in delta_lookup: if float(delta_lookup[item]).is_integer(): expression += str(int(delta_lookup[item])) else: expression += str(delta_lookup[item]) else: expression += item return sign + expression else: return sign + str(duration) @staticmethod def get_calendar_mode(): """Get current calendar mode.""" return Calendar.default().mode def is_offset(self, offset): """Return True if the string offset can be parsed as an offset.""" return (self.REC_OFFSET.match(offset) is not None) @staticmethod def set_calendar_mode(calendar_mode=None): """Set calendar mode for subsequent operations. Raise KeyError if calendar_mode is invalid. """ if not calendar_mode: calendar_mode = os.getenv("ROSE_CYCLING_MODE") if calendar_mode and calendar_mode in Calendar.MODES: Calendar.default().set_mode(calendar_mode) def strftime(self, time_point, print_format): """Use either the isodatetime or datetime strftime time formatting.""" try: return time_point.strftime(print_format) except ValueError: return self.get_datetime_strftime(time_point, print_format) def strptime(self, time_point_str, parse_format): """Use either the isodatetime or datetime strptime time parsing.""" try: return self.time_point_parser.strptime(time_point_str, parse_format) except ValueError: return self.get_datetime_strptime(time_point_str, parse_format) @classmethod def get_datetime_strftime(cls, time_point, print_format): """Use the datetime library's strftime as a fallback.""" calendar_date = time_point.copy().to_calendar_date() year, month, day = calendar_date.get_calendar_date() hour, minute, second = time_point.get_hour_minute_second() microsecond = int(1.0e6 * (second - int(second))) hour = int(hour) minute = int(minute) second = int(second) date_time = datetime(year, month, day, hour, minute, second, microsecond) return date_time.strftime(print_format) def get_datetime_strptime(self, time_point_str, parse_format): """Use the datetime library's strptime as a fallback.""" date_time = datetime.strptime(time_point_str, parse_format) return self.time_point_parser.parse(date_time.isoformat())
class RoseDateTimeOperator(object): """A class to parse and print date string with an offset.""" CURRENT_TIME_DUMP_FORMAT = u"CCYY-MM-DDThh:mm:ss+hh:mm" CURRENT_TIME_DUMP_FORMAT_Z = u"CCYY-MM-DDThh:mm:ssZ" NEGATIVE = "-" # strptime formats and their compatibility with the ISO 8601 parser. PARSE_FORMATS = [ ("%a %b %d %H:%M:%S %Y", True), # ctime ("%a %b %d %H:%M:%S %Z %Y", True), # Unix "date" ("%Y-%m-%dT%H:%M:%S", False), # ISO8601, extended ("%Y%m%dT%H%M%S", False), # ISO8601, basic ("%Y%m%d%H", False) # Cylc (current) ] REC_OFFSET = re.compile(r"""\A[\+\-]?(?:\d+[wdhms])+\Z""", re.I) REC_OFFSET_FIND = re.compile(r"""(?P<num>\d+)(?P<unit>[wdhms])""") STR_NOW = "now" STR_REF = "ref" TASK_CYCLE_TIME_ENV = "ROSE_TASK_CYCLE_TIME" UNITS = {"w": "weeks", "d": "days", "h": "hours", "m": "minutes", "s": "seconds"} def __init__(self, parse_format=None, utc_mode=False, calendar_mode=None, ref_point_str=None): """Constructor. parse_format -- If specified, parse with the specified format. Otherwise, parse with one of the format strings in self.PARSE_FORMATS. The format should be a string compatible to strptime(3). utc_mode -- If True, parse/print in UTC mode rather than local or other timezones. calendar_mode -- Set calendar mode, for isodatetime.data.Calendar. ref_point_str -- Set the reference time point for operations. If not specified, operations use current date time. """ self.parse_formats = self.PARSE_FORMATS self.custom_parse_format = parse_format self.utc_mode = utc_mode if self.utc_mode: assumed_time_zone = (0, 0) else: assumed_time_zone = None self.set_calendar_mode(calendar_mode) self.time_point_dumper = TimePointDumper() self.time_point_parser = TimePointParser( assumed_time_zone=assumed_time_zone) self.duration_parser = DurationParser() self.ref_point_str = ref_point_str def date_format(self, print_format, time_point=None): """Reformat time_point according to print_format. time_point -- The time point to format. Otherwise, use ref date time. """ if time_point is None: time_point = self.date_parse()[0] if print_format is None: return str(time_point) if "%" in print_format: try: return time_point.strftime(print_format) except ValueError: return self.get_datetime_strftime(time_point, print_format) return self.time_point_dumper.dump(time_point, print_format) def date_parse(self, time_point_str=None): """Parse time_point_str. Return (t, format) where t is a isodatetime.data.TimePoint object and format is the format that matches time_point_str. time_point_str -- The time point string to parse. Otherwise, use ref time. """ if time_point_str is None or time_point_str == self.STR_REF: time_point_str = self.ref_point_str if time_point_str is None or time_point_str == self.STR_NOW: time_point = get_timepoint_for_now() time_point.set_time_zone_to_local() if self.utc_mode or time_point.get_time_zone_utc(): # is in UTC parse_format = self.CURRENT_TIME_DUMP_FORMAT_Z else: parse_format = self.CURRENT_TIME_DUMP_FORMAT elif self.custom_parse_format is not None: parse_format = self.custom_parse_format time_point = self.strptime(time_point_str, parse_format) else: parse_formats = list(self.parse_formats) time_point = None while parse_formats: parse_format, should_use_datetime = parse_formats.pop(0) try: if should_use_datetime: time_point = self.get_datetime_strptime( time_point_str, parse_format) else: time_point = self.time_point_parser.strptime( time_point_str, parse_format) break except ValueError: pass if time_point is None: time_point = self.time_point_parser.parse( time_point_str, dump_as_parsed=True) parse_format = time_point.dump_format if self.utc_mode: time_point.set_time_zone_to_utc() return time_point, parse_format def date_shift(self, time_point=None, offset=None): """Return a date string with an offset. time_point -- A time point or time point string. Otherwise, use current time. offset -- If specified, it should be a string containing the offset that has the format "[+/-]nU[nU...]" where "n" is an integer, and U is a unit matching a key in self.UNITS. """ if time_point is None: time_point = self.date_parse()[0] # Offset if offset: sign = "+" if offset.startswith("-") or offset.startswith("+"): sign = offset[0] offset = offset[1:] if offset.startswith("P"): # Parse and apply. try: duration = self.duration_parser.parse(offset) except ValueError: raise OffsetValueError(offset) if sign == "-": time_point -= duration else: time_point += duration else: # Backwards compatibility for e.g. "-1h" if not self.is_offset(offset): raise OffsetValueError(offset) for num, unit in self.REC_OFFSET_FIND.findall(offset.lower()): num = int(num) if sign == "-": num = -num key = self.UNITS[unit] time_point += Duration(**{key: num}) return time_point def date_diff(self, time_point_1=None, time_point_2=None): """Return (duration, is_negative) between two TimePoint objects. duration -- is a Duration instance. is_negative -- is a RoseDateTimeOperator.NEGATIVE if time_point_2 is in the past of time_point_1. """ if time_point_2 < time_point_1: return (time_point_1 - time_point_2, self.NEGATIVE) else: return (time_point_2 - time_point_1, "") @classmethod def date_diff_format(cls, print_format, duration, sign): """Format a duration.""" if print_format: delta_lookup = {"y": duration.years, "m": duration.months, "d": duration.days, "h": duration.hours, "M": duration.minutes, "s": duration.seconds} expression = "" for item in print_format: if item in delta_lookup: if float(delta_lookup[item]).is_integer(): expression += str(int(delta_lookup[item])) else: expression += str(delta_lookup[item]) else: expression += item return sign + expression else: return sign + str(duration) @staticmethod def get_calendar_mode(): """Get current calendar mode.""" return Calendar.default().mode def is_offset(self, offset): """Return True if the string offset can be parsed as an offset.""" return (self.REC_OFFSET.match(offset) is not None) @staticmethod def set_calendar_mode(calendar_mode=None): """Set calendar mode for subsequent operations. Raise KeyError if calendar_mode is invalid. """ if not calendar_mode: calendar_mode = os.getenv("ROSE_CYCLING_MODE") if calendar_mode and calendar_mode in Calendar.MODES: Calendar.default().set_mode(calendar_mode) def strftime(self, time_point, print_format): """Use either the isodatetime or datetime strftime time formatting.""" try: return time_point.strftime(print_format) except ValueError: return self.get_datetime_strftime(time_point, print_format) def strptime(self, time_point_str, parse_format): """Use either the isodatetime or datetime strptime time parsing.""" try: return self.time_point_parser.strptime(time_point_str, parse_format) except ValueError: return self.get_datetime_strptime(time_point_str, parse_format) @classmethod def get_datetime_strftime(cls, time_point, print_format): """Use the datetime library's strftime as a fallback.""" calendar_date = time_point.copy().to_calendar_date() year, month, day = calendar_date.get_calendar_date() hour, minute, second = time_point.get_hour_minute_second() microsecond = int(1.0e6 * (second - int(second))) hour = int(hour) minute = int(minute) second = int(second) date_time = datetime(year, month, day, hour, minute, second, microsecond) return date_time.strftime(print_format) def get_datetime_strptime(self, time_point_str, parse_format): """Use the datetime library's strptime as a fallback.""" date_time = datetime.strptime(time_point_str, parse_format) return self.time_point_parser.parse(date_time.isoformat())