I thought this is going to be a very simple question yet I can't solve it. In my app I want to display the current time depending on the region setting of the user. If my phone normally displays 17:48, I want my app to show it in this format. If it shows 5:48pm, then the same rule should apply to my app. Yet, whatever I do, it only shows the am/pm version. I am using the System.DateTime class. I saw some solutions where I can do like this (setting the time to Austrian time format):
string time = DateTime.Now.ToString("t", de-AT);
and it works!
However, I don't want to set it manually, but using the region format depending on the users phone setting.
I tried getting the language country name with CultureInfo.CurrentCulture like this:
string time = DateTime.Now.ToString("t", CultureInfo.CurrentCulture.Name);
Which doesn't work, because CultureInfo.CurrentCulture.Name always displays en-US, even though my phone is set to Austrian region.
When I use the GeographicRegion class and print this:
GeographicRegion userRegion = new GeographicRegion();
string region = userRegion.CodeTwoLetter;
I will get AT as a string and "Österreich" (= Austria) when printing userRegion.NativeName;
Is there any way to get this done?
After many hours of searching I found a way to make it work! I use the DateTimeFormatter class, which does exactly what I want. At least as far as I know.
GeographicRegion userRegion = new GeographicRegion();
string regionCode = userRegion.CodeTwoLetter;
DateTimeFormatter timeFormatter = new DateTimeFormatter("hour minute", new[] { regionCode });
string correctTime = timeFormatter.Format(DateTime.Now);
DateTimeFormatter dateFormatter = new DateTimeFormatter("dayofweek month day", new[] { regionCode });
string correctDate = dateFormatter.Format(DateTime.Now);
If someone sees a mistake in that and it won't work in all reagions, please tell me.
You can use
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
DateTime dt = TimeZoneInfo.ConvertTime(DateTime.Now, timeZoneInfo);
Related
I am trying to convert a DateTime into UTC from a source timezone. Date, time and timezone are taken as inputs from a user (presented as dropdown menu options).
Following is a portion of the code utilized for conversion:
string inputDateString = "2019-11-12T09:00:00.000"; //Taken as input from user
string inputTimeZoneString = "(UTC-03:00) Brasilia"; // Taken as input from user
DateTime dtStartdatetime = DateTime.Parse(inputDateString);
string sourceTimeZone = string.Empty;
foreach (TimeZoneInfo a in TimeZoneInfo.GetSystemTimeZones())
{
if (a.DisplayName == objCalendar.timezone)
{
sourceTimeZone = a.Id;
}
string strTimeZone = a.DisplayName.Substring(a.DisplayName.IndexOf(')') + 1);
string strTimeZone1 = objCalendar.timezone.Substring(objCalendar.timezone.IndexOf(')') + 1);
if (strTimeZone.Trim() == strTimeZone1.Trim())
{
sourceTimeZone = a.Id;
}
}
DateTime utc_time_start = TimeZoneInfo.ConvertTimeToUtc(dtStartdatetime, TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZone));
Console.WriteLine(utc_time_start.ToString("yyyyMMddTHHmmssZ"));
The problem is that this piece of code gives 20191112T120000Z as output when run on Dev system (based in IST timezone) whereas same code results in 20191112T110000Z as output when run on server (based in EST). Is this behavior due to difference in timezone of the systems on which it is being run? What is could be a possible solution for this situation? A particular time from a particular timezone should result in same UTC time irrespective of the machine where the code executes.
The time zone that your server is running in does not impact this code.
The difference is due to the end of DST for Brazil in 2019.
Windows released an update in July 2019 to cover this scenario. Specifically, this change is addressed by KB4507704 and Windows 10 Build 17763.652. Your dev environment has this update, the server does not. You should ensure your server is receiving Windows Updates. If it's missing this (5 months after release), it's probably missing more critical security updates as well.
Additionally, I strongly discourage matching time zone by display name for a few reasons:
The display names are localized by the primary language of the operating system, so they will be different, for example, on a server set for English than on a server set for Portuguese. (The globalization and localization settings in .NET are not used for this.)
The display names are potentially volatile. That is, if there is a reason to change the display name in a future update, the string will change from what you previously had used.
Instead, pass the Id of the time zone as an input to TimeZoneInfo.FindSystemTimeZoneById, and skip the matching bit in the middle entirely.
You are not leaving your loop after you found a source time zone.
In your second part you search for a partial string of the time zone. Most probably this part finds a second time zone on your server. This can happen if the OS or .net versions differ.
Try:
string inputDateString = "2019-11-12T09:00:00.000"; //Taken as input from user
string inputTimeZoneString = "(UTC-03:00) Brasilia"; // Taken as input from user
var dtStartdatetime = DateTime.Parse(inputDateString);
string sourceTimeZone = string.Empty;
foreach (TimeZoneInfo a in TimeZoneInfo.GetSystemTimeZones())
{
if (a.DisplayName == inputTimeZoneString)
{
sourceTimeZone = a.Id;
break;
}
string strTimeZone = a.DisplayName.Substring(a.DisplayName.IndexOf(')') + 1);
string strTimeZone1 = inputTimeZoneString.Substring(inputTimeZoneString.IndexOf(')') + 1);
if (strTimeZone.Trim() == strTimeZone1.Trim())
{
sourceTimeZone = a.Id;
break;
}
}
DateTime utc_time_start = TimeZoneInfo.ConvertTimeToUtc(dtStartdatetime, TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZone));
Console.WriteLine(utc_time_start.ToString("yyyyMMddTHHmmssZ"));
DateTime.Parse("AD3AD08")
[2017-08-03 12:00:00 AM]
Why does that string (which looks like just a normal hex string to me) get parsed successfully as a date? I can see the 3 and the 8 get parsed as months and days. But otherwise it doesn't make sense to me.
tl;dr: You can use what DateTimeFormatInfo.GetEraName/GetAbbreviatedEraName return as delimiter, ignoring the case. The order is: day, month, year (optional).
It seems you can always use the calendar's current era's abbreviated name or full era-name as delimiter for the DateTime tokens. For english cultures it is AD or A.D., e.g. for german cultures it is n. Chr..
var enCulture = new CultureInfo("en-GB");
System.Threading.Thread.CurrentThread.CurrentCulture = enCulture;
var fi = enCulture.DateTimeFormat;
int currentEra = enCulture.Calendar.GetEra(DateTime.Now);
var eraName = fi.GetEraName(currentEra);
var shortEra = fi.GetAbbreviatedEraName(currentEra);
var date = DateTime.Parse($"{shortEra}3{shortEra}08"); // AD or A.D. works
var deCulture = new CultureInfo("de-DE");
System.Threading.Thread.CurrentThread.CurrentCulture = deCulture;
fi = deCulture.DateTimeFormat;
currentEra = deCulture.Calendar.GetEra(DateTime.Now);
eraName = fi.GetEraName(currentEra);
shortEra = fi.GetAbbreviatedEraName(currentEra);
date = DateTime.Parse($"{shortEra}3{shortEra}08"); // n. Chr. works
Interestingly it is case-insensitive, so ad works also. That is documented in DateTimeFormatInfo.GetEra:
The era name is the name a calendar uses to refer to a period of time
reckoned from a fixed point or event. For example, "A.D." or "C.E." is
the current era in the Gregorian calendar. The comparison with eraName
is case-insensitive, for example, "A.D." is equivalent to "a.d.".
The gregorian calendar has only one era, so Calendar.GetEra(DateTime.Now) isn't really necessary. I haven't found any further documentation yet.
Here are some samples that all work and will be parsed to christmas 2017:
DateTime christmas = DateTime.Parse("ad25ad12ad2017ad");
christmas = DateTime.Parse("AD25ad12ad2017");
christmas = DateTime.Parse("25ad12ad2017AD");
christmas = DateTime.Parse("25ad12ad2017");
christmas = DateTime.Parse("A.D.25ad12ad2017");
christmas = DateTime.Parse("A.D.25ad12ad"); // current year is used
christmas = DateTime.Parse("A.D.25ad12"); // current year is used
You can confirm that this is era and not some UTF encoded character by modifying culture abbreviated era name (era name is stored in DateTimeFormatInfo.m_abbrevEraNames and DateTimeFormatInfo.m_abbrevEnglishEraNames private fields, and for invariant culture abbreviated era name is string array with just one value - "AD"). m_eraNames field also stores full (non-abbreviated) era name ("A.D." for invariant culture) which can also be used instead of "AD".
var cul = (CultureInfo) CultureInfo.InvariantCulture.Clone();
// set DateTimeFormatInfo.AbbreviatedEraNames to "BLA"
typeof(DateTimeFormatInfo).GetField("m_abbrevEraNames", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(cul.DateTimeFormat, new string[] {"BLA"});
// set DateTimeFormatInfo.AbbreviatedEnglishEraNames to "BLA"
typeof(DateTimeFormatInfo).GetField("m_abbrevEnglishEraNames", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(cul.DateTimeFormat, new string[] { "BLA" });
var date = DateTime.Parse("AD03AD08", cul); // now it fails
var date = DateTime.Parse("A.D.03A.D.08", cul); // still works because we
// did not modify non-abbreviated era name
var date = DateTime.Parse("BLA03BLA08", cul); // this one works
Now why it treats era name like that is not quite obvious... Probably after meeting such token it sets date era and continues parsing, so it serves as separator in a sense it just moves to parsing next token after this one. Documentation for DateTime.Parse states that:
This method attempts to parse string completely and avoid throwing a
FormatException. It ignores unrecognized data if possible and fills in
missing month, day, and year information with the current date
While this does not mention anything about eras - such behavior aligns with "avoid throwing FormatException whenever possible" design.
I'm generating a .ics document using iCal.Net.
Event calendarEvent = new Event
{
IsAllDay = false,
Summary = "summary",
Description = "summary",
DtStart = new CalDateTime(TimeZoneInfo.ConvertTimeFromUtc(start,TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), "America/New_York"),
DtEnd = new CalDateTime(TimeZoneInfo.ConvertTimeFromUtc(end, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), "America/New_York")
};
var calendar = new Calendar();
calendar.AddTimeZone(new VTimeZone("America/New_York"));
calendar.Events.Add(calendarEvent);
var serializer = new CalendarSerializer(new SerializationContext());
return serializer.SerializeToString(calendar);
Usually, the resulting start and end date look similar to this:
DTEND;TZID=America/New_York:20170106T132000
DTSTAMP:20170104T005548Z
DTSTART;TZID=America/New_York:20170106T130000
However, they are occasionally generated like this:
DTEND;TZID=America/New_York:20180105T002000
DTSTAMP:20170105T191635Z
DTSTART;TZID=America/New_York;VALUE=DATE:20180105
In particular, notice the VALUE=DATE:20180105 next to DTSTART. For whatever reason, this causes most calendar applications to read the event as an all day event (even though, as you can see above, I explicitly say it isn't).
What's going on?
EDIT:
The start dates are June 1, 2017, 1PM for the first example, and May 1, 2018, 12AM for the second. I think it has something to do with the fact that in the second datetime, the time is the default value
Apparently since date + 12AM is 0 ticks after date, iCal.net assumes that you are referring to date (without the time) when you create an instance of CalDateTime.
If you look at the object properties on new CalDateTime(TimeZoneInfo.ConvertTimeFromUtc(start,TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), "America/New_York") where start is at 12AM, you can see that HasTime is false.
Fortunately, this property is get; set;. So simply set it to true, and the problem vanishes.
I am trying to output a Hebrew calendar date in English with C#. The following outputs the date in Hebrew:
var ci = System.Globalization.CultureInfo.CreateSpecificCulture("he-IL");
ci.DateTimeFormat.Calendar = new System.Globalization.HebrewCalendar();
Response.Write(DateTime.Today.ToString("MMM d, yyyy", ci));
Response.Write(DateTime.Today.ToString("d-M-y", ci));
Gives
כסלו כ"ו, תשע"ה
כ"ו-ג'-תשע"ה
for December 18, 2014. Change the CultureInfo to "en-US" raises an "Not a valid calendar for the given culture." error. I am trying to get
26 Kislev 5775
and
26-09-5775
I could not figure out how to set the array of month names for leap years or the array of day numbers so that they are rendered as English numbers rather than hebrew letters. My solution was:
Globals.cs
public static string[] HebrewMonthNames =
{
"Tishrei",
"Cheshvan",
"Kislev",
"Tevet",
"Shevat",
"Adar",
"Nissan",
"Iyar",
"Sivan",
"Tamuz",
"Av",
"Elul"
};
public static string[] HebrewMonthNamesLeapYear =
{
"Tishrei",
"Cheshvan",
"Kislev",
"Tevet",
"Shevat",
"Adar I",
"Adar II",
"Nissan",
"Iyar",
"Sivan",
"Tamuz",
"Av",
"Elul"
};
Utils.cs
public string FormatHebrewDate(DateTime dtGregorian)
{
System.Globalization.HebrewCalendar hCal = new System.Globalization.HebrewCalendar();
string sDate = hCal.GetDayOfMonth(dtGregorian).ToString() + " ";
if (hCal.IsLeapYear(hCal.GetYear(dtGregorian)))
{
sDate += Globals.HebrewMonthNamesLeapYear[hCal.GetMonth(dtGregorian) - 1];
}
else
{
sDate += Globals.HebrewMonthNames[hCal.GetMonth(dtGregorian) - 1];
}
sDate += " " + hCal.GetYear(dtGregorian).ToString();
return sDate;
}
Option 1:
You can override the DateTimeFormatInfo.MonthNames and MonthGenitiveNames properties as well as their corresponding AbbreviatedMonthNames and AbbreviatedMonthGenitiveNames properties.
They are simple 1-dimensional string[] arrays and have a public setters, which allows you to add your custom translations to the CultureInfo:
When this property is set, the array must be one-dimensional and must
have exactly 13 elements. Calendar objects accommodate calendars with
13 months. The first element (the element at index zero) represents
the first month of the year defined by the Calendar property.
If you set the MonthNames property, you must also set the
MonthGenitiveNames property.
If the custom pattern includes the format pattern "MMMM",
DateTime.ToString displays the value of MonthNames in place of the
"MMMM" in the format pattern.
This property is affected if the value of the Calendar property
changes.
So you could modify your code example to this:
// I am just using German Number representations for the example.
// Use additional string Arrays to suit the abbrevated
// and the Genetive names.
// Replaye with whatever suits your needs.
string[] monthNames =
{
"Eins",
"Zwei",
"Drei",
"Vier",
"Fünf",
"Sechs",
"Sieben",
"Acht",
"Neun",
"Zehn",
"Elf",
"Zwölf",
string.Empty
};
// Assign each string Array to its corresponding property.
// I am using the same Array here just as an example for
// what is possible and because I am lazy... :-)
ci.DateTimeFormat.MonthNames = monthNames;
ci.DateTimeFormat.MonthGenitiveNames = monthNames;
ci.DateTimeFormat.AbbreviatedMonthNames = monthNames;
ci.DateTimeFormat.AbbreviatedMonthGenitiveNames = monthNames;
These names will then be used in with your format string in the output, just as you want it to have.
Each time you change the calendar, these overrides will be lost. So you need to make sure to re-assign the custom values if you need it.
[Update] Option 2:
A more persistent approach might be to use the CultureAndRegionInfoBuilder Class.
Defines a custom culture that is new or based on another culture and
country/region. The custom culture can be installed on a computer and
subsequently used by any application that is running on that computer.
You can either create a complete replacement version of the "he-IL" culture or create a variation with just your custom translations, or anything in between.
Using this approach you do not have to manually make sure that the translations are in place after each Culture-switch in the appliaction like in Option 1. Once the new Custom Culture is registered, you can use it like any other CultureInfo.
Please note that your application will need administrative priviledges to register a new Custom Culture.
The creation of a Custom Culture is not too complicated as the following code snippet shows.
Example from MSDN: CultureAndRegionInfoBuilder
The following example defines a custom ru-US culture that represents
the Russian language in the United States. The example defines the
custom culture by loading settings from the Russian (Russia)
CultureInfo object and the U.S. RegionInfo object, and then sets a
number of CultureAndRegionInfoBuilder properties. The example
registers the custom culture, and then instantiates it and makes it
the current thread culture.
using System;
using System.Globalization;
using System.Threading;
public class Example
{
public static void Main()
{
// Create a custom culture for ru-US.
CultureAndRegionInfoBuilder car1 = new CultureAndRegionInfoBuilder("ru-US",
CultureAndRegionModifiers.None);
car1.LoadDataFromCultureInfo(CultureInfo.CreateSpecificCulture("ru-RU"));
car1.LoadDataFromRegionInfo(new RegionInfo("en-US"));
car1.CultureEnglishName = "Russian (United States)";
car1.CultureNativeName = "русский (США)";
car1.CurrencyNativeName = "Доллар (США)";
car1.RegionNativeName = "США";
// Register the culture.
try {
car1.Register();
}
catch (InvalidOperationException) {
// Swallow the exception: the culture already is registered.
}
// Use the custom culture.
CultureInfo ci = CultureInfo.CreateSpecificCulture("ru-US");
Thread.CurrentThread.CurrentCulture = ci;
Console.WriteLine("Current Culture: {0}",
Thread.CurrentThread.CurrentCulture.Name);
Console.WriteLine("Writing System: {0}",
Thread.CurrentThread.CurrentCulture.TextInfo);
}
}
// The example displays the following output:
// Current Culture: ru-US
// Writing System: TextInfo - ru-US
I know this isn't an ideal answer, but you could manually input the list of Hebrew months, and use DateTime.Today.Month as an index into that list. Similarly, DateTime.Today.Day and .Year give integer output that you can use. Sorry, it seems a bit wrong to roll your own formatting, doesn't it?
You could still use string.format() to ensure it looks the way you want.
You can use this (I know it's not c#, but you should be able to get what I'm doing here):
Dim c As New CultureInfo("he-IL")
c.DateTimeFormat.Calendar.ToDateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, Now.Second, Now.Millisecond).ToString("MMMM", New CultureInfo("en-GB"))
MessageBox.Show(c.DateTimeFormat.Calendar.ToDateTime(Now.Year, Now.Month, Now.Day, Now.Hour, Now.Minute, Now.Second, Now.Millisecond).ToString("MMMM", New CultureInfo("en-GB")))
But it will give you the Gregorian Calendar name (December)
piojo's suggestion to build up a dictionary object that contains the English version of the Hebrew name might work better
I do not believe that .NET has culture information that you want to use. However, you can create your own CultureInfo and modify the DateTimeFormat to suit your needs:
var cultureInfo = CultureInfo.CreateSpecificCulture("he-IL");
cultureInfo.DateTimeFormat.Calendar = new HebrewCalendar();
cultureInfo.DateTimeFormat.AbbreviatedMonthNames = new[] {
"Translation of תשרי",
"Translation of חשון",
// 11 more elements
};
cultureInfo.DateTimeFormat.AbbreviatedMonthGenitiveNames = new[] { ... };
cultureInfo.DateTimeFormat.MonthNames = new[] { ... };
cultureInfo.DateTimeFormat.MonthGenitiveNames = new[] { ... };
(Sorry for not providing the correct translations but I do not know Hebrew.)
You can then use this cultureInfo exactly as you do in your question.
If required you can also modify the day names in a similar fashion.
It is important that the calendar is set before modifying the various month and date name properties. The number of expected entries in the month name arrays changes as the calendar changes.
I currently have a program that takes the value from a datePicker and have the date saved as a string. I only needed the date not the time so i used the following code to save the date value:
DateTime StartDate;
String inMyString;
savedDate = datePicker1.SelectedDate.Value.Date;
inMyString = savedDate.Date.ToShortDateString()
I have the inMyString pushedBack into my list and now i want to place it back into the datePicker.
On MSDN is shows me the following example to set the date.
dateTimePicker1.Value = new DateTime(2001, 10, 20);
the problem is that having .Value after my date picker is not an option (it doesn't show in Intellisense.)
I have also tried
datePicker1.SelectedDate.Value= new DateTime(inMyString)
and also converting the inMyString to type DateTime but it still does not work.
Any thoughts on how to do this?
Any Suggestions and comments are appreciated.
Thanks!
Try this:
datePicker1.SelectedDate = new DateTime(2001, 10, 20);
If you need to take datetime from string:
datePicker1.SelectedDate = DateTime.Parse(inMyString);
Side note:
You can replace those 3 lines:
String inMyString;
savedDate = datePicker1.SelectedDate.Value.Date;
inMyString = savedDate.Date.ToShortDateString();
with one:
var inMyString = datePicker1.SelectedDate.Value.ToShortDateString();
Another side note: don't know if there is a reason to it, but you might consider storing datetime as dattime, not as a string.
If you want to show today's date in WPF C#, use this method:
datePicker1.SelectedDate = DateTime.Today.AddDays(-1);