summaryrefslogtreecommitdiffstats
path: root/intl/components/src/DateTimePatternGenerator.h
blob: d9d6de3928b2e31d36f9b73fea2890b7e4bc2fbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef intl_components_DateTimePatternGenerator_h_
#define intl_components_DateTimePatternGenerator_h_

#include "unicode/udatpg.h"
#include "mozilla/EnumSet.h"
#include "mozilla/Result.h"
#include "mozilla/Span.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/intl/ICU4CGlue.h"
#include "mozilla/intl/ICUError.h"

namespace mozilla::intl {

class DisplayNames;

/**
 * The DateTimePatternGenerator is the machinery used to work with DateTime
 * pattern manipulation. It is expensive to create one, and so generally it is
 * created once and then cached. It may be needed to be passed in as an argument
 * for different mozilla::intl APIs.
 */
class DateTimePatternGenerator final {
 public:
  explicit DateTimePatternGenerator(UDateTimePatternGenerator* aGenerator)
      : mGenerator(aGenerator) {
    MOZ_ASSERT(aGenerator);
  };

  // Transfer ownership of the UDateTimePatternGenerator in the move
  // constructor.
  DateTimePatternGenerator(DateTimePatternGenerator&& other) noexcept;

  // Transfer ownership of the UEnumeration in the move assignment operator.
  DateTimePatternGenerator& operator=(
      DateTimePatternGenerator&& other) noexcept;

  // Disallow copy.
  DateTimePatternGenerator(const DateTimePatternGenerator&) = delete;
  DateTimePatternGenerator& operator=(const DateTimePatternGenerator&) = delete;

  ~DateTimePatternGenerator();

  static Result<UniquePtr<DateTimePatternGenerator>, ICUError> TryCreate(
      const char* aLocale);

  enum class PatternMatchOption {
    /**
     * Adjust the 'hour' field in the resolved pattern to match the input
     * skeleton width.
     */
    HourField,

    /**
     * Adjust the 'minute' field in the resolved pattern to match the input
     * skeleton width.
     */
    MinuteField,

    /**
     * Adjust the 'second' field in the resolved pattern to match the input
     * skeleton width.
     */
    SecondField,
  };

  /**
   * Given a skeleton (a string with unordered datetime fields), get a best
   * pattern that will fit for that locale. This pattern will be filled into the
   * buffer. e.g. The skeleton "yMd" would return the pattern "M/d/y" for en-US,
   * or "dd/MM/y" for en-GB.
   */
  template <typename B>
  ICUResult GetBestPattern(Span<const char16_t> aSkeleton, B& aBuffer,
                           EnumSet<PatternMatchOption> options = {}) {
    return FillBufferWithICUCall(
        aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
          return udatpg_getBestPatternWithOptions(
              mGenerator.GetMut(), aSkeleton.data(),
              static_cast<int32_t>(aSkeleton.Length()),
              toUDateTimePatternMatchOptions(options), target, length, status);
        });
  }

  /**
   * Get a skeleton (a string with unordered datetime fields) from a pattern.
   * For example, both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd".
   */
  template <typename B>
  static ICUResult GetSkeleton(Span<const char16_t> aPattern, B& aBuffer) {
    // At one time udatpg_getSkeleton required a UDateTimePatternGenerator*, but
    // now it is valid to pass in a nullptr.
    return FillBufferWithICUCall(
        aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
          return udatpg_getSkeleton(nullptr, aPattern.data(),
                                    static_cast<int32_t>(aPattern.Length()),
                                    target, length, status);
        });
  }

  /**
   * Get a pattern of the form "{1} {0}" to combine separate date and time
   * patterns into a single pattern. The "{0}" part is the placeholder for the
   * time pattern and "{1}" is the placeholder for the date pattern.
   *
   * See dateTimeFormat from
   * https://unicode.org/reports/tr35/tr35-dates.html#dateTimeFormat
   *
   * Note:
   * In CLDR, it's called Date-Time Combined Format
   * https://cldr.unicode.org/translation/date-time/datetime-patterns#h.x7ca7qwzh4m
   *
   * The naming 'placeholder pattern' is from ICU4X.
   * https://unicode-org.github.io/icu4x-docs/doc/icu_pattern/index.html
   */
  Span<const char16_t> GetPlaceholderPattern() const {
    int32_t length;
    const char16_t* combined =
        udatpg_getDateTimeFormat(mGenerator.GetConst(), &length);
    return Span{combined, static_cast<size_t>(length)};
  }

 private:
  // Allow other mozilla::intl components to access the underlying
  // UDateTimePatternGenerator.
  friend class DisplayNames;

  UDateTimePatternGenerator* GetUDateTimePatternGenerator() {
    return mGenerator.GetMut();
  }

  ICUPointer<UDateTimePatternGenerator> mGenerator =
      ICUPointer<UDateTimePatternGenerator>(nullptr);

  static UDateTimePatternMatchOptions toUDateTimePatternMatchOptions(
      EnumSet<PatternMatchOption> options) {
    struct OptionMap {
      PatternMatchOption from;
      UDateTimePatternMatchOptions to;
    } static constexpr map[] = {
        {PatternMatchOption::HourField, UDATPG_MATCH_HOUR_FIELD_LENGTH},
#ifndef U_HIDE_INTERNAL_API
        {PatternMatchOption::MinuteField, UDATPG_MATCH_MINUTE_FIELD_LENGTH},
        {PatternMatchOption::SecondField, UDATPG_MATCH_SECOND_FIELD_LENGTH},
#endif
    };

    UDateTimePatternMatchOptions result = UDATPG_MATCH_NO_OPTIONS;
    for (const auto& entry : map) {
      if (options.contains(entry.from)) {
        result = UDateTimePatternMatchOptions(result | entry.to);
      }
    }
    return result;
  }
};

}  // namespace mozilla::intl
#endif