| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065 | /******************************************************************************* copyright: Copyright (c) 2005 John Chapman. All rights reserved license: BSD style: $(LICENSE) version: Jan 2005: initial release Mar 2009: extracted from locale, and converted to a struct author: John Chapman, Kris, mwarning Support for formatting date/time values, in a locale-specific manner. See DateTimeLocale.format() for a description on how formatting is performed (below). Reference links: --- http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo(VS.71).aspx --- ******************************************************************************/ module tango.text.convert.DateTime; private import tango.core.Exception; private import tango.time.WallClock; private import tango.time.chrono.Calendar, tango.time.chrono.Gregorian; private import Utf = tango.text.convert.Utf; private import Integer = tango.text.convert.Integer; version (WithExtensions) private import tango.text.convert.Extensions; /****************************************************************************** O/S specifics ******************************************************************************/ version (Windows) private import tango.sys.win32.UserGdi; else { private import tango.stdc.stringz; private import tango.stdc.posix.langinfo; } /****************************************************************************** The default DateTimeLocale instance ******************************************************************************/ public __gshared DateTimeLocale DateTimeDefault; shared static this() { DateTimeDefault = DateTimeLocale.create(); version (WithExtensions) { Extensions8.add (typeid(Time), &DateTimeDefault.bridge!(char)); Extensions16.add (typeid(Time), &DateTimeDefault.bridge!(wchar)); Extensions32.add (typeid(Time), &DateTimeDefault.bridge!(dchar)); } } /****************************************************************************** How to format locale-specific date/time output ******************************************************************************/ struct DateTimeLocale { enum immutable(char)[] rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; enum immutable(char)[] sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss"; enum immutable(char)[] universalSortableDateTimePattern = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'Z'"; Calendar assignedCalendar; const(char)[] shortDatePattern, shortTimePattern, longDatePattern, longTimePattern, fullDateTimePattern, generalShortTimePattern, generalLongTimePattern, monthDayPattern, yearMonthPattern; const(char)[] amDesignator, pmDesignator; const(char)[] timeSeparator, dateSeparator; const(char)[][] dayNames, monthNames, abbreviatedDayNames, abbreviatedMonthNames; /********************************************************************** Format the given Time value into the provided output, using the specified layout. The layout can be a generic variant or a custom one, where generics are indicated via a single character: <pre> "t" = 7:04 "T" = 7:04:02 PM "d" = 3/30/2009 "D" = Monday, March 30, 2009 "f" = Monday, March 30, 2009 7:04 PM "F" = Monday, March 30, 2009 7:04:02 PM "g" = 3/30/2009 7:04 PM "G" = 3/30/2009 7:04:02 PM "y" "Y" = March, 2009 "r" "R" = Mon, 30 Mar 2009 19:04:02 GMT "s" = 2009-03-30T19:04:02 "u" = 2009-03-30 19:04:02Z </pre> For the US locale, these generic layouts are expanded in the following manner: <pre> "t" = "h:mm" "T" = "h:mm:ss tt" "d" = "M/d/yyyy" "D" = "dddd, MMMM d, yyyy" "f" = "dddd, MMMM d, yyyy h:mm tt" "F" = "dddd, MMMM d, yyyy h:mm:ss tt" "g" = "M/d/yyyy h:mm tt" "G" = "M/d/yyyy h:mm:ss tt" "y" "Y" = "MMMM, yyyy" "r" "R" = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" "s" = "yyyy'-'MM'-'dd'T'HH':'mm':'ss" "u" = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'Z'" </pre> Custom layouts are constructed using a combination of the character codes indicated on the right, above. For example, a layout of "dddd, dd MMM yyyy HH':'mm':'ss zzzz" will emit something like this: --- Monday, 30 Mar 2009 19:04:02 -08:00 --- Using these format indicators with Layout (Stdout etc) is straightforward. Formatting integers, for example, is done like so: --- Stdout.formatln ("{:u}", 5); Stdout.formatln ("{:b}", 5); Stdout.formatln ("{:x}", 5); --- Formatting date/time values is similar, where the format indicators are provided after the colon: --- Stdout.formatln ("{:t}", Clock.now); Stdout.formatln ("{:D}", Clock.now); Stdout.formatln ("{:dddd, dd MMMM yyyy HH:mm}", Clock.now); --- **********************************************************************/ char[] format (char[] output, Time dateTime, const(char)[] layout) { // default to general format if (layout.length is 0) layout = "G"; // might be one of our shortcuts if (layout.length is 1) layout = expandKnownFormat (layout); auto res=Result(output); return formatCustom (res, dateTime, layout); } /********************************************************************** **********************************************************************/ T[] formatWide(T) (T[] output, Time dateTime, const(T)[] fmt) { static if (is (T == char)) return format (output, dateTime, fmt); else { char[128] tmp0 = void; char[128] tmp1 = void; return Utf.fromString8(format(tmp0, dateTime, Utf.toString(fmt, tmp1)), output); } } /********************************************************************** Return a generic English/US instance **********************************************************************/ @property static DateTimeLocale* generic () { return &EngUS; } /********************************************************************** Return the assigned Calendar instance, using Gregorian as the default **********************************************************************/ @property Calendar calendar () { if (assignedCalendar is null) assignedCalendar = Gregorian.generic; return assignedCalendar; } /********************************************************************** Return a short day name **********************************************************************/ const(char)[] abbreviatedDayName (Calendar.DayOfWeek dayOfWeek) { return abbreviatedDayNames [cast(int) dayOfWeek]; } /********************************************************************** Return a long day name **********************************************************************/ const(char)[] dayName (Calendar.DayOfWeek dayOfWeek) { return dayNames [cast(int) dayOfWeek]; } /********************************************************************** Return a short month name **********************************************************************/ const(char)[] abbreviatedMonthName (int month) { assert (month > 0 && month < 13); return abbreviatedMonthNames [month - 1]; } /********************************************************************** Return a long month name **********************************************************************/ const(char)[] monthName (int month) { assert (month > 0 && month < 13); return monthNames [month - 1]; } version (Windows) { /********************************************************************** create and populate an instance via O/S configuration for the current user **********************************************************************/ static DateTimeLocale create () { static char[] toString (char[] dst, LCID id, LCTYPE type) { wchar[256] wide = void; auto len = GetLocaleInfoW (id, type, null, 0); if (len && len < wide.length) { GetLocaleInfoW (id, type, wide.ptr, wide.length); len = WideCharToMultiByte (CP_UTF8, 0, wide.ptr, len-1, cast(PCHAR)dst.ptr, dst.length, null, null); return dst [0..len].dup; } throw new Exception ("DateTime :: GetLocaleInfo failed"); } DateTimeLocale dt; char[256] tmp = void; auto lcid = LOCALE_USER_DEFAULT; for (auto i=LOCALE_SDAYNAME1; i <= LOCALE_SDAYNAME7; ++i) dt.dayNames ~= toString (tmp, lcid, i); for (auto i=LOCALE_SABBREVDAYNAME1; i <= LOCALE_SABBREVDAYNAME7; ++i) dt.abbreviatedDayNames ~= toString (tmp, lcid, i); for (auto i=LOCALE_SMONTHNAME1; i <= LOCALE_SMONTHNAME12; ++i) dt.monthNames ~= toString (tmp, lcid, i); for (auto i=LOCALE_SABBREVMONTHNAME1; i <= LOCALE_SABBREVMONTHNAME12; ++i) dt.abbreviatedMonthNames ~= toString (tmp, lcid, i); dt.dateSeparator = toString (tmp, lcid, LOCALE_SDATE); dt.timeSeparator = toString (tmp, lcid, LOCALE_STIME); dt.amDesignator = toString (tmp, lcid, LOCALE_S1159); dt.pmDesignator = toString (tmp, lcid, LOCALE_S2359); dt.longDatePattern = toString (tmp, lcid, LOCALE_SLONGDATE); dt.shortDatePattern = toString (tmp, lcid, LOCALE_SSHORTDATE); dt.yearMonthPattern = toString (tmp, lcid, LOCALE_SYEARMONTH); dt.longTimePattern = toString (tmp, lcid, LOCALE_STIMEFORMAT); // synthesize a short time auto s = dt.shortTimePattern = dt.longTimePattern; for (auto i=s.length; i--;) if (s[i] is dt.timeSeparator[0]) { dt.shortTimePattern = s[0..i]; break; } dt.fullDateTimePattern = dt.longDatePattern ~ " " ~ dt.longTimePattern; dt.generalLongTimePattern = dt.shortDatePattern ~ " " ~ dt.longTimePattern; dt.generalShortTimePattern = dt.shortDatePattern ~ " " ~ dt.shortTimePattern; return dt; } } else { /********************************************************************** create and populate an instance via O/S configuration for the current user **********************************************************************/ static DateTimeLocale create () { //extract separator const(char)[] extractSeparator(const(char)[] str, const(char)[] def) { for (auto i = 0; i < str.length; ++i) { char c = str[i]; if ((c == '%') || (c == ' ') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) continue; return str[i..i+1]; } return def; } const(char)[] getString(nl_item id, const(char)[] def = null) { char* p = nl_langinfo(id); return p ? fromStringz(p).dup : def; } const(char)[] getFormatString(nl_item id, const(char)[] def = null) { const(char)[] posix_str = getString(id, def); return convert(posix_str); } DateTimeLocale dt; for (auto i = DAY_1; i <= DAY_7; ++i) dt.dayNames ~= getString (i); for (auto i = ABDAY_1; i <= ABDAY_7; ++i) dt.abbreviatedDayNames ~= getString (i); for (auto i = MON_1; i <= MON_12; ++i) dt.monthNames ~= getString (i); for (auto i = ABMON_1; i <= ABMON_12; ++i) dt.abbreviatedMonthNames ~= getString (i); dt.amDesignator = getString (AM_STR, "AM"); dt.pmDesignator = getString (PM_STR, "PM"); dt.longDatePattern = "dddd, MMMM d, yyyy"; //default dt.shortDatePattern = getFormatString(D_FMT, "M/d/yyyy"); dt.longTimePattern = getFormatString(T_FMT, "h:mm:ss tt"); dt.shortTimePattern = "h:mm"; //default dt.yearMonthPattern = "MMMM, yyyy"; //no posix equivalent? dt.fullDateTimePattern = getFormatString(D_T_FMT, "dddd, MMMM d, yyyy h:mm:ss tt"); dt.dateSeparator = extractSeparator(dt.shortDatePattern, "/"); dt.timeSeparator = extractSeparator(dt.longTimePattern, ":"); //extract shortTimePattern from longTimePattern for (auto i = dt.longTimePattern.length; i--;) { if (dt.longTimePattern[i] == dt.timeSeparator[$-1]) { dt.shortTimePattern = dt.longTimePattern[0..i]; break; } } //extract longDatePattern from fullDateTimePattern auto pos = dt.fullDateTimePattern.length - dt.longTimePattern.length - 2; if (pos < dt.fullDateTimePattern.length) dt.longDatePattern = dt.fullDateTimePattern[0..pos]; dt.fullDateTimePattern = dt.longDatePattern ~ " " ~ dt.longTimePattern; dt.generalLongTimePattern = dt.shortDatePattern ~ " " ~ dt.longTimePattern; dt.generalShortTimePattern = dt.shortDatePattern ~ " " ~ dt.shortTimePattern; return dt; } /********************************************************************** Convert POSIX date time format to .NET format syntax. **********************************************************************/ private static char[] convert(const(char)[] fmt) { char[32] ret; size_t len; void put(const(char)[] str) { assert((len+str.length) <= ret.length); ret[len..len+str.length] = str; len += str.length; } for (auto i = 0; i < fmt.length; ++i) { char c = fmt[i]; if (c != '%') { assert((len+1) <= ret.length); ret[len] = c; len += 1; continue; } i++; if (i >= fmt.length) break; c = fmt[i]; switch (c) { case 'a': //locale's abbreviated weekday name. put("ddd"); //The abbreviated name of the day of the week, break; case 'A': //locale's full weekday name. put("dddd"); break; case 'b': //locale's abbreviated month name put("MMM"); break; case 'B': //locale's full month name put("MMMM"); break; case 'd': //day of the month as a decimal number [01,31] put("dd"); // The day of the month. Single-digit //days will have a leading zero. break; case 'D': //same as %m/%d/%y. put("MM/dd/yy"); break; case 'e': //day of the month as a decimal number [1,31]; //a single digit is preceded by a space put("d"); //The day of the month. Single-digit days //will not have a leading zero. break; case 'h': //same as %b. put("MMM"); break; case 'H': //hour (24-hour clock) as a decimal number [00,23] put("HH"); //The hour in a 24-hour clock. Single-digit //hours will have a leading zero. break; case 'I': //the hour (12-hour clock) as a decimal number [01,12] put("hh"); //The hour in a 12-hour clock. //Single-digit hours will have a leading zero. break; case 'm': //month as a decimal number [01,12] put("MM"); //The numeric month. Single-digit //months will have a leading zero. break; case 'M': //minute as a decimal number [00,59] put("mm"); //The minute. Single-digit minutes //will have a leading zero. break; case 'n': //newline character put("\n"); break; case 'p': //locale's equivalent of either a.m. or p.m put("tt"); break; case 'r': //time in a.m. and p.m. notation; //equivalent to %I:%M:%S %p. put("hh:mm:ss tt"); break; case 'R': //time in 24 hour notation (%H:%M) put("HH:mm"); break; case 'S': //second as a decimal number [00,61] put("ss"); //The second. Single-digit seconds //will have a leading zero. break; case 't': //tab character. put("\t"); break; case 'T': //equivalent to (%H:%M:%S) put("HH:mm:ss"); break; case 'u': //weekday as a decimal number [1,7], //with 1 representing Monday case 'U': //week number of the year //(Sunday as the first day of the week) as a decimal number [00,53] case 'V': //week number of the year //(Monday as the first day of the week) as a decimal number [01,53]. //If the week containing 1 January has four or more days //in the new year, then it is considered week 1. //Otherwise, it is the last week of the previous year, and the next week is week 1. case 'w': //weekday as a decimal number [0,6], with 0 representing Sunday case 'W': //week number of the year (Monday as the first day of the week) //as a decimal number [00,53]. //All days in a new year preceding the first Monday //are considered to be in week 0. case 'x': //locale's appropriate date representation case 'X': //locale's appropriate time representation case 'c': //locale's appropriate date and time representation case 'C': //century number (the year divided by 100 and //truncated to an integer) as a decimal number [00-99] case 'j': //day of the year as a decimal number [001,366] assert(0); //break; case 'y': //year without century as a decimal number [00,99] put("yy"); // The year without the century. If the year without //the century is less than 10, the year is displayed with a leading zero. break; case 'Y': //year with century as a decimal number put("yyyy"); //The year in four digits, including the century. break; case 'Z': //timezone name or abbreviation, //or by no bytes if no timezone information exists //assert(0); break; case '%': put("%"); break; default: assert(0); } } return ret[0..len].dup; } } /********************************************************************** **********************************************************************/ private const(char)[] expandKnownFormat (const(char)[] format) { const(char)[] f; switch (format[0]) { case 'd': f = shortDatePattern; break; case 'D': f = longDatePattern; break; case 'f': f = longDatePattern ~ " " ~ shortTimePattern; break; case 'F': f = fullDateTimePattern; break; case 'g': f = generalShortTimePattern; break; case 'G': f = generalLongTimePattern; break; case 'r': case 'R': f = rfc1123Pattern; break; case 's': f = sortableDateTimePattern; break; case 'u': f = universalSortableDateTimePattern; break; case 't': f = shortTimePattern; break; case 'T': f = longTimePattern; break; case 'y': case 'Y': f = yearMonthPattern; break; default: return ("'{invalid time format}'"); } return f; } /********************************************************************** **********************************************************************/ private char[] formatCustom (ref Result result, Time dateTime, const(char)[] format) { uint len, doy, dow, era; uint day, year, month; int index; char[10] tmp = void; auto time = dateTime.time; // extract date components calendar.split (dateTime, year, month, day, doy, dow, era); // sweep format specifiers ... while (index < format.length) { char c = format[index]; switch (c) { // day case 'd': len = parseRepeat (format, index, c); if (len <= 2) result ~= formatInt (tmp, day, len); else result ~= formatDayOfWeek (cast(Calendar.DayOfWeek) dow, len); break; // millis case 'f': len = parseRepeat (format, index, c); auto num = Integer.itoa (tmp, time.millis); if(len > num.length) { result ~= num; // append '0's static char[8] zeros = '0'; auto zc = len - num.length; zc = (zc > zeros.length) ? zeros.length : zc; result ~= zeros[0..zc]; } else result ~= num[0..len]; break; // millis, no trailing zeros case 'F': len = parseRepeat (format, index, c); auto num = Integer.itoa (tmp, time.millis); auto idx = (len < num.length) ? len : num.length; // strip '0's while(idx && num[idx-1] is '0') --idx; result ~= num[0..idx]; break; // month case 'M': len = parseRepeat (format, index, c); if (len <= 2) result ~= formatInt (tmp, month, len); else result ~= formatMonth (month, len); break; // year case 'y': len = parseRepeat (format, index, c); // Two-digit years for Japanese if (calendar.id is Calendar.JAPAN) result ~= formatInt (tmp, year, 2); else { if (len <= 2) result ~= formatInt (tmp, year % 100, len); else result ~= formatInt (tmp, year, len); } break; // hour (12-hour clock) case 'h': len = parseRepeat (format, index, c); int hour = time.hours % 12; if (hour is 0) hour = 12; result ~= formatInt (tmp, hour, len); break; // hour (24-hour clock) case 'H': len = parseRepeat (format, index, c); result ~= formatInt (tmp, time.hours, len); break; // minute case 'm': len = parseRepeat (format, index, c); result ~= formatInt (tmp, time.minutes, len); break; // second case 's': len = parseRepeat (format, index, c); result ~= formatInt (tmp, time.seconds, len); break; // AM/PM case 't': len = parseRepeat (format, index, c); if (len is 1) { if (time.hours < 12) { if (amDesignator.length != 0) result ~= amDesignator[0]; } else { if (pmDesignator.length != 0) result ~= pmDesignator[0]; } } else result ~= (time.hours < 12) ? amDesignator : pmDesignator; break; // timezone offset case 'z': len = parseRepeat (format, index, c); auto minutes = cast(int) (WallClock.zone.minutes); if (minutes < 0) { minutes = -minutes; result ~= '-'; } else result ~= '+'; int hours = minutes / 60; minutes %= 60; if (len is 1) result ~= formatInt (tmp, hours, 1); else if (len is 2) result ~= formatInt (tmp, hours, 2); else { result ~= formatInt (tmp, hours, 2); result ~= formatInt (tmp, minutes, 2); } break; // time separator case ':': len = 1; result ~= timeSeparator; break; // date separator case '/': len = 1; result ~= dateSeparator; break; // string literal case '\"': case '\'': len = parseQuote (result, format, index); break; // other default: len = 1; result ~= c; break; } index += len; } return result.get; } /********************************************************************** **********************************************************************/ private const(char)[] formatMonth (int month, int rpt) { if (rpt is 3) return abbreviatedMonthName (month); return monthName (month); } /********************************************************************** **********************************************************************/ private const(char)[] formatDayOfWeek (Calendar.DayOfWeek dayOfWeek, int rpt) { if (rpt is 3) return abbreviatedDayName (dayOfWeek); return dayName (dayOfWeek); } /********************************************************************** **********************************************************************/ private T[] bridge(T) (T[] result, void* arg, const(T)[] format) { return formatWide (result, *cast(Time*) arg, format); } /********************************************************************** **********************************************************************/ private static int parseRepeat(const(char)[] format, int pos, char c) { int n = pos + 1; while (n < format.length && format[n] is c) n++; return n - pos; } /********************************************************************** **********************************************************************/ private static char[] formatInt (char[] tmp, int v, int minimum) { auto num = Integer.itoa (tmp, v); if ((minimum -= num.length) > 0) { auto p = tmp.ptr + tmp.length - num.length; while (minimum--) *--p = '0'; num = tmp [p-tmp.ptr .. $]; } return num; } /********************************************************************** **********************************************************************/ private static int parseQuote (ref Result result, const(char)[] format, int pos) { int start = pos; char chQuote = format[pos++]; bool found; while (pos < format.length) { char c = format[pos++]; if (c is chQuote) { found = true; break; } else if (c is '\\') { // escaped if (pos < format.length) result ~= format[pos++]; } else result ~= c; } return pos - start; } } /****************************************************************************** An english/usa locale Used as generic DateTimeLocale. ******************************************************************************/ private DateTimeLocale EngUS = { shortDatePattern : "M/d/yyyy", shortTimePattern : "h:mm", longDatePattern : "dddd, MMMM d, yyyy", longTimePattern : "h:mm:ss tt", fullDateTimePattern : "dddd, MMMM d, yyyy h:mm:ss tt", generalShortTimePattern : "M/d/yyyy h:mm", generalLongTimePattern : "M/d/yyyy h:mm:ss tt", monthDayPattern : "MMMM d", yearMonthPattern : "MMMM, yyyy", amDesignator : "AM", pmDesignator : "PM", timeSeparator : ":", dateSeparator : "/", dayNames : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], monthNames : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October" "November", "December"], abbreviatedDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], abbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct" "Nov", "Dec"], }; /****************************************************************************** ******************************************************************************/ private struct Result { private size_t index; private char[] target_; /********************************************************************** **********************************************************************/ private static Result opCall (char[] target) { Result result; result.target_ = target; return result; } /********************************************************************** **********************************************************************/ private void opCatAssign (const(char)[] rhs) { auto end = index + rhs.length; assert (end < target_.length); target_[index .. end] = rhs; index = end; } /********************************************************************** **********************************************************************/ private void opCatAssign (char rhs) { assert (index < target_.length); target_[index++] = rhs; } /********************************************************************** **********************************************************************/ @property private char[] get () { return target_[0 .. index]; } } /****************************************************************************** ******************************************************************************/ debug (DateTime) { import tango.io.Stdout; void main() { char[100] tmp; auto time = WallClock.now; auto locale = DateTimeLocale.create; Stdout.formatln ("d: {}", locale.format (tmp, time, "d")); Stdout.formatln ("D: {}", locale.format (tmp, time, "D")); Stdout.formatln ("f: {}", locale.format (tmp, time, "f")); Stdout.formatln ("F: {}", locale.format (tmp, time, "F")); Stdout.formatln ("g: {}", locale.format (tmp, time, "g")); Stdout.formatln ("G: {}", locale.format (tmp, time, "G")); Stdout.formatln ("r: {}", locale.format (tmp, time, "r")); Stdout.formatln ("s: {}", locale.format (tmp, time, "s")); Stdout.formatln ("t: {}", locale.format (tmp, time, "t")); Stdout.formatln ("T: {}", locale.format (tmp, time, "T")); Stdout.formatln ("y: {}", locale.format (tmp, time, "y")); Stdout.formatln ("u: {}", locale.format (tmp, time, "u")); Stdout.formatln ("@: {}", locale.format (tmp, time, "@")); Stdout.formatln ("{}", locale.generic.format (tmp, time, "ddd, dd MMM yyyy HH':'mm':'ss zzzz")); } } |