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.
Related
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);
I'm having an issue with the Monotouch UIDatePicker being 1 hour behind. I think this is to do with Time Zones or something similar. I've tried explicitly setting the TimeZone and Locale of my UIDatePicker but this doesn't seem to be helping.
datePicker.Locale = NSLocale.CurrentLocale;
datePicker.TimeZone = NSTimeZone.LocalTimeZone;
In the ValueChanged handler the following line returns a value 1 hour earlier than the time selected in the user interface:
var date = DateTime.SpecifyKind((s as UIDatePicker).Date, DateTimeKind.Local).ToLocalTime();
In the ValueChanged Handler I've double checked that the Locale and TimeZone is the same as what was set. The TimeZone is Europe/Dublin and Locale en_US.
This information was retrieved by:
datePicker.Locale.LocaleIdentifier;
datePicker.DatePicker.TimeZone;
Is there another step I'm missing?
Thanks!
The date returned from DatePicker is in UTC format. There are several methods of converting UTC to local time. As this answer states ToLocalTime is the best one.
DateTime.SpecifyKind(datePicker.Date, DateTimeKind.Utc).ToLocalTime();
After var date = DateTime ..., add the following:
if (date.IsDaylightSavingTime ())
date = date.AddHours (1);
This magical code fixed all my problems with daylight savings and timezones. Write if you need help.
NSDate sourceDate = date;
NSTimeZone sourceTimeZone = new NSTimeZone ("UTC");
NSTimeZone destinationTimeZone = NSTimeZone.LocalTimeZone;
int sourceGMTOffset = sourceTimeZone.SecondsFromGMT (sourceDate);
int destinationGMTOffset = destinationTimeZone.SecondsFromGMT (sourceDate);
int interval = destinationGMTOffset - sourceGMTOffset;
var destinationDate = sourceDate.AddSeconds (interval);
var dateTime = new DateTime(2001, 1, 1, 0, 0,0).AddSeconds(destinationDate.SecondsSinceReferenceDate);
DescriptionLabel.Text = dateTime.ToString ("dd.MM.yyyy. HH:mm");
this.date = destinationDate;
I have as an input:
The time (8:00AM)
An Olson Timezone (America/New_York)
and I need to convert the time into another Olson Timezone (America/Los_Angeles)
What is the best way in .net or nodatime to do that conversion. I am basically looking for the equivalent of this method in C#:
var timeInDestinationTimeZone = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(CurrentSlot.Date, TimeZoneInfo.Local.Id,
room.Location.TimeZone.TimeZoneName);
but this .Net method above only works with Windows Timezone names (and i have Olson names)
Observe:
var tzdb = DateTimeZoneProviders.Tzdb;
var zone1 = tzdb["America/New_York"];
var ldt1 = new LocalDateTime(2013, 3, 4, 8, 0); // March 4th, 2013 - 8:00 AM
var zdt1 = zone1.AtLeniently(ldt1);
var zone2 = tzdb["America/Los_Angeles"];
var zdt2 = zdt1.ToInstant().InZone(zone2);
var ldt2 = zdt2.LocalDateTime;
Notice the call to AtLeniently - that's because you don't have enough information to be absolutely certain of the moment in time you are talking about. For example, if you were talking about 1:30 AM on the day of a DST fall-back transition, you wouldn't know if you were talking about before or after the transition. AtLeniently will make the assumption you meant after. If you don't want that behavior, you have to provide an offset so you know which local time you were talking about.
The actual conversion is being done by ToInstant which is getting the UTC moment you're talking about, and then InZone which is applying it to the target zone.
An alternative to the second part of Matt's (perfectly good) answer:
// All of this part as before...
var tzdb = DateTimeZoneProviders.Tzdb;
var zone1 = tzdb["America/New_York"];
var ldt1 = new LocalDateTime(2013, 3, 4, 8, 0); // March 4th, 2013 - 8:00 AM
var zdt1 = zone1.AtLeniently(ldt1);
var zone2 = tzdb["America/Los_Angeles"];
// This is just slightly simpler - using WithZone, which automatically retains
// the calendar of the existing ZonedDateTime, and avoids having to convert
// to Instant manually
var zdt2 = zdt1.WithZone(zone2);
var ldt2 = zdt2.LocalDateTime;
I have users that can be in different timezones and I'm looking to determine the UTC value of the beginning of their days and their months. Inside an object, I have a method that attempts to do that; it looks like this:
private void SetUserStartTimesUTC()
{
DateTime TheNow = DateTime.UtcNow.ConvertUTCTimeToUserTime(this.UserTimezoneID);
DateTime TheUserStartDateUserTime = TheNow.Date;
DateTime TheUserStartMonthUserTime = new DateTime(TheNow.Year, TheNow.Month, 1);
DateTime TheUserEndMonthUserTime = TheUserStartMonthUserTime.AddMonths(1);
this.UserStartOfDayUTC = TheUserStartDateUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
this.UserStartOfMonthUTC = TheUserStartMonthUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
this.UserEndOfMonthUTC = TheUserEndMonthUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
}
And this method depends on two other extension methods that do the conversions between a user's time and UTC time
public static DateTime ConvertUserTimeToUTCTime(this DateTime TheUserTime, string TheTimezoneID)
{
TimeZoneInfo TheTZ = TimeZoneInfo.FindSystemTimeZoneById(TheTimezoneID);
DateTime TheUTCTime = new DateTime();
if (TheTZ != null)
{
DateTime UserTime = new DateTime(TheUserTime.Year, TheUserTime.Month, TheUserTime.Day, TheUserTime.Hour, TheUserTime.Minute, TheUserTime.Second);
TheUTCTime = TimeZoneInfo.ConvertTimeToUtc(UserTime, TheTZ);
}
return TheUTCTime;
}
public static DateTime ConvertUTCTimeToUserTime(this DateTime TheUTCTime, string TheTimezoneID)
{
TimeZoneInfo TheTZ = TimeZoneInfo.FindSystemTimeZoneById(TheTimezoneID);
DateTime TheUserTime = new DateTime();
if (TheTZ != null)
{
DateTime UTCTime = new DateTime(TheUTCTime.Year, TheUTCTime.Month, TheUTCTime.Day, TheUTCTime.Hour, TheUTCTime.Minute, 0, DateTimeKind.Utc);
TheUserTime = TimeZoneInfo.ConvertTime(UTCTime, TheTZ);
}
return TheUserTime;
}
Now I've been dealing with timezone issues for a while and I know that timezone issues can introduce off-by-one bugs that can be hard to detect.
Does my implementation of timezones seem to be correct or is there an edge case that will create some sort of off-by-one bug?
Thanks for your suggestions.
Your methods seem needlessly complicated, to be honest.
Why would you have a parameter called TheUTCTime and then create a UTC version of it? Shouldn't it already have a Kind of UTC? Even if it didn't, you would be better off using DateTime.SpecifyKind - currently when converting one way you wipe out the seconds, whereas converting the other way you don't... in both cases you wipe out any sub-second values.
Also:
TimeZoneInfo.FindSystemTimeZoneById never returns null
Returning new DateTime() (i.e. January 1st 0001 AD) if the time zone can't be found seems like a really poor way of indicating an error
There's no need to have a local variable in your conversion methods; just return the result of calling ConvertTime directly
Your "end of month" is really "start of the next month"; that may be what you want, but it's not clear.
Personally I would strongly advise you to avoid the BCL DateTime for all of this entirely. I'm entirely biased being the main author, but I'd at least hope that you'd find Noda Time more pleasant to work with... it separates out the idea of "date with no time component", "time with no date component", "local date and time with no specific time zone" and "date and time in a particular time zone"... so the type system helps you to only do sensible things.
EDIT: If you really have to do this within the BCL types, I'd write it like this:
private void SetUserStartTimesUTC()
{
DateTime nowUtc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(UserTimeZoneID);
// User-local values, all with a Kind of Unspecified.
DateTime now = TimeZoneInfo.ConvertTime(nowUtc, zone);
DateTime today = now.Date;
DateTime startOfThisMonth = todayUser.AddDays(1 - today.Day);
DateTime startOfNextMonth = startOfThisMonth.AddMonths(1);
// Now convert back to UTC... see note below
UserStartOfDayUTC = TimeZoneInfo.ConvertTimeToUtc(today, zone);
UserStartOfMonthUTC = TimeZoneInfo.ConvertTimeToUtc(startOfThisMonth, zone);
UserEndOfMonthUTC = TimeZoneInfo.ConvertTimeToUtc(startOfNextMonth, zone);
}
The extension methods you've added really don't provide much benefit, as you can see.
Now, the code mentions a "note" - you're currently always assuming that midnight always exists and is unambiguous. That's not true in all time zones. For example, in Brazil, on daylight saving changes forward, the time skips from midnight to 1am - so midnight itself is invalid, basically.
In Noda Time we fix this by having DateTimeZone.AtStartOfDay(LocalDate) but it's not as easy with the BCL.
For comparison, the equivalent Noda Time code would look like this:
private void SetUserStartTimesUTC()
{
// clock would be a dependency; you *could* use SystemClock.Instance.Now,
// but the code would be much more testable if you injected it.
Instant now = clock.Now;
// You can choose to use TZDB or the BCL time zones
DateTimeZone zone = zoneProvider.FindSystemTimeZoneById(UserTimeZoneID);
LocalDateTime userLocalNow = now.InZone(zone);
LocalDate today = userLocalNow.Date;
LocalDate startOfThisMonth = today.PlusDays(1 - today.Day);
LocalDate startOfNextMonth = startOfThisMonth.PlusMonths(1);
UserStartOfDayUTC = zone.AtStartOfDay(today);
UserStartOfMonthUTC = zone.AtStartOfDay(startOfThisMonth);
UserEndOfMonthUTC = zone.AtStartOfDay(startOfNextMonth);
}
... where the properties would be of type ZonedDateTime (which remembers the time zone). You could change them to be of type Instant (which is just a point in time) if you want, just chaining a ToInstant call for each property setter.
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);