“Pretty” date generator
I have this "pretty" date string generator in C# - pass it a date and it returns a string with "5 minutes ago" or "2 weeks, 3 days ago", etc.
It's a little verbose and hogs 61 lines, and I'm wondering if I'm missing out on some nice C# features or something. What (if any) is the best way to clean up this code? Are there any cool c# features I can use here?
public static string getTimeSpan(DateTime postDate)
{
string stringy = "";
TimeSpan diff = DateTime.Now.Subtract(postDate);
double years = Math.Floor(diff.TotalDays / 365);
double weeks = Math.Floor(diff.TotalDays / 7);
double days = diff.Days;
double hours = diff.Hours + days * 24;
double minutes = diff.Minutes + hours * 60;
if (minutes <= 1) {
stringy = "Just Now";
} else if (years >= 1) {
if (years >= 2) {
stringy = years.ToString() + " years ago";
} else {
stringy = "1 year ago";
}
} else if (weeks >= 1) {
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
} else if (days >= 1) {
if ((hours - days * 24) > 0) {
if ((hours - days * 24) > 1) {
stringy = ", " + (hours - days * 24).ToString() + " hours";
} else {
stringy = ", " + (hours - days * 24).ToString() + " hour";
}
}
if (days >= 2) {
stringy = days.ToString() + " days" + stringy + " ago";
} else {
stringy = "1 day" + stringy + " ago";
}
} else if (hours >= 1) {
if ((minutes - hours * 60) > 0) {
if ((minutes - hours * 60) > 1) {
stringy = ", " + (minutes - hours * 60).ToString() + " minutes";
} else {
stringy = ", " + (minutes - hours * 60).ToString() + " minute";
}
}
if (hours >= 2) {
stringy = hours.ToString() + " hours" + stringy + " ago";
} else {
stringy = "1 hour" + stringy + " ago";
}
} else if (minutes > 1) {
stringy = minutes.ToString() + " minutes ago";
}
return stringy;
}
c# datetime
add a comment |
I have this "pretty" date string generator in C# - pass it a date and it returns a string with "5 minutes ago" or "2 weeks, 3 days ago", etc.
It's a little verbose and hogs 61 lines, and I'm wondering if I'm missing out on some nice C# features or something. What (if any) is the best way to clean up this code? Are there any cool c# features I can use here?
public static string getTimeSpan(DateTime postDate)
{
string stringy = "";
TimeSpan diff = DateTime.Now.Subtract(postDate);
double years = Math.Floor(diff.TotalDays / 365);
double weeks = Math.Floor(diff.TotalDays / 7);
double days = diff.Days;
double hours = diff.Hours + days * 24;
double minutes = diff.Minutes + hours * 60;
if (minutes <= 1) {
stringy = "Just Now";
} else if (years >= 1) {
if (years >= 2) {
stringy = years.ToString() + " years ago";
} else {
stringy = "1 year ago";
}
} else if (weeks >= 1) {
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
} else if (days >= 1) {
if ((hours - days * 24) > 0) {
if ((hours - days * 24) > 1) {
stringy = ", " + (hours - days * 24).ToString() + " hours";
} else {
stringy = ", " + (hours - days * 24).ToString() + " hour";
}
}
if (days >= 2) {
stringy = days.ToString() + " days" + stringy + " ago";
} else {
stringy = "1 day" + stringy + " ago";
}
} else if (hours >= 1) {
if ((minutes - hours * 60) > 0) {
if ((minutes - hours * 60) > 1) {
stringy = ", " + (minutes - hours * 60).ToString() + " minutes";
} else {
stringy = ", " + (minutes - hours * 60).ToString() + " minute";
}
}
if (hours >= 2) {
stringy = hours.ToString() + " hours" + stringy + " ago";
} else {
stringy = "1 hour" + stringy + " ago";
}
} else if (minutes > 1) {
stringy = minutes.ToString() + " minutes ago";
}
return stringy;
}
c# datetime
add a comment |
I have this "pretty" date string generator in C# - pass it a date and it returns a string with "5 minutes ago" or "2 weeks, 3 days ago", etc.
It's a little verbose and hogs 61 lines, and I'm wondering if I'm missing out on some nice C# features or something. What (if any) is the best way to clean up this code? Are there any cool c# features I can use here?
public static string getTimeSpan(DateTime postDate)
{
string stringy = "";
TimeSpan diff = DateTime.Now.Subtract(postDate);
double years = Math.Floor(diff.TotalDays / 365);
double weeks = Math.Floor(diff.TotalDays / 7);
double days = diff.Days;
double hours = diff.Hours + days * 24;
double minutes = diff.Minutes + hours * 60;
if (minutes <= 1) {
stringy = "Just Now";
} else if (years >= 1) {
if (years >= 2) {
stringy = years.ToString() + " years ago";
} else {
stringy = "1 year ago";
}
} else if (weeks >= 1) {
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
} else if (days >= 1) {
if ((hours - days * 24) > 0) {
if ((hours - days * 24) > 1) {
stringy = ", " + (hours - days * 24).ToString() + " hours";
} else {
stringy = ", " + (hours - days * 24).ToString() + " hour";
}
}
if (days >= 2) {
stringy = days.ToString() + " days" + stringy + " ago";
} else {
stringy = "1 day" + stringy + " ago";
}
} else if (hours >= 1) {
if ((minutes - hours * 60) > 0) {
if ((minutes - hours * 60) > 1) {
stringy = ", " + (minutes - hours * 60).ToString() + " minutes";
} else {
stringy = ", " + (minutes - hours * 60).ToString() + " minute";
}
}
if (hours >= 2) {
stringy = hours.ToString() + " hours" + stringy + " ago";
} else {
stringy = "1 hour" + stringy + " ago";
}
} else if (minutes > 1) {
stringy = minutes.ToString() + " minutes ago";
}
return stringy;
}
c# datetime
I have this "pretty" date string generator in C# - pass it a date and it returns a string with "5 minutes ago" or "2 weeks, 3 days ago", etc.
It's a little verbose and hogs 61 lines, and I'm wondering if I'm missing out on some nice C# features or something. What (if any) is the best way to clean up this code? Are there any cool c# features I can use here?
public static string getTimeSpan(DateTime postDate)
{
string stringy = "";
TimeSpan diff = DateTime.Now.Subtract(postDate);
double years = Math.Floor(diff.TotalDays / 365);
double weeks = Math.Floor(diff.TotalDays / 7);
double days = diff.Days;
double hours = diff.Hours + days * 24;
double minutes = diff.Minutes + hours * 60;
if (minutes <= 1) {
stringy = "Just Now";
} else if (years >= 1) {
if (years >= 2) {
stringy = years.ToString() + " years ago";
} else {
stringy = "1 year ago";
}
} else if (weeks >= 1) {
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
} else if (days >= 1) {
if ((hours - days * 24) > 0) {
if ((hours - days * 24) > 1) {
stringy = ", " + (hours - days * 24).ToString() + " hours";
} else {
stringy = ", " + (hours - days * 24).ToString() + " hour";
}
}
if (days >= 2) {
stringy = days.ToString() + " days" + stringy + " ago";
} else {
stringy = "1 day" + stringy + " ago";
}
} else if (hours >= 1) {
if ((minutes - hours * 60) > 0) {
if ((minutes - hours * 60) > 1) {
stringy = ", " + (minutes - hours * 60).ToString() + " minutes";
} else {
stringy = ", " + (minutes - hours * 60).ToString() + " minute";
}
}
if (hours >= 2) {
stringy = hours.ToString() + " hours" + stringy + " ago";
} else {
stringy = "1 hour" + stringy + " ago";
}
} else if (minutes > 1) {
stringy = minutes.ToString() + " minutes ago";
}
return stringy;
}
c# datetime
c# datetime
edited Mar 6 '15 at 13:30
Jamal♦
30.2k11116226
30.2k11116226
asked May 31 '11 at 16:51
Thomas Shields
238313
238313
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
- Use PascalCase for the method name
- Move the calc for years and months lower to be minutely "more efficient"
- Use inline
return
to reduces nesting - Use ternary operator (
?:
) for simple logic to reduceif/else
clutter - Use the
format
override ofToString(string format)
to reduce string concats - Use
string.Format
with a ternary to reduce duplication
The shorter version I came up with is 40 lines, but you can judge if it readable enough.
public static string GetTimeSpan(DateTime postDate) {
string stringy = string.Empty;
TimeSpan diff = DateTime.Now.Subtract(postDate);
double days = diff.Days;
double hours = diff.Hours + days*24;
double minutes = diff.Minutes + hours*60;
if (minutes <= 1) {
return "Just Now";
}
double years = Math.Floor(diff.TotalDays/365);
if (years >= 1) {
return string.Format("{0} year{1} ago", years, years >= 2 ? "s" : null);
}
double weeks = Math.Floor(diff.TotalDays/7);
if (weeks >= 1) {
double partOfWeek = days - weeks*7;
if (partOfWeek > 0) {
stringy = string.Format(", {0} day{1}", partOfWeek, partOfWeek > 1 ? "s" : null);
}
return string.Format("{0} week{1}{2} ago", weeks, weeks >= 2 ? "s" : null, stringy);
}
if (days >= 1) {
double partOfDay = hours - days*24;
if (partOfDay > 0) {
stringy = string.Format(", {0} hour{1}", partOfDay, partOfDay > 1 ? "s" : null);
}
return string.Format("{0} day{1}{2} ago", days, days >= 2 ? "s" : null, stringy);
}
if (hours >= 1) {
double partOfHour = minutes - hours*60;
if (partOfHour > 0) {
stringy = string.Format(", {0} minute{1}", partOfHour, partOfHour > 1 ? "s" : null);
}
return string.Format("{0} hour{1}{2} ago", hours, hours >= 2 ? "s" : null, stringy);
}
// Only condition left is minutes > 1
return minutes.ToString("{0} minutes ago");
}
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove lastreturn
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which isstring.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
add a comment |
Some things are repeated:
days - weeks * 7
hours - days * 24
minutes - hours * 60
These can and should be made into their own variables - but what you are really after seems to be
days % 7
hours % 24
minutes % 60
You can replace:
double hours = diff.Hours + days * 24;
with
double hours = diff.TotalHours;
There is also a TotalMinutes. You can just use the Math.Floor() of these values to get an int.
I see that you are going for a single exit point for this function, but I think that readability would be improved if you got some of the simpler paths shorter:
if (minutes <= 1)
return "Just Now";
if (years >= 1) {
if (years >= 2) {
return years.ToString() + " years ago";
} else {
return "1 year ago";
}
EDIT to add:
There's a repeated block of code that could be refactored to its own function:
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
The body of the extracted function would look like:
if (smallUnitCount > 0) {
if (smallUnitCount > 1) {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitPluralName);
} else {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitSingularName);
}
}
if (largeUnitCount >= 2) {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitPluralName, stringy);
} else {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitSingularName, stringy);
}
add a comment |
I would use casting double
to int
instead of Floor
in your case. Firstly because I'm a little bit cautious about equality comparison of doubles in years >= 1
. I would write it in this way:
int years = (int)(diff.TotalDays/365);
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
Yep, with positive doubles casting toint
doesFloor
exactly. See Explicit conversion section here. It has exactly your sample.
– Snowbear
May 31 '11 at 21:48
add a comment |
You can make it an extension, so you can do
string result = DateTime.Now.GetTimeSpan();
Here is how I did it a few time ago
/// <summary>
/// Provide extentions for the DateTime Object.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets the relative time for a datetime.
/// </summary>
/// <param name="dateTime">The datetime to get the relative time.</param>
/// <returns>A relative time in english.</returns>
public static string GetTimeSpan(this DateTime dateTime)
{
TimeSpan diff = DateTime.Now.Subtract(dateTime);
if (diff.TotalMinutes < 1)
{
return string.Format("{0:D2} second{1} ago", diff.Seconds, PluralizeIfNeeded(diff.Seconds));
}
if (diff.TotalHours < 1)
{
return string.Format("{0:D2} minute{1} ago", diff.Minutes, PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays < 1)
{
return string.Format("{0:D2} hour{2} and {1:D2} minute{3} ago", diff.Hours, diff.Minutes, PluralizeIfNeeded(diff.Hours), PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 2)
{
return string.Format(
"{0:D2} day{3}, {1:D2} hour{4} and {2:D2} minute{5} ago",
diff.Days,
diff.Hours,
diff.Minutes,
PluralizeIfNeeded(diff.Days),
PluralizeIfNeeded(diff.Hours),
PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 30)
{
return string.Format("{0:D2} days ago", diff.TotalDays);
}
return string.Format("{0:g}", dateTime);
}
/// <summary>
/// Gets a 's' if value is > 1.
/// </summary>
/// <param name="testValue">The value to test.</param>
/// <returns>An 's' if value is > 1, otherwise an empty string.</returns>
private static string PluralizeIfNeeded(int testValue)
{
return testValue > 1 ? "s" : string.Empty;
}
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f2738%2fpretty-date-generator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
- Use PascalCase for the method name
- Move the calc for years and months lower to be minutely "more efficient"
- Use inline
return
to reduces nesting - Use ternary operator (
?:
) for simple logic to reduceif/else
clutter - Use the
format
override ofToString(string format)
to reduce string concats - Use
string.Format
with a ternary to reduce duplication
The shorter version I came up with is 40 lines, but you can judge if it readable enough.
public static string GetTimeSpan(DateTime postDate) {
string stringy = string.Empty;
TimeSpan diff = DateTime.Now.Subtract(postDate);
double days = diff.Days;
double hours = diff.Hours + days*24;
double minutes = diff.Minutes + hours*60;
if (minutes <= 1) {
return "Just Now";
}
double years = Math.Floor(diff.TotalDays/365);
if (years >= 1) {
return string.Format("{0} year{1} ago", years, years >= 2 ? "s" : null);
}
double weeks = Math.Floor(diff.TotalDays/7);
if (weeks >= 1) {
double partOfWeek = days - weeks*7;
if (partOfWeek > 0) {
stringy = string.Format(", {0} day{1}", partOfWeek, partOfWeek > 1 ? "s" : null);
}
return string.Format("{0} week{1}{2} ago", weeks, weeks >= 2 ? "s" : null, stringy);
}
if (days >= 1) {
double partOfDay = hours - days*24;
if (partOfDay > 0) {
stringy = string.Format(", {0} hour{1}", partOfDay, partOfDay > 1 ? "s" : null);
}
return string.Format("{0} day{1}{2} ago", days, days >= 2 ? "s" : null, stringy);
}
if (hours >= 1) {
double partOfHour = minutes - hours*60;
if (partOfHour > 0) {
stringy = string.Format(", {0} minute{1}", partOfHour, partOfHour > 1 ? "s" : null);
}
return string.Format("{0} hour{1}{2} ago", hours, hours >= 2 ? "s" : null, stringy);
}
// Only condition left is minutes > 1
return minutes.ToString("{0} minutes ago");
}
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove lastreturn
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which isstring.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
add a comment |
- Use PascalCase for the method name
- Move the calc for years and months lower to be minutely "more efficient"
- Use inline
return
to reduces nesting - Use ternary operator (
?:
) for simple logic to reduceif/else
clutter - Use the
format
override ofToString(string format)
to reduce string concats - Use
string.Format
with a ternary to reduce duplication
The shorter version I came up with is 40 lines, but you can judge if it readable enough.
public static string GetTimeSpan(DateTime postDate) {
string stringy = string.Empty;
TimeSpan diff = DateTime.Now.Subtract(postDate);
double days = diff.Days;
double hours = diff.Hours + days*24;
double minutes = diff.Minutes + hours*60;
if (minutes <= 1) {
return "Just Now";
}
double years = Math.Floor(diff.TotalDays/365);
if (years >= 1) {
return string.Format("{0} year{1} ago", years, years >= 2 ? "s" : null);
}
double weeks = Math.Floor(diff.TotalDays/7);
if (weeks >= 1) {
double partOfWeek = days - weeks*7;
if (partOfWeek > 0) {
stringy = string.Format(", {0} day{1}", partOfWeek, partOfWeek > 1 ? "s" : null);
}
return string.Format("{0} week{1}{2} ago", weeks, weeks >= 2 ? "s" : null, stringy);
}
if (days >= 1) {
double partOfDay = hours - days*24;
if (partOfDay > 0) {
stringy = string.Format(", {0} hour{1}", partOfDay, partOfDay > 1 ? "s" : null);
}
return string.Format("{0} day{1}{2} ago", days, days >= 2 ? "s" : null, stringy);
}
if (hours >= 1) {
double partOfHour = minutes - hours*60;
if (partOfHour > 0) {
stringy = string.Format(", {0} minute{1}", partOfHour, partOfHour > 1 ? "s" : null);
}
return string.Format("{0} hour{1}{2} ago", hours, hours >= 2 ? "s" : null, stringy);
}
// Only condition left is minutes > 1
return minutes.ToString("{0} minutes ago");
}
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove lastreturn
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which isstring.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
add a comment |
- Use PascalCase for the method name
- Move the calc for years and months lower to be minutely "more efficient"
- Use inline
return
to reduces nesting - Use ternary operator (
?:
) for simple logic to reduceif/else
clutter - Use the
format
override ofToString(string format)
to reduce string concats - Use
string.Format
with a ternary to reduce duplication
The shorter version I came up with is 40 lines, but you can judge if it readable enough.
public static string GetTimeSpan(DateTime postDate) {
string stringy = string.Empty;
TimeSpan diff = DateTime.Now.Subtract(postDate);
double days = diff.Days;
double hours = diff.Hours + days*24;
double minutes = diff.Minutes + hours*60;
if (minutes <= 1) {
return "Just Now";
}
double years = Math.Floor(diff.TotalDays/365);
if (years >= 1) {
return string.Format("{0} year{1} ago", years, years >= 2 ? "s" : null);
}
double weeks = Math.Floor(diff.TotalDays/7);
if (weeks >= 1) {
double partOfWeek = days - weeks*7;
if (partOfWeek > 0) {
stringy = string.Format(", {0} day{1}", partOfWeek, partOfWeek > 1 ? "s" : null);
}
return string.Format("{0} week{1}{2} ago", weeks, weeks >= 2 ? "s" : null, stringy);
}
if (days >= 1) {
double partOfDay = hours - days*24;
if (partOfDay > 0) {
stringy = string.Format(", {0} hour{1}", partOfDay, partOfDay > 1 ? "s" : null);
}
return string.Format("{0} day{1}{2} ago", days, days >= 2 ? "s" : null, stringy);
}
if (hours >= 1) {
double partOfHour = minutes - hours*60;
if (partOfHour > 0) {
stringy = string.Format(", {0} minute{1}", partOfHour, partOfHour > 1 ? "s" : null);
}
return string.Format("{0} hour{1}{2} ago", hours, hours >= 2 ? "s" : null, stringy);
}
// Only condition left is minutes > 1
return minutes.ToString("{0} minutes ago");
}
- Use PascalCase for the method name
- Move the calc for years and months lower to be minutely "more efficient"
- Use inline
return
to reduces nesting - Use ternary operator (
?:
) for simple logic to reduceif/else
clutter - Use the
format
override ofToString(string format)
to reduce string concats - Use
string.Format
with a ternary to reduce duplication
The shorter version I came up with is 40 lines, but you can judge if it readable enough.
public static string GetTimeSpan(DateTime postDate) {
string stringy = string.Empty;
TimeSpan diff = DateTime.Now.Subtract(postDate);
double days = diff.Days;
double hours = diff.Hours + days*24;
double minutes = diff.Minutes + hours*60;
if (minutes <= 1) {
return "Just Now";
}
double years = Math.Floor(diff.TotalDays/365);
if (years >= 1) {
return string.Format("{0} year{1} ago", years, years >= 2 ? "s" : null);
}
double weeks = Math.Floor(diff.TotalDays/7);
if (weeks >= 1) {
double partOfWeek = days - weeks*7;
if (partOfWeek > 0) {
stringy = string.Format(", {0} day{1}", partOfWeek, partOfWeek > 1 ? "s" : null);
}
return string.Format("{0} week{1}{2} ago", weeks, weeks >= 2 ? "s" : null, stringy);
}
if (days >= 1) {
double partOfDay = hours - days*24;
if (partOfDay > 0) {
stringy = string.Format(", {0} hour{1}", partOfDay, partOfDay > 1 ? "s" : null);
}
return string.Format("{0} day{1}{2} ago", days, days >= 2 ? "s" : null, stringy);
}
if (hours >= 1) {
double partOfHour = minutes - hours*60;
if (partOfHour > 0) {
stringy = string.Format(", {0} minute{1}", partOfHour, partOfHour > 1 ? "s" : null);
}
return string.Format("{0} hour{1}{2} ago", hours, hours >= 2 ? "s" : null, stringy);
}
// Only condition left is minutes > 1
return minutes.ToString("{0} minutes ago");
}
edited Mar 6 '15 at 13:25
Heslacher
44.8k460155
44.8k460155
answered May 31 '11 at 20:37
Ed Chapel
29615
29615
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove lastreturn
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which isstring.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
add a comment |
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove lastreturn
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which isstring.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
wow thanks! I didn't even know about half these operators and methods and such!
– Thomas Shields
May 31 '11 at 20:46
It is important to remove last
return
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which is string.Empty
– Snowbear
May 31 '11 at 22:05
It is important to remove last
return
in such cases so compiler will verify that all cases are checked and handled and there is no scenario when this method will return initial value which is string.Empty
– Snowbear
May 31 '11 at 22:05
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
Updated the last line to be more explicit with the final return.
– Ed Chapel
May 31 '11 at 22:30
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
How about future dates? :p
– Chibueze Opata
Sep 15 '15 at 19:03
add a comment |
Some things are repeated:
days - weeks * 7
hours - days * 24
minutes - hours * 60
These can and should be made into their own variables - but what you are really after seems to be
days % 7
hours % 24
minutes % 60
You can replace:
double hours = diff.Hours + days * 24;
with
double hours = diff.TotalHours;
There is also a TotalMinutes. You can just use the Math.Floor() of these values to get an int.
I see that you are going for a single exit point for this function, but I think that readability would be improved if you got some of the simpler paths shorter:
if (minutes <= 1)
return "Just Now";
if (years >= 1) {
if (years >= 2) {
return years.ToString() + " years ago";
} else {
return "1 year ago";
}
EDIT to add:
There's a repeated block of code that could be refactored to its own function:
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
The body of the extracted function would look like:
if (smallUnitCount > 0) {
if (smallUnitCount > 1) {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitPluralName);
} else {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitSingularName);
}
}
if (largeUnitCount >= 2) {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitPluralName, stringy);
} else {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitSingularName, stringy);
}
add a comment |
Some things are repeated:
days - weeks * 7
hours - days * 24
minutes - hours * 60
These can and should be made into their own variables - but what you are really after seems to be
days % 7
hours % 24
minutes % 60
You can replace:
double hours = diff.Hours + days * 24;
with
double hours = diff.TotalHours;
There is also a TotalMinutes. You can just use the Math.Floor() of these values to get an int.
I see that you are going for a single exit point for this function, but I think that readability would be improved if you got some of the simpler paths shorter:
if (minutes <= 1)
return "Just Now";
if (years >= 1) {
if (years >= 2) {
return years.ToString() + " years ago";
} else {
return "1 year ago";
}
EDIT to add:
There's a repeated block of code that could be refactored to its own function:
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
The body of the extracted function would look like:
if (smallUnitCount > 0) {
if (smallUnitCount > 1) {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitPluralName);
} else {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitSingularName);
}
}
if (largeUnitCount >= 2) {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitPluralName, stringy);
} else {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitSingularName, stringy);
}
add a comment |
Some things are repeated:
days - weeks * 7
hours - days * 24
minutes - hours * 60
These can and should be made into their own variables - but what you are really after seems to be
days % 7
hours % 24
minutes % 60
You can replace:
double hours = diff.Hours + days * 24;
with
double hours = diff.TotalHours;
There is also a TotalMinutes. You can just use the Math.Floor() of these values to get an int.
I see that you are going for a single exit point for this function, but I think that readability would be improved if you got some of the simpler paths shorter:
if (minutes <= 1)
return "Just Now";
if (years >= 1) {
if (years >= 2) {
return years.ToString() + " years ago";
} else {
return "1 year ago";
}
EDIT to add:
There's a repeated block of code that could be refactored to its own function:
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
The body of the extracted function would look like:
if (smallUnitCount > 0) {
if (smallUnitCount > 1) {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitPluralName);
} else {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitSingularName);
}
}
if (largeUnitCount >= 2) {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitPluralName, stringy);
} else {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitSingularName, stringy);
}
Some things are repeated:
days - weeks * 7
hours - days * 24
minutes - hours * 60
These can and should be made into their own variables - but what you are really after seems to be
days % 7
hours % 24
minutes % 60
You can replace:
double hours = diff.Hours + days * 24;
with
double hours = diff.TotalHours;
There is also a TotalMinutes. You can just use the Math.Floor() of these values to get an int.
I see that you are going for a single exit point for this function, but I think that readability would be improved if you got some of the simpler paths shorter:
if (minutes <= 1)
return "Just Now";
if (years >= 1) {
if (years >= 2) {
return years.ToString() + " years ago";
} else {
return "1 year ago";
}
EDIT to add:
There's a repeated block of code that could be refactored to its own function:
if ((days - weeks * 7) > 0) {
if ((days - weeks * 7) > 1) {
stringy = ", " + (days - weeks * 7).ToString() + " days";
} else {
stringy = ", " + (days - weeks * 7).ToString() + " day";
}
}
if (weeks >= 2) {
stringy = weeks.ToString() + " weeks" + stringy + " ago";
} else {
stringy = "1 week" + stringy + " ago";
}
The body of the extracted function would look like:
if (smallUnitCount > 0) {
if (smallUnitCount > 1) {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitPluralName);
} else {
stringy = String.Format(", {0} {1}", smallUnitCount.ToString() , smallUnitSingularName);
}
}
if (largeUnitCount >= 2) {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitPluralName, stringy);
} else {
stringy = String.Format("{0} {1}{2} ago", largeUnitCount.ToString, largeUnitSingularName, stringy);
}
edited May 31 '11 at 20:36
answered May 31 '11 at 19:49
Jeff Paulsen
31114
31114
add a comment |
add a comment |
I would use casting double
to int
instead of Floor
in your case. Firstly because I'm a little bit cautious about equality comparison of doubles in years >= 1
. I would write it in this way:
int years = (int)(diff.TotalDays/365);
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
Yep, with positive doubles casting toint
doesFloor
exactly. See Explicit conversion section here. It has exactly your sample.
– Snowbear
May 31 '11 at 21:48
add a comment |
I would use casting double
to int
instead of Floor
in your case. Firstly because I'm a little bit cautious about equality comparison of doubles in years >= 1
. I would write it in this way:
int years = (int)(diff.TotalDays/365);
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
Yep, with positive doubles casting toint
doesFloor
exactly. See Explicit conversion section here. It has exactly your sample.
– Snowbear
May 31 '11 at 21:48
add a comment |
I would use casting double
to int
instead of Floor
in your case. Firstly because I'm a little bit cautious about equality comparison of doubles in years >= 1
. I would write it in this way:
int years = (int)(diff.TotalDays/365);
I would use casting double
to int
instead of Floor
in your case. Firstly because I'm a little bit cautious about equality comparison of doubles in years >= 1
. I would write it in this way:
int years = (int)(diff.TotalDays/365);
answered May 31 '11 at 21:30
Snowbear
3,50311323
3,50311323
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
Yep, with positive doubles casting toint
doesFloor
exactly. See Explicit conversion section here. It has exactly your sample.
– Snowbear
May 31 '11 at 21:48
add a comment |
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
Yep, with positive doubles casting toint
doesFloor
exactly. See Explicit conversion section here. It has exactly your sample.
– Snowbear
May 31 '11 at 21:48
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
would doing so accomplish the same as Floor? that is, would casting .8 to an int return 0? Because that's important (i don't want .8 to round to 1 and have the function say that it's been a year when it's only really been 80% of a year)
– Thomas Shields
May 31 '11 at 21:35
1
1
Yep, with positive doubles casting to
int
does Floor
exactly. See Explicit conversion section here. It has exactly your sample.– Snowbear
May 31 '11 at 21:48
Yep, with positive doubles casting to
int
does Floor
exactly. See Explicit conversion section here. It has exactly your sample.– Snowbear
May 31 '11 at 21:48
add a comment |
You can make it an extension, so you can do
string result = DateTime.Now.GetTimeSpan();
Here is how I did it a few time ago
/// <summary>
/// Provide extentions for the DateTime Object.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets the relative time for a datetime.
/// </summary>
/// <param name="dateTime">The datetime to get the relative time.</param>
/// <returns>A relative time in english.</returns>
public static string GetTimeSpan(this DateTime dateTime)
{
TimeSpan diff = DateTime.Now.Subtract(dateTime);
if (diff.TotalMinutes < 1)
{
return string.Format("{0:D2} second{1} ago", diff.Seconds, PluralizeIfNeeded(diff.Seconds));
}
if (diff.TotalHours < 1)
{
return string.Format("{0:D2} minute{1} ago", diff.Minutes, PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays < 1)
{
return string.Format("{0:D2} hour{2} and {1:D2} minute{3} ago", diff.Hours, diff.Minutes, PluralizeIfNeeded(diff.Hours), PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 2)
{
return string.Format(
"{0:D2} day{3}, {1:D2} hour{4} and {2:D2} minute{5} ago",
diff.Days,
diff.Hours,
diff.Minutes,
PluralizeIfNeeded(diff.Days),
PluralizeIfNeeded(diff.Hours),
PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 30)
{
return string.Format("{0:D2} days ago", diff.TotalDays);
}
return string.Format("{0:g}", dateTime);
}
/// <summary>
/// Gets a 's' if value is > 1.
/// </summary>
/// <param name="testValue">The value to test.</param>
/// <returns>An 's' if value is > 1, otherwise an empty string.</returns>
private static string PluralizeIfNeeded(int testValue)
{
return testValue > 1 ? "s" : string.Empty;
}
}
add a comment |
You can make it an extension, so you can do
string result = DateTime.Now.GetTimeSpan();
Here is how I did it a few time ago
/// <summary>
/// Provide extentions for the DateTime Object.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets the relative time for a datetime.
/// </summary>
/// <param name="dateTime">The datetime to get the relative time.</param>
/// <returns>A relative time in english.</returns>
public static string GetTimeSpan(this DateTime dateTime)
{
TimeSpan diff = DateTime.Now.Subtract(dateTime);
if (diff.TotalMinutes < 1)
{
return string.Format("{0:D2} second{1} ago", diff.Seconds, PluralizeIfNeeded(diff.Seconds));
}
if (diff.TotalHours < 1)
{
return string.Format("{0:D2} minute{1} ago", diff.Minutes, PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays < 1)
{
return string.Format("{0:D2} hour{2} and {1:D2} minute{3} ago", diff.Hours, diff.Minutes, PluralizeIfNeeded(diff.Hours), PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 2)
{
return string.Format(
"{0:D2} day{3}, {1:D2} hour{4} and {2:D2} minute{5} ago",
diff.Days,
diff.Hours,
diff.Minutes,
PluralizeIfNeeded(diff.Days),
PluralizeIfNeeded(diff.Hours),
PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 30)
{
return string.Format("{0:D2} days ago", diff.TotalDays);
}
return string.Format("{0:g}", dateTime);
}
/// <summary>
/// Gets a 's' if value is > 1.
/// </summary>
/// <param name="testValue">The value to test.</param>
/// <returns>An 's' if value is > 1, otherwise an empty string.</returns>
private static string PluralizeIfNeeded(int testValue)
{
return testValue > 1 ? "s" : string.Empty;
}
}
add a comment |
You can make it an extension, so you can do
string result = DateTime.Now.GetTimeSpan();
Here is how I did it a few time ago
/// <summary>
/// Provide extentions for the DateTime Object.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets the relative time for a datetime.
/// </summary>
/// <param name="dateTime">The datetime to get the relative time.</param>
/// <returns>A relative time in english.</returns>
public static string GetTimeSpan(this DateTime dateTime)
{
TimeSpan diff = DateTime.Now.Subtract(dateTime);
if (diff.TotalMinutes < 1)
{
return string.Format("{0:D2} second{1} ago", diff.Seconds, PluralizeIfNeeded(diff.Seconds));
}
if (diff.TotalHours < 1)
{
return string.Format("{0:D2} minute{1} ago", diff.Minutes, PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays < 1)
{
return string.Format("{0:D2} hour{2} and {1:D2} minute{3} ago", diff.Hours, diff.Minutes, PluralizeIfNeeded(diff.Hours), PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 2)
{
return string.Format(
"{0:D2} day{3}, {1:D2} hour{4} and {2:D2} minute{5} ago",
diff.Days,
diff.Hours,
diff.Minutes,
PluralizeIfNeeded(diff.Days),
PluralizeIfNeeded(diff.Hours),
PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 30)
{
return string.Format("{0:D2} days ago", diff.TotalDays);
}
return string.Format("{0:g}", dateTime);
}
/// <summary>
/// Gets a 's' if value is > 1.
/// </summary>
/// <param name="testValue">The value to test.</param>
/// <returns>An 's' if value is > 1, otherwise an empty string.</returns>
private static string PluralizeIfNeeded(int testValue)
{
return testValue > 1 ? "s" : string.Empty;
}
}
You can make it an extension, so you can do
string result = DateTime.Now.GetTimeSpan();
Here is how I did it a few time ago
/// <summary>
/// Provide extentions for the DateTime Object.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets the relative time for a datetime.
/// </summary>
/// <param name="dateTime">The datetime to get the relative time.</param>
/// <returns>A relative time in english.</returns>
public static string GetTimeSpan(this DateTime dateTime)
{
TimeSpan diff = DateTime.Now.Subtract(dateTime);
if (diff.TotalMinutes < 1)
{
return string.Format("{0:D2} second{1} ago", diff.Seconds, PluralizeIfNeeded(diff.Seconds));
}
if (diff.TotalHours < 1)
{
return string.Format("{0:D2} minute{1} ago", diff.Minutes, PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays < 1)
{
return string.Format("{0:D2} hour{2} and {1:D2} minute{3} ago", diff.Hours, diff.Minutes, PluralizeIfNeeded(diff.Hours), PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 2)
{
return string.Format(
"{0:D2} day{3}, {1:D2} hour{4} and {2:D2} minute{5} ago",
diff.Days,
diff.Hours,
diff.Minutes,
PluralizeIfNeeded(diff.Days),
PluralizeIfNeeded(diff.Hours),
PluralizeIfNeeded(diff.Minutes));
}
if (diff.TotalDays <= 30)
{
return string.Format("{0:D2} days ago", diff.TotalDays);
}
return string.Format("{0:g}", dateTime);
}
/// <summary>
/// Gets a 's' if value is > 1.
/// </summary>
/// <param name="testValue">The value to test.</param>
/// <returns>An 's' if value is > 1, otherwise an empty string.</returns>
private static string PluralizeIfNeeded(int testValue)
{
return testValue > 1 ? "s" : string.Empty;
}
}
answered Jun 1 '11 at 9:48
Sam
11
11
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f2738%2fpretty-date-generator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown