iCalendar timezone definition for DST period - icalendar

By definition European Summer Time begins at last Sunday of March and ends by last Sunday of October
see: https://en.wikipedia.org/wiki/Summer_Time_in_Europe
iCalendar event generated from thunderbird's lightning calendar use this definition of timezone with timestamps from year 1970
BEGIN:VTIMEZONE
TZID:Europe/Prague
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
Since last Sunday of March and October probably won't be the same day every year, shouldn't there be different date every year?
For example for 2016:
BEGIN:VTIMEZONE
TZID:Europe/Prague
BEGIN:DAYLIGHT
...
DTSTART:20160327T020000
...
END:DAYLIGHT
BEGIN:STANDARD
...
DTSTART:20161030T030000
...
END:STANDARD
END:VTIMEZONE
Is it right or am I missing something?

The DTSTART indicates the start date, but after that RRULE is used to calculate all the next occurences.
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
This means:
Recur every year
On every 3rd month
On the last sunday of that month

Related

RRule for semi-weekly events

I've been trying to figure out how to compose an RRULE that has events on alternating days each week; for example:
Week A: Monday at 4pm
Week B: Friday at 4pm
Where Week A and Week B alternate continuously into the future.
The closest I've gotten is this:
RRULE:FREQ=WEEKLY;COUNT=30;INTERVAL=2;BYDAY=MO,FR
which is close, but gives me the monday and friday on the same week. How can I code it to select the second friday in the two-week interval? This seems much less complex than much of what can be done with RRULEs, so I'm likely missing something obvious?
If this is intended to go on forever, with no 'COUNT', then I think the only way you can do it is to have two events.
Using any other of the BY's even BYSETPOS gets one into trouble at some point at the end of the year, or the end of the sequence as far as you defined it. Two events allows one to iterate the Monday and the Friday cleanly.
The choice of DTSTART for each event is crucial to be sure that you are recurring the right Fridays after the Mondays. You have something like M fm FM fm FM fm
From
https://calendar.google.com/calendar/ical/eppua87dpfnq85bsrrt2siqlcs%40group.calendar.google.com/public/basic.ics
BEGIN:VEVENT
DTSTART:20211022T160000Z
DTEND:20211022T170000Z
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=FR
DTSTAMP:20211012T064353Z
UID:STACKTESTFRIDAY
CREATED:20171102T185254Z
DESCRIPTION:
LAST-MODIFIED:20211012T064141Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:Afternoon Tea on Friday
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART:20211011T160000Z
DTEND:20211011T180000Z
RRULE:FREQ=WEEKLY;WKST=MO;INTERVAL=2;BYDAY=MO
DTSTAMP:20211012T064353Z
UID:STACKTESTMONDAY
CREATED:20171102T185254Z
DESCRIPTION:
LAST-MODIFIED:20211012T064112Z
LOCATION:
SEQUENCE:2
STATUS:CONFIRMED
SUMMARY:Afternoon Tea on Monday
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
Here another option that might work for you. In a YEARLY rule you can prepend the BYDAY elements with an offset n to identify the n-th instance of that weekday in the year.
If your recurring event starts on the 20th Monday of the year you could build a rule like this to iterate 30 instances with alternating weekdays.
RRULE:FREQ=YEARLY;BYDAY=20MO,21FR,22MO,23FR,24MO,25FR,26MO,27FR,28MO,29FR,30MO,31FR,32MO,33FR,34MO,35FR,36MO,37FR,38MO,39FR,40MO,41FR,42MO,43FR,44MO,45FR,46MO,47FR,48MO,49FR
Note, this assumes the year starts on a Saturday, Sunday or Monday.
If the year starts on a Tuesday, Wednesday, Thursday or Friday, the weekdays have to have the same number (because in that case the n-th Monday and the n+1-th Friday occur in the same week), i.e.
RRULE:FREQ=YEARLY;BYDAY=20MO,22FR,22MO,24FR,24MO,26FR,26MO,28FR,28MO,30FR,30MO,32FR,32MO,34FR,34MO,36FR,36MO,38FR,38MO,40FR,40MO,42FR,42MO,44FR,44MO,46FR,46MO,48FR,48MO,50FR

How to fix incorrect Date due to timezone in Core Data from using Calendar.current.startOfDay?

I have erroneously used Calendar.current.startOfDay(for: Date()) to populate a date attribute in Core Data. This means that when users cross different timezones I may have different dates unintentially stored in the date attribute field e.g.
Timezone 1 - 25th 23:00
Timezone 2 - 25th 22:00
Timezone 3 - 26th 05:00
I need to update the Calendar to use UTC Timezone but I need to also perform a migration so that the existing entries in Core Data read like this…
Result:
Timezone 1 - 26th 00:00
Timezone 2 - 26th 00:00
Timezone 3 - 26th 00:00
What are the steps to perform this migration. If I do a UTC startOfDay on it Timezone 1 would get 25th 00:00 instead of 26th 0:00 which is what it should be. Is it possible to accurately update existing entries?
Edit:
For some context I need a reliable way to get all the entries for the 26th for example. I used startOfDay to store the date as it meant I could query by it too and have the relevant entry returned (at any moment in time get the startOfDay and it will give me the entries for the whole day). For historical dates I can do the same - let's say the user has navigated back 2 days I can take startOfDay and subtract 2 days using Calendar.current.date(byAdding: .day, value: -2, to: date) and query for that.
So now the timezone breaks the above logic but is there some way to fix this? If I loop through the entries I can figure out the date it was supposed to be for and perhaps change the attribute to a string - e.g. 26-05-2021 or start to store day, month, year instead and query that.
From reading your answer Duncan I don't think I want to use UTC calendar as it would start to store the entry against the incorrect date from the users perspective dependent on their timezone e.g. user moves to next day and utc is still on previous.
Edit 2:
In a migration I will take the date that is stored and map it to new day, month and year properties storing those instead by getting them from Calendar.current.dateComponents([.day, .month, .year], from: date). Then instead of query by date I will query by day month and year of the Calendar.current where the user is. The side effect here is there is potential the user adds something for today (27th) changes timezone and sees 26th data but I don't think it can be avoided and the old data will then show as intended.
If you took the current time and used Calendar.current.startOfDay(for: Date()) to calculate midnight in the user's local time zone, you have a loss of information. You don't know what time of day the operation was performed. If you saved the time of day in the local time zone in another field, you could reconstruct a Date in UTC.
It isn't clear that what you did was wrong. The day, month, and year is only meaningful in a specific time zone. I am in the Washington DC metro area. We are in daylight savings time (EDT). It is currently 20:56 on the 26th of May. However, it's 1:56 AM on the 27th of May in London, 2:57 AM in Munich, and 3:57 AM in Tel Aviv. All at the exact same instant in time. In UTC it is 0:57 AM on the 27th of May.
Most people think of the calendar date in their local time zone. That is their frame of reference. If you ask me the date right now, I'll tell you it's the evening of the 26th of May. Unless I know you are in a different time zone, that's the "right" answer to me.
If I start out at midnight on a given day in my time zone, calling Calendar.current.startOfDay(for: Date()) each hour, I'll get midnight that day for all 24 hours in my local time zone. For the first 20 hours of the day, that would be the same result I would get if I created a Calendar in UTC and made the same call. However, at 20:00 EDT, I would start getting the next calendar day if I made the same query in UTC.
If you don't know what time of day you made the call to Calendar.current.startOfDay(for: Date()), there is no foolproof to figure out the day/month year in UTC at the instant you made the call. It depends on the time of day in the local timezone, and that timezone's offset from UTC.
Consider this code:
var calendarUTC = Calendar(identifier: .gregorian)
if let utcTimeZone = TimeZone(identifier: "UTC") {
print("Valid time zone")
calendarUTC.timeZone = utcTimeZone
}
print ("Start of day in UTC is \(calendarUTC.startOfDay(for: Date()))")
print ("Start of day in local time zone is \(Calendar.current.startOfDay(for: Date()))")
That outputs:
Start of day in UTC is 2021-05-27 00:00:00 +0000
Start of day in local time zone is 2021-05-26 04:00:00 +0000
That's because right now, which is 20:56 on 26 May in my time zone, it's 0:56 on 27 May in UTC. So if I ask the UTC calendar for the start of day for now (Date()) I get midnight on 27 May, in UTC.
If I ask the same question of my local calendar, I get midnight on 26 may in my time zone, which is 4:00 AM on 26 May in UTC.
If I ran the same code this morning at 8:00 AM in my time zone, I would have gotten the output:
Start of day in UTC is 2021-05-26 00:00:00 +0000
Start of day in local time zone is 2021-05-26 04:00:00 +0000
(Since at 8:00 AM on 26 May in EDT is also 26 May in UTC.)
It's tricky and not 100% reliable and only works if you know that all days were created using startOfDay. But first you need to decide what you want. Say one date was created at 10pm in the New York, and one at exactly the same moment in London, at 4am the next day. What day do you want to be stored?
If your date stored is 25th, 10pm, then you know it was created in a timezone where the day started at 10pm UTC. You are lucky, there are only two time zones that would have created this, one without DST, one with DST. So you know it happened in one of these two time zones, within 24 hours.
Unfortunately, time zones cover 26 hours. Fortunately, only some islands in the Pacific Ocean have same time and different dates (+13 and -11 hours). For these places, you cannot possibly know which date is correct, but very few people would be affected.

Trigger Azure Data Factory Pipeline on second Friday from end of month and on Tues, Wed and Thurs of the same week

I need a Pipeline to trigger on the Monthend week (Tues, Wed, Thurs and Friday) of a month.
Monthend is defined as,
"Last but one" Friday Or
Second Friday from the end of the month.
For Example, For month of June 2021, 18th is the Monthend (Orange color as shown in the image)
Calendar Image
If its just on Monthend i.e. Second Friday from the end of calendar month, its easy. Just use Occurrance as -2 and day as Friday in the Scheduled trigger and add to a pipeline to trigger,
"schedule": {
"monthlyOccurrences": [
{
"day": "Friday",
"occurrence": -2
}
]
}
but I also need to run on the Tues, Wed and Thurs of the same week, which I find it difficult as these weekdays can be second or third from the end of the calendar month. For example: For June 2021, as shown in the image, I also need to run on 15th (Third Tuesday from the end of calendar month), 16th (Third Wednesday from the end of calendar month), 17th (Second Thursday from the end of calendar month).
Can you let me know if this can be implemented using triggers of Azure data factory? If not, any otherways of implementing? Thank You!
The scheduled trigger alone is not capable of that logic (as of 2021-05-04). Easiest solution would be to use some other scheduling application.
For a purely Data-Factory solution, schedule the trigger for all the days the desired days could possibly occur on. Then modify the pipeline to do logic to determine whether the current day is actually one of the desired days.
Implementation details and sample code
The logic:
Find the last day of the month (First of the next month less 1 day).
Subtract a week so you are in the second-to-last week
Loop over [0,-1,-2,-3,-4,-5,-6] as number of days to add to the date. This produces the dates of each day of the week.
Use the dayOfWeek function to change the date into which day of the week it is
Filter to get the Friday date
Ask whether today is between the Friday date and Friday date - 3 days

Define time intervals using day of week/month (ISO 8601?)

I want to define time intervals like:
Each week, from Monday 12:00 a.m. to Sunday 11:59:59 p.m.
Each day from 12:00 a.m. to 11:59:59 p.m.
Each month from the first day of the month 12:00 a.m. to the last day of the month 11:59:59 p.m.
Is this possible using ISO 8601, or any other well-known standard?
You're asking for repeating periods of a week, a day and a month. The period should be defined as P1W, P1D or P1M respectively. Given an appropriate start point, I'd expect things such as the following to work:
R/2015-02-16/P1W - repeating weekly periods, starting on a known Monday.
R/2015-02-17/P1D - repeating daily periods, starting today
R/2015-02/P1M - repeating monthly periods, starting this month
These should be ISO 8601 compliant.

Appointment in calendars use wrong time from ics file

I am generating a .ics file. When I import it to a calendar app (such as the on on OSX or iOS) it says:
20:00 to 21:00
18:00 to 19:00 (GMT)
But I need to have the time of the appointment to be 18:00 to 19:00 by default (GMT time). How can I do it?
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:xxx
X-WR-TIMEZONE:Europe/Zurich
X-WR-CALDESC:
BEGIN:VTIMEZONE
TZID:Europe/Zurich
X-LIC-LOCATION:Europe/Zurich
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
Event:
BEGIN:VEVENT
DTSTART:20140929T180000Z
DTEND:20140929T190000Z
DTSTAMP:20141001T223333Z
UID:542c651df4095
DESCRIPTION:Keine
SUMMARY:Mindblowers
LOCATION:
URL;VALUE=URI:xxx
END:VEVENT
most calendar applications will display in the timezone that you have set in the app. So to see times in GMT time, set the timezone of the app to GMT.
Alternatively you may not really be meaning that you want 18:00 to 19:00 (GMT) but that you want 18:00 to 19:00 no matter what timezone the app is in. This is called a 'floating time' (like an alarm that wakes you at 7am no matter what city or timezone you are in, no at 7am back home).
https://www.rfc-editor.org/rfc/rfc5545#page-32
For example, the following represents January 18, 1998, at
11 PM:
19980118T230000
DATE-TIME values of this type are said to be "floating" and are
not bound to any time zone in particular. They are used to
represent the same hour, minute, and second value regardless of
which time zone is currently being observed.