1 /** 2 * Time formats for string formatting and parsing. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.time.common; 15 16 import ae.utils.text; 17 18 struct TimeFormats 19 { 20 static: 21 const ATOM = `Y-m-d\TH:i:sP`; 22 const COOKIE = `l, d-M-y H:i:s T`; 23 const ISO8601 = `Y-m-d\TH:i:sO`; 24 const RFC822 = `D, d M y H:i:s O`; 25 const RFC850 = `l, d-M-y H:i:s T`; 26 const RFC1036 = `D, d M y H:i:s O`; 27 const RFC1123 = `D, d M Y H:i:s O`; 28 const RFC2822 = `D, d M Y H:i:s O`; 29 const RFC3339 = `Y-m-d\TH:i:sP`; 30 const RSS = `D, d M Y H:i:s O`; 31 const W3C = `Y-m-d\TH:i:sP`; 32 33 const HTML5DATE = `Y-m-d`; 34 35 /// Format produced by std.date.toString, e.g. "Tue Jun 07 13:23:19 GMT+0100 2011" 36 const STD_DATE = `D M d H:i:s \G\M\TO Y`; 37 } 38 39 /// We assume that no timezone will have a name longer than this. 40 /// If one does, it is truncated to this length. 41 enum MaxTimezoneNameLength = 256; 42 43 /// Calculate the maximum amount of characters needed to store a time in this format. 44 /// Can be evaluated at compile-time. 45 size_t timeFormatSize(string fmt) 46 { 47 import std.algorithm.iteration : map, reduce; 48 import std.algorithm.comparison : max; 49 50 static size_t maxLength(in string[] names) { return reduce!max(map!`a.length`(WeekdayShortNames)); } 51 52 size_t size = 0; 53 bool escaping = false; 54 foreach (char c; fmt) 55 if (escaping) 56 size++, escaping = false; 57 else 58 switch (c) 59 { 60 case 'N': 61 case 'w': 62 case 'L': 63 case 'I': 64 size++; 65 break; 66 case 'd': 67 case 'j': 68 case 'S': 69 case 'W': 70 case 'm': 71 case 'n': 72 case 't': 73 case 'y': 74 case 'a': 75 case 'A': 76 case 'g': 77 case 'G': 78 case 'h': 79 case 'H': 80 case 'i': 81 case 's': 82 size += 2; 83 break; 84 case 'z': 85 case 'E': // not standard 86 size += 3; 87 break; 88 case 'Y': 89 size += 4; 90 break; 91 case 'Z': // Timezone offset in seconds 92 case 'O': 93 size += 5; 94 break; 95 case 'u': 96 case 'P': 97 size += 6; 98 break; 99 case 'T': 100 size += 32; 101 break; 102 103 case 'D': 104 size += maxLength(WeekdayShortNames); 105 break; 106 case 'l': 107 size += maxLength(WeekdayLongNames); 108 break; 109 case 'F': 110 size += maxLength(MonthLongNames); 111 break; 112 case 'M': 113 size += maxLength(MonthShortNames); 114 break; 115 116 case 'e': // Timezone name 117 return MaxTimezoneNameLength; 118 119 // Full date/time 120 case 'c': 121 enum ISOExtLength = "-0004-01-05T00:00:02.052092+10:00".length; 122 size += ISOExtLength; 123 break; 124 case 'r': 125 size += timeFormatSize(TimeFormats.RFC2822); 126 break; 127 case 'U': 128 size += DecimalSize!int; 129 break; 130 131 // Escape next character 132 case '\\': 133 escaping = true; 134 break; 135 136 // Other characters (whitespace, delimiters) 137 default: 138 size++; 139 } 140 141 return size; 142 } 143 144 static assert(timeFormatSize(TimeFormats.STD_DATE) == "Tue Jun 07 13:23:19 GMT+0100 2011".length); 145 146 // *************************************************************************** 147 148 const WeekdayShortNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; 149 const WeekdayLongNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 150 const MonthShortNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 151 const MonthLongNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 152