// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_TIME_DATE_HELPER_H_
#define ASH_SYSTEM_TIME_DATE_HELPER_H_
#include <string>
#include "ash/ash_export.h"
#include "ash/public/cpp/locale_update_controller.h"
#include "base/memory/singleton.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
#include "third_party/icu/source/i18n/unicode/dtitvfmt.h"
#include "third_party/icu/source/i18n/unicode/dtptngen.h"
#include "third_party/icu/source/i18n/unicode/gregocal.h"
#include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
namespace ash {
// A singleton class used to create and cache `GregorianCalendar`,
// `icu::SimpleDateFormat` and `icu::DateIntervalFormat` objects, so that they
// don't have to be recreated each time when querying the time difference or
// formatting a time. This improves performance since creating
// `icu::SimpleDateFormat` and `icu::DateIntervalFormat` objects is expensive.
class DateHelper : public LocaleChangeObserver,
public system::TimezoneSettings::Observer {
public:
// Returns the singleton instance.
ASH_EXPORT static DateHelper* GetInstance();
// Creates a formatter object used to format dates from the given `pattern`.
icu::SimpleDateFormat CreateSimpleDateFormatter(const char* pattern);
// Creates a formatter object used to format dates without calling the
// `getBestPattern` function, which resolves the input pattern to the best
// fit, which is not always what we want. e.g. 'mm' returns 'm' even though we
// want it zero-padded (03 vs. 3 when given 12:03)
icu::SimpleDateFormat CreateSimpleDateFormatterWithoutBestPattern(
const char* pattern);
// Creates a formatter object that extracts the hours field from a given date.
// Uses `pattern` to differentiate between 12 and 24 hour clock formats.
icu::SimpleDateFormat CreateHoursFormatter(const char* pattern);
// Creates a date interval formatter object that formats a `DateInterval` into
// text as compactly as possible.
// Note that even if a pattern does not request a certain date part, it will
// be automatically included if that part is different between two dates (e.g.
// for `pattern=hm` (hours and minutes in twelve hour clock format),
// "18 Nov 2021 8:30"..."18 Nov 2021 9:30" => "8:30 – 9:30 AM", but
// "18 Nov 2021 8:30"..."19 Nov 2021 7:20" =>
// "11/18/2021, 8:30 AM – 11/19/2021, 7:20 AM").
std::unique_ptr<icu::DateIntervalFormat> CreateDateIntervalFormatter(
const char* pattern);
// Returns a formatted string of a `time` using the given `formatter`.
std::u16string GetFormattedTime(const icu::DateFormat* formatter,
const base::Time& time);
// Returns a formatted interval string using the given `formatter`.
ASH_EXPORT std::u16string GetFormattedInterval(
const icu::DateIntervalFormat* formatter,
const base::Time& start_time,
const base::Time& end_time);
// Get the time difference to UTC time based on the time passed in and the
// system timezone. Daylight saving is considered.
ASH_EXPORT base::TimeDelta GetTimeDifference(base::Time date) const;
// Gets the local midnight in UTC time of the `date`.
// e.g. If the `date` is Apr 1st 1:00 (which is Mar 31st 18:00 PST), the
// local timezone is PST and time difference is 7 hrs. It returns Mar 31st
// 7:00, which is Mar 31st 00:00 PST.
ASH_EXPORT base::Time GetLocalMidnight(base::Time date);
icu::SimpleDateFormat& day_of_month_formatter() {
return day_of_month_formatter_;
}
icu::SimpleDateFormat& month_day_formatter() { return month_day_formatter_; }
icu::SimpleDateFormat& month_day_year_formatter() {
return month_day_year_formatter_;
}
icu::SimpleDateFormat& month_day_year_week_formatter() {
return month_day_year_week_formatter_;
}
icu::SimpleDateFormat& month_name_formatter() {
return month_name_formatter_;
}
icu::SimpleDateFormat& month_name_year_formatter() {
return month_name_year_formatter_;
}
icu::SimpleDateFormat& time_zone_formatter() { return time_zone_formatter_; }
icu::SimpleDateFormat& twelve_hour_clock_formatter() {
return twelve_hour_clock_formatter_;
}
icu::SimpleDateFormat& twenty_four_hour_clock_formatter() {
return twenty_four_hour_clock_formatter_;
}
icu::SimpleDateFormat& day_of_week_formatter() {
return day_of_week_formatter_;
}
icu::SimpleDateFormat& week_title_formatter() {
return week_title_formatter_;
}
icu::SimpleDateFormat& year_formatter() { return year_formatter_; }
icu::SimpleDateFormat& twelve_hour_clock_hours_formatter() {
return twelve_hour_clock_hours_formatter_;
}
icu::SimpleDateFormat& twenty_four_hour_clock_hours_formatter() {
return twenty_four_hour_clock_hours_formatter_;
}
icu::SimpleDateFormat& minutes_formatter() { return minutes_formatter_; }
const icu::DateIntervalFormat* twelve_hour_clock_interval_formatter() {
return twelve_hour_clock_interval_formatter_.get();
}
const icu::DateIntervalFormat* twenty_four_hour_clock_interval_formatter() {
return twenty_four_hour_clock_interval_formatter_.get();
}
std::vector<std::u16string> week_titles() { return week_titles_; }
// Reset after a locale change in the test.
ASH_EXPORT void ResetForTesting();
private:
friend base::DefaultSingletonTraits<DateHelper>;
friend class DateHelperUnittest;
DateHelper();
DateHelper(const DateHelper& other) = delete;
DateHelper& operator=(const DateHelper& other) = delete;
~DateHelper() override;
// Resets the icu::SimpleDateFormat objects after a time zone change.
ASH_EXPORT void ResetFormatters();
// Calculates the week titles based on the language setting.
ASH_EXPORT void CalculateLocalWeekTitles();
// system::TimezoneSettings::Observer:
void TimezoneChanged(const icu::TimeZone& timezone) override;
// LocaleChangeObserver:
// Although the device will restart whenever there's locale change and this
// instance will be re-constructed, however this dose not cover all the cases.
// The locale between the login screen and the user's screen can be different.
// (For example: different languages are set in different accounts, and the
// login screen will use the owener's locale setting.)
void OnLocaleChanged() override;
// Formatter for getting the day of month.
icu::SimpleDateFormat day_of_month_formatter_;
// Formatter for getting the month name and day of month.
icu::SimpleDateFormat month_day_formatter_;
// Formatter for getting the month name, day of month, and year.
icu::SimpleDateFormat month_day_year_formatter_;
// Formatter for getting the month, day, year and day of week.
icu::SimpleDateFormat month_day_year_week_formatter_;
// Formatter for getting the name of month.
icu::SimpleDateFormat month_name_formatter_;
// Formatter for getting the month name and year.
icu::SimpleDateFormat month_name_year_formatter_;
// Formatter for getting the time zone.
icu::SimpleDateFormat time_zone_formatter_;
// Formatter for 12 hour clock hours and minutes.
icu::SimpleDateFormat twelve_hour_clock_formatter_;
// Formatter for 24 hour clock hours and minutes.
icu::SimpleDateFormat twenty_four_hour_clock_formatter_;
// Formatter for getting the day of week. Returns 1 - 7.
icu::SimpleDateFormat day_of_week_formatter_;
// Formatter for getting the week title. e.g. M, T, W.
icu::SimpleDateFormat week_title_formatter_;
// Formatter for getting the year.
icu::SimpleDateFormat year_formatter_;
// Formatter for getting the hours in a 12 hour clock format.
icu::SimpleDateFormat twelve_hour_clock_hours_formatter_;
// Formatter for getting the hours in a 24 hour clock format.
icu::SimpleDateFormat twenty_four_hour_clock_hours_formatter_;
// Formatter for getting the minutes.
icu::SimpleDateFormat minutes_formatter_;
// Interval formatter for two dates. Formats time in twelve
// hour clock format (e.g. 8:30 – 9:30 PM or 11:30 AM – 2:30 PM).
std::unique_ptr<icu::DateIntervalFormat>
twelve_hour_clock_interval_formatter_;
// Interval formatter for two dates. Formats time in twenty
// four hour clock format (e.g. 20:30 – 21:30).
std::unique_ptr<icu::DateIntervalFormat>
twenty_four_hour_clock_interval_formatter_;
// Week title list based on the language setting. e.g. SMTWTFS in English.
std::vector<std::u16string> week_titles_;
std::unique_ptr<icu::GregorianCalendar> gregorian_calendar_;
base::ScopedObservation<system::TimezoneSettings,
system::TimezoneSettings::Observer>
time_zone_settings_observer_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_TIME_DATE_HELPER_H_