1 /****************************************************************************** 2 3 Formats an UNIX time value to a HTTP compliant date/time string 4 5 Formats an UNIX time value to a HTTP compliant (RFC 1123) date/time string. 6 Contains a static length array as string buffer to provide 7 memory-friendliness. 8 9 Copyright: 10 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 11 All rights reserved. 12 13 License: 14 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 15 Alternatively, this file may be distributed under the terms of the Tango 16 3-Clause BSD License (see LICENSE_BSD.txt for details). 17 18 ******************************************************************************/ 19 20 module ocean.net.http.time.HttpTimeFormatter; 21 22 23 import ocean.meta.types.Qualifiers; 24 import ocean.core.Verify; 25 import core.stdc.time: time_t, tm, time; 26 import core.sys.posix.time: gmtime_r, localtime_r; 27 import core.stdc.stdlib: lldiv; 28 29 version (unittest) import ocean.core.Test; 30 31 /******************************************************************************/ 32 33 struct HttpTimeFormatter 34 { 35 /************************************************************************** 36 37 Date/time string length constant 38 39 **************************************************************************/ 40 41 public enum size_t ResultLength = "Sun, 06 Nov 1994 08:49:37 GMT".length; 42 43 /************************************************************************** 44 45 Callback function to obtain the wall clock time. By default (null) the 46 system time is queried using time() of the C stdlib. 47 An application may set its own time function, if desired. 48 49 **************************************************************************/ 50 51 public static time_t function ( ) now = null; 52 53 /************************************************************************** 54 55 Date/time string destination buffer 56 57 **************************************************************************/ 58 59 private char[ResultLength] buf; 60 61 /************************************************************************** 62 63 Weekday/month name constants 64 65 **************************************************************************/ 66 67 private enum istring[7] weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; 68 private enum istring[12] months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 69 70 /************************************************************************** 71 72 Generates a HTTP compliant date/time string (asctime) from t. 73 74 Params: 75 t = UNIX time value to be formatted as HTTP date/time string 76 77 Returns: 78 HTTP date/time string from UNIX time value t. Do not modify (exposes 79 an internal buffer). 80 81 Throws: 82 Exception if formatting failed (supposed never to happen) 83 84 **************************************************************************/ 85 86 public mstring format ( time_t t ) 87 { 88 return this.format(this.buf, t); 89 } 90 91 /************************************************************************** 92 93 Ditto; uses the current wall clock time. 94 95 Returns: 96 HTTP date/time string from UNIX time value t. Do not modify (exposes 97 an internal buffer). 98 99 Throws: 100 Exception if formatting failed (supposed never to happen) 101 102 **************************************************************************/ 103 104 public mstring format ( ) 105 { 106 return this.format(this.buf); 107 } 108 109 /************************************************************************** 110 111 Generates a HTTP compliant date/time string from t and stores it in dst. 112 dst.length must be ResultLength. 113 114 Params: 115 dst = destination string 116 t = UNIX time value to be formatted as HTTP date/time string 117 118 Returns: 119 slice to valid result data in dst, starting at dst[0] 120 121 Throws: 122 Exception if formatting failed (supposed never to happen) 123 124 **************************************************************************/ 125 126 public static mstring format ( mstring dst, time_t t ) 127 { 128 verify(dst.length == ResultLength); 129 130 tm datetime; 131 132 tm* datetimep = gmtime_r(&t, &datetime); 133 134 if (datetimep is null) throw new Exception("time conversion failed", __FILE__, __LINE__); 135 136 with (*datetimep) 137 { 138 dst[ 0 .. 3] = weekdays[tm_wday]; 139 dst[ 3 .. 5] = ", "; 140 fmt(dst[ 5 .. 7], tm_mday); 141 dst[ 7 ] = ' '; 142 dst[ 8 .. 11] = months[tm_mon]; 143 dst[11 ] = ' '; 144 fmt(dst[12 .. 16], tm_year + 1900); 145 dst[16 ] = ' '; 146 fmt(dst[17 .. 19], tm_hour); 147 dst[19 ] = ':'; 148 fmt(dst[20 .. 22], tm_min); 149 dst[22 ] = ':'; 150 fmt(dst[23 .. 25], tm_sec); 151 } 152 153 dst[$ - " GMT".length .. $] = " GMT"; 154 155 return dst; 156 } 157 158 /************************************************************************** 159 160 Ditto; uses the current wall clock time. 161 162 Params: 163 dst = destination string 164 165 Returns: 166 slice to valid result data in dst, starting at dst[0] 167 168 Throws: 169 Exception if formatting failed (supposed never to happen) 170 171 **************************************************************************/ 172 173 public static mstring format ( mstring dst ) 174 { 175 return format(dst, now? now() : time(null)); 176 } 177 178 /************************************************************************** 179 180 Converts n to a decimal string, left-padding with '0'. 181 n must be at least 0 and fit into dst (be less than 10 ^ dst.length). 182 183 Params: 184 dst = destination string 185 n = number to convert 186 187 **************************************************************************/ 188 189 private static void fmt ( mstring dst, long n ) 190 out 191 { 192 assert (!n, "decimal formatting overflow"); 193 } 194 do 195 { 196 verify(n >= 0); 197 198 foreach_reverse (ref c; dst) 199 { 200 with (lldiv(n, 10)) 201 { 202 c = cast(char) (rem + '0'); 203 n = quot; 204 } 205 } 206 } 207 } 208 209 210 unittest 211 { 212 char[HttpTimeFormatter.ResultLength] buf; 213 test (HttpTimeFormatter.format(buf, 352716457) == "Fri, 06 Mar 1981 08:47:37 GMT"); 214 }