The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
format_timespan.hpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 - 2025
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13*/
14
15#pragma once
16
19
20#include <array>
21#include <chrono>
22#include <string>
23#include <vector>
24
25namespace utils
26{
27
28/**
29 * Formats a timespan into human-readable text for player authentication functions.
30 *
31 * This is generally meant for player-facing text rather than lightweight tasks like
32 * debug logging. The resulting output may differ based on current language settings.
33 *
34 * This is intentionally not a very thorough representation of time intervals.
35 * See <https://github.com/wesnoth/wesnoth/issues/6036> for more information.
36 *
37 * @param span The timespan to format
38 * @param detailed Whether to display more specific values such as "3 months, 2 days,
39 * 30 minutes, and 1 second". If not specified or set to @a false, the
40 * return value will ONLY include most significant time unit (e.g. "3
41 * months").
42 * @return A human-readable timespan description.
43 *
44 * @note The implementation formats the given timespan according to periods defined by
45 * the C++ chrono standard. As such, a year is defined as its average Gregorian
46 * length of 365.2425 days, while months are exactly 1/12 of a year. Furthermore,
47 * it doesn't take into account leap years or leap seconds. If you need to
48 * account for those, you are better off importing a new library and providing it
49 * with more specific information about the start and end times of the interval;
50 * otherwise your next best option is to hire a fortune teller to manually service
51 * your requests every time instead of this function.
52 */
53template<typename Rep, typename Period>
54static std::string format_timespan(const std::chrono::duration<Rep, Period>& span, bool detailed = false)
55{
56 if(span.count() <= 0) {
57 return _("timespan^expired");
58 }
59
60 std::vector<t_string> display_text;
61 auto [years, months, weeks, days, hours, minutes, seconds] = chrono::deconstruct_duration<chrono::years, chrono::months, chrono::weeks, chrono::days, std::chrono::hours, std::chrono::minutes, std::chrono::seconds>(span);
62
63 const auto push_description = [&display_text, detailed](const auto& time_component, const auto& fmt_singular, const auto& fmt_plural)
64 {
65 auto amount = time_component.count();
66 if(amount <= 0) {
67 return true; // Continue to next element
68 }
69
70 display_text.emplace_back(VNGETTEXT(fmt_singular, fmt_plural, amount, {{"num", std::to_string(amount)}}));
71 return detailed;
72 };
73
74 // TRANSLATORS: The "timespan^$num xxxxx" strings originating from the same file
75 // as the string with this comment MUST be translated following the usual rules
76 // for WML variable interpolation -- that is, without including or translating
77 // the caret^ prefix, and leaving the $num variable specification intact, since
78 // it is technically code. The only translatable natural word to be found here
79 // is the time unit (year, month, etc.) For example, for French you would
80 // translate "timespan^$num years" as "$num ans", thus allowing the game UI to
81 // generate output such as "39 ans" after variable interpolation.
82 push_description(years, N_n("timespan^$num year", "timespan^$num years" )) &&
83 push_description(months, N_n("timespan^$num month", "timespan^$num months" )) &&
84 push_description(weeks, N_n("timespan^$num week", "timespan^$num weeks" )) &&
85 push_description(days, N_n("timespan^$num day", "timespan^$num days" )) &&
86 push_description(hours, N_n("timespan^$num hour", "timespan^$num hours" )) &&
87 push_description(minutes, N_n("timespan^$num minute", "timespan^$num minutes")) &&
88 push_description(seconds, N_n("timespan^$num second", "timespan^$num seconds"));
89
90 return format_conjunct_list(_("timespan^expired"), display_text);
91}
92
93} // namespace utils
#define VNGETTEXT(msgid, msgid_plural, count,...)
#define N_n(String1, String2)
Definition gettext.hpp:109
static std::string _(const char *str)
Definition gettext.hpp:100
static std::string format_timespan(const std::chrono::duration< Rep, Period > &span, bool detailed=false)
Formats a timespan into human-readable text for player authentication functions.
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.