I have timestamp of Oracle:
string timestamp = "23-JUN-14 09.39.04.000000000 AM";
I am not able to parse it into system date time object. I used:
CultureInfo provider = CultureInfo.InvariantCulture;
String format = "yy-MMM-dd hh:mm:ss:fffffff";
string timestamp = "10-DEC-07 10.32.47.797201123 AM";
{
var date = DateTime.ParseExact(timestamp, format, provider);
DateTime dateTime = DateTime.ParseExact(timestamp.ToString(), "dd-MMM-y HH:mm:ss", CultureInfo.InvariantCulture);
}
It is still passing error. It is working 7 f after m but not more than that. I used try Parse, try ParseExact - is there any way?
According to https://stackoverflow.com/a/23198962/328864, there is no way to skip parts of an exact pattern, so i guess you could do something like this:
CultureInfo provider = CultureInfo.InvariantCulture;
string timestamp = "10-DEC-07 10.32.47.797201123 AM";
String format = String.Format("yy-MMM-dd hh.mm.ss.fffffff{0} tt", timestamp.Substring(26,2));
DateTime date = DateTime.ParseExact(timestamp, format, provider);
Console.WriteLine(date);
Not very pretty though.
Once we started to use ODP.NET, we had to implement an extension like below:
public static T ConvertOracleValue<T>(this object value)
{
if (value != null)
{
Type typeOfValue = value.GetType();
if (typeOfValue.Namespace.Contains("Oracle.DataAccess"))
{
if (typeOfValue.Name.Equals("OracleTimeStamp"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStamp ots = (Oracle.DataAccess.Types.OracleTimeStamp)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
if (typeOfValue.Name.Equals("OracleTimeStampLTZ"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStampLTZ ots = (Oracle.DataAccess.Types.OracleTimeStampLTZ)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
if (typeOfValue.Name.Equals("OracleTimeStampTZ"))
{
int tempInt = 0;
Oracle.DataAccess.Types.OracleTimeStampTZ ots = (Oracle.DataAccess.Types.OracleTimeStampTZ)value;
tempInt = Int32.TryParse(ots.Millisecond.ToString("000").Substring(0, 3), out tempInt) ? tempInt : 0;
DateTime ret = new DateTime(ots.Year, ots.Month, ots.Day, ots.Hour, ots.Minute, ots.Second, tempInt);
return ConvertHelper.ConvertValue<T>(ret);
}
string temp = value.ToString();
return ConvertHelper.ConvertValue<T>(temp);
}
}
else
{
return default(T);
}
return ConvertHelper.ConvertValue<T>(value);
}
where ConvertHelper.ConvertValue is another extension:
public static class ConvertHelper
{
public static T ConvertValue<T>(object value)
{
Type typeOfT = typeof(T);
if (typeOfT.BaseType != null && typeOfT.BaseType.ToString() == "System.Enum")
{
return (T)Enum.Parse(typeOfT, Convert.ToString(value));
}
if ((value == null || value == Convert.DBNull) && (typeOfT.IsValueType))
{
return default(T);
}
if (value is IConvertible)
{
return (T)Convert.ChangeType(value, typeOfT, new CultureInfo("en-GB"));
}
return (T)Convert.ChangeType(value, typeOfT);
}
}
This worked like a charm in our test, integration and production environments.
.NET DateTime structure has a precision of tick - 100 nanoseconds - 0.0000001 of second - 7 decimal positions after the point.
Oracle TimeStamp has a precision of up to nanosecond - 0.000000001 - 9 decimal positions after the point.
That is why standard DateTime cannot store all possible oracle TimeStamps. And its parsing function simply fail on more precise string representations of TimeStamp.
So, what could be tried:
Format your TimeStamps in query to some format parseable by DataTime(with loss of precision if necessary) - http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
Or create your own more precise CustomTimeStamp .Net structure and parse to it manually
Related
How can the method be implemented in C#?
string StartTime = "06:10 PM";
string Endtime = "08:10 PM";
DateTime current_time = DateTime.Now;
bool validTime = validTimeFindout(StartTime,Endtime,current_time);
bool validTimeFindout(string StartTime, string Endtime,DateTime current_time){
// This method should return true
// when the current_time>= StartTime && current_time<=Endtime
// otherwise false
}
I tried to find out the valid time in the specific range and for that validTimeFindout method will help and here the method is comparing the time get from local pc and compare them with StartTime and Endtime
Starting with .NET 6, you can use the TimeOnly Struct:
static bool IsTimeBetween(string startTime, string endTime, DateTime dateTime)
{
if (TimeOnly.TryParse(startTime, out var t1) &&
TimeOnly.TryParse(endTime, out var t2))
{
return TimeOnly.FromDateTime(dateTime).IsBetween(t1, t2);
}
return false;
}
Note that TimeOnly.IsBetween supports time ranges that span midnight such as 23:00-01:00.
You can use DateTime.TryParse() method to parse a string into the DateTime datatype.
DateTime datatypes can be compared as numeric datatypes with <, <=, ==, !=, >=, >
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time){
DateTime start;
DateTime.TryParse(StartTime, out start);
DateTime end;
DateTime.TryParse(Endtime, out end);
return current_time >= start && current_time <= end;
}
You just need to convert the inputs from string to datetime and do the calculations: datetime.parse()
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time)
{
DateTime _startTime = DateTime.Parse(StartTime);
DateTime _endTime = DateTime.Parse(Endtime);
if (current_time >= _startTime && current_time <= _endTime)
return true;
else
return false;
}
First you need to convert those strings to DateTime objects.
Then you should compare those DateTime objects and current time.
using System.Globalization;
string StartTime = "06:10 PM";
string Endtime = "08:10 PM";
DateTime current_time = DateTime.Now;
try {
bool validTime = validTimeFindout(StartTime, Endtime, current_time);
Console.WriteLine(validTime);
}catch(Exception exc) {
Console.WriteLine(exc.Message);
}
bool validTimeFindout(string StartTime, string Endtime, DateTime current_time) {
DateTime start = ParseTimeString(StartTime);
DateTime end = ParseTimeString(Endtime);
if(current_time.CompareTo(start) >= 0 && current_time.CompareTo(end) <= 0) {
return true;
} else {
return false;
}
}
DateTime ParseTimeString(string timeString) {
string format = "hh:mm tt";
DateTime result;
if (DateTime.TryParseExact(timeString, format, CultureInfo.GetCultureInfo("en-US"), DateTimeStyles.None, out result)) {
return result;
} else {
throw new Exception("Cannot parse time!");
}
}
There are also several things you can rethink and fix.
Naming pattern - IMHO is valid is not a good name for method checking if a DateTime is between other DateTimes.
Use single naming convention and stick to it. The most popular convention in C# is cammel case (like startTime).
Is it really needed to store those DateTimes in strings formatted like "01:10 PM"? Why doesn't you simply store DateTime objects?
I have written a logic in c# that determines the nextCallDate based on the given cobDate. cobDate is current date -1.
So if there are more than one future date in the given string then it should return the nearest future date to the cobdate and ignore the rest
For eg if the cobdate is 2020/02/12 and the string is
;2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;5; 20200328;3;101.1250;5;20210328;3;100.00;
Then NextCallDate would be 2020/03/28.
I need to return blank for dates in the past.
So say in the example if the given string has all the dates in the past then it should return blank.
Given string ;2;1;2;5;20120918;3;100.000000;
Here is what I have written
private DateTime? GetNextCallDate(string nextCallDate)
{
DateTime cobDate = DateTime.Now.Date.AddDays(-1);
var parts = nextCallDate.Split(';');
foreach (var part in parts)
{
DateTime parsedNextCallDate = DateTime.Parse(part);
if (parsedNextCallDate.Date > cobDate.Date)
{
return parsedNextCallDate;
}
}
return null;
}
You should probably be using DateTime.TryParse instead of Parse, since some of the values are not dates. Also, it looks like you're returning the first date that's greater than cobDate, not the nearest one.
To resolve this, we first set parsedNextCallDate to null, and this will be our default return value. Then we can check each part if it's a DateTime using the return value from TryParse, and then compare the value to both cobDate and parsedNextCallDate. If the date is greater than cobDate and less than parasedNextCallDate (or if parasedNextCallDate isn't set yet), then we update parasedNextCallDate to the new value. At the end, we just return parasedNextCallDate:
public static DateTime? GetNextCallDate(string input)
{
DateTime? nextCallDate = null;
if (string.IsNullOrWhiteSpace(input)) return nextCallDate;
var yesterday = DateTime.Today.AddDays(-1);
var inputItems = input.Split(';');
foreach (var inputItem in inputItems)
{
DateTime itemDate;
// If inputItem is a DateTime and it's greater than yesterday
if (DateTime.TryParseExact(inputItem.Trim(), "yyyyMMdd", null,
DateTimeStyles.None, out itemDate) &&
itemDate.Date > yesterday)
{
// and if nextCallDate doesn't have a value or the parsed value
// is less than nextCallDate, assign nextCallDate to this value
if (!nextCallDate.HasValue || itemDate < nextCallDate)
{
nextCallDate = itemDate;
}
}
}
return nextCallDate;
}
Here's one way solve your problem. Breaking things down into steps often makes things easier to reason with and easier to test. I'm often working on server side apps so I like the new span/memory classes. So first thing is to split our input string into chunks:
static IEnumerable<ReadOnlyMemory<char>> ReduceToPossibleDates(string source)
{
const int ExpectedDateLen = 9; // includes separator
int last = 0;
var mem = source.AsMemory();
for (int i = 0; i < source.Length; ++i)
{
if (';' == mem.Span[i])
{
int length = i - last;
if (length == ExpectedDateLen)
{
yield return mem.Slice(last+1,length-1);
}
last = i;
}
}
}
This gives us a stream of ReadOnlyMemory that all contains what we think should be dates. Next we can do another method to consume those chunks and turn them into dates.
static IEnumerable<DateTime> ToDateTime(IEnumerable<ReadOnlyMemory<char>> rawDates)
{
foreach (var rawDate in rawDates)
{
if (DateTime.TryParseExact(rawDate.Span,"yyyyMMdd".AsSpan(),
CultureInfo.InvariantCulture,
DateTimeStyles.None, out var date))
yield return date;
}
}
Once we have that we can treat the stream of dates however we want. In this case we check to find the first that's after our COB.
static void Main(string[] _)
{
const string GoodData = ";2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;"+
"5;20200328;3;101.1250;5;20210328;3;100.00;";
const string NoDateData = ";2;1;2;5;20120918;3;100.000000;";
var cobDate = new DateTime(2020, 2,12); // some actual close of business date...
var nextCallDate = ToDateTime(ReduceToPossibleDates(GoodData))
.FirstOrDefault(x => x >= cobDate);
var noDateExpected = ToDateTime(ReduceToPossibleDates(NoDateData))
.FirstOrDefault(x => x >= cobDate);
if (nextCallDate != default(DateTime))
Console.WriteLine(nextCallDate);
else
Console.WriteLine("no call date.");
if (noDateExpected != default(DateTime))
Console.WriteLine(nextCallDate);
else
Console.WriteLine("no call date.");
}
It would be a little cleaner with extension methods but you get the idea.
If a currency amount is very large, I'm trying to abbreviate it.
For example:
if (amt > 1000000)
{
decimal d = (decimal)Math.Round(amt / 1000, 0);
return String.Format("{0:C0}", d) + " K";
}
If a number is given over 1 million, it will take off the last 3 digits and replace with a K. Works just fine when the currency symbol (like $ is on the left hand side)
However, some currency symbols get put on the right hand side.
So instead of a nice looking $100 K for USD, I'd get 100 € K for French Euros.
How can I change the format to put the K immediately after the numbers, and before the currency symbol.
Seems like it might be a step too far. Any ideas?
I would create a class with the IFormatProvider like this
public class MoneyFormat: IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string fmt, object arg, IFormatProvider formatProvider)
{
if (arg.GetType() != typeof(decimal))
try
{
return HandleOtherFormats(fmt, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid", fmt), e);
}
string ufmt = fmt.ToUpper(CultureInfo.InvariantCulture);
if (!(ufmt == "K"))
try
{
return HandleOtherFormats(fmt, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid", fmt), e);
}
decimal result;
if (decimal.TryParse(arg.ToString(), out result))
{
if (result >= 1000000)
{
decimal d = (decimal)Math.Round(result / 10000, 0);
CultureInfo clone = (CultureInfo)CultureInfo.CurrentCulture.Clone();
string oldCurrSymbol = clone.NumberFormat.CurrencySymbol;
clone.NumberFormat.CurrencySymbol = "";
return String.Format(clone, "{0:C0}", d).Trim() + " K" + oldCurrSymbol;
}
}
else
return string.Format("{0:C0}", result) + " K";
}
private string HandleOtherFormats(string format, object arg)
{
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
else if (arg != null)
return arg.ToString();
else
return string.Empty;
}
}
Then you can call it in your format like so:
return string.Format( new MoneyFormat(), "{0:K}", amt);
You can then tweek the way you want to represent your "K" or other reference symbols that you want to add
CultureInfo("fr-fr") : 100 K€
CultureInfo("en-us") : 100 K$
CultureInfo("ru-RU") : 100 Kр.
You can use the CurrencyPositivePattern to determine if the currency symbol comes before or after the number. Then you can modify the CurrencySymbol to suit your needs.
decimal amt = 10000000;
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //set up France as current culture
NumberFormatInfo NFI = CultureInfo.CurrentCulture.NumberFormat;
string currencySymbol = NFI.CurrencySymbol;
int currencyPosition = NFI.CurrencyPositivePattern;
if (amt > 1000000)
{
if (currencyPosition == 3) // n $
{
NFI.CurrencySymbol = "K " + currencySymbol;
}
decimal d = (decimal)Math.Round(amt / 1000, 0);
string output = d.ToString("c");
}
I know this is not the best implementation of a custom number format, but this is just to get the idea across.
See:
NumberFormatInfo.CurrencyPositivePattern Property
Ok, so I have a date stored in UK format (dd/mm/yy) which I need to display in the locale of wherever the user might be.
The issue is that this date can be 000000 (00/00/2000); so I can't convert it to DateTime directly, as DateTime doesn't support 0 values for day or month.
I have this so far:
int dateInt = ddmmyy;
var year = (dateInt % 100) + 2000;
var month = (dateInt / 100) % 100;
var day = (dateInt / 100000);
var result = new DateTime(year, month, day); //2014/00/00 at this point, so breaks.
var resultStr = result.ToString(CultureInfo.InvariantCulture);
return resultStr;
What's the correct way to add support for 0 values initially? I've tried changing the 0 to 1 before converting to DateTime, running the conversion and then replacing with a 0 again; but due to culture variants I see no way that this method can support other cultures, which is the purpose of this conversion to begin with.
Any ideas? I'm guessing this is a common issue.
Is this what you need ?
using System;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
int[] savedDates = new int[] { 000000, 010000, 000013 };
foreach (var item in savedDates)
{
DateTime date = ConvertToDate(item);
Console.WriteLine(item.ToString("D6") + " => " + date.ToShortDateString());
}
Console.ReadLine();
}
private static DateTime ConvertToDate(int item)
{
string temp = item.ToString("D6");
int day = int.Parse(temp.Substring(0, 2));
int month = int.Parse(temp.Substring(2, 2));
int year = int.Parse(temp.Substring(4, 2));
if (day == 0)
day = 1;
if (month == 0)
month = 1;
year += 2000;
return new DateTime(year, month, day);
}
}
}
I would not store dates like this as the methodology for doing so is already provided by the .NET framework.
The best way to store dates would be to use Culture.InvariantCulture for string conversion cases and then convert to local culture for display purposes as necessary. DateTime itself is culture-independent so converting between cultures is very easy.
I need to format a double value to one decimal place without it rounding.
double value = 3.984568438706
string result = "";
What I have tried is:
1)
result = value.ToString("##.##", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 3.98%
2)
result = value.ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4%
3)
result = value.ToString("##.0", System.Globalization.CultureInfo.InvariantCulture) + "%";
// returns 4.0%
4) (Following other suggestions)
value = (value / 100);
result = String.Format("{0:P1}", Math.Truncate(value * 10000) / 10000);
// returns 4.0%
result = string.Format("{0:0.0%}",value); // returns 4.0%
What I need to display is the value 3.9%
Thanks for any help in advance.
result=string.Format("{0:0.0}",Math.Truncate(value*10)/10);
I would make a utility method to handle this:
static double Truncate(double value, int digits)
{
double mult = System.Math.Pow(10.0, digits);
return System.Math.Truncate(value * mult) / mult;
}
You could then do:
result = Truncate(value, 1).ToString("##.#", System.Globalization.CultureInfo.InvariantCulture) + "%";
Note that you may also want Math.Floor instead of truncate - but it depends on how you want negative values handled.
I know this is a old thread but I've just had to do this. While the approaches here work I want a easy way to be able to affect a lot of calls so using the Math.Truncate on all the calls to string.format wasn't really a good option.
Thus, I made a custom format provider which would allow me to add truncation to the formatting string, eg
string.format(new FormatProvider(), "{0:T}", 1.1299); // 1.12
string.format(new FormatProvider(), "{0:T(3)", 1.12399); // 1.123
string.format(new FormatProvider(), "{0:T(1)0,000.0", 1000.9999); // 1,000.9
The implementation is pretty simple and is easily extendible to other requirements.
public class FormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof (ICustomFormatter))
{
return this;
}
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (arg.GetType() != typeof (double))
{
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
if (format.StartsWith("T"))
{
int dp = 2;
int idx = 1;
if (format.Length > 1)
{
if (format[1] == '(')
{
int closeIdx = format.IndexOf(')');
if (closeIdx > 0)
{
if (int.TryParse(format.Substring(2, closeIdx - 2), out dp))
{
idx = closeIdx + 1;
}
}
else
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
}
double mult = Math.Pow(10, dp);
arg = Math.Truncate((double)arg * mult) / mult;
format = format.Substring(idx);
}
try
{
return HandleOtherFormats(format, arg);
}
catch (FormatException e)
{
throw new FormatException(string.Format("The format of '{0}' is invalid.", format));
}
}
private string HandleOtherFormats(string format, object arg)
{
if (arg is IFormattable)
{
return ((IFormattable) arg).ToString(format, CultureInfo.CurrentCulture);
}
return arg != null ? arg.ToString() : String.Empty;
}
}
ToString() doesn't do it. You have to add extra code. The other answers show math approaches, my approach below is kind of outside-the-box.
string result = value.ToString();
Console.WriteLine("{0}", result.Substring(0, result.LastIndexOf('.') + 2));
This is a fairly simple brute force approach, but it does the trick when the decimal is a '.'. Here's an extension method to ease the pain (and deals with the decimal point).
public static class Extensions
{
public static string ToStringNoTruncate(this double me, int decimalplaces = 1)
{
string result = me.ToString();
char dec = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
return result.Substring(0, result.LastIndexOf(dec) + decimalplaces + 1);
}
}
( Math.Truncate( ( value * 10 ) ) / 1000 ).ToString( "#.#%" )
Just use modulo operator + built in ToString:
result = (value - (value % 0.1)).ToString("N1") + "%";