Looping around in Enumeration in C# - 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.

Related

C# time bool always returning false

I have a bool that tells me if the current time is in between two other times it will go dark at 8 (20:00) in the evening and light at 7 in the morning.. I am not sure if I should be doing 7 or 07 I have tried booth but still getting false?
Can anyone tell me why this is always returning false? Not much else to say really just that it's always returning false when it is currently in between the two times currently.. GTM Timezone London, Thanks!
public static bool NightTime
{
get
{
TimeSpan span = LastMoodlightUpdate - DateTime.Now;
TimeSpan start = new TimeSpan(20, 0, 0); //Dark at 20:00
TimeSpan end = new TimeSpan(07, 0, 0); //Light at 07:00
TimeSpan now = DateTime.Now.TimeOfDay;
return ((now > start) && (now < end));
}
}
The problem here is that comparing two TimeSpan values is a simple numeric comparison.
As such, no time can both be larger than 20:00 and smaller than 07:00 in terms of values. We humans can deal with such incongruities, but the computer can't.
You need to consider what you want here:
|---------|..................................|-----------|
00 07 20 24
You want the times in dashes, basically you should use || instead of &&:
return (now > start) || (now < end);
Since the time of day cannot be negative, nor can it reach 24:00 or higher, this will give you what you want.
I don't get the purpose of all your code. Isn't it as simple as this?
public static bool NightTime
{
get
{
var hour = System.DateTime.Now.Hour;
return (hour <=7 || hour >= 20);
}
}

Semi-Monthly Date Calculation in 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);
}
}

Looping through enum with initializer gives unexpected result

The enum I've created looks like this:
enum MonthOfTheYear : byte
{
January,
February,
March,
April,
May,
June,
July = 0,
August,
September,
October,
November,
December
}
As you can see, July has an initializer of 0.
This has some interesting (side) effects: there seems to be "pairing" of integer values. February ànd August now have values of 1, March ànd September have 2 etc.:
MonthOfTheYear theMonth = MonthOfTheYear.February;
Console.WriteLine(theMonth + " has integer value of " + (int)theMonth);
and
MonthOfTheYear theMonth = MonthOfTheYear.August;
Console.WriteLine(theMonth + " has integer value of " + (int)theMonth);
clearly show this. So far, weird as I find that, I'm willing to go along.
EDIT: I get that assigning July 0 makes the indices start over. I DON'T get why they can co-exist within the same enum.
BUT! IF I then loop through the enum and output all the underlying integer values, weirdness ensues.
MonthOfTheYear theMonth = MonthOfTheYear.January;
for (int i = 0; i < 12; i++)
{
Console.WriteLine(theMonth + " has integer value of " + (int)theMonth++);
}
outputs
July has integer value of 0
February has integer value of 1
September has integer value of 2
April has integer value of 3
May has integer value of 4
June has integer value of 5
6 has integer value of 6
7 has integer value of 7
8 has integer value of 8
9 has integer value of 9
10 has integer value of 10
11 has integer value of 11
I was hoping someone could explain to me what's going on behind the scenes, because the integer values are successive, so I'm thinking this is outputting as expected but I'm not seeing it as of yet.
Firstly, when you specify a value in the definition of an enum, subsequent values number consecutively from there - and even if you specify 0 somewhere, the first value will start numbering from 0. Thus your underlying byte values are:
enum MonthOfTheYear : byte
{
January = 0, // not specified, so starts at 0
February = 1,
March = 2,
April = 3,
May = 4,
June = 5,
July = 0, // specified, so starts numbering from 0 again
August = 1,
September = 2,
October = 3,
November = 4,
December = 5
}
When you increment an enum value with ++, it just increments the underlying byte - it doesn't look at the definition of the enum and go to the element on the next line!
If this byte doesn't have a corresponding defined entry, that doesn't mean it's invalid at all - just that, when you convert the enum value to a string, you get the byte value as a string.
If the byte has several corresponding defined entries... Actually, I'm not sure exactly which entry converting it to a string will give you, but it's clearly not necessarily the first one.
Basically MonthOfTheYear.February == MonthOfTheYear.August so whether you're calling ToString on it or just looking at it in the debugger, there's no guarantee that one won't get switched for the other.
http://msdn.microsoft.com/en-us/library/system.enum.getname.aspx
If multiple enumeration members have the same underlying value, the
GetName method guarantees that it will return the name of one of those
enumeration members. However, it does not guarantee that it will
always return the name of the same enumeration member. As a result,
when multiple enumeration members have the same value, your
application code should never depend on the method returning a
particular member's name.
So, to sum it up, when you have multiple members with the same value, the name you get for a particular value is any of the members with that value.
Use the method
Enum.GetName
Here is an example:
using System;
public class GetNameTest {
enum Colors { Red, Green, Blue, Yellow };
enum Styles { Plaid, Striped, Tartan, Corduroy };
public static void Main() {
Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
}
}
// The example displays the following output:
// The 4th value of the Colors Enum is Yellow
//
You get an full explanation here:
http://msdn.microsoft.com/de-de/library/system.enum.getname.aspx
You explicitly set july to be the first month in the enum. This messes things up.
Try this:
enum MonthOfTheYear : byte {
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
}
for (int i = 0; i < 12; i++) {
Console.WriteLine(String.Format("{0} has integer value of {1}", Enum.GetName(typeof(MonthOfTheYear), i), i));
}
Because you set july to zero it resets the indexer from that point. If you want this strange order to be in place, consider rearranging the order in your enum.

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.

C# binary literals

Is there a way to write binary literals in C#, like prefixing hexadecimal with 0x? 0b doesn't work.
If not, what is an easy way to do it? Some kind of string conversion?
Update
C# 7.0 now has binary literals, which is awesome.
[Flags]
enum Days
{
None = 0,
Sunday = 0b0000001,
Monday = 0b0000010, // 2
Tuesday = 0b0000100, // 4
Wednesday = 0b0001000, // 8
Thursday = 0b0010000, // 16
Friday = 0b0100000, // etc.
Saturday = 0b1000000,
Weekend = Saturday | Sunday,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}
Original Post
Since the topic seems to have turned to declaring bit-based flag values in enums, I thought it would be worth pointing out a handy trick for this sort of thing. The left-shift operator (<<) will allow you to push a bit to a specific binary position. Combine that with the ability to declare enum values in terms of other values in the same class, and you have a very easy-to-read declarative syntax for bit flag enums.
[Flags]
enum Days
{
None = 0,
Sunday = 1,
Monday = 1 << 1, // 2
Tuesday = 1 << 2, // 4
Wednesday = 1 << 3, // 8
Thursday = 1 << 4, // 16
Friday = 1 << 5, // etc.
Saturday = 1 << 6,
Weekend = Saturday | Sunday,
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday
}
C# 7.0 supports binary literals (and optional digit separators via underscore characters).
An example:
int myValue = 0b0010_0110_0000_0011;
You can also find more information on the Roslyn GitHub page.
Only integer and hex directly, I'm afraid (ECMA 334v4):
9.4.4.2 Integer literals Integer literals are used to write values of
types int, uint, long, and ulong.
Integer literals have two possible
forms: decimal and hexadecimal.
To parse, you can use:
int i = Convert.ToInt32("01101101", 2);
Adding to #StriplingWarrior's answer about bit flags in enums, there's an easy convention you can use in hexadecimal for counting upwards through the bit shifts. Use the sequence 1-2-4-8, move one column to the left, and repeat.
[Flags]
enum Scenery
{
Trees = 0x001, // 000000000001
Grass = 0x002, // 000000000010
Flowers = 0x004, // 000000000100
Cactus = 0x008, // 000000001000
Birds = 0x010, // 000000010000
Bushes = 0x020, // 000000100000
Shrubs = 0x040, // 000001000000
Trails = 0x080, // 000010000000
Ferns = 0x100, // 000100000000
Rocks = 0x200, // 001000000000
Animals = 0x400, // 010000000000
Moss = 0x800, // 100000000000
}
Scan down starting with the right column and notice the pattern 1-2-4-8 (shift) 1-2-4-8 (shift) ...
To answer the original question, I second #Sahuagin's suggestion to use hexadecimal literals. If you're working with binary numbers often enough for this to be a concern, it's worth your while to get the hang of hexadecimal.
If you need to see binary numbers in source code, I suggest adding comments with binary literals like I have above.
You can always create quasi-literals, constants which contain the value you are after:
const int b001 = 1;
const int b010 = 2;
const int b011 = 3;
// etc ...
Debug.Assert((b001 | b010) == b011);
If you use them often then you can wrap them in a static class for re-use.
However, slightliy off-topic, if you have any semantics associated with the bits (known at compile time) I would suggest using an Enum instead:
enum Flags
{
First = 0,
Second = 1,
Third = 2,
SecondAndThird = 3
}
// later ...
Debug.Assert((Flags.Second | Flags.Third) == Flags.SecondAndThird);
If you look at the language feature implementation status of the .NET Compiler Platform ("Roslyn") you can clearly see that in C# 6.0 this is a planned feature, so in the next release we can do it in the usual way.
string sTable="static class BinaryTable\r\n{";
string stemp = "";
for (int i = 0; i < 256; i++)
{
stemp = System.Convert.ToString(i, 2);
while(stemp.Length<8) stemp = "0" + stemp;
sTable += "\tconst char nb" + stemp + "=" + i.ToString() + ";\r\n";
}
sTable += "}";
Clipboard.Clear();
Clipboard.SetText ( sTable);
MessageBox.Show(sTable);
Using this, for 8bit binary, I use this to make a static class and it puts it into the clipboard.. Then it gets pasted into the project and added to the Using section, so anything with nb001010 is taken out of a table, at least static, but still...
I use C# for a lot of PIC graphics coding and use 0b101010 a lot in Hi-Tech C
--sample from code outpt--
static class BinaryTable
{ const char nb00000000=0;
const char nb00000001=1;
const char nb00000010=2;
const char nb00000011=3;
const char nb00000100=4;
//etc, etc, etc, etc, etc, etc, etc,
}
:-)
NEAL
Binary literal feature was not implemented in C# 6.0 & Visual Studio 2015. but on 30-March 2016 Microsoft announced the new version of Visual Studio '15' Preview with that we can use binary literals.
We can use one or more than one Underscore( _ ) character for digit separators. so the code snippet would look something like:
int x = 0b10___10_0__________________00; //binary value of 80
int SeventyFive = 0B100_________1011; //binary value of 75
WriteLine($" {x} \n {SeventyFive}");
and we can use either of 0b and 0B as shown in the above code snippet.
if you do not want to use digit separator you can use it without digit separator like below code snippet
int x = 0b1010000; //binary value of 80
int SeventyFive = 0B1001011; //binary value of 75
WriteLine($" {x} \n {SeventyFive}");
While not possible using a Literal, maybe a BitConverter can also be a solution?
Though the string parsing solution is the most popular, I don't like it, because parsing string can be a great performance hit in some situations.
When there is needed a kind of a bitfield or binary mask, I'd rather write it like
long bitMask = 1011001;
And later
int bit5 = BitField.GetBit(bitMask, 5);
Or
bool flag5 = BitField.GetFlag(bitMask, 5);`
Where BitField class is
public static class BitField
{
public static int GetBit(int bitField, int index)
{
return (bitField / (int)Math.Pow(10, index)) % 10;
}
public static bool GetFlag(int bitField, int index)
{
return GetBit(bitField, index) == 1;
}
}
You can use 0b000001 since Visual Studio 2017 (C# 7.0)
Basically, I think the answer is NO, there is no easy way. Use decimal or hexadecimal constants - they are simple and clear. #RoyTinkers answer is also good - use a comment.
int someHexFlag = 0x010; // 000000010000
int someDecFlag = 8; // 000000001000
The others answers here present several useful work-a rounds, but I think they aren't better then the simple answer. C# language designers probably considered a '0b' prefix unnecessary. HEX is easy to convert to binary, and most programmers are going to have to know the DEC equivalents of 0-8 anyways.
Also, when examining values in the debugger, they will be displayed has HEX or DEC.

Categories

Resources