1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2005 John Chapman. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        Initial release: 2005
8 
9         author:         John Chapman
10 
11         Contains classes that provide information about locales, such as 
12         the language and calendars, as well as cultural conventions used 
13         for formatting dates, currency and numbers. Use these classes when 
14         writing applications for an international audience.
15 
16 ******************************************************************************/
17 
18 /**
19  * $(MEMBERTABLE
20  * $(TR
21  * $(TH Interface)
22  * $(TH Description)
23  * )
24  * $(TR
25  * $(TD $(LINK2 #IFormatService, IFormatService))
26  * $(TD Retrieves an object to control formatting.)
27  * )
28  * )
29  *
30  * $(MEMBERTABLE
31  * $(TR
32  * $(TH Class)
33  * $(TH Description)
34  * )
35  * $(TR
36  * $(TD $(LINK2 #Calendar, Calendar))
37  * $(TD Represents time in week, month and year divisions.)
38  * )
39  * $(TR
40  * $(TD $(LINK2 #Culture, Culture))
41  * $(TD Provides information about a culture, such as its name, calendar and date and number format patterns.)
42  * )
43  * $(TR
44  * $(TD $(LINK2 #DateTimeFormat, DateTimeFormat))
45  * $(TD Determines how $(LINK2 #Time, Time) values are formatted, depending on the culture.)
46  * )
47  * $(TR
48  * $(TD $(LINK2 #DaylightSavingTime, DaylightSavingTime))
49  * $(TD Represents a period of daylight-saving time.)
50  * )
51  * $(TR
52  * $(TD $(LINK2 #Gregorian, Gregorian))
53  * $(TD Represents the Gregorian calendar.)
54  * )
55  * $(TR
56  * $(TD $(LINK2 #Hebrew, Hebrew))
57  * $(TD Represents the Hebrew calendar.)
58  * )
59  * $(TR
60  * $(TD $(LINK2 #Hijri, Hijri))
61  * $(TD Represents the Hijri calendar.)
62  * )
63  * $(TR
64  * $(TD $(LINK2 #Japanese, Japanese))
65  * $(TD Represents the Japanese calendar.)
66  * )
67  * $(TR
68  * $(TD $(LINK2 #Korean, Korean))
69  * $(TD Represents the Korean calendar.)
70  * )
71  * $(TR
72  * $(TD $(LINK2 #NumberFormat, NumberFormat))
73  * $(TD Determines how numbers are formatted, according to the current culture.)
74  * )
75  * $(TR
76  * $(TD $(LINK2 #Region, Region))
77  * $(TD Provides information about a region.)
78  * )
79  * $(TR
80  * $(TD $(LINK2 #Taiwan, Taiwan))
81  * $(TD Represents the Taiwan calendar.)
82  * )
83  * $(TR
84  * $(TD $(LINK2 #ThaiBuddhist, ThaiBuddhist))
85  * $(TD Represents the Thai Buddhist calendar.)
86  * )
87  * )
88  *
89  * $(MEMBERTABLE
90  * $(TR
91  * $(TH Struct)
92  * $(TH Description)
93  * )
94  * $(TR
95  * $(TD $(LINK2 #Time, Time))
96  * $(TD Represents time expressed as a date and time of day.)
97  * )
98  * $(TR
99  * $(TD $(LINK2 #TimeSpan, TimeSpan))
100  * $(TD Represents a time interval.)
101  * )
102  * )
103  */
104 
105 module tango.text.locale.Core;
106 
107 private import  tango.core.Exception;
108 
109 private import  tango.text.locale.Data;
110 
111 private import  tango.time.Time;
112 
113 private import  tango.time.chrono.Hijri,
114                 tango.time.chrono.Korean,
115                 tango.time.chrono.Taiwan,
116                 tango.time.chrono.Hebrew,
117                 tango.time.chrono.Calendar,
118                 tango.time.chrono.Japanese,
119                 tango.time.chrono.Gregorian,
120                 tango.time.chrono.ThaiBuddhist;
121         
122 version (Windows)
123          private import tango.text.locale.Win32;
124 
125 version (Posix)
126          private import tango.text.locale.Posix;
127 
128 
129 // Initializes an array.
130 private template arrayOf(T) {
131   private T[] arrayOf(T[] params ...) {
132     return params.dup;
133   }
134 }
135 
136 
137 /**
138  * Defines the types of cultures that can be retrieved from Culture.getCultures.
139  */
140 public enum CultureTypes {
141   Neutral = 1,             /// Refers to cultures that are associated with a language but not specific to a country or region.
142   Specific = 2,            /// Refers to cultures that are specific to a country or region.
143   All = Neutral | Specific /// Refers to all cultures.
144 }
145 
146 
147 /**
148  * $(ANCHOR _IFormatService)
149  * Retrieves an object to control formatting.
150  * 
151  * A class implements $(LINK2 #IFormatService_getFormat, getFormat) to retrieve an object that provides format information for the implementing type.
152  * Remarks: IFormatService is implemented by $(LINK2 #Culture, Culture), $(LINK2 #NumberFormat, NumberFormat) and $(LINK2 #DateTimeFormat, DateTimeFormat) to provide locale-specific formatting of
153  * numbers and date and time values.
154  */
155 public interface IFormatService {
156 
157   /**
158    * $(ANCHOR IFormatService_getFormat)
159    * Retrieves an object that supports formatting for the specified _type.
160    * Returns: The current instance if type is the same _type as the current instance; otherwise, null.
161    * Params: type = An object that specifies the _type of formatting to retrieve.
162    */
163   Object getFormat(TypeInfo type);
164 
165 }
166 
167 /**
168  * $(ANCHOR _Culture)
169  * Provides information about a culture, such as its name, calendar and date and number format patterns.
170  * Remarks: tango.text.locale adopts the RFC 1766 standard for culture names in the format <language>"-"<region>. 
171  * <language> is a lower-case two-letter code defined by ISO 639-1. <region> is an upper-case 
172  * two-letter code defined by ISO 3166. For example, "en-GB" is UK English.
173  * $(BR)$(BR)There are three types of culture: invariant, neutral and specific. The invariant culture is not tied to
174  * any specific region, although it is associated with the English language. A neutral culture is associated with
175  * a language, but not with a region. A specific culture is associated with a language and a region. "es" is a neutral 
176  * culture. "es-MX" is a specific culture.
177  * $(BR)$(BR)Instances of $(LINK2 #DateTimeFormat, DateTimeFormat) and $(LINK2 #NumberFormat, NumberFormat) cannot be created for neutral cultures.
178  * Examples:
179  * ---
180  * import tango.io.Stdout, tango.text.locale.Core;
181  *
182  * void main() {
183  *   Culture culture = new Culture("it-IT");
184  *
185  *   Stdout.formatln("englishName: {}", culture.englishName);
186  *   Stdout.formatln("nativeName: {}", culture.nativeName);
187  *   Stdout.formatln("name: {}", culture.name);
188  *   Stdout.formatln("parent: {}", culture.parent.name);
189  *   Stdout.formatln("isNeutral: {}", culture.isNeutral);
190  * }
191  *
192  * // Produces the following output:
193  * // englishName: Italian (Italy)
194  * // nativeName: italiano (Italia)
195  * // name: it-IT
196  * // parent: it
197  * // isNeutral: false
198  * ---
199  */
200 public class Culture : IFormatService {
201 
202   private enum int LCID_INVARIANT = 0x007F;
203 
204   private static Culture[immutable(char)[]] namedCultures;
205   private static Culture[int] idCultures;
206   private static Culture[immutable(char)[]] ietfCultures;
207 
208   private static Culture currentCulture_;
209   private static Culture userDefaultCulture_; // The user's default culture (GetUserDefaultLCID).
210   private static Culture invariantCulture_; // The invariant culture is associated with the English language.
211   private Calendar calendar_;
212   private Culture parent_;
213   private const(CultureData)* cultureData_;
214   private bool isReadOnly_;
215   private NumberFormat numberFormat_;
216   private DateTimeFormat dateTimeFormat_;
217 
218   shared static this() {
219     invariantCulture_ = new Culture(LCID_INVARIANT);
220     invariantCulture_.isReadOnly_ = true;
221 
222     userDefaultCulture_ = new Culture(nativeMethods.getUserCulture());
223     if (userDefaultCulture_ is null)
224       // Fallback
225       userDefaultCulture_ = invariantCulture;
226     else
227       userDefaultCulture_.isReadOnly_ = true;
228   }
229 
230   static ~this() {
231     namedCultures = null;
232     idCultures = null;
233     ietfCultures = null;
234   }
235 
236   /**
237    * Initializes a new Culture instance from the supplied name.
238    * Params: cultureName = The name of the Culture.
239    */
240   public this(const(char)[] cultureName) {
241     cultureData_ = CultureData.getDataFromCultureName(cultureName);
242   }
243 
244   /**
245    * Initializes a new Culture instance from the supplied culture identifier.
246    * Params: cultureID = The identifer (LCID) of the Culture.
247    * Remarks: Culture identifiers correspond to a Windows LCID.
248    */
249   public this(int cultureID) {
250     cultureData_ = CultureData.getDataFromCultureID(cultureID);
251   }
252 
253   /**
254    * Retrieves an object defining how to format the specified type.
255    * Params: type = The TypeInfo of the resulting formatting object.
256    * Returns: If type is typeid($(LINK2 #NumberFormat, NumberFormat)), the value of the $(LINK2 #Culture_numberFormat, numberFormat) property. If type is typeid($(LINK2 #DateTimeFormat, DateTimeFormat)), the
257    * value of the $(LINK2 #Culture_dateTimeFormat, dateTimeFormat) property. Otherwise, null.
258    * Remarks: Implements $(LINK2 #IFormatService_getFormat, IFormatService.getFormat).
259    */
260   public Object getFormat(TypeInfo type) {
261     if (type is typeid(NumberFormat))
262       return numberFormat;
263     else if (type is typeid(DateTimeFormat))
264       return dateTimeFormat;
265     return null;
266   }
267 
268 version (Clone)
269 {
270   /**
271    * Copies the current Culture instance.
272    * Returns: A copy of the current Culture instance.
273    * Remarks: The values of the $(LINK2 #Culture_numberFormat, numberFormat), $(LINK2 #Culture_dateTimeFormat, dateTimeFormat) and $(LINK2 #Culture_calendar, calendar) properties are copied also.
274    */
275   public Object clone() {
276     Culture culture = cast(Culture)cloneObject(this);
277     if (!culture.isNeutral) {
278       if (dateTimeFormat_ !is null)
279         culture.dateTimeFormat_ = cast(DateTimeFormat)dateTimeFormat_.clone();
280       if (numberFormat_ !is null)
281         culture.numberFormat_ = cast(NumberFormat)numberFormat_.clone();
282     }
283     if (calendar_ !is null)
284       culture.calendar_ = cast(Calendar)calendar_.clone();
285     return culture;
286   }
287 }
288 
289   /**
290    * Returns a read-only instance of a culture using the specified culture identifier.
291    * Params: cultureID = The identifier of the culture.
292    * Returns: A read-only culture instance.
293    * Remarks: Instances returned by this method are cached.
294    */
295   public static Culture getCulture(int cultureID) {
296     Culture culture = getCultureInternal(cultureID, null);
297 
298 version (Posix) {
299     if (culture is null)
300         error ("Culture not found - if this was not tried set by the application, Tango\n"
301             ~ "will expect that a locale is set via environment variables LANG or LC_ALL.");
302 }
303 
304     return culture;
305   }
306 
307   /**
308    * Returns a read-only instance of a culture using the specified culture name.
309    * Params: cultureName = The name of the culture.
310    * Returns: A read-only culture instance.
311    * Remarks: Instances returned by this method are cached.
312    */
313   public static Culture getCulture(const(char)[] cultureName) {
314     if (cultureName is null)
315        error("Value cannot be null.");
316     Culture culture = getCultureInternal(0, cultureName);
317     if (culture is null)
318       error("Culture name " ~ cultureName.idup ~ " is not supported.");
319     return culture;
320   }
321 
322   /**
323     * Returns a read-only instance using the specified name, as defined by the RFC 3066 standard and maintained by the IETF.
324     * Params: name = The name of the language.
325     * Returns: A read-only culture instance.
326     */
327   public static Culture getCultureFromIetfLanguageTag(const(char)[] name) {
328     if (name is null)
329       error("Value cannot be null.");
330     Culture culture = getCultureInternal(-1, name);
331     if (culture is null)
332       error("Culture IETF name " ~ name.idup ~ " is not a known IETF name.");
333     return culture;
334   }
335 
336   private static Culture getCultureInternal(int cultureID, const(char)[] cname) {
337     // If cultureID is - 1, name is an IETF name; if it's 0, name is a culture name; otherwise, it's a valid LCID.
338     const(char)[] name = cname;
339     char[] temp_name;
340     foreach (i, c; cname)
341        if (c is '_') {
342          temp_name = cname.dup;
343          temp_name[i] = '-';
344          name = temp_name;
345          break;
346        }
347 
348     // Look up tables first.
349     if (cultureID == 0) {
350       if (Culture* culture = name in namedCultures)
351         return *culture;
352     }
353     else if (cultureID > 0) {
354       if (Culture* culture = cultureID in idCultures)
355         return *culture;
356     }
357     else if (cultureID == -1) {
358       if (Culture* culture = name in ietfCultures)
359         return *culture;
360     }
361 
362     // Nothing found, create a new instance.
363     Culture culture;
364 
365     try {
366       if (cultureID == -1) {
367         name = CultureData.getCultureNameFromIetfName(name);
368         if (name is null)
369           return null;
370       }
371       else if (cultureID == 0)
372         culture = new Culture(name);
373       else if (userDefaultCulture_ !is null && userDefaultCulture_.id == cultureID) {
374         culture = userDefaultCulture_;
375       }
376       else
377         culture = new Culture(cultureID);
378     }
379     catch (LocaleException) {
380       return null;
381     }
382 
383     culture.isReadOnly_ = true;
384 
385     // Now cache the new instance in all tables.
386     ietfCultures[culture.ietfLanguageTag] = culture;
387     namedCultures[culture.name] = culture;
388     idCultures[culture.id] = culture;
389 
390     return culture;
391   }
392 
393   /**
394    * Returns a list of cultures filtered by the specified $(LINK2 constants.html#CultureTypes, CultureTypes).
395    * Params: types = A combination of CultureTypes.
396    * Returns: An array of Culture instances containing cultures specified by types.
397    */
398   public static Culture[] getCultures(CultureTypes types) {
399     bool includeSpecific = (types & CultureTypes.Specific) != 0;
400     bool includeNeutral = (types & CultureTypes.Neutral) != 0;
401 
402     int[] cultures;
403     for (int i = 0; i < CultureData.cultureDataTable.length; i++) {
404       if ((CultureData.cultureDataTable[i].isNeutral && includeNeutral) || (!CultureData.cultureDataTable[i].isNeutral && includeSpecific))
405         cultures ~= CultureData.cultureDataTable[i].lcid;
406     }
407 
408     Culture[] result = new Culture[cultures.length];
409     foreach (int i, int cultureID; cultures)
410       result[i] = new Culture(cultureID);
411     return result;
412   }
413 
414   /**
415    * Returns the name of the Culture.
416    * Returns: A string containing the name of the Culture in the format &lt;language&gt;"-"&lt;region&gt;.
417    */
418   public override immutable(char)[] toString() {
419     return cultureData_.name.idup;
420   }
421 
422   public override bool opEquals(Object obj) {
423     if (obj is this)
424       return true;
425     Culture other = cast(Culture)obj;
426     if (other is null)
427       return false;
428     return other.name == name; // This needs to be changed so it's culturally aware.
429   }
430 
431   /**
432    * $(ANCHOR Culture_current)
433    * $(I Property.) Retrieves the culture of the current user.
434    * Returns: The Culture instance representing the user's current culture.
435    */
436   @property public static Culture current() {
437     if (currentCulture_ !is null)
438       return currentCulture_;
439 
440     if (userDefaultCulture_ !is null) {
441       // If the user has changed their locale settings since last we checked, invalidate our data.
442       if (userDefaultCulture_.id != nativeMethods.getUserCulture())
443         userDefaultCulture_ = null;
444     }
445     if (userDefaultCulture_ is null) {
446       userDefaultCulture_ = new Culture(nativeMethods.getUserCulture());
447       if (userDefaultCulture_ is null)
448         userDefaultCulture_ = invariantCulture;
449       else
450         userDefaultCulture_.isReadOnly_ = true;
451     }
452 
453     return userDefaultCulture_;
454   }
455   /**
456    * $(I Property.) Assigns the culture of the _current user.
457    * Params: value = The Culture instance representing the user's _current culture.
458    * Examples:
459    * The following examples shows how to change the _current culture.
460    * ---
461    * import tango.io.Print, tango.text.locale.Common;
462    *
463    * void main() {
464    *   // Displays the name of the current culture.
465    *   Println("The current culture is %s.", Culture.current.englishName);
466    *
467    *   // Changes the current culture to el-GR.
468    *   Culture.current = new Culture("el-GR");
469    *   Println("The current culture is now %s.", Culture.current.englishName);
470    * }
471    *
472    * // Produces the following output:
473    * // The current culture is English (United Kingdom).
474    * // The current culture is now Greek (Greece).
475    * ---
476    */
477   @property public static void current(Culture value) {
478     checkNeutral(value);
479     nativeMethods.setUserCulture(value.id);
480     currentCulture_ = value;
481   }
482 
483   /**
484    * $(I Property.) Retrieves the invariant Culture.
485    * Returns: The Culture instance that is invariant.
486    * Remarks: The invariant culture is culture-independent. It is not tied to any specific region, but is associated
487    * with the English language.
488    */
489   @property public static Culture invariantCulture() {
490     return invariantCulture_;
491   }
492 
493   /**
494    * $(I Property.) Retrieves the identifier of the Culture.
495    * Returns: The culture identifier of the current instance.
496    * Remarks: The culture identifier corresponds to the Windows locale identifier (LCID). It can therefore be used when 
497    * interfacing with the Windows NLS functions.
498    */
499   @property public const int id() {
500     return cultureData_.lcid;
501   }
502 
503   /**
504    * $(ANCHOR Culture_name)
505    * $(I Property.) Retrieves the name of the Culture in the format &lt;language&gt;"-"&lt;region&gt;.
506    * Returns: The name of the current instance. For example, the name of the UK English culture is "en-GB".
507    */
508   @property public const const(char)[] name() {
509     return cultureData_.name;
510   }
511 
512   /**
513    * $(I Property.) Retrieves the name of the Culture in the format &lt;languagename&gt; (&lt;regionname&gt;) in English.
514    * Returns: The name of the current instance in English. For example, the englishName of the UK English culture 
515    * is "English (United Kingdom)".
516    */
517   @property public const const(char)[] englishName() {
518     return cultureData_.englishName;
519   }
520 
521   /**
522    * $(I Property.) Retrieves the name of the Culture in the format &lt;languagename&gt; (&lt;regionname&gt;) in its native language.
523    * Returns: The name of the current instance in its native language. For example, if Culture.name is "de-DE", nativeName is 
524    * "Deutsch (Deutschland)".
525    */
526   @property public const const(char)[] nativeName() {
527     return cultureData_.nativeName;
528   }
529 
530   /**
531    * $(I Property.) Retrieves the two-letter language code of the culture.
532    * Returns: The two-letter language code of the Culture instance. For example, the twoLetterLanguageName for English is "en".
533    */
534   @property public const const(char)[] twoLetterLanguageName() {
535     return cultureData_.isoLangName;
536   }
537 
538   /**
539    * $(I Property.) Retrieves the three-letter language code of the culture.
540    * Returns: The three-letter language code of the Culture instance. For example, the threeLetterLanguageName for English is "eng".
541    */
542   @property public const const(char)[] threeLetterLanguageName() {
543     return cultureData_.isoLangName2;
544   }
545 
546   /**
547    * $(I Property.) Retrieves the RFC 3066 identification for a language.
548    * Returns: A string representing the RFC 3066 language identification.
549    */
550   @property public const final const(char)[] ietfLanguageTag() {
551     return cultureData_.ietfTag;
552   }
553 
554   /**
555    * $(I Property.) Retrieves the Culture representing the parent of the current instance.
556    * Returns: The Culture representing the parent of the current instance.
557    */
558   @property public Culture parent() {
559     if (parent_ is null) {
560       try {
561         int parentCulture = cultureData_.parent;
562         if (parentCulture == LCID_INVARIANT)
563           parent_ = invariantCulture;
564         else
565           parent_ = new Culture(parentCulture);
566       }
567       catch {
568         parent_ = invariantCulture;
569       }
570     }
571     return parent_;
572   }
573 
574   /**
575    * $(I Property.) Retrieves a value indicating whether the current instance is a neutral culture.
576    * Returns: true is the current Culture represents a neutral culture; otherwise, false.
577    * Examples:
578    * The following example displays which cultures using Chinese are neutral.
579    * ---
580    * import tango.io.Print, tango.text.locale.Common;
581    *
582    * void main() {
583    *   foreach (c; Culture.getCultures(CultureTypes.All)) {
584    *     if (c.twoLetterLanguageName == "zh") {
585    *       Print(c.englishName);
586    *       if (c.isNeutral)
587    *         Println("neutral");
588    *       else
589    *         Println("specific");
590    *     }
591    *   }
592    * }
593    *
594    * // Produces the following output:
595    * // Chinese (Simplified) - neutral
596    * // Chinese (Taiwan) - specific
597    * // Chinese (People's Republic of China) - specific
598    * // Chinese (Hong Kong S.A.R.) - specific
599    * // Chinese (Singapore) - specific
600    * // Chinese (Macao S.A.R.) - specific
601    * // Chinese (Traditional) - neutral
602    * ---
603    */
604   @property public const bool isNeutral() {
605     return cultureData_.isNeutral;
606   }
607 
608   /**
609    * $(I Property.) Retrieves a value indicating whether the instance is read-only.
610    * Returns: true if the instance is read-only; otherwise, false.
611    * Remarks: If the culture is read-only, the $(LINK2 #Culture_dateTimeFormat, dateTimeFormat) and $(LINK2 #Culture_numberFormat, numberFormat) properties return 
612    * read-only instances.
613    */
614   @property public const final bool isReadOnly() {
615     return isReadOnly_;
616   }
617 
618   /**
619    * $(ANCHOR Culture_calendar)
620    * $(I Property.) Retrieves the calendar used by the culture.
621    * Returns: A Calendar instance respresenting the calendar used by the culture.
622    */
623   @property public Calendar calendar() {
624     if (calendar_ is null) {
625       calendar_ = getCalendarInstance(cultureData_.calendarType, isReadOnly_);
626     }
627     return calendar_;
628   }
629 
630   /**
631    * $(I Property.) Retrieves the list of calendars that can be used by the culture.
632    * Returns: An array of type Calendar representing the calendars that can be used by the culture.
633    */
634   @property public Calendar[] optionalCalendars() {
635     Calendar[] cals = new Calendar[cultureData_.optionalCalendars.length];
636     foreach (int i, int calID; cultureData_.optionalCalendars)
637       cals[i] = getCalendarInstance(calID);
638     return cals;
639   }
640 
641   /**
642    * $(ANCHOR Culture_numberFormat)
643    * $(I Property.) Retrieves a NumberFormat defining the culturally appropriate format for displaying numbers and currency.
644    * Returns: A NumberFormat defining the culturally appropriate format for displaying numbers and currency.
645   */
646   @property public NumberFormat numberFormat() {
647     checkNeutral(this);
648     if (numberFormat_ is null) {
649       numberFormat_ = new NumberFormat(cultureData_);
650       numberFormat_.isReadOnly_ = isReadOnly_;
651     }
652     return numberFormat_;
653   }
654   /**
655    * $(I Property.) Assigns a NumberFormat defining the culturally appropriate format for displaying numbers and currency.
656    * Params: values = A NumberFormat defining the culturally appropriate format for displaying numbers and currency.
657    */
658   @property public void numberFormat(NumberFormat value) {
659     checkReadOnly();
660     numberFormat_ = value;
661   }
662 
663   /**
664    * $(ANCHOR Culture_dateTimeFormat)
665    * $(I Property.) Retrieves a DateTimeFormat defining the culturally appropriate format for displaying dates and times.
666    * Returns: A DateTimeFormat defining the culturally appropriate format for displaying dates and times.
667    */
668   @property public DateTimeFormat dateTimeFormat() {
669     checkNeutral(this);
670     if (dateTimeFormat_ is null) {
671       dateTimeFormat_ = new DateTimeFormat(cultureData_, calendar);
672       dateTimeFormat_.isReadOnly_ = isReadOnly_;
673     }
674     return dateTimeFormat_;
675   }
676   /**
677    * $(I Property.) Assigns a DateTimeFormat defining the culturally appropriate format for displaying dates and times.
678    * Params: values = A DateTimeFormat defining the culturally appropriate format for displaying dates and times.
679    */
680   @property public void dateTimeFormat(DateTimeFormat value) {
681     checkReadOnly();
682     dateTimeFormat_ = value;
683   }
684 
685   private static void checkNeutral(Culture culture) {
686     if (culture.isNeutral)
687       error("Culture '" ~ culture.name.idup ~ "' is a neutral culture. It cannot be used in formatting and therefore cannot be set as the current culture.");
688   }
689 
690   private void checkReadOnly() {
691     if (isReadOnly_)
692       error("Instance is read-only.");
693   }
694 
695   private static Calendar getCalendarInstance(int calendarType, bool readOnly=false) {
696     switch (calendarType) {
697       case Calendar.JAPAN:
698         return new Japanese();
699       case Calendar.TAIWAN:
700         return new Taiwan();
701       case Calendar.KOREA:
702         return new Korean();
703       case Calendar.HIJRI:
704         return new Hijri();
705       case Calendar.THAI:
706         return new ThaiBuddhist();
707       case Calendar.HEBREW:
708         return new Hebrew;
709       case Calendar.GREGORIAN_US:
710       case Calendar.GREGORIAN_ME_FRENCH:
711       case Calendar.GREGORIAN_ARABIC:
712       case Calendar.GREGORIAN_XLIT_ENGLISH:
713       case Calendar.GREGORIAN_XLIT_FRENCH:
714         return new Gregorian(cast(Gregorian.Type) calendarType);
715       default:
716         break;
717     }
718     return new Gregorian();
719   }
720 
721 }
722 
723 /**
724  * $(ANCHOR _Region)
725  * Provides information about a region.
726  * Remarks: Region does not represent user preferences. It does not depend on the user's language or culture.
727  * Examples:
728  * The following example displays some of the properties of the Region class:
729  * ---
730  * import tango.io.Print, tango.text.locale.Common;
731  *
732  * void main() {
733  *   Region region = new Region("en-GB");
734  *   Println("name:              %s", region.name);
735  *   Println("englishName:       %s", region.englishName);
736  *   Println("isMetric:          %s", region.isMetric);
737  *   Println("currencySymbol:    %s", region.currencySymbol);
738  *   Println("isoCurrencySymbol: %s", region.isoCurrencySymbol);
739  * }
740  *
741  * // Produces the following output.
742  * // name:              en-GB
743  * // englishName:       United Kingdom
744  * // isMetric:          true
745  * // currencySymbol:    £
746  * // isoCurrencySymbol: GBP
747  * ---
748  */
749 public class Region {
750 
751   private const(CultureData)* cultureData_;
752   private static Region currentRegion_;
753   private const(char)[] name_;
754 
755   /**
756    * Initializes a new Region instance based on the region associated with the specified culture identifier.
757    * Params: cultureID = A culture indentifier.
758    * Remarks: The name of the Region instance is set to the ISO 3166 two-letter code for that region.
759    */
760   public this(int cultureID) {
761     cultureData_ = CultureData.getDataFromCultureID(cultureID);
762     if (cultureData_.isNeutral)
763         error ("Cannot use a neutral culture to create a region.");
764     name_ = cultureData_.regionName;
765   }
766 
767   /**
768    * $(ANCHOR Region_ctor_name)
769    * Initializes a new Region instance based on the region specified by name.
770    * Params: name = A two-letter ISO 3166 code for the region. Or, a culture $(LINK2 #Culture_name, _name) consisting of the language and region.
771    */
772   public this(const(char)[] name) {
773     cultureData_ = CultureData.getDataFromRegionName(name);
774     name_ = name;
775     if (cultureData_.isNeutral)
776         error ("The region name " ~ name.idup ~ " corresponds to a neutral culture and cannot be used to create a region.");
777   }
778 
779   package this(const(CultureData)* cultureData) {
780     cultureData_ = cultureData;
781     name_ = cultureData.regionName;
782   }
783 
784   /**
785    * $(I Property.) Retrieves the Region used by the current $(LINK2 #Culture, Culture).
786    * Returns: The Region instance associated with the current Culture.
787    */
788   @property public static Region current() {
789     if (currentRegion_ is null)
790       currentRegion_ = new Region(Culture.current.cultureData_);
791     return currentRegion_;
792   }
793 
794   /**
795    * $(I Property.) Retrieves a unique identifier for the geographical location of the region.
796    * Returns: An $(B int) uniquely identifying the geographical location.
797    */
798   @property public const int geoID() {
799     return cultureData_.geoId;
800   }
801 
802   /**
803    * $(ANCHOR Region_name)
804    * $(I Property.) Retrieves the ISO 3166 code, or the name, of the current Region.
805    * Returns: The value specified by the name parameter of the $(LINK2 #Region_ctor_name, Region(char[])) constructor.
806    */
807   @property public const const(char)[] name() {
808     return name_;
809   }
810 
811   /**
812    * $(I Property.) Retrieves the full name of the region in English.
813    * Returns: The full name of the region in English.
814    */
815   @property public const const(char)[] englishName() {
816     return cultureData_.englishCountry;
817   }
818 
819   /**
820    * $(I Property.) Retrieves the full name of the region in its native language.
821    * Returns: The full name of the region in the language associated with the region code.
822    */
823   @property public const const(char)[] nativeName() {
824     return cultureData_.nativeCountry;
825   }
826 
827   /**
828    * $(I Property.) Retrieves the two-letter ISO 3166 code of the region.
829    * Returns: The two-letter ISO 3166 code of the region.
830    */
831   @property public const const(char)[] twoLetterRegionName() {
832     return cultureData_.regionName;
833   }
834 
835   /**
836    * $(I Property.) Retrieves the three-letter ISO 3166 code of the region.
837    * Returns: The three-letter ISO 3166 code of the region.
838    */
839   @property public const const(char)[] threeLetterRegionName() {
840     return cultureData_.isoRegionName;
841   }
842 
843   /**
844    * $(I Property.) Retrieves the currency symbol of the region.
845    * Returns: The currency symbol of the region.
846    */
847   @property public const const(char)[] currencySymbol() {
848     return cultureData_.currency;
849   }
850 
851   /**
852    * $(I Property.) Retrieves the three-character currency symbol of the region.
853    * Returns: The three-character currency symbol of the region.
854    */
855   @property public const const(char)[] isoCurrencySymbol() {
856     return cultureData_.intlSymbol;
857   }
858 
859   /**
860    * $(I Property.) Retrieves the name in English of the currency used in the region.
861    * Returns: The name in English of the currency used in the region.
862    */
863   @property public const const(char)[] currencyEnglishName() {
864     return cultureData_.englishCurrency;
865   }
866 
867   /**
868    * $(I Property.) Retrieves the name in the native language of the region of the currency used in the region.
869    * Returns: The name in the native language of the region of the currency used in the region.
870    */
871   @property public const const(char)[] currencyNativeName() {
872     return cultureData_.nativeCurrency;
873   }
874 
875   /**
876    * $(I Property.) Retrieves a value indicating whether the region uses the metric system for measurements.
877    * Returns: true is the region uses the metric system; otherwise, false.
878    */
879   @property public const bool isMetric() {
880     return cultureData_.isMetric;
881   }
882 
883   /**
884    * Returns a string containing the ISO 3166 code, or the $(LINK2 #Region_name, name), of the current Region.
885    * Returns: A string containing the ISO 3166 code, or the name, of the current Region.
886    */
887   public override immutable(char)[] toString() {
888     return name_.idup;
889   }
890 
891 }
892 
893 /**
894  * $(ANCHOR _NumberFormat)
895  * Determines how numbers are formatted, according to the current culture.
896  * Remarks: Numbers are formatted using format patterns retrieved from a NumberFormat instance.
897  * This class implements $(LINK2 #IFormatService_getFormat, IFormatService.getFormat).
898  * Examples:
899  * The following example shows how to retrieve an instance of NumberFormat for a Culture
900  * and use it to display number formatting information.
901  * ---
902  * import tango.io.Print, tango.text.locale.Common;
903  *
904  * void main(char[][] args) {
905  *   foreach (c; Culture.getCultures(CultureTypes.Specific)) {
906  *     if (c.twoLetterLanguageName == "en") {
907  *       NumberFormat fmt = c.numberFormat;
908  *       Println("The currency symbol for %s is '%s'", 
909  *         c.englishName, 
910  *         fmt.currencySymbol);
911  *     }
912  *   }
913  * }
914  *
915  * // Produces the following output:
916  * // The currency symbol for English (United States) is '$'
917  * // The currency symbol for English (United Kingdom) is '£'
918  * // The currency symbol for English (Australia) is '$'
919  * // The currency symbol for English (Canada) is '$'
920  * // The currency symbol for English (New Zealand) is '$'
921  * // The currency symbol for English (Ireland) is '€'
922  * // The currency symbol for English (South Africa) is 'R'
923  * // The currency symbol for English (Jamaica) is 'J$'
924  * // The currency symbol for English (Caribbean) is '$'
925  * // The currency symbol for English (Belize) is 'BZ$'
926  * // The currency symbol for English (Trinidad and Tobago) is 'TT$'
927  * // The currency symbol for English (Zimbabwe) is 'Z$'
928  * // The currency symbol for English (Republic of the Philippines) is 'Php'
929  *---
930  */
931 public class NumberFormat : IFormatService {
932 
933   package bool isReadOnly_;
934   private static NumberFormat invariantFormat_;
935 
936   private int numberDecimalDigits_;
937   private int numberNegativePattern_;
938   private int currencyDecimalDigits_;
939   private int currencyNegativePattern_;
940   private int currencyPositivePattern_;
941   private const(int)[] numberGroupSizes_;
942   private const(int)[] currencyGroupSizes_;
943   private const(char)[] numberGroupSeparator_;
944   private const(char)[] numberDecimalSeparator_;
945   private const(char)[] currencyGroupSeparator_;
946   private const(char)[] currencyDecimalSeparator_;
947   private const(char)[] currencySymbol_;
948   private const(char)[] negativeSign_;
949   private const(char)[] positiveSign_;
950   private const(char)[] nanSymbol_;
951   private const(char)[] negativeInfinitySymbol_;
952   private const(char)[] positiveInfinitySymbol_;
953   private const(char[])[] nativeDigits_;
954 
955   /**
956    * Initializes a new, culturally independent instance.
957    *
958    * Remarks: Modify the properties of the new instance to define custom formatting.
959    */
960   public this() {
961     this(null);
962   }
963 
964   package this(const(CultureData)* cultureData) {
965     // Initialize invariant data.
966     numberDecimalDigits_ = 2;
967     numberNegativePattern_ = 1;
968     currencyDecimalDigits_ = 2;
969     numberGroupSizes_ = arrayOf!(int)(3);
970     currencyGroupSizes_ = arrayOf!(int)(3);
971     numberGroupSeparator_ = ",";
972     numberDecimalSeparator_ = ".";
973     currencyGroupSeparator_ = ",";
974     currencyDecimalSeparator_ = ".";
975     currencySymbol_ = "\u00A4";
976     negativeSign_ = "-";
977     positiveSign_ = "+";
978     nanSymbol_ = "NaN";
979     negativeInfinitySymbol_ = "-Infinity";
980     positiveInfinitySymbol_ = "Infinity";
981     nativeDigits_ = arrayOf!(const(char)[])("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
982 
983     if (cultureData !is null && cultureData.lcid != Culture.LCID_INVARIANT) {
984       // Initialize culture-specific data.
985       numberDecimalDigits_ = cultureData.digits;
986       numberNegativePattern_ = cultureData.negativeNumber;
987       currencyDecimalDigits_ = cultureData.currencyDigits;
988       currencyNegativePattern_ = cultureData.negativeCurrency;
989       currencyPositivePattern_ = cultureData.positiveCurrency;
990       numberGroupSizes_ = cultureData.grouping;
991       currencyGroupSizes_ = cultureData.monetaryGrouping;
992       numberGroupSeparator_ = cultureData.thousand;
993       numberDecimalSeparator_ = cultureData.decimal;
994       currencyGroupSeparator_ = cultureData.monetaryThousand;
995       currencyDecimalSeparator_ = cultureData.monetaryDecimal;
996       currencySymbol_ = cultureData.currency;
997       negativeSign_ = cultureData.negativeSign;
998       positiveSign_ = cultureData.positiveSign;
999       nanSymbol_ = cultureData.nan;
1000       negativeInfinitySymbol_ = cultureData.negInfinity;
1001       positiveInfinitySymbol_ = cultureData.posInfinity;
1002       nativeDigits_ = cultureData.nativeDigits;
1003     }
1004   }
1005 
1006   /**
1007    * Retrieves an object defining how to format the specified type.
1008    * Params: type = The TypeInfo of the resulting formatting object.
1009    * Returns: If type is typeid($(LINK2 #NumberFormat, NumberFormat)), the current NumberFormat instance. Otherwise, null.
1010    * Remarks: Implements $(LINK2 #IFormatService_getFormat, IFormatService.getFormat).
1011    */
1012   public Object getFormat(TypeInfo type) {
1013     return (type is typeid(NumberFormat)) ? this : null;
1014   }
1015 
1016 version (Clone)
1017 {
1018   /**
1019    * Creates a copy of the instance.
1020    */
1021   public Object clone() {
1022     NumberFormat copy = cast(NumberFormat)cloneObject(this);
1023     copy.isReadOnly_ = false;
1024     return copy;
1025   }
1026 }
1027 
1028   /**
1029    * Retrieves the NumberFormat for the specified $(LINK2 #IFormatService, IFormatService).
1030    * Params: formatService = The IFormatService used to retrieve NumberFormat.
1031    * Returns: The NumberFormat for the specified IFormatService.
1032    * Remarks: The method calls $(LINK2 #IFormatService_getFormat, IFormatService.getFormat) with typeof(NumberFormat). If formatService is null,
1033    * then the value of the current property is returned.
1034    */
1035   public static NumberFormat getInstance(IFormatService formatService) {
1036     Culture culture = cast(Culture)formatService;
1037     if (culture !is null) {
1038       if (culture.numberFormat_ !is null)
1039         return culture.numberFormat_;
1040       return culture.numberFormat;
1041     }
1042     if (NumberFormat numberFormat = cast(NumberFormat)formatService)
1043       return numberFormat;
1044     if (formatService !is null) {
1045       if (NumberFormat numberFormat = cast(NumberFormat)(formatService.getFormat(typeid(NumberFormat))))
1046         return numberFormat;
1047     }
1048     return current;
1049   }
1050 
1051   /**
1052    * $(I Property.) Retrieves a read-only NumberFormat instance from the current culture.
1053    * Returns: A read-only NumberFormat instance from the current culture.
1054    */
1055   @property public static NumberFormat current() {
1056     return Culture.current.numberFormat;
1057   }
1058 
1059   /**
1060    * $(ANCHOR NumberFormat_invariantFormat)
1061    * $(I Property.) Retrieves the read-only, culturally independent NumberFormat instance.
1062    * Returns: The read-only, culturally independent NumberFormat instance.
1063    */
1064   public static NumberFormat invariantFormat() {
1065     if (invariantFormat_ is null) {
1066       invariantFormat_ = new NumberFormat;
1067       invariantFormat_.isReadOnly_ = true;
1068     }
1069     return invariantFormat_;
1070   }
1071 
1072   /**
1073    * $(I Property.) Retrieves a value indicating whether the instance is read-only.
1074    * Returns: true if the instance is read-only; otherwise, false.
1075    */
1076   @property public final const bool isReadOnly() {
1077     return isReadOnly_;
1078   }
1079 
1080   /**
1081    * $(I Property.) Retrieves the number of decimal places used for numbers.
1082    * Returns: The number of decimal places used for numbers. For $(LINK2 #NumberFormat_invariantFormat, invariantFormat), the default is 2.
1083    */
1084   @property public final const int numberDecimalDigits() {
1085     return numberDecimalDigits_;
1086   }
1087   /**
1088    * Assigns the number of decimal digits used for numbers.
1089    * Params: value = The number of decimal places used for numbers.
1090    * Throws: Exception if the property is being set and the instance is read-only.
1091    * Examples:
1092    * The following example shows the effect of changing numberDecimalDigits.
1093    * ---
1094    * import tango.io.Print, tango.text.locale.Common;
1095    *
1096    * void main() {
1097    *   // Get the NumberFormat from the en-GB culture.
1098    *   NumberFormat fmt = (new Culture("en-GB")).numberFormat;
1099    *
1100    *   // Display a value with the default number of decimal digits.
1101    *   int n = 5678;
1102    *   Println(Formatter.format(fmt, "{0:N}", n));
1103    *
1104    *   // Display the value with six decimal digits.
1105    *   fmt.numberDecimalDigits = 6;
1106    *   Println(Formatter.format(fmt, "{0:N}", n));
1107    * }
1108    *
1109    * // Produces the following output:
1110    * // 5,678.00
1111    * // 5,678.000000
1112    * ---
1113    */
1114   @property public final void numberDecimalDigits(int value) {
1115     checkReadOnly();
1116     numberDecimalDigits_ = value;
1117   }
1118 
1119   /**
1120    * $(I Property.) Retrieves the format pattern for negative numbers.
1121    * Returns: The format pattern for negative numbers. For invariantFormat, the default is 1 (representing "-n").
1122    * Remarks: The following table shows valid values for this property.
1123    *
1124    * <table class="definitionTable">
1125    * <tr><th>Value</th><th>Pattern</th></tr>
1126    * <tr><td>0</td><td>(n)</td></tr>
1127    * <tr><td>1</td><td>-n</td></tr>
1128    * <tr><td>2</td><td>- n</td></tr>
1129    * <tr><td>3</td><td>n-</td></tr>
1130    * <tr><td>4</td><td>n -</td></tr>
1131    * </table>
1132    */
1133   @property public final const int numberNegativePattern() {
1134     return numberNegativePattern_;
1135   }
1136   /**
1137    * $(I Property.) Assigns the format pattern for negative numbers.
1138    * Params: value = The format pattern for negative numbers.
1139    * Examples:
1140    * The following example shows the effect of the different patterns.
1141    * ---
1142    * import tango.io.Print, tango.text.locale.Common;
1143    *
1144    * void main() {
1145    *   NumberFormat fmt = new NumberFormat;
1146    *   int n = -5678;
1147    *
1148    *   // Display the default pattern.
1149    *   Println(Formatter.format(fmt, "{0:N}", n));
1150    *
1151    *   // Display all patterns.
1152    *   for (int i = 0; i <= 4; i++) {
1153    *     fmt.numberNegativePattern = i;
1154    *     Println(Formatter.format(fmt, "{0:N}", n));
1155    *   }
1156    * }
1157    *
1158    * // Produces the following output:
1159    * // (5,678.00)
1160    * // (5,678.00)
1161    * // -5,678.00
1162    * // - 5,678.00
1163    * // 5,678.00-
1164    * // 5,678.00 -
1165    * ---
1166    */
1167   @property public final void numberNegativePattern(int value) {
1168     checkReadOnly();
1169     numberNegativePattern_ = value;
1170   }
1171 
1172   /**
1173    * $(I Property.) Retrieves the number of decimal places to use in currency values.
1174    * Returns: The number of decimal digits to use in currency values.
1175    */
1176   @property public const final int currencyDecimalDigits() {
1177     return currencyDecimalDigits_;
1178   }
1179   /**
1180    * $(I Property.) Assigns the number of decimal places to use in currency values.
1181    * Params: value = The number of decimal digits to use in currency values.
1182    */
1183   @property public final void currencyDecimalDigits(int value) {
1184     checkReadOnly();
1185     currencyDecimalDigits_ = value;
1186   }
1187 
1188   /**
1189    * $(I Property.) Retrieves the formal pattern to use for negative currency values.
1190    * Returns: The format pattern to use for negative currency values.
1191    */
1192   @property public const final int currencyNegativePattern() {
1193     return currencyNegativePattern_;
1194   }
1195   /**
1196    * $(I Property.) Assigns the formal pattern to use for negative currency values.
1197    * Params: value = The format pattern to use for negative currency values.
1198    */
1199   @property public final void currencyNegativePattern(int value) {
1200     checkReadOnly();
1201     currencyNegativePattern_ = value;
1202   }
1203 
1204   /**
1205    * $(I Property.) Retrieves the formal pattern to use for positive currency values.
1206    * Returns: The format pattern to use for positive currency values.
1207    */
1208   @property public const final int currencyPositivePattern() {
1209     return currencyPositivePattern_;
1210   }
1211   /**
1212    * $(I Property.) Assigns the formal pattern to use for positive currency values.
1213    * Returns: The format pattern to use for positive currency values.
1214    */
1215   @property public final void currencyPositivePattern(int value) {
1216     checkReadOnly();
1217     currencyPositivePattern_ = value;
1218   }
1219 
1220   /**
1221    * $(I Property.) Retrieves the number of digits int each group to the left of the decimal place in numbers.
1222    * Returns: The number of digits int each group to the left of the decimal place in numbers.
1223    */
1224   @property public const final const(int)[] numberGroupSizes() {
1225     return numberGroupSizes_;
1226   }
1227   /**
1228    * $(I Property.) Assigns the number of digits int each group to the left of the decimal place in numbers.
1229    * Params: value = The number of digits int each group to the left of the decimal place in numbers.
1230    */
1231   @property public final void numberGroupSizes(const(int)[] value) {
1232     checkReadOnly();
1233     numberGroupSizes_ = value;
1234   }
1235 
1236   /**
1237    * $(I Property.) Retrieves the number of digits int each group to the left of the decimal place in currency values.
1238    * Returns: The number of digits int each group to the left of the decimal place in currency values.
1239    */
1240   @property public const final const(int)[] currencyGroupSizes() {
1241     return currencyGroupSizes_;
1242   }
1243   /**
1244    * $(I Property.) Assigns the number of digits int each group to the left of the decimal place in currency values.
1245    * Params: value = The number of digits int each group to the left of the decimal place in currency values.
1246    */
1247   @property public final void currencyGroupSizes(const(int)[] value) {
1248     checkReadOnly();
1249     currencyGroupSizes_ = value;
1250   }
1251 
1252   /**
1253    * $(I Property.) Retrieves the string separating groups of digits to the left of the decimal place in numbers.
1254    * Returns: The string separating groups of digits to the left of the decimal place in numbers. For example, ",".
1255    */
1256   @property public const final const(char)[] numberGroupSeparator() {
1257     return numberGroupSeparator_;
1258   }
1259   /**
1260    * $(I Property.) Assigns the string separating groups of digits to the left of the decimal place in numbers.
1261    * Params: value = The string separating groups of digits to the left of the decimal place in numbers.
1262    */
1263   @property public final void numberGroupSeparator(const(char)[] value) {
1264     checkReadOnly();
1265     numberGroupSeparator_ = value;
1266   }
1267 
1268   /**
1269    * $(I Property.) Retrieves the string used as the decimal separator in numbers.
1270    * Returns: The string used as the decimal separator in numbers. For example, ".".
1271    */
1272   @property public const final const(char)[] numberDecimalSeparator() {
1273     return numberDecimalSeparator_;
1274   }
1275   /**
1276    * $(I Property.) Assigns the string used as the decimal separator in numbers.
1277    * Params: value = The string used as the decimal separator in numbers.
1278    */
1279   @property public final void numberDecimalSeparator(const(char)[] value) {
1280     checkReadOnly();
1281     numberDecimalSeparator_ = value;
1282   }
1283 
1284   /**
1285    * $(I Property.) Retrieves the string separating groups of digits to the left of the decimal place in currency values.
1286    * Returns: The string separating groups of digits to the left of the decimal place in currency values. For example, ",".
1287    */
1288   @property public const final const(char)[] currencyGroupSeparator() {
1289     return currencyGroupSeparator_;
1290   }
1291   /**
1292    * $(I Property.) Assigns the string separating groups of digits to the left of the decimal place in currency values.
1293    * Params: value = The string separating groups of digits to the left of the decimal place in currency values.
1294    */
1295   @property public final void currencyGroupSeparator(const(char)[] value) {
1296     checkReadOnly();
1297     currencyGroupSeparator_ = value;
1298   }
1299 
1300   /**
1301    * $(I Property.) Retrieves the string used as the decimal separator in currency values.
1302    * Returns: The string used as the decimal separator in currency values. For example, ".".
1303    */
1304   @property public const final const(char)[] currencyDecimalSeparator() {
1305     return currencyDecimalSeparator_;
1306   }
1307   /**
1308    * $(I Property.) Assigns the string used as the decimal separator in currency values.
1309    * Params: value = The string used as the decimal separator in currency values.
1310    */
1311   @property public final void currencyDecimalSeparator(const(char)[] value) {
1312     checkReadOnly();
1313     currencyDecimalSeparator_ = value;
1314   }
1315 
1316   /**
1317    * $(I Property.) Retrieves the string used as the currency symbol.
1318    * Returns: The string used as the currency symbol. For example, "£".
1319    */
1320   @property public const final const(char)[] currencySymbol() {
1321     return currencySymbol_;
1322   }
1323   /**
1324    * $(I Property.) Assigns the string used as the currency symbol.
1325    * Params: value = The string used as the currency symbol.
1326    */
1327   @property public final void currencySymbol(const(char)[] value) {
1328     checkReadOnly();
1329     currencySymbol_ = value;
1330   }
1331 
1332   /**
1333    * $(I Property.) Retrieves the string denoting that a number is negative.
1334    * Returns: The string denoting that a number is negative. For example, "-".
1335    */
1336   @property public const final const(char)[] negativeSign() {
1337     return negativeSign_;
1338   }
1339   /**
1340    * $(I Property.) Assigns the string denoting that a number is negative.
1341    * Params: value = The string denoting that a number is negative.
1342    */
1343   @property public final void negativeSign(const(char)[] value) {
1344     checkReadOnly();
1345     negativeSign_ = value;
1346   }
1347 
1348   /**
1349    * $(I Property.) Retrieves the string denoting that a number is positive.
1350    * Returns: The string denoting that a number is positive. For example, "+".
1351    */
1352   @property public const final const(char)[] positiveSign() {
1353     return positiveSign_;
1354   }
1355   /**
1356    * $(I Property.) Assigns the string denoting that a number is positive.
1357    * Params: value = The string denoting that a number is positive.
1358    */
1359   @property public final void positiveSign(const(char)[] value) {
1360     checkReadOnly();
1361     positiveSign_ = value;
1362   }
1363 
1364   /**
1365    * $(I Property.) Retrieves the string representing the NaN (not a number) value.
1366    * Returns: The string representing the NaN value. For example, "NaN".
1367    */
1368   @property public const final const(char)[] nanSymbol() {
1369     return nanSymbol_;
1370   }
1371   /**
1372    * $(I Property.) Assigns the string representing the NaN (not a number) value.
1373    * Params: value = The string representing the NaN value.
1374    */
1375   @property public final void nanSymbol(const(char)[] value) {
1376     checkReadOnly();
1377     nanSymbol_ = value;
1378   }
1379 
1380   /**
1381    * $(I Property.) Retrieves the string representing negative infinity.
1382    * Returns: The string representing negative infinity. For example, "-Infinity".
1383    */
1384   @property public const final const(char)[] negativeInfinitySymbol() {
1385     return negativeInfinitySymbol_;
1386   }
1387   /**
1388    * $(I Property.) Assigns the string representing negative infinity.
1389    * Params: value = The string representing negative infinity.
1390    */
1391   @property public final void negativeInfinitySymbol(const(char)[] value) {
1392     checkReadOnly();
1393     negativeInfinitySymbol_ = value;
1394   }
1395 
1396   /**
1397    * $(I Property.) Retrieves the string representing positive infinity.
1398    * Returns: The string representing positive infinity. For example, "Infinity".
1399    */
1400   @property public const final const(char)[] positiveInfinitySymbol() {
1401     return positiveInfinitySymbol_;
1402   }
1403   /**
1404    * $(I Property.) Assigns the string representing positive infinity.
1405    * Params: value = The string representing positive infinity.
1406    */
1407   @property public final void positiveInfinitySymbol(const(char)[] value) {
1408     checkReadOnly();
1409     positiveInfinitySymbol_ = value;
1410   }
1411 
1412   /**
1413    * $(I Property.) Retrieves a string array of native equivalents of the digits 0 to 9.
1414    * Returns: A string array of native equivalents of the digits 0 to 9.
1415    */
1416   @property public const final const(char[])[] nativeDigits() {
1417     return nativeDigits_;
1418   }
1419   /**
1420    * $(I Property.) Assigns a string array of native equivalents of the digits 0 to 9.
1421    * Params: value = A string array of native equivalents of the digits 0 to 9.
1422    */
1423   @property public final void nativeDigits(const(char[])[] value) {
1424     checkReadOnly();
1425     nativeDigits_ = value;
1426   }
1427 
1428   @property private const void checkReadOnly() {
1429     if (isReadOnly_)
1430         error("NumberFormat instance is read-only.");
1431   }
1432 
1433 }
1434 
1435 /**
1436  * $(ANCHOR _DateTimeFormat)
1437  * Determines how $(LINK2 #Time, Time) values are formatted, depending on the culture.
1438  * Remarks: To create a DateTimeFormat for a specific culture, create a $(LINK2 #Culture, Culture) for that culture and
1439  * retrieve its $(LINK2 #Culture_dateTimeFormat, dateTimeFormat) property. To create a DateTimeFormat for the user's current 
1440  * culture, use the $(LINK2 #Culture_current, current) property.
1441  */
1442 public class DateTimeFormat : IFormatService {
1443 
1444   private __gshared immutable const(char)[] rfc1123Pattern_ = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
1445   private __gshared immutable const(char)[] sortableDateTimePattern_ = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
1446   private __gshared immutable const(char)[] universalSortableDateTimePattern_ = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'Z'";
1447   private __gshared immutable const(char)[] allStandardFormats = [ 'd', 'D', 'f', 'F', 'g', 'G', 'm', 'M', 'r', 'R', 's', 't', 'T', 'u', 'U', 'y', 'Y' ];
1448 
1449 
1450   package bool isReadOnly_;
1451   private __gshared DateTimeFormat invariantFormat_;
1452   private const(CultureData)* cultureData_;
1453 
1454   private Calendar calendar_;
1455   private const(int)[] optionalCalendars_;
1456   private int firstDayOfWeek_ = -1;
1457   private int calendarWeekRule_ = -1;
1458   private const(char)[] dateSeparator_;
1459   private const(char)[] timeSeparator_;
1460   private const(char)[] amDesignator_;
1461   private const(char)[] pmDesignator_;
1462   private const(char)[] shortDatePattern_;
1463   private const(char)[] shortTimePattern_;
1464   private const(char)[] longDatePattern_;
1465   private const(char)[] longTimePattern_;
1466   private const(char)[] monthDayPattern_;
1467   private const(char)[] yearMonthPattern_;
1468   private const(char[])[] abbreviatedDayNames_;
1469   private const(char[])[] dayNames_;
1470   private const(char[])[] abbreviatedMonthNames_;
1471   private const(char[])[] monthNames_;
1472 
1473   private const(char)[] fullDateTimePattern_;
1474   private const(char)[] generalShortTimePattern_;
1475   private const(char)[] generalLongTimePattern_;
1476 
1477   private const(char[])[] shortTimePatterns_;
1478   private const(char[])[] shortDatePatterns_;
1479   private const(char[])[] longTimePatterns_;
1480   private const(char[])[] longDatePatterns_;
1481   private const(char[])[] yearMonthPatterns_;
1482 
1483   /**
1484    * $(ANCHOR DateTimeFormat_ctor)
1485    * Initializes an instance that is writable and culture-independent.
1486    */
1487   package this() {
1488     // This ctor is used by invariantFormat so we can't set the calendar property.
1489     cultureData_ = Culture.invariantCulture.cultureData_;
1490     calendar_ = Gregorian.generic;
1491     initialize();
1492   }
1493 
1494   package this(const(CultureData)* cultureData, Calendar calendar) {
1495     cultureData_ = cultureData;
1496     this.calendar = calendar;
1497   }
1498 
1499   /**
1500    * $(ANCHOR DateTimeFormat_getFormat)
1501    * Retrieves an object defining how to format the specified type.
1502    * Params: type = The TypeInfo of the resulting formatting object.
1503    * Returns: If type is typeid(DateTimeFormat), the current DateTimeFormat instance. Otherwise, null.
1504    * Remarks: Implements $(LINK2 #IFormatService_getFormat, IFormatService.getFormat).
1505    */
1506   public Object getFormat(TypeInfo type) {
1507     return (type is typeid(DateTimeFormat)) ? this : null;
1508   }
1509 
1510 version(Clone)
1511 {
1512   /**
1513    */
1514   public Object clone() {
1515     DateTimeFormat other = cast(DateTimeFormat)cloneObject(this);
1516     other.calendar_ = cast(Calendar)calendar.clone();
1517     other.isReadOnly_ = false;
1518     return other;
1519   }
1520 }
1521 
1522   @property package const(char)[][] shortTimePatterns() {
1523     if (shortTimePatterns_ is null)
1524       shortTimePatterns_ = cultureData_.shortTimes;
1525     return shortTimePatterns_.dup;
1526   }
1527 
1528   @property package const(char)[][] shortDatePatterns() {
1529     if (shortDatePatterns_ is null)
1530       shortDatePatterns_ = cultureData_.shortDates;
1531     return shortDatePatterns_.dup;
1532   }
1533 
1534   @property package const(char)[][] longTimePatterns() {
1535     if (longTimePatterns_ is null)
1536       longTimePatterns_ = cultureData_.longTimes;
1537     return longTimePatterns_.dup;
1538   }
1539 
1540   @property package const(char)[][] longDatePatterns() {
1541     if (longDatePatterns_ is null)
1542       longDatePatterns_ = cultureData_.longDates;
1543     return longDatePatterns_.dup;
1544   }
1545 
1546   @property package const(char[])[] yearMonthPatterns() {
1547     if (yearMonthPatterns_ is null)
1548       yearMonthPatterns_ = cultureData_.yearMonths;
1549     return yearMonthPatterns_;
1550   }
1551 
1552   /**
1553    * $(ANCHOR DateTimeFormat_getAllDateTimePatterns)
1554    * Retrieves the standard patterns in which Time values can be formatted.
1555    * Returns: An array of strings containing the standard patterns in which Time values can be formatted.
1556    */
1557   public final const(char)[][] getAllDateTimePatterns() {
1558     const(char)[][] result;
1559     foreach (char format; DateTimeFormat.allStandardFormats)
1560       result ~= getAllDateTimePatterns(format);
1561     return result;
1562   }
1563 
1564   /**
1565    * $(ANCHOR DateTimeFormat_getAllDateTimePatterns_char)
1566    * Retrieves the standard patterns in which Time values can be formatted using the specified format character.
1567    * Returns: An array of strings containing the standard patterns in which Time values can be formatted using the specified format character.
1568    */
1569   public final const(char)[][] getAllDateTimePatterns(char format) {
1570 
1571     const(char)[][] combinePatterns(const(char)[][] patterns1, const(char)[][] patterns2) {
1572       const(char)[][] result = new const(char)[][patterns1.length * patterns2.length];
1573       for (int i = 0; i < patterns1.length; i++) {
1574         for (int j = 0; j < patterns2.length; j++)
1575           result[i * patterns2.length + j] = patterns1[i] ~ " " ~ patterns2[j];
1576       }
1577       return result;
1578     }
1579 
1580     // format must be one of allStandardFormats.
1581     const(char)[][] result;
1582     switch (format) {
1583       case 'd':
1584         result ~= shortDatePatterns;
1585         break;
1586       case 'D':
1587         result ~= longDatePatterns;
1588         break;
1589       case 'f':
1590         result ~= combinePatterns(longDatePatterns, shortTimePatterns);
1591         break;
1592       case 'F':
1593         result ~= combinePatterns(longDatePatterns, longTimePatterns);
1594         break;
1595       case 'g':
1596         result ~= combinePatterns(shortDatePatterns, shortTimePatterns);
1597         break;
1598       case 'G':
1599         result ~= combinePatterns(shortDatePatterns, longTimePatterns);
1600         break;
1601       case 'm':
1602       case 'M':
1603         result ~= monthDayPattern;
1604         break;
1605       case 'r':
1606       case 'R':
1607         result ~= rfc1123Pattern_;
1608         break;
1609       case 's':
1610         result ~= sortableDateTimePattern_;
1611         break;
1612       case 't':
1613         result ~= shortTimePatterns;
1614         break;
1615       case 'T':
1616         result ~= longTimePatterns;
1617         break;
1618       case 'u':
1619         result ~= universalSortableDateTimePattern_;
1620         break;
1621       case 'U':
1622         result ~= combinePatterns(longDatePatterns, longTimePatterns);
1623         break;
1624       case 'y':
1625       case 'Y':
1626         result ~= yearMonthPatterns;
1627         break;
1628       default:
1629         error("The specified format was not valid.");
1630     }
1631     return result;
1632   }
1633 
1634   /**
1635    * $(ANCHOR DateTimeFormat_getAbbreviatedDayName)
1636    * Retrieves the abbreviated name of the specified day of the week based on the culture of the instance.
1637    * Params: dayOfWeek = A DayOfWeek value.
1638    * Returns: The abbreviated name of the day of the week represented by dayOfWeek.
1639    */
1640   public final const(char)[] getAbbreviatedDayName(Calendar.DayOfWeek dayOfWeek) {
1641     return abbreviatedDayNames[cast(int)dayOfWeek];
1642   }
1643 
1644   /**
1645    * $(ANCHOR DateTimeFormat_getDayName)
1646    * Retrieves the full name of the specified day of the week based on the culture of the instance.
1647    * Params: dayOfWeek = A DayOfWeek value.
1648    * Returns: The full name of the day of the week represented by dayOfWeek.
1649    */
1650   public final const(char)[] getDayName(Calendar.DayOfWeek dayOfWeek) {
1651     return dayNames[cast(int)dayOfWeek];
1652   }
1653 
1654   /**
1655    * $(ANCHOR DateTimeFormat_getAbbreviatedMonthName)
1656    * Retrieves the abbreviated name of the specified month based on the culture of the instance.
1657    * Params: month = An integer between 1 and 13 indicating the name of the _month to return.
1658    * Returns: The abbreviated name of the _month represented by month.
1659    */
1660   public final const(char)[] getAbbreviatedMonthName(int month) {
1661     return abbreviatedMonthNames[month - 1];
1662   }
1663 
1664   /**
1665    * $(ANCHOR DateTimeFormat_getMonthName)
1666    * Retrieves the full name of the specified month based on the culture of the instance.
1667    * Params: month = An integer between 1 and 13 indicating the name of the _month to return.
1668    * Returns: The full name of the _month represented by month.
1669    */
1670   public final const(char)[] getMonthName(int month) {
1671     return monthNames[month - 1];
1672   }
1673 
1674   /**
1675    * $(ANCHOR DateTimeFormat_getInstance)
1676    * Retrieves the DateTimeFormat for the specified IFormatService.
1677    * Params: formatService = The IFormatService used to retrieve DateTimeFormat.
1678    * Returns: The DateTimeFormat for the specified IFormatService.
1679    * Remarks: The method calls $(LINK2 #IFormatService_getFormat, IFormatService.getFormat) with typeof(DateTimeFormat). If formatService is null,
1680    * then the value of the current property is returned.
1681    */
1682   public static DateTimeFormat getInstance(IFormatService formatService) {
1683     Culture culture = cast(Culture)formatService;
1684     if (culture !is null) {
1685       if (culture.dateTimeFormat_ !is null)
1686         return culture.dateTimeFormat_;
1687       return culture.dateTimeFormat;
1688     }
1689     if (DateTimeFormat dateTimeFormat = cast(DateTimeFormat)formatService)
1690       return dateTimeFormat;
1691     if (formatService !is null) {
1692       if (DateTimeFormat dateTimeFormat = cast(DateTimeFormat)(formatService.getFormat(typeid(DateTimeFormat))))
1693         return dateTimeFormat;
1694     }
1695     return current;
1696   }
1697 
1698   /**
1699    * $(ANCHOR DateTimeFormat_current)
1700    * $(I Property.) Retrieves a read-only DateTimeFormat instance from the current culture.
1701    * Returns: A read-only DateTimeFormat instance from the current culture.
1702    */
1703   @property public static DateTimeFormat current() {
1704     return Culture.current.dateTimeFormat;
1705   }
1706 
1707   /**
1708    * $(ANCHOR DateTimeFormat_invariantFormat)
1709    * $(I Property.) Retrieves a read-only DateTimeFormat instance that is culturally independent.
1710    * Returns: A read-only DateTimeFormat instance that is culturally independent.
1711    */
1712   public static DateTimeFormat invariantFormat() {
1713     if (invariantFormat_ is null) {
1714       invariantFormat_ = new DateTimeFormat;
1715       invariantFormat_.calendar = new Gregorian();
1716       invariantFormat_.isReadOnly_ = true;
1717     }
1718     return invariantFormat_;
1719   }
1720 
1721   /**
1722    * $(ANCHOR DateTimeFormat_isReadOnly)
1723    * $(I Property.) Retrieves a value indicating whether the instance is read-only.
1724    * Returns: true is the instance is read-only; otherwise, false.
1725    */
1726   @property public final bool isReadOnly() {
1727     return isReadOnly_;
1728   }
1729 
1730   /**
1731    * $(I Property.) Retrieves the calendar used by the current culture.
1732    * Returns: The Calendar determining the calendar used by the current culture. For example, the Gregorian.
1733    */
1734   @property public final Calendar calendar() {
1735     assert(calendar_ !is null);
1736     return calendar_;
1737   }
1738   /**
1739    * $(ANCHOR DateTimeFormat_calendar)
1740    * $(I Property.) Assigns the calendar to be used by the current culture.
1741    * Params: value = The Calendar determining the calendar to be used by the current culture.
1742    * Exceptions: If value is not valid for the current culture, an Exception is thrown.
1743    */
1744   @property public final void calendar(Calendar value) {
1745     checkReadOnly();
1746     if (value !is calendar_) {
1747       for (int i = 0; i < optionalCalendars.length; i++) {
1748         if (optionalCalendars[i] == value.id) {
1749           if (calendar_ !is null) {
1750             // Clear current properties.
1751             shortDatePattern_ = null;
1752             longDatePattern_ = null;
1753             shortTimePattern_ = null;
1754             yearMonthPattern_ = null;
1755             monthDayPattern_ = null;
1756             generalShortTimePattern_ = null;
1757             generalLongTimePattern_ = null;
1758             fullDateTimePattern_ = null;
1759             shortDatePatterns_ = null;
1760             longDatePatterns_ = null;
1761             yearMonthPatterns_ = null;
1762             abbreviatedDayNames_ = null;
1763             abbreviatedMonthNames_ = null;
1764             dayNames_ = null;
1765             monthNames_ = null;
1766           }
1767           calendar_ = value;
1768           initialize();
1769           return;
1770         }
1771       }
1772       error("Not a valid calendar for the culture.");
1773     }
1774   }
1775 
1776   /**
1777    * $(ANCHOR DateTimeFormat_firstDayOfWeek)
1778    * $(I Property.) Retrieves the first day of the week.
1779    * Returns: A DayOfWeek value indicating the first day of the week.
1780    */
1781   @property public final Calendar.DayOfWeek firstDayOfWeek() {
1782     return cast(Calendar.DayOfWeek)firstDayOfWeek_;
1783   }
1784   /**
1785    * $(I Property.) Assigns the first day of the week.
1786    * Params: valie = A DayOfWeek value indicating the first day of the week.
1787    */
1788   @property public final void firstDayOfWeek(Calendar.DayOfWeek value) {
1789     checkReadOnly();
1790     firstDayOfWeek_ = value;
1791   }
1792 
1793   /**
1794    * $(ANCHOR DateTimeFormat_calendarWeekRule)
1795    * $(I Property.) Retrieves the _value indicating the rule used to determine the first week of the year.
1796    * Returns: A CalendarWeekRule _value determining the first week of the year.
1797    */
1798   @property public final Calendar.WeekRule calendarWeekRule() {
1799     return cast(Calendar.WeekRule) calendarWeekRule_;
1800   }
1801   /**
1802    * $(I Property.) Assigns the _value indicating the rule used to determine the first week of the year.
1803    * Params: value = A CalendarWeekRule _value determining the first week of the year.
1804    */
1805   @property public final void calendarWeekRule(Calendar.WeekRule value) {
1806     checkReadOnly();
1807     calendarWeekRule_ = value;
1808   }
1809 
1810   /**
1811    * $(ANCHOR DateTimeFormat_nativeCalendarName)
1812    * $(I Property.) Retrieves the native name of the calendar associated with the current instance.
1813    * Returns: The native name of the calendar associated with the current instance.
1814    */
1815   @property public final const(char)[] nativeCalendarName() {
1816     return cultureData_.nativeCalName;
1817   }
1818 
1819   /**
1820    * $(ANCHOR DateTimeFormat_dateSeparator)
1821    * $(I Property.) Retrieves the string separating date components.
1822    * Returns: The string separating date components.
1823    */
1824   @property public final const(char)[] dateSeparator() {
1825     if (dateSeparator_ is null)
1826       dateSeparator_ = cultureData_.date;
1827     return dateSeparator_;
1828   }
1829   /**
1830    * $(I Property.) Assigns the string separating date components.
1831    * Params: value = The string separating date components.
1832    */
1833   @property public final void dateSeparator(const(char)[] value) {
1834     checkReadOnly();
1835     dateSeparator_ = value;
1836   }
1837 
1838   /**
1839    * $(ANCHOR DateTimeFormat_timeSeparator)
1840    * $(I Property.) Retrieves the string separating time components.
1841    * Returns: The string separating time components.
1842    */
1843   @property public final const(char)[] timeSeparator() {
1844     if (timeSeparator_ is null)
1845       timeSeparator_ = cultureData_.time;
1846     return timeSeparator_;
1847   }
1848   /**
1849    * $(I Property.) Assigns the string separating time components.
1850    * Params: value = The string separating time components.
1851    */
1852   @property public final void timeSeparator(const(char)[] value) {
1853     checkReadOnly();
1854     timeSeparator_ = value;
1855   }
1856 
1857   /**
1858    * $(ANCHOR DateTimeFormat_amDesignator)
1859    * $(I Property.) Retrieves the string designator for hours before noon.
1860    * Returns: The string designator for hours before noon. For example, "AM".
1861    */
1862   @property public final const(char)[] amDesignator() {
1863     assert(amDesignator_ !is null);
1864     return amDesignator_;
1865   }
1866   /**
1867    * $(I Property.) Assigns the string designator for hours before noon.
1868    * Params: value = The string designator for hours before noon.
1869    */
1870   @property public final void amDesignator(const(char)[] value) {
1871     checkReadOnly();
1872     amDesignator_ = value;
1873   }
1874 
1875   /**
1876    * $(ANCHOR DateTimeFormat_pmDesignator)
1877    * $(I Property.) Retrieves the string designator for hours after noon.
1878    * Returns: The string designator for hours after noon. For example, "PM".
1879    */
1880   @property public final const(char)[] pmDesignator() {
1881     assert(pmDesignator_ !is null);
1882     return pmDesignator_;
1883   }
1884   /**
1885    * $(I Property.) Assigns the string designator for hours after noon.
1886    * Params: value = The string designator for hours after noon.
1887    */
1888   @property public final void pmDesignator(const(char)[] value) {
1889     checkReadOnly();
1890     pmDesignator_ = value;
1891   }
1892 
1893   /**
1894    * $(ANCHOR DateTimeFormat_shortDatePattern)
1895    * $(I Property.) Retrieves the format pattern for a short date value.
1896    * Returns: The format pattern for a short date value.
1897    */
1898   @property public final const(char)[] shortDatePattern() {
1899     assert(shortDatePattern_ !is null);
1900     return shortDatePattern_;
1901   }
1902   /**
1903    * $(I Property.) Assigns the format pattern for a short date _value.
1904    * Params: value = The format pattern for a short date _value.
1905    */
1906   @property public final void shortDatePattern(const(char)[] value) {
1907     checkReadOnly();
1908     if (shortDatePatterns_ !is null)
1909       shortDatePatterns_ = [value];
1910     shortDatePattern_ = value;
1911     generalLongTimePattern_ = null;
1912     generalShortTimePattern_ = null;
1913   }
1914 
1915   /**
1916    * $(ANCHOR DateTimeFormat_shortTimePattern)
1917    * $(I Property.) Retrieves the format pattern for a short time value.
1918    * Returns: The format pattern for a short time value.
1919    */
1920   @property public final const(char)[] shortTimePattern() {
1921     if (shortTimePattern_ is null)
1922       shortTimePattern_ = cultureData_.shortTime;
1923     return shortTimePattern_;
1924   }
1925   /**
1926    * $(I Property.) Assigns the format pattern for a short time _value.
1927    * Params: value = The format pattern for a short time _value.
1928    */
1929   @property public final void shortTimePattern(const(char)[] value) {
1930     checkReadOnly();
1931     shortTimePattern_ = value;
1932     generalShortTimePattern_ = null;
1933   }
1934 
1935   /**
1936    * $(ANCHOR DateTimeFormat_longDatePattern)
1937    * $(I Property.) Retrieves the format pattern for a long date value.
1938    * Returns: The format pattern for a long date value.
1939    */
1940   @property public final const(char)[] longDatePattern() {
1941     assert(longDatePattern_ !is null);
1942     return longDatePattern_;
1943   }
1944   /**
1945    * $(I Property.) Assigns the format pattern for a long date _value.
1946    * Params: value = The format pattern for a long date _value.
1947    */
1948   @property public final void longDatePattern(const(char)[] value) {
1949     checkReadOnly();
1950     if (longDatePatterns_ !is null)
1951       longDatePatterns_ = [value];
1952     longDatePattern_ = value;
1953     fullDateTimePattern_ = null;
1954   }
1955 
1956   /**
1957    * $(ANCHOR DateTimeFormat_longTimePattern)
1958    * $(I Property.) Retrieves the format pattern for a long time value.
1959    * Returns: The format pattern for a long time value.
1960    */
1961   @property public final const(char)[] longTimePattern() {
1962     assert(longTimePattern_ !is null);
1963     return longTimePattern_;
1964   }
1965   /**
1966    * $(I Property.) Assigns the format pattern for a long time _value.
1967    * Params: value = The format pattern for a long time _value.
1968    */
1969   @property public final void longTimePattern(const(char)[] value) {
1970     checkReadOnly();
1971     longTimePattern_ = value;
1972     fullDateTimePattern_ = null;
1973   }
1974 
1975   /**
1976    * $(ANCHOR DateTimeFormat_monthDayPattern)
1977    * $(I Property.) Retrieves the format pattern for a month and day value.
1978    * Returns: The format pattern for a month and day value.
1979    */
1980   @property public final const(char)[] monthDayPattern() {
1981     if (monthDayPattern_ is null)
1982       monthDayPattern_ = cultureData_.monthDay;
1983     return monthDayPattern_;
1984   }
1985   /**
1986    * $(I Property.) Assigns the format pattern for a month and day _value.
1987    * Params: value = The format pattern for a month and day _value.
1988    */
1989   @property public final void monthDayPattern(const(char)[] value) {
1990     checkReadOnly();
1991     monthDayPattern_ = value;
1992   }
1993 
1994   /**
1995    * $(ANCHOR DateTimeFormat_yearMonthPattern)
1996    * $(I Property.) Retrieves the format pattern for a year and month value.
1997    * Returns: The format pattern for a year and month value.
1998    */
1999   @property public final const(char)[] yearMonthPattern() {
2000     assert(yearMonthPattern_ !is null);
2001     return yearMonthPattern_;
2002   }
2003   /**
2004    * $(I Property.) Assigns the format pattern for a year and month _value.
2005    * Params: value = The format pattern for a year and month _value.
2006    */
2007   @property public final void yearMonthPattern(const(char)[] value) {
2008     checkReadOnly();
2009     yearMonthPattern_ = value;
2010   }
2011 
2012   /**
2013    * $(ANCHOR DateTimeFormat_abbreviatedDayNames)
2014    * $(I Property.) Retrieves a string array containing the abbreviated names of the days of the week.
2015    * Returns: A string array containing the abbreviated names of the days of the week. For $(LINK2 #DateTimeFormat_invariantFormat, invariantFormat),
2016    *   this contains "Sun", "Mon", "Tue", "Wed", "Thu", "Fri" and "Sat".
2017    */
2018   @property public final const(char)[][] abbreviatedDayNames() {
2019     if (abbreviatedDayNames_ is null)
2020       abbreviatedDayNames_ = cultureData_.abbrevDayNames;
2021     return abbreviatedDayNames_.dup;
2022   }
2023   /**
2024    * $(I Property.) Assigns a string array containing the abbreviated names of the days of the week.
2025    * Params: value = A string array containing the abbreviated names of the days of the week.
2026    */
2027   @property public final void abbreviatedDayNames(const(char)[][] value) {
2028     checkReadOnly();
2029     abbreviatedDayNames_ = value;
2030   }
2031 
2032   /**
2033    * $(ANCHOR DateTimeFormat_dayNames)
2034    * $(I Property.) Retrieves a string array containing the full names of the days of the week.
2035    * Returns: A string array containing the full names of the days of the week. For $(LINK2 #DateTimeFormat_invariantFormat, invariantFormat),
2036    *   this contains "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" and "Saturday".
2037    */
2038   @property public final const(char)[][] dayNames() {
2039     if (dayNames_ is null)
2040       dayNames_ = cultureData_.dayNames;
2041     return dayNames_.dup;
2042   }
2043   /**
2044    * $(I Property.) Assigns a string array containing the full names of the days of the week.
2045    * Params: value = A string array containing the full names of the days of the week.
2046    */
2047   @property public final void dayNames(const(char)[][] value) {
2048     checkReadOnly();
2049     dayNames_ = value;
2050   }
2051 
2052   /**
2053    * $(ANCHOR DateTimeFormat_abbreviatedMonthNames)
2054    * $(I Property.) Retrieves a string array containing the abbreviated names of the months.
2055    * Returns: A string array containing the abbreviated names of the months. For $(LINK2 #DateTimeFormat_invariantFormat, invariantFormat),
2056    *   this contains "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" and "".
2057    */
2058   @property public final const(char)[][] abbreviatedMonthNames() {
2059     if (abbreviatedMonthNames_ is null)
2060       abbreviatedMonthNames_ = cultureData_.abbrevMonthNames;
2061     return abbreviatedMonthNames_.dup;
2062   }
2063   /**
2064    * $(I Property.) Assigns a string array containing the abbreviated names of the months.
2065    * Params: value = A string array containing the abbreviated names of the months.
2066    */
2067   @property public final void abbreviatedMonthNames(const(char)[][] value) {
2068     checkReadOnly();
2069     abbreviatedMonthNames_ = value;
2070   }
2071 
2072   /**
2073    * $(ANCHOR DateTimeFormat_monthNames)
2074    * $(I Property.) Retrieves a string array containing the full names of the months.
2075    * Returns: A string array containing the full names of the months. For $(LINK2 #DateTimeFormat_invariantFormat, invariantFormat),
2076    *   this contains "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" and "".
2077    */
2078   @property public final const(char)[][] monthNames() {
2079     if (monthNames_ is null)
2080       monthNames_ = cultureData_.monthNames;
2081     return monthNames_.dup;
2082   }
2083   /**
2084    * $(I Property.) Assigns a string array containing the full names of the months.
2085    * Params: value = A string array containing the full names of the months.
2086    */
2087   @property public final void monthNames(const(char)[][] value) {
2088     checkReadOnly();
2089     monthNames_ = value;
2090   }
2091 
2092   /**
2093    * $(ANCHOR DateTimeFormat_fullDateTimePattern)
2094    * $(I Property.) Retrieves the format pattern for a long date and a long time value.
2095    * Returns: The format pattern for a long date and a long time value.
2096    */
2097   @property public final const(char)[] fullDateTimePattern() {
2098     if (fullDateTimePattern_ is null)
2099       fullDateTimePattern_ = longDatePattern ~ " " ~ longTimePattern;
2100     return fullDateTimePattern_;
2101   }
2102   /**
2103    * $(I Property.) Assigns the format pattern for a long date and a long time _value.
2104    * Params: value = The format pattern for a long date and a long time _value.
2105    */
2106   @property public final void fullDateTimePattern(const(char)[] value) {
2107     checkReadOnly();
2108     fullDateTimePattern_ = value;
2109   }
2110 
2111   /**
2112    * $(ANCHOR DateTimeFormat_rfc1123Pattern)
2113    * $(I Property.) Retrieves the format pattern based on the IETF RFC 1123 specification, for a time value.
2114    * Returns: The format pattern based on the IETF RFC 1123 specification, for a time value.
2115    */
2116   @property public const final const(char)[] rfc1123Pattern() {
2117     return rfc1123Pattern_;
2118   }
2119 
2120   /**
2121    * $(ANCHOR DateTimeFormat_sortableDateTimePattern)
2122    * $(I Property.) Retrieves the format pattern for a sortable date and time value.
2123    * Returns: The format pattern for a sortable date and time value.
2124    */
2125   @property public const final const(char)[] sortableDateTimePattern() {
2126     return sortableDateTimePattern_;
2127   }
2128 
2129   /**
2130    * $(ANCHOR DateTimeFormat_universalSortableDateTimePattern)
2131    * $(I Property.) Retrieves the format pattern for a universal date and time value.
2132    * Returns: The format pattern for a universal date and time value.
2133    */
2134   @property public const final const(char)[] universalSortableDateTimePattern() {
2135     return universalSortableDateTimePattern_;
2136   }
2137 
2138   @property package const(char)[] generalShortTimePattern() {
2139     if (generalShortTimePattern_ is null)
2140       generalShortTimePattern_ = shortDatePattern ~ " " ~ shortTimePattern;
2141     return generalShortTimePattern_;
2142   }
2143 
2144   @property package const(char)[] generalLongTimePattern() {
2145     if (generalLongTimePattern_ is null)
2146       generalLongTimePattern_ = shortDatePattern ~ " " ~ longTimePattern;
2147     return generalLongTimePattern_;
2148   }
2149 
2150   @property private const void checkReadOnly() {
2151     if (isReadOnly_)
2152         error("DateTimeFormat instance is read-only.");
2153   }
2154 
2155   private void initialize() {
2156     if (longTimePattern_ is null)
2157       longTimePattern_ = cultureData_.longTime;
2158     if (shortDatePattern_ is null)
2159       shortDatePattern_ = cultureData_.shortDate;
2160     if (longDatePattern_ is null)
2161       longDatePattern_ = cultureData_.longDate;
2162     if (yearMonthPattern_ is null)
2163       yearMonthPattern_ = cultureData_.yearMonth;
2164     if (amDesignator_ is null)
2165       amDesignator_ = cultureData_.am;
2166     if (pmDesignator_ is null)
2167       pmDesignator_ = cultureData_.pm;
2168     if (firstDayOfWeek_ is -1)
2169       firstDayOfWeek_ = cultureData_.firstDayOfWeek;
2170     if (calendarWeekRule_ == -1)
2171       calendarWeekRule_ = cultureData_.firstDayOfYear;
2172   }
2173 
2174   @property private const(int)[] optionalCalendars() {
2175     if (optionalCalendars_ is null)
2176       optionalCalendars_ = cultureData_.optionalCalendars;
2177     return optionalCalendars_;
2178   }
2179 
2180   private const void error(immutable(char)[] msg) {
2181      throw new LocaleException (msg);
2182   }
2183 
2184 }
2185 
2186