def test_normalize_timestamp(self): timestamp = 1591161115.194556 millisecond_timestamp = 1591161115194 microsecond_timestamp = 1591161115194556 assert util.normalize_timestamp(timestamp) == timestamp assert util.normalize_timestamp( millisecond_timestamp) == 1591161115.194 assert util.normalize_timestamp( microsecond_timestamp) == 1591161115.194556 with pytest.raises(ValueError): util.normalize_timestamp(3e17)
def fromtimestamp(cls, timestamp, tzinfo=None): """ Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp, converted to the given timezone. :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either. :param tzinfo: (optional) a ``tzinfo`` object. Defaults to local time. """ if tzinfo is None: tzinfo = dateutil_tz.tzlocal() elif util.isstr(tzinfo): tzinfo = parser.TzinfoParser.parse(tzinfo) if not util.is_timestamp(timestamp): raise ValueError( "The provided timestamp '{}' is invalid.".format(timestamp)) timestamp = util.normalize_timestamp(float(timestamp)) dt = datetime.fromtimestamp(timestamp, tzinfo) return cls( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo, )
def utcfromtimestamp(cls, timestamp): """Constructs an :class:`Arrow <arrow.arrow.Arrow>` object from a timestamp, in UTC time. :param timestamp: an ``int`` or ``float`` timestamp, or a ``str`` that converts to either. """ if not util.is_timestamp(timestamp): raise ValueError( "The provided timestamp '{}' is invalid.".format(timestamp) ) timestamp = util.normalize_timestamp(float(timestamp)) dt = datetime.utcfromtimestamp(timestamp) return cls( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, dateutil_tz.tzutc(), )
def _build_datetime(parts): weekdate = parts.get("weekdate") if weekdate is not None: # we can use strptime (%G, %V, %u) in python 3.6 but these tokens aren't available before that year, week = int(weekdate[0]), int(weekdate[1]) if weekdate[2] is not None: day = int(weekdate[2]) else: # day not given, default to 1 day = 1 dt = iso_to_gregorian(year, week, day) parts["year"] = dt.year parts["month"] = dt.month parts["day"] = dt.day timestamp = parts.get("timestamp") if timestamp is not None: return datetime.fromtimestamp(timestamp, tz=tz.tzutc()) expanded_timestamp = parts.get("expanded_timestamp") if expanded_timestamp is not None: return datetime.fromtimestamp( normalize_timestamp(expanded_timestamp), tz=tz.tzutc(), ) day_of_year = parts.get("day_of_year") if day_of_year is not None: year = parts.get("year") month = parts.get("month") if year is None: raise ParserError( "Year component is required with the DDD and DDDD tokens." ) if month is not None: raise ParserError( "Month component is not allowed with the DDD and DDDD tokens." ) date_string = "{}-{}".format(year, day_of_year) try: dt = datetime.strptime(date_string, "%Y-%j") except ValueError: raise ParserError( "The provided day of year '{}' is invalid.".format(day_of_year) ) parts["year"] = dt.year parts["month"] = dt.month parts["day"] = dt.day day_of_week = parts.get("day_of_week") day = parts.get("day") # If day is passed, ignore day of week if day_of_week is not None and day is None: year = parts.get("year", 1970) month = parts.get("month", 1) day = 1 # dddd => first day of week after epoch # dddd YYYY => first day of week in specified year # dddd MM YYYY => first day of week in specified year and month # dddd MM => first day after epoch in specified month next_weekday_dt = next_weekday(datetime(year, month, day), day_of_week) parts["year"] = next_weekday_dt.year parts["month"] = next_weekday_dt.month parts["day"] = next_weekday_dt.day am_pm = parts.get("am_pm") hour = parts.get("hour", 0) if am_pm == "pm" and hour < 12: hour += 12 elif am_pm == "am" and hour == 12: hour = 0 # Support for midnight at the end of day if hour == 24: if parts.get("minute", 0) != 0: raise ParserError("Midnight at the end of day must not contain minutes") if parts.get("second", 0) != 0: raise ParserError("Midnight at the end of day must not contain seconds") if parts.get("microsecond", 0) != 0: raise ParserError( "Midnight at the end of day must not contain microseconds" ) hour = 0 day_increment = 1 else: day_increment = 0 # account for rounding up to 1000000 microsecond = parts.get("microsecond", 0) if microsecond == 1000000: microsecond = 0 second_increment = 1 else: second_increment = 0 increment = timedelta(days=day_increment, seconds=second_increment) return ( datetime( year=parts.get("year", 1), month=parts.get("month", 1), day=parts.get("day", 1), hour=hour, minute=parts.get("minute", 0), second=parts.get("second", 0), microsecond=microsecond, tzinfo=parts.get("tzinfo"), ) + increment )
def _build_datetime(parts: _Parts) -> datetime: weekdate = parts.get("weekdate") if weekdate is not None: year, week = int(weekdate[0]), int(weekdate[1]) if weekdate[2] is not None: _day = int(weekdate[2]) else: # day not given, default to 1 _day = 1 date_string = f"{year}-{week}-{_day}" # tokens for ISO 8601 weekdates dt = datetime.strptime(date_string, "%G-%V-%u") parts["year"] = dt.year parts["month"] = dt.month parts["day"] = dt.day timestamp = parts.get("timestamp") if timestamp is not None: return datetime.fromtimestamp(timestamp, tz=tz.tzutc()) expanded_timestamp = parts.get("expanded_timestamp") if expanded_timestamp is not None: return datetime.fromtimestamp( normalize_timestamp(expanded_timestamp), tz=tz.tzutc(), ) day_of_year = parts.get("day_of_year") if day_of_year is not None: _year = parts.get("year") month = parts.get("month") if _year is None: raise ParserError( "Year component is required with the DDD and DDDD tokens." ) if month is not None: raise ParserError( "Month component is not allowed with the DDD and DDDD tokens." ) date_string = f"{_year}-{day_of_year}" try: dt = datetime.strptime(date_string, "%Y-%j") except ValueError: raise ParserError( f"The provided day of year {day_of_year!r} is invalid." ) parts["year"] = dt.year parts["month"] = dt.month parts["day"] = dt.day day_of_week: Optional[int] = parts.get("day_of_week") day = parts.get("day") # If day is passed, ignore day of week if day_of_week is not None and day is None: year = parts.get("year", 1970) month = parts.get("month", 1) day = 1 # dddd => first day of week after epoch # dddd YYYY => first day of week in specified year # dddd MM YYYY => first day of week in specified year and month # dddd MM => first day after epoch in specified month next_weekday_dt = next_weekday(datetime(year, month, day), day_of_week) parts["year"] = next_weekday_dt.year parts["month"] = next_weekday_dt.month parts["day"] = next_weekday_dt.day am_pm = parts.get("am_pm") hour = parts.get("hour", 0) if am_pm == "pm" and hour < 12: hour += 12 elif am_pm == "am" and hour == 12: hour = 0 # Support for midnight at the end of day if hour == 24: if parts.get("minute", 0) != 0: raise ParserError("Midnight at the end of day must not contain minutes") if parts.get("second", 0) != 0: raise ParserError("Midnight at the end of day must not contain seconds") if parts.get("microsecond", 0) != 0: raise ParserError( "Midnight at the end of day must not contain microseconds" ) hour = 0 day_increment = 1 else: day_increment = 0 # account for rounding up to 1000000 microsecond = parts.get("microsecond", 0) if microsecond == 1000000: microsecond = 0 second_increment = 1 else: second_increment = 0 increment = timedelta(days=day_increment, seconds=second_increment) return ( datetime( year=parts.get("year", 1), month=parts.get("month", 1), day=parts.get("day", 1), hour=hour, minute=parts.get("minute", 0), second=parts.get("second", 0), microsecond=microsecond, tzinfo=parts.get("tzinfo"), ) + increment )