def _coerce_cycletime(value, keys, _): """Coerce value to a cycle point.""" if not value: return None value = _strip_and_unquote(keys, value) if value == "now": # Handle this later in config.py when the suite UTC mode is known. return value if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", keys, value) return value
def _coerce_cycletime( value, keys, args ): """Coerce value to a cycle point.""" value = _strip_and_unquote( keys, value ) if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", keys, value) set_syntax_version(VERSION_NEW, "cycle point: %s" % itemstr( keys[:-1], keys[-1], value)) return value
def _coerce_cycletime(value, keys, _): """Coerce value to a cycle point.""" if not value: return None value = _strip_and_unquote(keys, value) if value == "now": # Handle this later in config.py when the suite UTC mode is known. return value if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", 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 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_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 test_invalid_components(): parser = TimePointParser() for date, invalid in { '2000-01-01T00:00:60': ['second_of_minute=60'], '2000-01-01T00:60:00': ['minute_of_hour=60'], '2000-01-01T60:00:00': ['hour_of_day=60'], '2000-01-32T00:00:00': ['day_of_month=32'], '2000-13-00T00:00:00': ['month_of_year=13'], '2000-13-32T60:60:60': ['month_of_year=13', 'day_of_month=32', 'hour_of_day=60', 'minute_of_hour=60', 'second_of_minute=60'] }.items(): with pytest.raises(ValueError) as exc: parser.parse(date) for item in invalid: assert item in str(exc)
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(value, keys, args): """Coerce value to a cycle point.""" value = _strip_and_unquote(keys, value) if re.match(r"\d+$", value): # Could be an old date-time cycle point format, or integer format. return value if value.startswith("-") or value.startswith("+"): # We don't know the value given for num expanded year digits... for i in range(1, 101): parser = TimePointParser(num_expanded_year_digits=i) try: parser.parse(value) except ValueError: continue return value raise IllegalValueError("cycle point", keys, value) parser = TimePointParser() try: parser.parse(value) except ValueError: raise IllegalValueError("cycle point", keys, value) set_syntax_version(VERSION_NEW, "cycle point: %s" % itemstr(keys[:-1], keys[-1], value)) return value
def suite_state(suite, task, point, offset=None, status='succeeded', message=None, cylc_run_dir=None, debug=False): """Connect to a suite DB and query the requested task state. Reports satisfied only if the remote suite state has been achieved. Returns all suite state args to pass on to triggering tasks. """ cylc_run_dir = os.path.expandvars( os.path.expanduser(cylc_run_dir or glbl_cfg().get_host_item('run directory'))) if offset is not None: point = str(add_offset(point, offset)) try: checker = CylcSuiteDBChecker(cylc_run_dir, suite) except (OSError, sqlite3.Error): # Failed to connect to DB; target suite may not be started. return (False, None) fmt = checker.get_remote_point_format() if fmt: my_parser = TimePointParser() point = str(my_parser.parse(point, dump_format=fmt)) if message is not None: satisfied = checker.task_state_met(task, point, message=message) else: satisfied = checker.task_state_met(task, point, status=status) results = { 'suite': suite, 'task': task, 'point': point, 'offset': offset, 'status': status, 'message': message, 'cylc_run_dir': cylc_run_dir } return (satisfied, results)
def add_offset(cycle_point, offset): """Add a (positive or negative) offset to a cycle point. Return the result. """ my_parser = TimePointParser() my_target_point = my_parser.parse(cycle_point, dump_as_parsed=True) my_offset_parser = DurationParser() oper = "+" if offset.startswith("-") or offset.startswith("+"): oper = offset[0] offset = offset[1:] if offset.startswith("P"): my_shift = my_offset_parser.parse(offset) if oper == "-": my_target_point -= my_shift else: my_target_point += my_shift else: raise ValueError("ERROR, bad offset format: %s" % offset) return my_target_point
def suite_state(suite, task, point, offset=None, status='succeeded', message=None, cylc_run_dir=None, debug=False): """Connect to a suite DB and query the requested task state. Reports satisfied only if the remote suite state has been achieved. Returns all suite state args to pass on to triggering tasks. """ cylc_run_dir = os.path.expandvars( os.path.expanduser( cylc_run_dir or glbl_cfg().get_host_item('run directory'))) if offset is not None: point = str(add_offset(point, offset)) try: checker = CylcSuiteDBChecker(cylc_run_dir, suite) except (OSError, sqlite3.Error): # Failed to connect to DB; target suite may not be started. return (False, None) fmt = checker.get_remote_point_format() if fmt: my_parser = TimePointParser() point = str(my_parser.parse(point, dump_format=fmt)) if message is not None: satisfied = checker.task_state_met(task, point, message=message) else: satisfied = checker.task_state_met(task, point, status=status) results = { 'suite': suite, 'task': task, 'point': point, 'offset': offset, 'status': status, 'message': message, 'cylc_run_dir': cylc_run_dir } return (satisfied, results)
def get_unix_time_from_time_string(time_string): """Convert a time string into a unix timestemp.""" parser = TimePointParser() time_point = parser.parse(time_string) return time_point.get("seconds_since_unix_epoch")
def get_unix_time_from_time_string(time_string): """Convert a time string into a unix timestemp.""" parser = TimePointParser() time_point = parser.parse(time_string) return time_point.get("seconds_since_unix_epoch")
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())