Semi-Monthly Date Calculation in C# - c#

I am trying to do calculations on date using "Semi-Monthly". How many days in semi month? AddMonths (0.5) does not work :)
Console.WriteLine("SemiMonthly : " + date.AddMonths(0.5)); //This does not work

I guess that adding half of a month means according to this month, then you could do:
public static DateTime AddHalfMonth(DateTime dt, MidpointRounding rounding)
{
int daysInMonth = System.DateTime.DaysInMonth(dt.Year, dt.Month);
if(daysInMonth % 2 == 0)
return dt.AddDays(daysInMonth / 2);
else if(rounding == MidpointRounding.ToEven)
return dt.AddDays(daysInMonth / 2);
else
return dt.AddDays((daysInMonth + 1) / 2);
}
You can use it in this way:
DateTime inHalfMonth = AddHalfMonth(date, MidpointRounding.ToEven);

If you want, you can add half days of the current month by doing this:
DateTime a = new DateTime();
a.AddDays(DateTime.DaysInMonth(a.Year, a.Month)/2);

You're going to have to define what exactly "semi-monthly" means, and in doing so, you'll answer your own question.
For simplicity, I would suggest you just use the first and 15th of each month.

You will need to define your own convention for this - there is no canonical notion of a "half month".

Borrowed some concepts from Tim's answer but the additional logic allows for a schedule to never deviate from the half-month mark. I had noticed that if I ran the function recursively starting from Jan 16 the 24th item was ending up Dec 20-something. I've simplified the function and made it an extension.
public static class DateTimeExtensions
{
public static DateTime AddHalfMonth(this DateTime dt)
{
int daysInMonth = System.DateTime.DaysInMonth(dt.Year, dt.Month);
if (daysInMonth % 2 == 0 || dt.Day < daysInMonth / 2)
{
return dt.AddDays(daysInMonth / 2);
}
return dt.AddDays((daysInMonth + 1) / 2);
}
}

Related

Looping around in Enumeration in C#

Is there some way to automatically loopback when the value of an Enumeration reaches the end while adding or subtracting values.
E.g. When using DateTime.DayOfWeek it is defined as:
public enum DayOfWeek
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}
I have a function where I'm checking if a string array contains yesterday.
DateTime rn = DateTime.Now;
string[] daysOfWeek = {"Monday", "Tuesday", "Saturday"};
if (daysOfWeek.Contains((rn.DayOfWeek - 1).ToString()))
...
If today is Sunday. Is there some way to make the rn.DayOfWeek enumeration to loop back around the beginning to Saturday in this case?
Similarly if I'm adding instead of subtracting, can one somehow loop around the end from Saturday (6) back to Sunday (0)?
You shouldn't be adding/subtracting when dealing with enums. That is dangerous ground. Instead, you should be adding/subtracting days to your DateTime:
string today = DateTime.Now.DayOfWeek.ToString();
string tomorrow = DateTime.Now.AddDays(1).DayOfWeek.ToString();
string yesterday = DateTime.Now.AddDays(-1).DayOfWeek.ToString();
I guess in this particular case it could be
DateTime rn = DateTime.Now.AddDays(5);
string[] daysOfWeek = { "Monday", "Tuesday", "Saturday" };
if (daysOfWeek.Contains(((rn.DayOfWeek) == 0 ? rn.DayOfWeek + 6 : rn.DayOfWeek - 1).ToString()))
Console.WriteLine("Found");
Although I agree that such code generally should not be used.
You cannot have an automatic "looping" behavior on random enumerations, however, there are some ways you could handle your problem.
Most of the time, you could simply use the % operator, which in the case of DayOfWeek would amount to something like this:
// Prints Friday
Console.WriteLine((DayOfWeek)(((int)DayOfWeek.Saturday + 6) % 7));
You could also write an extension method like this one:
public static class DateTimeExtensions
{
public static DayOfWeek AddDays(this DayOfWeek dayOfWeek, int count)
{
if (dayOfWeek < 0 || (int)dayOfWeek > 6) throw new ArgumentOutOfRangeException();
int adjustedValue = ((int)dayOfWeek + count) % 7;
return (DayOfWeek)(adjustedValue < 0 ? adjustedValue + 7 : adjustedValue);
}
}
With this extension method, you could do something like this:
Console.WriteLine(DayOfWeek.Sunday.AddDays(-1)); // Prints "Saturday"
Console.WriteLine(DayOfWeek.Sunday.AddDays(1)); // Prints "Monday"
Console.WriteLine(DayOfWeek.Sunday.AddDays(7)); // Prints "Sunday"
Console.WriteLine(DayOfWeek.Sunday.AddDays(-7)); // Prints "Sunday"
There's no built in way to do this but if you're happy working with integers then it's pretty easy, you just want to remap all your values from -X to +X into the 0-6 range so for a positive value you'd do
value+1 (to remap to 1-7 range since we're going to divide by 7) %7 (this gives you the remainder of the division by 7, so 1%7 = 1 8 = 1 etc etc, you've got it looping every 7 days) -1 (to remap out 1-7 range to your 0-6 enum value) and cast that as your enum.
Now we just need to consider negative value and convert the negative value into something positive first before passing it into our previous function. In this case we want to map the oposite days (-1 is the same as +6, one day before the start of the week is 6 days after the end of the week etc), so if your value is negative you want to do abs(value) to make it positive and the substract it from 7 to remap it into the positive range
Here's some code (untested)
if(value < 0)
{
value = 7 - Math.Abs(value)
}
value++; (remap 0-6 to 1-7)
value = value % 7; (remap 1 - int.maxvalue to 1-7)
value--; (remap 1-7 to 0-6;
And now value contains the number that maps to your enum.
You can create a following extension method (can easily be changed for string arrays).
public static bool ContainsYesterday(this DayOfWeek[] days)
{
return days.Contains(DateTime.Now.AddDays(-1).DayOfWeek);
}
edited as previous version had a bug specified in comment.

Bad calculations after Python to C# port

I'm working on a time-decay algorithm for a post system based on Reddit's model here:
http://amix.dk/blog/post/19588
My working port is here:
public class Calculation
{
protected DateTime Epoch = new DateTime(1970, 1, 1);
protected long EpochSeconds(DateTime dt)
{
var ts = dt.Subtract(Convert.ToDateTime("1/1/1970 8:00:00 AM"));
return ((((((ts.Days * 24) + ts.Hours) * 60) + ts.Minutes) * 60) + ts.Seconds);
}
protected int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
var s = Score(upVotes, downVotes);
var order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
var sign = Math.Sign(s);
var seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * ((double)seconds / 45000), 7);
}
}
Based on the model output from the link provided, I should see gradual decay at 0-13 hours, and sharp decay after that.
What I'm seeing is very homogeneous decay, and scores much higher than the output from the original code (original code: 3480-3471).
Here is how I'm testing:
Calculation c = new Calculation();
double now = c.HotScore(100, 2, DateTime.Now);
double fivehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-5));
double tenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-10));
double elevenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-11));
double twelvehoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-12));
double thirteenhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-13));
double fiftyhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-50));
double onehundredhoursago = c.HotScore(100, 2, DateTime.Now.AddHours(-100));
Console.WriteLine(now.ToString());
Console.WriteLine(fivehoursago.ToString());
Console.WriteLine(tenhoursago.ToString());
Console.WriteLine(elevenhoursago.ToString());
Console.WriteLine(twelvehoursago.ToString());
Console.WriteLine(thirteenhoursago.ToString());
Console.WriteLine(fiftyhoursago.ToString());
Console.WriteLine(onehundredhoursago.ToString());
Console.ReadLine();
Output values:
now: 4675.2993816
five hours: 4674.8993816
ten hours: 4674.4993816
eleven hours: 4674.4193816
twelve hours: 4674.3393816
thirteen hours: 4674.2593816
fifty hours: 4671.2993816
one-hundred hours: 4667.2993816
Clearly it's SORT of working right, but something is off. It could be related to the lack of true *nix Epoch support, or the lack of analogous microseconds calculation, but something isn't quite right.
Possible reference resources:
http://blogs.msdn.com/b/brada/archive/2004/03/20/93332.aspx
http://codeclimber.net.nz/archive/2007/07/10/convert-a-unix-timestamp-to-a-.net-datetime.aspx
Your primary problem is that the hot algorithm is time dependent. Your calculating the hot score at DateTime.Now, whereas the article was written on 23. Nov 2010 (look at the bottom of the article).
With some trial and error, it seems the data was calculated at approximately 2010-11-23 07:35. Try using that value rather than DateTime.Now, and you should get about the same results as the data in the graph shown.
Mind you, you could make the following improvements to your code:
public class Calculation
{
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
private double EpochSeconds(DateTime dt)
{
return (dt - Epoch).TotalSeconds;
}
private int Score(int upVotes, int downVotes)
{
return upVotes - downVotes;
}
public double HotScore(int upVotes, int downVotes, DateTime date)
{
int s = Score(upVotes, downVotes);
double order = Math.Log(Math.Max(Math.Abs(s), 1), 10);
int sign = Math.Sign(s);
double seconds = EpochSeconds(date) - 1134028003;
return Math.Round(order + sign * seconds / 45000, 7);
}
}
My results:
3479.0956039
3478.6956039
3478.2956039
3478.2156039
3478.1356039
3478.0556039
3475.0956039
3471.0956039
Changes:
Used the declared Epoch rather than a convert of 1970-01-01 08:00:00 (I think 08:00 is a mistake).
You can subtract two dates using a - b; which is the same as a.Subtract(b) but more succinct and it mirrors the original Python code.
A timespan does give you microsecond precision (Ticks are the smallest unit and equal 100 nanoseconds).
Also, TotalSeconds gives you the total number of seconds within a time span; no need to recalculate that. The fractional part even gives you your microsecond precision.
By returning double from EpochSeconds, you keep this precision.
Made the data types explicit rather than var to clearly indicate what variable is what (they match the method signatures, so no implicit upcasting).
Changed unneeded protected to private and made the Epoch a constant.

DateTime Round Up and Down

Ive been looking for a proper rounding mechanism but nothing I find seems to be exactly what I need.
I need to round up and round down seperately and I also need to account for the the case when its already rounded.
I need the following rounding to happen
5:00 -> RoundDown() -> 5:00
5:04 -> RoundDown() -> 5:00
5:09 -> RoundDown() -> 5:00
5:10 -> RoundDown() -> 5:10
4:00 -> RoundUp() -> 4:00
4:50 -> RoundUp() -> 4:50
4:51 -> RoundUp() -> 5:00
4:56 -> RoundUp() -> 5:00
Basically I need it to RoundUp() or RoundDown() to the nearest 10 minutes explicitly but it should also leave time untouched if it already is in a multiple of 10 minutes. Also I'd like to truncate any seconds to that they have no effect on the rounding procedure
4:50:45 -> 4:50:00 -> RoundUp() -> 4:50
Does anyone have any handy code to accomplish this.
I found this code somewhere but it rounds 5:00 -> RoundUp() -> 5:10 rather than leaving it intact because its already a multiple of 10 and needs no rounding. Also Im not sure how seconds would effect it
public static DateTime RoundDateTime(this DateTime dt, int minutes, RoundingDirection direction)
{
TimeSpan t;
switch (direction)
{
case RoundingDirection.Up:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes, 0)); break;
case RoundingDirection.Down:
t = (dt.Subtract(DateTime.MinValue)); break;
default:
t = (dt.Subtract(DateTime.MinValue)).Add(new TimeSpan(0, minutes / 2, 0)); break;
}
return DateTime.MinValue.Add(new TimeSpan(0,
(((int)t.TotalMinutes) / minutes) * minutes, 0));
}
Hope someone can edit that method to make it work for me. Thanks
This will let you round according to any interval given.
public static class DateTimeExtensions
{
public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
{
return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
}
public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
{
var overflow = dateTime.Ticks % interval.Ticks;
return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
}
public static DateTime Round(this DateTime dateTime, TimeSpan interval)
{
var halfIntervalTicks = (interval.Ticks + 1) >> 1;
return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
}
}
To take care of truncating the seconds, I would simply subtract the seconds and milliseconds from the date-time before sending them into the rounding functions.
How about:
case RoundingDirection.Up:
t = dt.AddMinutes((60 - dt.Minute) % 10);
case RoundingDirection.Down:
t = dt.AddMinutes(-dt.Minute % 10);
Demo: http://ideone.com/AlB7Q
Here is a fast way to truncate (round down)
var now = DateTime.Now;
var nowTicks = now.Ticks;
//removing the nanoseconds, miliseconds, and seconds from the nowTicks
var lastMinute = new DateTime(nowTicks - (nowTicks % (1000*1000*10*60)));
This function will round up or down to the nearest interval (minutes).
private static DateTime NormalizeReadingInterval(DateTime originalTime, int interval)
{
if (originalTime.Minute % interval == 0) return originalTime;
var epochTime = new DateTime(1900, 1, 1);
var minutes = (originalTime - epochTime).TotalMinutes;
var numIntervals = minutes / interval;
var roundedNumIntervals = Math.Round(numIntervals, 0);
return epochTime.AddMinutes(roundedNumIntervals * interval);
}
Another approach avoiding arithmetic using type long.
Using integer division, where a & b are positive integers:
a/b // rounding down
(a+b-1)/b // rounding up
((2*a)+b)/(2*b) // rounding to the nearest (0.5 up)
To round up:
public static DateTime UpToNearestXmin( DateTime dt, int block )
{
int a = dt.Minute;
int b = block;
int mins = block * (( a + b - 1 ) / b );
return new DateTime( dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0 ).AddMinutes( mins );
}
To round down or to nearest, change the mins calculation as appropriate.
The minutes are rounded. The seconds & milliseconds are zeroed which is expected behaviour.

Calculate the difference between two dates and get the value in years? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How do I calculate someone’s age in C#?
I want to calculate basically the age of employees - So we have DOB for each employee, So on
the C# Side I want to do something like this -
int age=Convert.Int32(DateTime.Now-DOB);
I can use days and manipulate then get the age...but I wanted to know if there something I can use directly to get the number of years.
Do you want calculate the age in years for an employee? Then you can use this snippet (from Calculate age in C#):
DateTime now = DateTime.Today;
int age = now.Year - bday.Year;
if (bday > now.AddYears(-age)) age--;
If not, then please specify. I'm having a hard time understanding what you want.
Subtracting two DateTime gives you a TimeSpan back. Unfortunately, the largest unit it gives you back is Days.
While not exact, you can estimate it, like this:
int days = (DateTime.Today - DOB).Days;
//assume 365.25 days per year
decimal years = days / 365.25m;
Edit: Whoops, TotalDays is a double, Days is an int.
On this site they have:
public static int CalculateAge(DateTime BirthDate)
{
int YearsPassed = DateTime.Now.Year - BirthDate.Year;
// Are we before the birth date this year? If so subtract one year from the mix
if (DateTime.Now.Month < BirthDate.Month || (DateTime.Now.Month == BirthDate.Month && DateTime.Now.Day < BirthDate.Day))
{
YearsPassed--;
}
return YearsPassed;
}
private static Int32 CalculateAge(DateTime DOB)
{
DateTime temp = DOB;
Int32 age = 0;
while ((temp = temp.AddYears(1)) < DateTime.Now)
age++;
return age;
}
Math.Round(DateTime.Now.Subtract(DOB).TotalDays/365.0)
As pointed out, this won't work. You'd have to do this:
(Int32)Math.Round((span.TotalDays - (span.TotalDays % 365.0)) / 365.0);
and at that point the other solution is less complex and continues to be accurate over larger spans.
Edit 2, how about:
Math.Floor(DateTime.Now.Subtract(DOB).TotalDays/365.0)
Christ I suck at basic math these days...
(DateTime.Now - DOB).TotalDays/365
Subtracting a DateTime struct from another DateTime struct will give you a TimeSpan struct which has the property TotalDays... then just divide by 365

Is there a better way in C# to round a DateTime to the nearest 5 seconds?

I want to round a DateTime to the nearest 5 seconds. This is the way I'm currently doing it but I was wondering if there was a better or more concise way?
DateTime now = DateTime.Now;
int second = 0;
// round to nearest 5 second mark
if (now.Second % 5 > 2.5)
{
// round up
second = now.Second + (5 - (now.Second % 5));
}
else
{
// round down
second = now.Second - (now.Second % 5);
}
DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second);
Please note that I've found these two previous questions, however they truncate rather than round the time.
(Sorry for the resurrection; I recognize it's an old and answered question - just adding some extra code for Google's sake.)
I started with JayMcClellan's answer, but then I wanted it to be more generic, rounding to arbitrary intervals (not just 5 seconds). So I ended up leaving Jay's method for one that uses Math.Round on ticks and put it into an extension method that can take arbitrary intervals and also offers the option of changing the rounding logic (banker's rounding versus away-from-zero). I'm posting here in case this is helpful to someone else as well:
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) {
return new TimeSpan(
Convert.ToInt64(Math.Round(
time.Ticks / (decimal)roundingInterval.Ticks,
roundingType
)) * roundingInterval.Ticks
);
}
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) {
return Round(time, roundingInterval, MidpointRounding.ToEven);
}
public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) {
return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
}
It won't win any awards for bare efficiency, but I find it easy to read and intuitive to use. Example usage:
new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs
The Ticks count of a DateTime represents 100-nanosecond intervals, so you can round to the nearest 5 seconds by rounding to the nearest 50000000-tick interval like this:
DateTime now = DateTime.Now;
DateTime rounded = new DateTime(((now.Ticks + 25000000) / 50000000) * 50000000);
That's more concise, but not necessarily better. It depends on whether you prefer brevity and speed over code clarity. Yours is arguably easier to understand.
Like you mentioned, it's fairly easy to truncate. So, just add 2.5 seconds, then truncate down.
I can't think of a better way, although I would probably factor out the round method:
static int Round(int n, int r)
{
if ((n % r) <= r / 2)
{
return n - (n % r);
}
return n + (r - (n % r));
}
Also, % returns an int, so comparing it to 2.5 strikes me as a little odd, even though it is correct. I'd use >= 3.
How about this (blending a few answers together)? I think it conveys the meaning well and should handle the edge cases (rounding to the next minute) elegantly due to AddSeconds.
// truncate to multiple of 5
int second = 5 * (int) (now.Second / 5);
DateTime dt = new DateTime(..., second);
// round-up if necessary
if (now.Second % 5 > 2.5)
{
dt = dt.AddSeconds(5);
}
The Ticks approach as shown by Jay is more concise, but may be a bit less readable. If you use that approach, at least reference TimeSpan.TicksPerSecond.
I couldn't recognize the difference between C# and a bar of soap (well, I couldn't when I originally wrote this answer, things have changed quite a bit in the years since) but, if you're looking for a more concise solution, I would just put the whole thing in a function - there's little that will be more concise in your code than a simple call to said function:
DateTime rounded = roundTo5Secs (DateTime.Now);
Then you can put whatever you want in the function and just document how it works, such as (assuming these are all integer operations):
secBase = now.Second / 5;
secExtra = now.Second % 5;
if (secExtra > 2) {
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase + 5);
}
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase);
You may also need some extra checks if secBase goes to 60 (unless C# DateTime objects are smart enough to bump up the minute (and hour if minute goes to 60, and so on).
Technically, you can never correctly round to an odd interval given only seconds.
2, 4, 6, 8, 10 <-- are no problem
If you are 'distributing' times in intervals and if the jitter is low, truncation is a lot
more tractable.
If you can pass milliseconds and round at a 500mS mark, you will be able to to odd
seconds and also slash the effect of jitter way down or eliminate it entirely.
Most simple and accurate one-liner:
private static DateTime QuantizeToEachNthSecond(DateTime dateTime, int nthSecond = 5)
{
return dateTime.AddTicks(-(dateTime.Ticks % (nthSecond * TimeSpan.TicksPerSecond)));
}
so, if you like each 5th second, if will be truncated to e.g. "10:12:02" -> "10:12:00", "10:12:08" -> "10:12:05" and so on.

Categories

Resources