Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
def get_unix_time_from_time_string(datetime_string):
    """Convert a datetime string into a unix timestamp.

    The datetime_string must match DATE_TIME_FORMAT_EXTENDED above,
    which is the extended ISO 8601 year-month-dayThour:minute:second format,
    plus a valid ISO 8601 time zone. For example, 2016-09-07T11:21:00+01:00,
    2016-12-25T06:00:00Z, or 2016-12-25T06:00:00+13.

    isodatetime is not used to do the whole parsing, partly for performance,
    but mostly because the calendar may be in non-Gregorian mode.

    """
    try:
        date_time_utc = datetime.strptime(
            datetime_string, DATE_TIME_FORMAT_EXTENDED + "Z")
    except ValueError:
        global PARSER
        if PARSER is None:
            from isodatetime.parsers import TimePointParser
            PARSER = TimePointParser()
        time_zone_info = PARSER.get_info(datetime_string)[1]
        time_zone_hour = int(time_zone_info["time_zone_hour"])
        time_zone_minute = int(time_zone_info.get("time_zone_minute", 0))
        offset_seconds = 3600 * time_zone_hour + 60 * time_zone_minute
        if "+" in datetime_string:
            datetime_string = datetime_string.split("+")[0]
        else:
            datetime_string = datetime_string.rsplit("-", 1)[0]
        date_time = datetime.strptime(
            datetime_string, DATE_TIME_FORMAT_EXTENDED)
        date_time_utc = date_time - timedelta(seconds=offset_seconds)
    return timegm(date_time_utc.timetuple())
Exemple #4
0
 def coerce_cycle_point(cls, value, keys):
     """Coerce value to a cycle point."""
     if not value:
         return None
     value = cls.strip_and_unquote(keys, value)
     if value == 'now':
         # Handle this later in config.py when the suite UTC mode is known.
         return value
     if value.isdigit():
         # 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):
             try:
                 TimePointParser(num_expanded_year_digits=i).parse(value)
             except ValueError:
                 continue
             return value
         raise IllegalValueError('cycle point', keys, value)
     try:
         TimePointParser().parse(value)
     except ValueError:
         raise IllegalValueError('cycle point', keys, value)
     return value
Exemple #5
0
def get_unix_time_from_time_string(datetime_string):
    """Convert a datetime string into a unix timestamp.

    The datetime_string must match DATE_TIME_FORMAT_EXTENDED above,
    which is the extended ISO 8601 year-month-dayThour:minute:second format,
    plus a valid ISO 8601 time zone. For example, 2016-09-07T11:21:00+01:00,
    2016-12-25T06:00:00Z, or 2016-12-25T06:00:00+13.

    isodatetime is not used to do the whole parsing, partly for performance,
    but mostly because the calendar may be in non-Gregorian mode.

    """
    try:
        date_time_utc = datetime.strptime(
            datetime_string, DATE_TIME_FORMAT_EXTENDED + "Z")
    except ValueError:
        global PARSER
        if PARSER is None:
            from isodatetime.parsers import TimePointParser
            PARSER = TimePointParser()
        time_zone_info = PARSER.get_info(datetime_string)[1]
        time_zone_hour = int(time_zone_info["time_zone_hour"])
        time_zone_minute = int(time_zone_info.get("time_zone_minute", 0))
        offset_seconds = 3600 * time_zone_hour + 60 * time_zone_minute
        if "+" in datetime_string:
            datetime_string, time_zone_string = datetime_string.split("+")
        else:
            datetime_string, time_zone_string = datetime_string.rsplit("-", 1)
        date_time = datetime.strptime(
            datetime_string, DATE_TIME_FORMAT_EXTENDED)
        date_time_utc = date_time - timedelta(seconds=offset_seconds)
    return timegm(date_time_utc.timetuple())
Exemple #6
0
def duration_to_time(duration, offset='00'):
    """Convert a duration to list of times of the day.

    Works with durations that are shorter than one day.

    Arguments:
        duration (str):
            The duration as a string e.g. "PT3H".
        offset (str):
            The offset from which to count,
            (i.e. the earliest time to consider).
            e.g. "00" (midnight, the default)

    Examples:
        # every six hours starting at T00
        >>> list(duration_to_time('PT6H'))
        ['T0000', 'T0600', 'T1200', 'T1800']
        
        # every six hours starting at T0030
        >>> list(duration_to_time('PT6H', '0030'))
        ['T0030', 'T0630', 'T1230', 'T1830']
    """
    tp1 = TimePointParser().parse('20000101T' + offset + 'Z')
    tp2 = TimePointParser().parse('20000102T00Z')
    dur = DurationParser().parse(duration)
    while tp1 < tp2:
        yield 'T%02d%02d' % (tp1.hour_of_day, tp1.minute_of_hour)
        tp1 = tp1 + dur
Exemple #7
0
def strftime(iso8601_datetime, strftime_str, strptime_str=None):
    """Format an iso8601 datetime string using an strftime string.

    Args:
        iso8601_datetime (str): Any valid ISO8601 datetime as a string.
        strftime_str (str): A valid strftime string to format the output
            datetime.
        strptime_str (str - optional): A valid strptime string defining the
            format of the provided iso8601_datetime.

    Return:
        The result of applying the strftime to the iso8601_datetime as parsed
        by the strptime string if provided.

    Raises:
        ISO8601SyntaxError: In the event of an invalid datetime string.
        StrftimeSyntaxError: In the event of an invalid strftime string.

    Examples:
        >>> # Basic usage.
        >>> strftime('2000-01-01T00Z', '%H')
        '00'
        >>> strftime('2000', '%H')
        '00'
        >>> strftime('2000', '%Y/%m/%d %H:%M:%S')
        '2000/01/01 00:00:00'
        >>> strftime('10661014T08+01', '%z')  # Timezone offset.
        '+0100'
        >>> strftime('10661014T08+01', '%j')  # Day of the year
        '287'

        >>> # Strptime.
        >>> strftime('12,30,2000', '%m', '%m,%d,%Y')
        '12'
        >>> strftime('1066/10/14 08:00:00', '%Y%m%dT%H', '%Y/%m/%d %H:%M:%S')
        '10661014T08'

        >>> # Exceptions.
        >>> try:
        ...     strftime('invalid', '%H')  # Invalid datetime.
        ... except Exception as exc:
        ...     print type(exc)
        <class 'isodatetime.parsers.ISO8601SyntaxError'>
        >>> try:
        ...     strftime('2000', '%invalid')  # Invalid strftime.
        ... except Exception as exc:
        ...     print type(exc)
        <class 'isodatetime.parser_spec.StrftimeSyntaxError'>
        >>> try:
        ...     strftime('2000', '%Y', '%invalid')  # Invalid strptime.
        ... except Exception as exc:
        ...     print type(exc)
        <class 'isodatetime.parser_spec.StrftimeSyntaxError'>
    """
    if not strptime_str:
        return TimePointParser().parse(iso8601_datetime).strftime(strftime_str)
    return TimePointParser().strptime(iso8601_datetime,
                                      strptime_str).strftime(strftime_str)
Exemple #8
0
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
Exemple #9
0
    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
Exemple #10
0
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 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)
Exemple #12
0
 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
Exemple #13
0
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
Exemple #14
0
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)
Exemple #15
0
 def coerce_cycle_point(cls, value, keys):
     """Coerce value to a cycle point."""
     if not value:
         return None
     value = cls.strip_and_unquote(keys, value)
     if value == 'now':
         # Handle this later in config.py when the suite UTC mode is known.
         return value
     if "next" in value or "previous" in value:
         # Handle this later, as for "now".
         return value
     if value.isdigit():
         # Could be an old date-time cycle point format, or integer format.
         return value
     if "P" not in value and (
             value.startswith('-') or value.startswith('+')):
         # We don't know the value given for num expanded year digits...
         for i in range(1, 101):
             try:
                 TimePointParser(num_expanded_year_digits=i).parse(value)
             except ValueError:
                 continue
             return value
         raise IllegalValueError('cycle point', keys, value)
     if "P" in value:
         # ICP is an offset
         parser = DurationParser()
         try:
             if value.startswith("-"):
                 # parser doesn't allow negative duration with this setup?
                 parser.parse(value[1:])
             else:
                 parser.parse(value)
             return value
         except ValueError:
             raise IllegalValueError("cycle point", keys, value)
     try:
         TimePointParser().parse(value)
     except ValueError:
         raise IllegalValueError('cycle point', keys, value)
     return value
Exemple #16
0
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
Exemple #17
0
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
Exemple #18
0
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)
Exemple #19
0
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
Exemple #20
0
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)
Exemple #21
0
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
Exemple #22
0
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")
Exemple #23
0
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())
Exemple #24
0
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())
Exemple #25
0
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")