1 /******************************************************************************* 2 3 Support for formatting date/time values, in a locale-specific 4 manner. See DateTimeLocale.format() for a description on how 5 formatting is performed (below). 6 7 Reference links: 8 --- 9 http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html 10 http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo(VS.71).aspx 11 --- 12 13 Copyright: 14 Copyright (c) 2005 John Chapman. 15 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 16 All rights reserved. 17 18 License: 19 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 20 See LICENSE_TANGO.txt for details. 21 22 Version: 23 Jan 2005: initial release 24 Mar 2009: extracted from locale, and 25 converted to a struct 26 27 Authors: John Chapman, Kris, mwarning 28 29 ******************************************************************************/ 30 31 module ocean.text.convert.DateTime_tango; 32 33 import ocean.meta.types.Qualifiers; 34 35 import ocean.core.ExceptionDefinitions; 36 import ocean.stdc.posix.langinfo; 37 import Integer = ocean.text.convert.Integer_tango; 38 import Utf = ocean.text.convert.Utf; 39 import ocean.text.convert.Formatter; 40 import ocean.text.util.StringC; 41 import ocean.time.chrono.Calendar; 42 import ocean.time.chrono.Gregorian; 43 import ocean.meta.types.Qualifiers; 44 import ocean.core.Verify; 45 46 import core.sys.posix.time; // timezone 47 48 /****************************************************************************** 49 50 The default DateTimeLocale instance 51 52 ******************************************************************************/ 53 54 public DateTimeLocale DateTimeDefault; 55 56 static this() 57 { 58 DateTimeDefault = DateTimeLocale.create; 59 } 60 61 /****************************************************************************** 62 63 How to format locale-specific date/time output 64 65 ******************************************************************************/ 66 67 struct DateTimeLocale 68 { 69 static rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'"; 70 static sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss"; 71 static universalSortableDateTimePattern = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'Z'"; 72 73 Calendar assignedCalendar; 74 75 cstring shortDatePattern, 76 shortTimePattern, 77 longDatePattern, 78 longTimePattern, 79 fullDateTimePattern, 80 generalShortTimePattern, 81 generalLongTimePattern, 82 monthDayPattern, 83 yearMonthPattern; 84 85 cstring amDesignator, pmDesignator; 86 87 cstring timeSeparator, dateSeparator; 88 89 cstring[] dayNames, monthNames, 90 abbreviatedDayNames, abbreviatedMonthNames; 91 92 /********************************************************************** 93 94 Format the given Time value into the provided output, 95 using the specified layout. The layout can be a generic 96 variant or a custom one, where generics are indicated 97 via a single character: 98 99 <pre> 100 "t" = 7:04 101 "T" = 7:04:02 PM 102 "d" = 3/30/2009 103 "D" = Monday, March 30, 2009 104 "f" = Monday, March 30, 2009 7:04 PM 105 "F" = Monday, March 30, 2009 7:04:02 PM 106 "g" = 3/30/2009 7:04 PM 107 "G" = 3/30/2009 7:04:02 PM 108 "y" 109 "Y" = March, 2009 110 "r" 111 "R" = Mon, 30 Mar 2009 19:04:02 GMT 112 "s" = 2009-03-30T19:04:02 113 "u" = 2009-03-30 19:04:02Z 114 </pre> 115 116 For the US locale, these generic layouts are expanded in the 117 following manner: 118 119 <pre> 120 "t" = "h:mm" 121 "T" = "h:mm:ss tt" 122 "d" = "M/d/yyyy" 123 "D" = "dddd, MMMM d, yyyy" 124 "f" = "dddd, MMMM d, yyyy h:mm tt" 125 "F" = "dddd, MMMM d, yyyy h:mm:ss tt" 126 "g" = "M/d/yyyy h:mm tt" 127 "G" = "M/d/yyyy h:mm:ss tt" 128 "y" 129 "Y" = "MMMM, yyyy" 130 "r" 131 "R" = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" 132 "s" = "yyyy'-'MM'-'dd'T'HH':'mm':'ss" 133 "u" = "yyyy'-'MM'-'dd' 'HH':'mm':'ss'Z'" 134 </pre> 135 136 Custom layouts are constructed using a combination of the 137 character codes indicated on the right, above. For example, 138 a layout of "dddd, dd MMM yyyy HH':'mm':'ss zzzz" will emit 139 something like this: 140 --- 141 Monday, 30 Mar 2009 19:04:02 -08:00 142 --- 143 144 Using these format indicators with Layout (Stdout etc) is 145 straightforward. Formatting integers, for example, is done 146 like so: 147 --- 148 Stdout.formatln ("{:u}", 5); 149 Stdout.formatln ("{:b}", 5); 150 Stdout.formatln ("{:x}", 5); 151 --- 152 153 Formatting date/time values is similar, where the format 154 indicators are provided after the colon: 155 --- 156 Stdout.formatln ("{:t}", Clock.now); 157 Stdout.formatln ("{:D}", Clock.now); 158 Stdout.formatln ("{:dddd, dd MMMM yyyy HH:mm}", Clock.now); 159 --- 160 161 **********************************************************************/ 162 163 char[] format (char[] output, Time dateTime, cstring layout) 164 { 165 // default to general format 166 if (layout.length is 0) 167 layout = "G"; 168 169 // might be one of our shortcuts 170 if (layout.length is 1) 171 layout = expandKnownFormat (layout); 172 173 auto res=Result(output); 174 scope sink = (cstring v) { res ~= v; return v.length; }; 175 this.formatCustom(sink, dateTime, layout); 176 return res.get; 177 } 178 179 /// Ditto 180 public void format (scope size_t delegate(cstring) output, Time dateTime, 181 cstring layout) 182 { 183 // default to general format 184 if (layout.length is 0) 185 layout = "G"; 186 187 // might be one of our shortcuts 188 if (layout.length is 1) 189 layout = expandKnownFormat (layout); 190 191 return formatCustom(output, dateTime, layout); 192 } 193 194 195 /********************************************************************** 196 197 Return a generic English/US instance 198 199 **********************************************************************/ 200 201 static DateTimeLocale* generic () 202 { 203 return &EngUS; 204 } 205 206 /********************************************************************** 207 208 Return the assigned Calendar instance, using Gregorian 209 as the default 210 211 **********************************************************************/ 212 213 Calendar calendar () 214 { 215 if (assignedCalendar is null) 216 assignedCalendar = Gregorian.generic; 217 return assignedCalendar; 218 } 219 220 /********************************************************************** 221 222 Return a short day name 223 224 **********************************************************************/ 225 226 cstring abbreviatedDayName (Calendar.DayOfWeek dayOfWeek) 227 { 228 return abbreviatedDayNames [cast(int) dayOfWeek]; 229 } 230 231 /********************************************************************** 232 233 Return a long day name 234 235 **********************************************************************/ 236 237 cstring dayName (Calendar.DayOfWeek dayOfWeek) 238 { 239 return dayNames [cast(int) dayOfWeek]; 240 } 241 242 /********************************************************************** 243 244 Return a short month name 245 246 **********************************************************************/ 247 248 cstring abbreviatedMonthName (int month) 249 { 250 verify(month > 0 && month < 13); 251 return abbreviatedMonthNames [month - 1]; 252 } 253 254 /********************************************************************** 255 256 Return a long month name 257 258 **********************************************************************/ 259 260 cstring monthName (int month) 261 { 262 verify(month > 0 && month < 13); 263 return monthNames [month - 1]; 264 } 265 266 /************************************************************************** 267 268 Create and populate an instance via O/S configuration 269 for the current user 270 271 ***************************************************************************/ 272 273 static DateTimeLocale create () 274 { 275 //extract separator 276 static cstring extractSeparator(cstring str, cstring def) 277 { 278 for (auto i = 0; i < str.length; ++i) 279 { 280 char c = str[i]; 281 if ((c == '%') || (c == ' ') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) 282 continue; 283 return str[i..i+1].dup; 284 } 285 return def; 286 } 287 288 static cstring getString(nl_item id, cstring def = null) 289 { 290 char* p = nl_langinfo(id); 291 return p ? StringC.toDString(p).dup : def; 292 } 293 294 static cstring getFormatString(nl_item id, cstring def = null) 295 { 296 auto posix_str = getString(id, def); 297 return convert(posix_str); 298 } 299 300 DateTimeLocale dt; 301 302 for (auto i = DAY_1; i <= DAY_7; ++i) 303 dt.dayNames ~= getString (i); 304 305 for (auto i = ABDAY_1; i <= ABDAY_7; ++i) 306 dt.abbreviatedDayNames ~= getString (i); 307 308 for (auto i = MON_1; i <= MON_12; ++i) 309 dt.monthNames ~= getString (i); 310 311 for (auto i = ABMON_1; i <= ABMON_12; ++i) 312 dt.abbreviatedMonthNames ~= getString (i); 313 314 dt.amDesignator = getString (AM_STR, "AM"); 315 dt.pmDesignator = getString (PM_STR, "PM"); 316 317 dt.longDatePattern = "dddd, MMMM d, yyyy"; //default 318 dt.shortDatePattern = getFormatString(D_FMT, "M/d/yyyy"); 319 320 dt.longTimePattern = getFormatString(T_FMT, "h:mm:ss tt"); 321 dt.shortTimePattern = "h:mm"; //default 322 323 dt.yearMonthPattern = "MMMM, yyyy"; //no posix equivalent? 324 dt.fullDateTimePattern = getFormatString(D_T_FMT, "dddd, MMMM d, yyyy h:mm:ss tt"); 325 326 dt.dateSeparator = extractSeparator(dt.shortDatePattern, "/"); 327 dt.timeSeparator = extractSeparator(dt.longTimePattern, ":"); 328 329 //extract shortTimePattern from longTimePattern 330 for (auto i = dt.longTimePattern.length; i--;) 331 { 332 if (dt.longTimePattern[i] == dt.timeSeparator[$-1]) 333 { 334 dt.shortTimePattern = dt.longTimePattern[0..i]; 335 break; 336 } 337 } 338 339 //extract longDatePattern from fullDateTimePattern 340 auto pos = dt.fullDateTimePattern.length - dt.longTimePattern.length - 2; 341 if (pos < dt.fullDateTimePattern.length) 342 dt.longDatePattern = dt.fullDateTimePattern[0..pos]; 343 344 dt.fullDateTimePattern = dt.longDatePattern ~ " " ~ dt.longTimePattern; 345 dt.generalLongTimePattern = dt.shortDatePattern ~ " " ~ dt.longTimePattern; 346 dt.generalShortTimePattern = dt.shortDatePattern ~ " " ~ dt.shortTimePattern; 347 348 return dt; 349 } 350 351 /*************************************************************************** 352 353 Convert POSIX date time format to .NET format syntax. 354 355 ***************************************************************************/ 356 357 private static char[] convert(cstring fmt) 358 { 359 char[32] ret; 360 size_t len; 361 362 void put(cstring str) 363 { 364 verify((len+str.length) <= ret.length); 365 ret[len..len+str.length] = str; 366 len += str.length; 367 } 368 369 for (auto i = 0; i < fmt.length; ++i) 370 { 371 char c = fmt[i]; 372 373 if (c != '%') 374 { 375 verify((len+1) <= ret.length); 376 ret[len] = c; 377 len += 1; 378 continue; 379 } 380 381 i++; 382 if (i >= fmt.length) 383 break; 384 385 c = fmt[i]; 386 switch (c) 387 { 388 case 'a': //locale's abbreviated weekday name. 389 put("ddd"); //The abbreviated name of the day of the week, 390 break; 391 392 case 'A': //locale's full weekday name. 393 put("dddd"); 394 break; 395 396 case 'b': //locale's abbreviated month name 397 put("MMM"); 398 break; 399 400 case 'B': //locale's full month name 401 put("MMMM"); 402 break; 403 404 case 'd': //day of the month as a decimal number [01,31] 405 put("dd"); // The day of the month. Single-digit 406 //days will have a leading zero. 407 break; 408 409 case 'D': //same as %m/%d/%y. 410 put("MM/dd/yy"); 411 break; 412 413 case 'e': //day of the month as a decimal number [1,31]; 414 //a single digit is preceded by a space 415 put("d"); //The day of the month. Single-digit days 416 //will not have a leading zero. 417 break; 418 419 case 'h': //same as %b. 420 put("MMM"); 421 break; 422 423 case 'H': 424 //hour (24-hour clock) as a decimal number [00,23] 425 put("HH"); //The hour in a 24-hour clock. Single-digit 426 //hours will have a leading zero. 427 break; 428 429 case 'I': //the hour (12-hour clock) as a decimal number [01,12] 430 put("hh"); //The hour in a 12-hour clock. 431 //Single-digit hours will have a leading zero. 432 break; 433 434 case 'm': //month as a decimal number [01,12] 435 put("MM"); //The numeric month. Single-digit 436 //months will have a leading zero. 437 break; 438 439 case 'M': //minute as a decimal number [00,59] 440 put("mm"); //The minute. Single-digit minutes 441 //will have a leading zero. 442 break; 443 444 case 'n': //newline character 445 put("\n"); 446 break; 447 448 case 'p': //locale's equivalent of either a.m. or p.m 449 put("tt"); 450 break; 451 452 case 'r': //time in a.m. and p.m. notation; 453 //equivalent to %I:%M:%S %p. 454 put("hh:mm:ss tt"); 455 break; 456 457 case 'R': //time in 24 hour notation (%H:%M) 458 put("HH:mm"); 459 break; 460 461 case 'S': //second as a decimal number [00,61] 462 put("ss"); //The second. Single-digit seconds 463 //will have a leading zero. 464 break; 465 466 case 't': //tab character. 467 put("\t"); 468 break; 469 470 case 'T': //equivalent to (%H:%M:%S) 471 put("HH:mm:ss"); 472 break; 473 474 case 'u': //weekday as a decimal number [1,7], 475 //with 1 representing Monday 476 case 'U': //week number of the year 477 //(Sunday as the first day of the week) as a decimal number [00,53] 478 case 'V': //week number of the year 479 //(Monday as the first day of the week) as a decimal number [01,53]. 480 //If the week containing 1 January has four or more days 481 //in the new year, then it is considered week 1. 482 //Otherwise, it is the last week of the previous year, and the next week is week 1. 483 case 'w': //weekday as a decimal number [0,6], with 0 representing Sunday 484 case 'W': //week number of the year (Monday as the first day of the week) 485 //as a decimal number [00,53]. 486 //All days in a new year preceding the first Monday 487 //are considered to be in week 0. 488 case 'x': //locale's appropriate date representation 489 case 'X': //locale's appropriate time representation 490 case 'c': //locale's appropriate date and time representation 491 case 'C': //century number (the year divided by 100 and 492 //truncated to an integer) as a decimal number [00-99] 493 case 'j': //day of the year as a decimal number [001,366] 494 assert(0); 495 496 case 'y': //year without century as a decimal number [00,99] 497 put("yy"); // The year without the century. If the year without 498 //the century is less than 10, the year is displayed with a leading zero. 499 break; 500 501 case 'Y': //year with century as a decimal number 502 put("yyyy"); //The year in four digits, including the century. 503 break; 504 505 case 'Z': //timezone name or abbreviation, 506 //or by no bytes if no timezone information exists 507 //assert(0); 508 break; 509 510 case '%': 511 put("%"); 512 break; 513 514 default: 515 assert(0); 516 } 517 } 518 return ret[0..len].dup; 519 } 520 521 /********************************************************************** 522 523 **********************************************************************/ 524 525 private cstring expandKnownFormat (cstring format) 526 { 527 cstring f; 528 529 switch (format[0]) 530 { 531 case 'd': 532 f = shortDatePattern; 533 break; 534 case 'D': 535 f = longDatePattern; 536 break; 537 case 'f': 538 f = longDatePattern ~ " " ~ shortTimePattern; 539 break; 540 case 'F': 541 f = fullDateTimePattern; 542 break; 543 case 'g': 544 f = generalShortTimePattern; 545 break; 546 case 'G': 547 f = generalLongTimePattern; 548 break; 549 case 'r': 550 case 'R': 551 f = rfc1123Pattern; 552 break; 553 case 's': 554 f = sortableDateTimePattern; 555 break; 556 case 'u': 557 f = universalSortableDateTimePattern; 558 break; 559 case 't': 560 f = shortTimePattern; 561 break; 562 case 'T': 563 f = longTimePattern; 564 break; 565 case 'y': 566 case 'Y': 567 f = yearMonthPattern; 568 break; 569 default: 570 return ("'{invalid time format}'"); 571 } 572 return f; 573 } 574 575 /********************************************************************** 576 577 **********************************************************************/ 578 579 private void formatCustom (scope size_t delegate(cstring) sink, Time dateTime, 580 cstring format) 581 { 582 uint len, 583 doy, 584 dow, 585 era; 586 uint day, 587 year, 588 month; 589 int index; 590 char[10] tmp = void; 591 auto time = dateTime.time; 592 593 // extract date components 594 calendar.split (dateTime, year, month, day, doy, dow, era); 595 596 // sweep format specifiers ... 597 while (index < format.length) 598 { 599 char c = format[index]; 600 601 switch (c) 602 { 603 // day 604 case 'd': 605 len = parseRepeat (format, index, c); 606 if (len <= 2) 607 sink(formatInt(tmp, day, len)); 608 else 609 sink(formatDayOfWeek (cast(Calendar.DayOfWeek) dow, len)); 610 break; 611 612 // millis 613 case 'f': 614 len = parseRepeat (format, index, c); 615 auto num = Integer.itoa (tmp, time.millis); 616 if(len > num.length) 617 { 618 sink(num); 619 620 // append '0's 621 static char[8] zeros = '0'; 622 auto zc = len - num.length; 623 zc = (zc > zeros.length) ? zeros.length : zc; 624 sink(zeros[0..zc]); 625 } 626 else 627 sink(num[0..len]); 628 break; 629 630 // millis, no trailing zeros 631 case 'F': 632 len = parseRepeat (format, index, c); 633 auto num = Integer.itoa (tmp, time.millis); 634 auto idx = (len < num.length) ? len : num.length; 635 636 // strip '0's 637 while(idx && num[idx-1] is '0') 638 --idx; 639 640 sink(num[0..idx]); 641 break; 642 643 // month 644 case 'M': 645 len = parseRepeat (format, index, c); 646 if (len <= 2) 647 sink(formatInt(tmp, month, len)); 648 else 649 sink(formatMonth(month, len)); 650 break; 651 652 // year 653 case 'y': 654 len = parseRepeat (format, index, c); 655 656 // Two-digit years for Japanese 657 if (calendar.id is Calendar.JAPAN) 658 sink(formatInt(tmp, year, 2)); 659 else 660 { 661 if (len <= 2) 662 sink(formatInt(tmp, year % 100, len)); 663 else 664 sink(formatInt(tmp, year, len)); 665 } 666 break; 667 668 // hour (12-hour clock) 669 case 'h': 670 len = parseRepeat (format, index, c); 671 int hour = time.hours % 12; 672 if (hour is 0) 673 hour = 12; 674 sink(formatInt(tmp, hour, len)); 675 break; 676 677 // hour (24-hour clock) 678 case 'H': 679 len = parseRepeat (format, index, c); 680 sink(formatInt(tmp, time.hours, len)); 681 break; 682 683 // minute 684 case 'm': 685 len = parseRepeat (format, index, c); 686 sink(formatInt(tmp, time.minutes, len)); 687 break; 688 689 // second 690 case 's': 691 len = parseRepeat (format, index, c); 692 sink(formatInt (tmp, time.seconds, len)); 693 break; 694 695 // AM/PM 696 case 't': 697 len = parseRepeat (format, index, c); 698 if (len is 1) 699 { 700 if (time.hours < 12) 701 { 702 if (amDesignator.length != 0) 703 sink((&amDesignator[0])[0 .. 1]); 704 } 705 else 706 { 707 if (pmDesignator.length != 0) 708 sink((&pmDesignator[0])[0 .. 1]); 709 } 710 } 711 else 712 sink((time.hours < 12) ? amDesignator : pmDesignator); 713 break; 714 715 // timezone offset 716 case 'z': 717 len = parseRepeat (format, index, c); 718 auto minutes = cast(int) (TimeSpan.fromSeconds(-timezone).minutes); 719 if (minutes < 0) 720 { 721 minutes = -minutes; 722 sink("-"); 723 } 724 else 725 sink("+"); 726 int hours = minutes / 60; 727 minutes %= 60; 728 729 if (len is 1) 730 sink(formatInt(tmp, hours, 1)); 731 else 732 if (len is 2) 733 sink(formatInt (tmp, hours, 2)); 734 else 735 { 736 sink(formatInt(tmp, hours, 2)); 737 sink(formatInt(tmp, minutes, 2)); 738 } 739 break; 740 741 // time separator 742 case ':': 743 len = 1; 744 sink(timeSeparator); 745 break; 746 747 // date separator 748 case '/': 749 len = 1; 750 sink(dateSeparator); 751 break; 752 753 // string literal 754 case '\"': 755 case '\'': 756 len = parseQuote(sink, format, index); 757 break; 758 759 // other 760 default: 761 len = 1; 762 sink((&c)[0 .. 1]); 763 break; 764 } 765 index += len; 766 } 767 } 768 769 /********************************************************************** 770 771 **********************************************************************/ 772 773 private cstring formatMonth (int month, int rpt) 774 { 775 if (rpt is 3) 776 return abbreviatedMonthName (month); 777 return monthName (month); 778 } 779 780 /********************************************************************** 781 782 **********************************************************************/ 783 784 private cstring formatDayOfWeek (Calendar.DayOfWeek dayOfWeek, int rpt) 785 { 786 if (rpt is 3) 787 return abbreviatedDayName (dayOfWeek); 788 return dayName (dayOfWeek); 789 } 790 791 /********************************************************************** 792 793 **********************************************************************/ 794 795 private static int parseRepeat(cstring format, int pos, char c) 796 { 797 int n = pos + 1; 798 while (n < format.length && format[n] is c) 799 n++; 800 return n - pos; 801 } 802 803 /********************************************************************** 804 805 **********************************************************************/ 806 807 private static char[] formatInt (char[] tmp, int v, int minimum) 808 { 809 auto num = Integer.itoa (tmp, v); 810 if ((minimum -= num.length) > 0) 811 { 812 auto p = tmp.ptr + tmp.length - num.length; 813 while (minimum--) 814 *--p = '0'; 815 num = tmp [p-tmp.ptr .. $]; 816 } 817 return num; 818 } 819 820 /********************************************************************** 821 822 **********************************************************************/ 823 824 private static int parseQuote (scope size_t delegate(cstring) sink, 825 cstring format, int pos) 826 { 827 int start = pos; 828 char chQuote = format[pos++]; 829 bool found; 830 while (pos < format.length) 831 { 832 char c = format[pos++]; 833 if (c is chQuote) 834 { 835 found = true; 836 break; 837 } 838 else 839 if (c is '\\') 840 { // escaped 841 if (pos < format.length) 842 { 843 sink(format[pos .. pos + 1]); 844 ++pos; 845 } 846 } 847 else 848 sink((&c)[0 .. 1]); 849 } 850 return pos - start; 851 } 852 } 853 854 /****************************************************************************** 855 856 An english/usa locale 857 Used as generic DateTimeLocale. 858 859 ******************************************************************************/ 860 861 private DateTimeLocale EngUS = { 862 shortDatePattern : "M/d/yyyy", 863 shortTimePattern : "h:mm", 864 longDatePattern : "dddd, MMMM d, yyyy", 865 longTimePattern : "h:mm:ss tt", 866 fullDateTimePattern : "dddd, MMMM d, yyyy h:mm:ss tt", 867 generalShortTimePattern : "M/d/yyyy h:mm", 868 generalLongTimePattern : "M/d/yyyy h:mm:ss tt", 869 monthDayPattern : "MMMM d", 870 yearMonthPattern : "MMMM, yyyy", 871 amDesignator : "AM", 872 pmDesignator : "PM", 873 timeSeparator : ":", 874 dateSeparator : "/", 875 dayNames : [ "Sunday", "Monday", "Tuesday", "Wednesday", 876 "Thursday", "Friday", "Saturday" ], 877 monthNames : [ "January", "February", "March", "April", 878 "May", "June", "July", "August", "September", 879 "October", "November", "December" ], 880 abbreviatedDayNames : [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], 881 abbreviatedMonthNames : [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", 882 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], 883 }; 884 885 886 /****************************************************************************** 887 888 ******************************************************************************/ 889 890 private struct Result 891 { 892 private size_t index; 893 private char[] target_; 894 895 /********************************************************************** 896 897 **********************************************************************/ 898 899 private static Result opCall (char[] target) 900 { 901 Result result; 902 903 result.target_ = target; 904 return result; 905 } 906 907 /********************************************************************** 908 909 **********************************************************************/ 910 911 private void opOpAssign (string op : "~") (cstring rhs) 912 { 913 auto end = index + rhs.length; 914 verify(end < target_.length); 915 916 target_[index .. end] = rhs; 917 index = end; 918 } 919 920 /********************************************************************** 921 922 **********************************************************************/ 923 924 private void opOpAssign (string op : "~") (char rhs) 925 { 926 verify(index < target_.length); 927 target_[index++] = rhs; 928 } 929 930 /********************************************************************** 931 932 **********************************************************************/ 933 934 private char[] get () 935 { 936 return target_[0 .. index]; 937 } 938 } 939 940 /******************************************************************************* 941 942 Params: 943 value = time value to wrap in pretty-printing struct 944 945 Returns: 946 wrapper struct which, when supplied to `Formatter`, prints `value` 947 using current default locale settings. 948 949 *******************************************************************************/ 950 951 public AsPrettyStr asPrettyStr ( Time value ) 952 { 953 return AsPrettyStr(value); 954 } 955 956 /******************************************************************************* 957 958 Wrapper struct which, when supplied to `Formatter`, prints `value` 959 using current default locale settings. 960 961 *******************************************************************************/ 962 963 public struct AsPrettyStr 964 { 965 private Time value; 966 967 public void toString (scope FormatterSink sink) 968 { 969 // Layout defaults to 'G' 970 scope dg = (cstring s) { sink(s); return s.length; }; 971 DateTimeDefault.format(dg, this.value, ""); 972 } 973 } 974 975 /****************************************************************************** 976 977 ******************************************************************************/ 978 979 debug (DateTime) 980 { 981 import ocean.io.Stdout; 982 983 void main() 984 { 985 char[100] tmp; 986 auto time = WallClock.now; 987 auto locale = DateTimeLocale.create; 988 989 Stdout.formatln ("d: {}", locale.format (tmp, time, "d")); 990 Stdout.formatln ("D: {}", locale.format (tmp, time, "D")); 991 Stdout.formatln ("f: {}", locale.format (tmp, time, "f")); 992 Stdout.formatln ("F: {}", locale.format (tmp, time, "F")); 993 Stdout.formatln ("g: {}", locale.format (tmp, time, "g")); 994 Stdout.formatln ("G: {}", locale.format (tmp, time, "G")); 995 Stdout.formatln ("r: {}", locale.format (tmp, time, "r")); 996 Stdout.formatln ("s: {}", locale.format (tmp, time, "s")); 997 Stdout.formatln ("t: {}", locale.format (tmp, time, "t")); 998 Stdout.formatln ("T: {}", locale.format (tmp, time, "T")); 999 Stdout.formatln ("y: {}", locale.format (tmp, time, "y")); 1000 Stdout.formatln ("u: {}", locale.format (tmp, time, "u")); 1001 Stdout.formatln ("@: {}", locale.format (tmp, time, "@")); 1002 Stdout.formatln ("{}", locale.generic.format (tmp, time, "ddd, dd MMM yyyy HH':'mm':'ss zzzz")); 1003 } 1004 }