Serenity Operating System
at hosted 287 lines 9.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/StringBuilder.h> 28#include <LibCore/DateTime.h> 29#include <sys/time.h> 30#include <time.h> 31 32namespace Core { 33 34DateTime DateTime::now() 35{ 36 return from_timestamp(time(nullptr)); 37} 38 39DateTime DateTime::create(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute, unsigned second) 40{ 41 DateTime dt; 42 dt.m_year = year; 43 dt.m_month = month; 44 dt.m_day = day; 45 dt.m_hour = hour; 46 dt.m_minute = minute; 47 dt.m_second = second; 48 49 struct tm tm = {}; 50 tm.tm_sec = (int)second; 51 tm.tm_min = (int)minute; 52 tm.tm_hour = (int)hour; 53 tm.tm_mday = (int)day; 54 tm.tm_mon = (int)month - 1; 55 tm.tm_year = (int)year - 1900; 56 tm.tm_wday = (int)dt.weekday(); 57 tm.tm_yday = (int)dt.day_of_year(); 58 dt.m_timestamp = mktime(&tm); 59 60 return dt; 61} 62 63DateTime DateTime::from_timestamp(time_t timestamp) 64{ 65 struct tm tm; 66 localtime_r(&timestamp, &tm); 67 DateTime dt; 68 dt.m_year = tm.tm_year + 1900; 69 dt.m_month = tm.tm_mon + 1; 70 dt.m_day = tm.tm_mday; 71 dt.m_hour = tm.tm_hour; 72 dt.m_minute = tm.tm_min; 73 dt.m_second = tm.tm_sec; 74 dt.m_timestamp = timestamp; 75 return dt; 76} 77 78unsigned DateTime::weekday() const 79{ 80 int target_year = m_year; 81 static const int seek_table[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 }; 82 if (m_month < 3) 83 --target_year; 84 85 return (target_year + target_year / 4 - target_year / 100 + target_year / 400 + seek_table[m_month - 1] + m_day) % 7; 86} 87 88unsigned DateTime::days_in_month() const 89{ 90 bool is_long_month = (m_month == 1 || m_month == 3 || m_month == 5 || m_month == 7 || m_month == 8 || m_month == 10 || m_month == 12); 91 92 if (m_month == 2) 93 return is_leap_year() ? 29 : 28; 94 95 return is_long_month ? 31 : 30; 96} 97 98unsigned DateTime::day_of_year() const 99{ 100 static const int seek_table[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 101 int day_of_year = seek_table[m_month - 1] + m_day; 102 103 if (is_leap_year() && m_month > 3) 104 day_of_year++; 105 106 return day_of_year - 1; 107} 108 109bool DateTime::is_leap_year() const 110{ 111 return ((m_year % 400 == 0) || (m_year % 4 == 0 && m_year % 100 != 0)); 112} 113 114void DateTime::set_time(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute, unsigned second) 115{ 116 m_year = year; 117 m_month = month; 118 m_day = day; 119 m_hour = hour; 120 m_minute = minute; 121 m_second = second; 122 123 struct tm tm = {}; 124 tm.tm_sec = (int)second; 125 tm.tm_min = (int)minute; 126 tm.tm_hour = (int)hour; 127 tm.tm_mday = (int)day; 128 tm.tm_mon = (int)month - 1; 129 tm.tm_year = (int)year - 1900; 130 tm.tm_wday = (int)weekday(); 131 tm.tm_yday = (int)day_of_year(); 132 m_timestamp = mktime(&tm); 133} 134 135String DateTime::to_string(const String& format) const 136{ 137 138 const char wday_short_names[7][4] = { 139 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 140 }; 141 const char wday_long_names[7][10] = { 142 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 143 }; 144 const char mon_short_names[12][4] = { 145 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 146 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 147 }; 148 const char mon_long_names[12][10] = { 149 "January", "February", "March", "April", "May", "June", 150 "July", "Auguest", "September", "October", "November", "December" 151 }; 152 153 struct tm tm; 154 localtime_r(&m_timestamp, &tm); 155 StringBuilder builder; 156 const int format_len = format.length(); 157 158 for (int i = 0; i < format_len; ++i) { 159 if (format[i] != '%') { 160 builder.append(format[i]); 161 } else { 162 if (++i == format_len) 163 return String(); 164 165 switch (format[i]) { 166 case 'a': 167 builder.append(wday_short_names[tm.tm_wday]); 168 break; 169 case 'A': 170 builder.append(wday_long_names[tm.tm_wday]); 171 break; 172 case 'b': 173 builder.append(mon_short_names[tm.tm_mon]); 174 break; 175 case 'B': 176 builder.append(mon_long_names[tm.tm_mon]); 177 break; 178 case 'C': 179 builder.appendf("%02d", (tm.tm_year + 1900) / 100); 180 break; 181 case 'd': 182 builder.appendf("%02d", tm.tm_mday); 183 break; 184 case 'D': 185 builder.appendf("%02d/%02d/%02d", tm.tm_mon + 1, tm.tm_mday, (tm.tm_year + 1900) % 100); 186 break; 187 case 'e': 188 builder.appendf("%2d", tm.tm_mday); 189 break; 190 case 'h': 191 builder.append(mon_short_names[tm.tm_mon]); 192 break; 193 case 'H': 194 builder.appendf("%02d", tm.tm_hour); 195 break; 196 case 'I': 197 builder.appendf("%02d", tm.tm_hour % 12); 198 break; 199 case 'j': 200 builder.appendf("%03d", tm.tm_yday + 1); 201 break; 202 case 'm': 203 builder.appendf("%02d", tm.tm_mon + 1); 204 break; 205 case 'M': 206 builder.appendf("%02d", tm.tm_min); 207 break; 208 case 'n': 209 builder.append('\n'); 210 break; 211 case 'p': 212 builder.append(tm.tm_hour < 12 ? "a.m." : "p.m."); 213 break; 214 case 'r': 215 builder.appendf("%02d:%02d:%02d %s", tm.tm_hour % 12, tm.tm_min, tm.tm_sec, tm.tm_hour < 12 ? "a.m." : "p.m."); 216 break; 217 case 'R': 218 builder.appendf("%02d:%02d", tm.tm_hour, tm.tm_min); 219 break; 220 case 'S': 221 builder.appendf("%02d", tm.tm_sec); 222 break; 223 case 't': 224 builder.append('\t'); 225 break; 226 case 'T': 227 builder.appendf("%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec); 228 break; 229 case 'u': 230 builder.appendf("%d", tm.tm_wday ? tm.tm_wday : 7); 231 break; 232 case 'U': { 233 const int wday_of_year_beginning = (tm.tm_wday + 6 * tm.tm_yday) % 7; 234 const int week_number = (tm.tm_yday + wday_of_year_beginning) / 7; 235 builder.appendf("%02d", week_number); 236 break; 237 } 238 case 'V': { 239 const int wday_of_year_beginning = (tm.tm_wday + 6 + 6 * tm.tm_yday) % 7; 240 int week_number = (tm.tm_yday + wday_of_year_beginning) / 7 + 1; 241 if (wday_of_year_beginning > 3) { 242 if (tm.tm_yday >= 7 - wday_of_year_beginning) 243 --week_number; 244 else { 245 const bool last_year_is_leap = ((tm.tm_year + 1900 - 1) % 4 == 0 && (tm.tm_year + 1900 - 1) % 100 != 0) || (tm.tm_year + 1900 - 1) % 400 == 0; 246 const int days_of_last_year = 365 + last_year_is_leap; 247 const int wday_of_last_year_beginning = (wday_of_year_beginning + 6 * days_of_last_year) % 7; 248 week_number = (days_of_last_year + wday_of_last_year_beginning) / 7 + 1; 249 if (wday_of_last_year_beginning > 3) 250 --week_number; 251 } 252 } 253 builder.appendf("%02d", week_number); 254 break; 255 } 256 case 'w': 257 builder.appendf("%d", tm.tm_wday); 258 break; 259 case 'W': { 260 const int wday_of_year_beginning = (tm.tm_wday + 6 + 6 * tm.tm_yday) % 7; 261 const int week_number = (tm.tm_yday + wday_of_year_beginning) / 7; 262 builder.appendf("%02d", week_number); 263 break; 264 } 265 case 'y': 266 builder.appendf("%02d", (tm.tm_year + 1900) % 100); 267 break; 268 case 'Y': 269 builder.appendf("%d", tm.tm_year + 1900); 270 break; 271 case '%': 272 builder.append('%'); 273 break; 274 default: 275 return String(); 276 } 277 } 278 } 279 280 return builder.build(); 281} 282 283const LogStream& operator<<(const LogStream& stream, const DateTime& value) 284{ 285 return stream << value.to_string(); 286} 287}