Problem with converting time according to timezone in C# - c#

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"));

Related

C# Script task in SSIS - Parsing a DateTime to get the hrs and mins and convert to an integer

I am currently checking the current date and time against the date and time appended to the end of a file name. I need to check the timing difference between the current time and the time at the end of the filename in order to see if there is greater than an hours difference between the two.
I need to Parse the hh:mm from the currenctTime variable, convert it to an int and compare it to the creationTime variable. Does anybody know how to do this?
string FileLocation = Dts.Variables["User::SAPGLDir"].Value.ToString();
string hrDifference = Dts.Variables["User::hrDifference"].Value.ToString();
bool lateFileInFolder = (bool)Dts.Variables["User::lateFileInFolder"].Value;
DateTime currentTime = DateTime.Now;
try
{
// get a list of all the files in the SAPGLDir
string[] inputFileList = Directory.GetFiles(#FileLocation);
//default state of boolean
lateFileInFolder = false;
//if there are files in the folder the do the following steps
if (inputFileList.Length > 0)
{
//foreach file name in the directory
foreach (string inputFileName in inputFileList)
{
//selecting the hour from the filename
string creationTime = inputFileName.Substring(24, 4);
//if for any file in the folder that the difference in time is greater than the hrDifference then set boolean lateFileInFolder = true
if ( )
{
lateFileInFolder = true;
}
//MessageBox.Show(inputFileName.ToString() + " " + creationHour.ToString());
}
}
I would suggest you to avoid script task in ssis. I learned it in my working experience with ssis.
Script task is sometimes risky as it can generate exceptions at any time because of multiple reasons mostly at the time of environment migration.
SSIS gives you a better solution. you can use expression task for extracting the date time part from the feed file name and comparing it with the current date time.
This would be easier as well as hassle free.
Hope, this would help you

Showing current time depending on region for Windows 10 UWP app

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);

Utc Date saving as Local date in Sqlite

I have an Sql database which contains a date field.
I use Dapper to update the database as follows:
const string sql = "UPDATE AdminDb.Users " +
"SET IsLoggedOn = 1, LastLoggedOn = #LastLoggedOn " +
"WHERE Username = #username";
var date = DateTime.UtcNow;
DatabaseConnectionBase.DatabaseConnection.Execute(sql, new { username, LastLoggedOn = date });
I am finding to my great annoyance when breaking before the actual update, the date variable reads 30/3/2015 9:32:54 however when I run the update the database saves the date as 30/3/2015 10:32:54
As the UK yesterday changed from GMT to BST (UTC +1) I am sure that the database seems to be trying to compensate for this as this issue never came up before.
I thought I had averted this sort of issue by using the DateTime.UtcNow property to save my date.
This is causing serious issues when validating users.
I am sure it is not my code as the date is correct going into the Dapper Execute method.
I can't see why Dapper would try to compensate as most developers would be screaming at such functionality
Which leads me to the conclusion that it must be something in Sqlite that is causing this issue. Perhaps there is a pragma I need to run?
As per a suggestion from another site I tried formatting the date as follows:
var date = DateTime.UtcNow.ToString("o");
The intention being to force the date into ISO-8601 format but I had no luck with that.
Has anyone got any ideas?
Thomas Levesque has the solution here:
// You just need to specify DateTimeKind=Utc in your connection string:
string connectionString = #"Data Source=D:\tmp\testSQLiteDate.db;DateTimeKind=Utc";
This happened to me too.
What I did was serialize the datetime to a string myself before adding it as a parameter.
internal const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
cmd.Parameters.Add("#Threshold", DbType.DateTime).Value = threshold.ToString(DateTimeFormat);
If you do the same thing with pure ADO.NET, does the same thing happen? I wonder if this is a database thing or a provider thing, rather than a library thing. Dapper has to ToLocalTime() or ToUniversalTime() calls - it passes the time through unaltered. On SQL Server, the following works fine in a BST setting:
public void SO29343103_UtcDates()
{
const string sql = "select #date";
var date = DateTime.UtcNow;
var returned = connection.Query<DateTime>(sql, new { date }).Single();
var delta = returned - date;
Assert.IsTrue(delta.TotalMilliseconds >= -1 && delta.TotalMilliseconds <= 1);
}

TimeZone conversion displaying EDT instead of EST

I am using this piece of code to convert "Eastern Time Zone" to "EST". Now it is showing "EDT". You dont see that abbr that often in places and would like to stick to "EST". How do I do this with NodaTime?
public static string GetTimeZoneAbbr(string timeZone)
{
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
if (timeZoneInfo != null)
{
var dateTime = DateTime.UtcNow;
var instant = Instant.FromDateTimeUtc(dateTime);
var tzdbSource = TzdbDateTimeZoneSource.Default;
var tzid = tzdbSource.MapTimeZoneId(timeZoneInfo);
var dateTimeZone = DateTimeZoneProviders.Tzdb[tzid];
var zoneInterval = dateTimeZone.GetZoneInterval(instant);
return zoneInterval.Name;
}
return string.Empty;
}
UPDATE
The answer below described how to parse and use the CLDR data. This is fine, but I've made it much easier by encompassing this all in a library. See this StackOverflow answer, read my blog post, and take a look at the TimeZoneNames library. Using this library is much easier than parsing the CLDR data yourself.
// You can pass either type of time zone identifier:
var tz = "America/New_York"; // IANA
var tz = "Eastern Standard Time"; // Windows
// You can get names or abbreviations for any language or locale
var names = TZNames.GetNamesForTimeZone(tz, "en-US");
var abbreviations = TZNames.GetAbbreviationsForTimeZone(tz, "en-US");
names.Generic == "Eastern Time"
names.Standard == "Eastern Standard Time"
names.Daylight == "Eastern Daylight Time"
abbreviations.Generic == "ET"
abbreviations.Standard == "EST"
abbreviations.Daylight == "EDT"
ORIGINAL ANSWER
I've written a bit in the question comments about why it's perfectly valid to show the abbreviated form, but allow me to also answer the question as it was asked.
Reiterating your question another way, you wish to start with a Microsoft Windows time zone id, and end up with a human-readable string that represents the entire time zone, and not just the time zone segment that is in effect.
You could just give them the TimeZoneInfo.DisplayName, but that isn't always going to be appropriate. For the US, you might get a display name back of "(UTC-05:00) Eastern Time (US & Canada), and you could strip off the leading offset and parenthesis to just give back "Eastern Time (US & Canada)". But that's not going to work for all time zones, since many just have display names that list cities, such as "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan".
A better approach would be to use the data from the Unicode CLDR Project. Noda Time has a portion of this data, but not everything you need for this particular problem. So I can't give you a code example that uses Noda Time. However, you can use the following steps against the raw CLDR data to achieve your goal:
Find the IANA time zone ID corresponding to the Windows time zone, such as you've already done in the code above, or using the CLDR Windows time zone mappings directly.
Lookup the IANA time zone in the CLDR MetaZones file.
Lookup the MetaZone in one of the CLDR translation data files, or charts such as this one. Use the "generic-long" or "generic-short" pattern, and the language of your choice, such as "en" for English.
So, in your case, starting with the Windows TimeZoneInfo.Id of "Eastern Standard Time":
IANA Zone = "America/New_York"
CLDR MetaZone = "America_Eastern"
generic-long [en] = "Eastern Time"
generic-short [en] = "ET"
Note that not every Windows time zone is mappable to an IANA zone, not every meta zone has a short name, and some zones that have never followed daylight saving time will only have a standard name instead of a generic name.
Here is some C# code that shows how to traverse the CLDR's XML data to get the generic long names for the TimeZoneInfo objects. It assumes you have access to the CLDR data at the path specified. Download the latest core.zip and extract, then point the basePath at that folder.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
static Dictionary<TimeZoneInfo, string> GetCldrGenericLongNames(string basePath, string language)
{
// Set some file paths
string winZonePath = basePath + #"\common\supplemental\windowsZones.xml";
string metaZonePath = basePath + #"\common\supplemental\metaZones.xml";
string langDataPath = basePath + #"\common\main\" + language + ".xml";
// Make sure the files exist
if (!File.Exists(winZonePath) || !File.Exists(metaZonePath) || !File.Exists(langDataPath))
{
throw new FileNotFoundException("Could not find CLDR files with language '" + language + "'.");
}
// Load the data files
var xmlWinZones = XDocument.Load(winZonePath);
var xmlMetaZones = XDocument.Load(metaZonePath);
var xmlLangData = XDocument.Load(langDataPath);
// Prepare the results dictionary
var results = new Dictionary<TimeZoneInfo, string>();
// Loop for each Windows time zone
foreach (var timeZoneInfo in TimeZoneInfo.GetSystemTimeZones())
{
// Get the IANA zone from the Windows zone
string pathToMapZone = "/supplementalData/windowsZones/mapTimezones/mapZone" +
"[#territory='001' and #other='" + timeZoneInfo.Id + "']";
var mapZoneNode = xmlWinZones.XPathSelectElement(pathToMapZone);
if (mapZoneNode == null) continue;
string primaryIanaZone = mapZoneNode.Attribute("type").Value;
// Get the MetaZone from the IANA zone
string pathToMetaZone = "/supplementalData/metaZones/metazoneInfo/timezone[#type='" + primaryIanaZone + "']/usesMetazone";
var metaZoneNode = xmlMetaZones.XPathSelectElements(pathToMetaZone).LastOrDefault();
if (metaZoneNode == null) continue;
string metaZone = metaZoneNode.Attribute("mzone").Value;
// Get the generic name for the MetaZone
string pathToNames = "/ldml/dates/timeZoneNames/metazone[#type='" + metaZone + "']/long";
var nameNodes = xmlLangData.XPathSelectElement(pathToNames);
var genericNameNode = nameNodes.Element("generic");
var standardNameNode = nameNodes.Element("standard");
string name = genericNameNode != null
? genericNameNode.Value
: standardNameNode != null
? standardNameNode.Value
: null;
// If we have valid results, add to the dictionary
if (name != null)
{
results.Add(timeZoneInfo, name);
}
}
return results;
}
Calling this will get you a dictionary which you can then use for lookups. Example:
// load the data once an cache it in a static variable
const string basePath = #"C:\path\to\extracted\cldr\core";
private static readonly Dictionary<TimeZoneInfo, string> timeZoneNames =
GetCldrGenericLongNames(basePath, "en");
// then later access it like this
string tzname = timeZoneNames[yourTimeZoneInfoObject];

Retrieved date from a SharePoint list is 4 hours into the future

Weird bug,
When inserting a SharePoint list item I set the date submitted to DateTime.Now and it is working fine as when I check SharePoint I see the item inserted just fine with the correct date and all values.
oListItem["Date_x0020_Submitted"] = DateTime.Now;
But when I go to retrieve the exact same list item for some reason the date comes back as 4 hours into the future!!
query.ViewXml = "<View><Query><OrderBy><FieldRef Name='Date_x0020_Submitted' Ascending='FALSE' /></OrderBy></Query><RowLimit>1</RowLimit></View>";
SP.ListItemCollection issuesCollection = oList.GetItems(query);
MyContext.Load(issuesCollection, (items => items.Include(item => item["ID"], item => item["Date_x0020_Submitted"])));
Just to reiterate, after inserting the item I can see it in the list with the correct exact date inserted, but when I retrieve it for some reason it is 4 hours into the future. For example in SharePoint it shows me 10:53, but when I retrieve it shows 2:53.
Does anyone know why this might be happening? It happens for every single item that I insert and retrieve.
The time which you store is your local time (UTC-5:00, probably with DST).
Is there any chance that the time returned by Sharepoint is returned as UTC time? Kind property of DateTime class tells what kind of time it is (this has changed through different versions of .NET framework):
var kind = DateTime.Now.Kind;
You can convert the returned time to local time and see if it matches:
var localTime = utcTime.ToLocalTime();
As you say that your time is given as a string (although I suspect that as that format doesn't look like string), you can try to parse it:
string fmt = "M/d/yyyy h:mm:ss tt"; // this is equivalent to format you have shown
string stime = "9/14/2012 3:38:04 PM"; // your string here
var time = DateTime.ParseExact(time, fmt, CultureInfo.InvariantCulture);
var local = time.ToLocalTime(); // => 14.9.2012. 17:38:04 in my timezone

Categories

Resources