TimeZone.h (12671B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef builtin_temporal_TimeZone_h 8 #define builtin_temporal_TimeZone_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/Maybe.h" 13 14 #include <array> 15 #include <stddef.h> 16 #include <stdint.h> 17 18 #include "builtin/temporal/TemporalTypes.h" 19 #include "js/RootingAPI.h" 20 #include "js/TypeDecls.h" 21 #include "js/Value.h" 22 #include "vm/JSObject.h" 23 #include "vm/NativeObject.h" 24 #include "vm/StringType.h" 25 26 class JS_PUBLIC_API JSTracer; 27 struct JSClassOps; 28 29 namespace mozilla::intl { 30 class TimeZone; 31 } 32 33 namespace js::temporal { 34 35 class TimeZoneObject : public NativeObject { 36 public: 37 static const JSClass class_; 38 39 static constexpr uint32_t IDENTIFIER_SLOT = 0; 40 static constexpr uint32_t PRIMARY_IDENTIFIER_SLOT = 1; 41 static constexpr uint32_t OFFSET_MINUTES_SLOT = 2; 42 static constexpr uint32_t INTL_TIMEZONE_SLOT = 3; 43 static constexpr uint32_t SLOT_COUNT = 4; 44 45 // Estimated memory use for intl::TimeZone (see IcuMemoryUsage). 46 static constexpr size_t EstimatedMemoryUse = 6840; 47 48 bool isOffset() const { return getFixedSlot(OFFSET_MINUTES_SLOT).isInt32(); } 49 50 JSLinearString* identifier() const { 51 return &getFixedSlot(IDENTIFIER_SLOT).toString()->asLinear(); 52 } 53 54 JSLinearString* primaryIdentifier() const { 55 MOZ_ASSERT(!isOffset()); 56 return &getFixedSlot(PRIMARY_IDENTIFIER_SLOT).toString()->asLinear(); 57 } 58 59 int32_t offsetMinutes() const { 60 MOZ_ASSERT(isOffset()); 61 return getFixedSlot(OFFSET_MINUTES_SLOT).toInt32(); 62 } 63 64 mozilla::intl::TimeZone* getTimeZone() const { 65 const auto& slot = getFixedSlot(INTL_TIMEZONE_SLOT); 66 if (slot.isUndefined()) { 67 return nullptr; 68 } 69 return static_cast<mozilla::intl::TimeZone*>(slot.toPrivate()); 70 } 71 72 void setTimeZone(mozilla::intl::TimeZone* timeZone) { 73 setFixedSlot(INTL_TIMEZONE_SLOT, JS::PrivateValue(timeZone)); 74 } 75 76 private: 77 static const JSClassOps classOps_; 78 79 static void finalize(JS::GCContext* gcx, JSObject* obj); 80 }; 81 82 } /* namespace js::temporal */ 83 84 namespace js::temporal { 85 86 /** 87 * Temporal time zones are either available named time zones or offset time 88 * zones. 89 * 90 * The identifier of an available named time zones is an available named 91 * time zone identifier, which is either a primary time zone identifier or a 92 * non-primary time zone identifier. 93 * 94 * The identifier of an offset time zone is an offset time zone identifier. 95 * 96 * Temporal methods always return the normalized format of a time zone 97 * identifier. Available named time zone identifier are always in normalized 98 * format. 99 * 100 * Examples of valid available time zone identifiers in normalized format: 101 * - "UTC" (primary identifier) 102 * - "Etc/UTC" (non-primary identifier) 103 * - "America/New_York" (primary identifier) 104 * - "+00:00" 105 * 106 * Examples of valid available time zone identifiers in non-normalized format: 107 * - "+00" 108 * - "-00:00" 109 * 110 * Examples of invalid available time zone identifiers: 111 * - "utc" (wrong case) 112 * - "+00:00:00" (sub-minute precision) 113 * - "+00:00:01" (sub-minute precision) 114 * 115 * The following two implementation approaches are possible: 116 * 117 * 1. Represent time zones as JSStrings. Additionally keep a mapping from 118 * JSString to `mozilla::intl::TimeZone` to avoid repeatedly creating new 119 * `mozilla::intl::TimeZone` for time zone operations. Offset string time 120 * zones have to be special cased, because they don't use 121 * `mozilla::intl::TimeZone`. Either detect offset strings by checking the 122 * time zone identifier or store offset strings as the offset in minutes 123 * value to avoid reparsing the offset string again and again. 124 * 2. Represent time zones as objects which hold `mozilla::intl::TimeZone` in 125 * an internal slot. 126 * 127 * Option 2 is a bit easier to implement, so we use this approach for now. 128 */ 129 class MOZ_STACK_CLASS TimeZoneValue final { 130 TimeZoneObject* object_ = nullptr; 131 132 public: 133 /** 134 * Default initialize this TimeZoneValue. 135 */ 136 TimeZoneValue() = default; 137 138 /** 139 * Initialize this TimeZoneValue with a time zone object. 140 */ 141 explicit TimeZoneValue(TimeZoneObject* timeZone) : object_(timeZone) { 142 MOZ_ASSERT(object_); 143 } 144 145 /** 146 * Initialize this TimeZoneValue from a slot Value. 147 */ 148 explicit TimeZoneValue(const JS::Value& value) 149 : object_(&value.toObject().as<TimeZoneObject>()) {} 150 151 /** 152 * Return true if this TimeZoneValue is not null. 153 */ 154 explicit operator bool() const { return !!object_; } 155 156 /** 157 * Return true if this TimeZoneValue is an offset time zone. 158 */ 159 bool isOffset() const { 160 MOZ_ASSERT(object_); 161 return object_->isOffset(); 162 } 163 164 /** 165 * Return the offset of an offset time zone. 166 */ 167 auto offsetMinutes() const { 168 MOZ_ASSERT(object_); 169 return object_->offsetMinutes(); 170 } 171 172 /** 173 * Return the time zone identifier. 174 */ 175 auto* identifier() const { 176 MOZ_ASSERT(object_); 177 return object_->identifier(); 178 } 179 180 /** 181 * Return the primary time zone identifier of a named time zone. 182 */ 183 auto* primaryIdentifier() const { 184 MOZ_ASSERT(object_); 185 return object_->primaryIdentifier(); 186 } 187 188 /** 189 * Return the time zone implementation. 190 */ 191 auto* getTimeZone() const { 192 MOZ_ASSERT(object_); 193 return object_->getTimeZone(); 194 } 195 196 /** 197 * Return the underlying TimeZoneObject. 198 */ 199 auto* toTimeZoneObject() const { 200 MOZ_ASSERT(object_); 201 return object_; 202 } 203 204 /** 205 * Return the slot Value representation of this TimeZoneValue. 206 */ 207 JS::Value toSlotValue() const { 208 MOZ_ASSERT(object_); 209 return JS::ObjectValue(*object_); 210 } 211 212 // Helper methods for (Mutable)WrappedPtrOperations. 213 auto address() { return &object_; } 214 auto address() const { return &object_; } 215 216 // Trace implementation. 217 void trace(JSTracer* trc); 218 }; 219 220 class PossibleEpochNanoseconds final { 221 // GetPossibleEpochNanoseconds can return up-to two elements. 222 static constexpr size_t MaxLength = 2; 223 224 std::array<EpochNanoseconds, MaxLength> array_ = {}; 225 size_t length_ = 0; 226 227 void append(const EpochNanoseconds& epochNs) { array_[length_++] = epochNs; } 228 229 public: 230 PossibleEpochNanoseconds() = default; 231 232 explicit PossibleEpochNanoseconds(const EpochNanoseconds& epochNs) { 233 append(epochNs); 234 } 235 236 explicit PossibleEpochNanoseconds(const EpochNanoseconds& earlier, 237 const EpochNanoseconds& later) { 238 MOZ_ASSERT(earlier <= later); 239 append(earlier); 240 append(later); 241 } 242 243 size_t length() const { return length_; } 244 bool empty() const { return length_ == 0; } 245 246 const auto& operator[](size_t i) const { return array_[i]; } 247 248 auto begin() const { return array_.begin(); } 249 auto end() const { return array_.begin() + length_; } 250 251 const auto& front() const { 252 MOZ_ASSERT(length_ > 0); 253 return array_[0]; 254 } 255 const auto& back() const { 256 MOZ_ASSERT(length_ > 0); 257 return array_[length_ - 1]; 258 } 259 }; 260 261 struct ParsedTimeZone; 262 enum class TemporalDisambiguation; 263 264 /** 265 * Create a new |TimeZoneObject| whose identifier is |identifier| and whose 266 * primary identifier is |primaryIdentifier|. 267 */ 268 TimeZoneObject* CreateTimeZoneObject( 269 JSContext* cx, JS::Handle<JSLinearString*> identifier, 270 JS::Handle<JSLinearString*> primaryIdentifier); 271 272 /** 273 * SystemTimeZoneIdentifier ( ) 274 */ 275 JSLinearString* ComputeSystemTimeZoneIdentifier(JSContext* cx); 276 277 /** 278 * SystemTimeZoneIdentifier ( ) 279 */ 280 JSLinearString* SystemTimeZoneIdentifier(JSContext* cx); 281 282 /** 283 * SystemTimeZoneIdentifier ( ) 284 */ 285 bool SystemTimeZone(JSContext* cx, JS::MutableHandle<TimeZoneValue> result); 286 287 /** 288 * ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike ) 289 */ 290 bool ToTemporalTimeZone(JSContext* cx, 291 JS::Handle<JS::Value> temporalTimeZoneLike, 292 JS::MutableHandle<TimeZoneValue> result); 293 294 /** 295 * ToTemporalTimeZoneIdentifier ( temporalTimeZoneLike ) 296 */ 297 bool ToTemporalTimeZone(JSContext* cx, JS::Handle<ParsedTimeZone> string, 298 JS::MutableHandle<TimeZoneValue> result); 299 300 /** 301 * Verifies that the given string is a valid time zone name. If it is a valid 302 * time zone name, returns the canonicalized time zone name. Canonicalization 303 * resolves link names to their target time zones. 304 */ 305 JSLinearString* ToValidCanonicalTimeZoneIdentifier( 306 JSContext* cx, JS::Handle<JSString*> timeZone); 307 308 /** 309 * TimeZoneEquals ( one, two ) 310 */ 311 bool TimeZoneEquals(const TimeZoneValue& one, const TimeZoneValue& two); 312 313 /** 314 * GetISODateTimeFor ( timeZone, epochNs ) 315 */ 316 ISODateTime GetISODateTimeFor(const EpochNanoseconds& epochNs, 317 int64_t offsetNanoseconds); 318 319 /** 320 * GetISODateTimeFor ( timeZone, epochNs ) 321 */ 322 bool GetISODateTimeFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone, 323 const EpochNanoseconds& epochNs, ISODateTime* result); 324 325 /** 326 * GetEpochNanosecondsFor ( timeZone, isoDateTime, disambiguation ) 327 */ 328 bool GetEpochNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone, 329 const ISODateTime& isoDateTime, 330 TemporalDisambiguation disambiguation, 331 EpochNanoseconds* result); 332 333 /** 334 * GetOffsetNanosecondsFor ( timeZone, epochNs ) 335 */ 336 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone, 337 const EpochNanoseconds& epochNs, 338 int64_t* offsetNanoseconds); 339 340 /** 341 * GetPossibleEpochNanoseconds ( timeZone, isoDateTime ) 342 */ 343 bool GetPossibleEpochNanoseconds(JSContext* cx, 344 JS::Handle<TimeZoneValue> timeZone, 345 const ISODateTime& isoDateTime, 346 PossibleEpochNanoseconds* result); 347 348 /** 349 * DisambiguatePossibleEpochNanoseconds ( possibleEpochNs, timeZone, 350 * isoDateTime, disambiguation ) 351 */ 352 bool DisambiguatePossibleEpochNanoseconds( 353 JSContext* cx, const PossibleEpochNanoseconds& possibleEpochNs, 354 JS::Handle<TimeZoneValue> timeZone, const ISODateTime& isoDateTime, 355 TemporalDisambiguation disambiguation, EpochNanoseconds* result); 356 357 /** 358 * GetNamedTimeZoneNextTransition ( timeZoneIdentifier, epochNanoseconds ) 359 */ 360 bool GetNamedTimeZoneNextTransition(JSContext* cx, 361 JS::Handle<TimeZoneValue> timeZone, 362 const EpochNanoseconds& epochNanoseconds, 363 mozilla::Maybe<EpochNanoseconds>* result); 364 365 /** 366 * GetNamedTimeZonePreviousTransition ( timeZoneIdentifier, epochNanoseconds ) 367 */ 368 bool GetNamedTimeZonePreviousTransition( 369 JSContext* cx, JS::Handle<TimeZoneValue> timeZone, 370 const EpochNanoseconds& epochNanoseconds, 371 mozilla::Maybe<EpochNanoseconds>* result); 372 373 /** 374 * GetStartOfDay ( timeZone, isoDate ) 375 */ 376 bool GetStartOfDay(JSContext* cx, JS::Handle<TimeZoneValue> timeZone, 377 const ISODate& isoDate, EpochNanoseconds* result); 378 379 // Helper for MutableWrappedPtrOperations. 380 bool WrapTimeZoneValueObject(JSContext* cx, 381 JS::MutableHandle<TimeZoneObject*> timeZone); 382 383 } /* namespace js::temporal */ 384 385 namespace js { 386 387 template <typename Wrapper> 388 class WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> { 389 const auto& container() const { 390 return static_cast<const Wrapper*>(this)->get(); 391 } 392 393 public: 394 explicit operator bool() const { return !!container(); } 395 396 bool isOffset() const { return container().isOffset(); } 397 398 auto offsetMinutes() const { return container().offsetMinutes(); } 399 400 auto* identifier() const { return container().identifier(); } 401 402 auto* primaryIdentifier() const { return container().primaryIdentifier(); } 403 404 auto* getTimeZone() const { return container().getTimeZone(); } 405 406 JS::Value toSlotValue() const { return container().toSlotValue(); } 407 }; 408 409 template <typename Wrapper> 410 class MutableWrappedPtrOperations<temporal::TimeZoneValue, Wrapper> 411 : public WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> { 412 auto& container() { return static_cast<Wrapper*>(this)->get(); } 413 414 public: 415 /** 416 * Wrap the time zone value into the current compartment. 417 */ 418 bool wrap(JSContext* cx) { 419 MOZ_ASSERT(container()); 420 auto mh = JS::MutableHandle<temporal::TimeZoneObject*>::fromMarkedLocation( 421 container().address()); 422 return temporal::WrapTimeZoneValueObject(cx, mh); 423 } 424 }; 425 426 } /* namespace js */ 427 428 #endif /* builtin_temporal_TimeZone_h */