chromium/ash/system/time/time_of_day.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/time/time_of_day.h"

#include "ash/system/time/local_time_converter.h"
#include "base/i18n/time_formatting.h"
#include "base/strings/utf_string_conversions.h"

namespace ash {

namespace {

constexpr int kMaxOffsetMinutes = 24 * 60;

}  // namespace

TimeOfDay::TimeOfDay(int offset_minutes)
    : offset_minutes_from_zero_hour_(
          offset_minutes == kMaxOffsetMinutes ? 0 : offset_minutes) {
  DCHECK_LE(offset_minutes_from_zero_hour_, kMaxOffsetMinutes);
}

// static
TimeOfDay TimeOfDay::FromTime(const base::Time& time) {
  base::Time::Exploded exploded;
  time.LocalExplode(&exploded);
  return TimeOfDay(exploded.hour * 60 + exploded.minute);
}

bool TimeOfDay::operator==(const TimeOfDay& rhs) const {
  return offset_minutes_from_zero_hour_ == rhs.offset_minutes_from_zero_hour_;
}

TimeOfDay& TimeOfDay::SetClock(const base::Clock* clock) {
  clock_ = clock;
  return *this;
}

TimeOfDay& TimeOfDay::SetLocalTimeConverter(
    const LocalTimeConverter* local_time_converter) {
  local_time_converter_ = local_time_converter;
  return *this;
}

std::optional<base::Time> TimeOfDay::ToTimeToday() const {
  base::Time::Exploded now;
  GetLocalTimeConverter().LocalExplode(GetNow(), &now);
  // Per the `LocalExplode()` API:
  // "If the conversion cannot be made, the output will be
  // assigned invalid values. Use Exploded::HasValidValues() to confirm a
  // successful conversion."
  if (!now.HasValidValues()) {
    return std::nullopt;
  }
  now.hour = (offset_minutes_from_zero_hour_ / 60) % 24;
  now.minute = offset_minutes_from_zero_hour_ % 60;
  now.second = 0;
  now.millisecond = 0;
  base::Time result;
  if (GetLocalTimeConverter().FromLocalExploded(now, &result)) {
    return result;
  }

  // Known failure cases:
  // 1) Daylight saving time can cause FromLocalExploded() to fail on the
  // transition day in the spring when TimeOfDay == 2:30 AM, and the time goes
  // instantaneously from 1:59 AM 3:00 AM. In this very rare case, it's OK for
  // this function to fail.
  // 2) crbug.com/1307913.
  return std::nullopt;
}

std::string TimeOfDay::ToString() const {
  const std::optional<base::Time> time_today = ToTimeToday();
  return time_today ? base::UTF16ToUTF8(base::TimeFormatTimeOfDay(*time_today))
                    : "unknown";
}

base::Time TimeOfDay::GetNow() const {
  return clock_ ? clock_->Now() : base::Time::Now();
}

const LocalTimeConverter& TimeOfDay::GetLocalTimeConverter() const {
  return local_time_converter_ ? *local_time_converter_
                               : LocalTimeConverter::GetDefaultInstance();
}

std::ostream& operator<<(std::ostream& os, const TimeOfDay& time_of_day) {
  return os << base::Minutes(time_of_day.offset_minutes_from_zero_hour());
}

}  // namespace ash