I'm running into a timezone issue when running my Flows on a schedule - and I can't seem to find a solution for it.
My timezone is currently +10:00 from UTC - Sydney, Australia.
For simplicities sake, I have a reoccurrence trigger running at 06:00 (6am local time) and 12:00 (12pm local time).
When I run my Actions - anything that needs a StartTime and an EndTime - I pass in the following parameters:
StartTime
Endtime
formatDateTime(utcNow(), 'yyyy-MM-dd"T"22:00:00.0000000Z')
formatDateTime( addDays(utcNow(), 1), 'yyyy-MM-dd"T"08:00:00.0000000Z')
The 22:00:00.0000000Z and 08:00:00.0000000Z above translate directly to 8am and 6pm respectively - essentially the entire work day hours.
Issue
When the trigger runs on the first occurrence, the StartTime and the EndTime are outputted to these values:
Tigger time - local
UTC date time
Start time UTC parameter
End time UTC parameter
2022-09-27 06:00
2022-09-26 20:00
2022-09-26T22:00:00.000000Z
2022-09-27T08:00:00.000000Z
When the trigger runs on the second occurrence, the StartTime and the EndTime are outputted to these values:
Tigger time - local
UTC date time
Start time UTC parameter
End time UTC parameter
2022-09-27 12:00
2022-09-27 02:00
2022-09-27T22:00:00.000000Z
2022-09-28T08:00:00.000000Z
Because the UTC date time has ticked over past midnight, the date value (dd) is now the next day due to the addDays(utcNow(), 1) expression.
This causes issues on the scripting of the Flow as when limiting the StartTime and EndTime of other actions, depending on when the Trigger runs is either looking at the local time's "Today" events or "Tomorrow" events.
For example, if I schedule an Out of Office / Automatic reply - and I schedule it to run on the 06:00 trigger, it will set my Automatic Reply to today - local time - 8am to 6pm. However, when it runs at the second trigger, 12:00 it will set my Automatic Reply to tomorrow - local time - 8am to 6pm.
Though this is not how I am using it, you can see it affects what actions do and perform.
Is there a way to ensure that it is always working of the current local date (dd) regardless if the UTC time has ticked over?
Have you tried using the addHours function instead of hard coding the hours in the formatdatetime function?
For the 6 am run I would try for the start time
formatDateTime(addHours(utcNow(), 2), 'yyyy-MM-ddTHH:mm:ss.0000000Z')
And for the end time
formatDateTime(addHours(utcNow(), 12), 'yyyy-MM-ddTHH:mm:ss.0000000Z')
You could also use a check to see which occurence run it is an combine that with the addhours approach.
equals(utcNow('HH'), '20')
It looks like, you want to do your Start time and End time calculations based on your LOCAL date, but you are doing the calculation based on the UTC date and thus get the wrong day, if UTC ticked over.
I guess the solution would be, to do your Start and End time calculations based on the LOCAL time instead of utcNow() and then transform the resulting LOCAL Start and End times into UTC times to be used as Start Time and End Time parameters.
Something to the effect of:
convertFromUtc(utcNow(),'+10:00','yyyy-MM-dd"T"06:00:00.000000')
to get the local start time of the current day and then from there
convertToUtc(localStartTime,'+10:00')
to get the UTC start time with the proper day.
And the same again for the End Time.
Though the other solutions seem logical in their workings, they dont account for daylight savings and other date time issues.
For the solution I ended up hard coding some of the data and calculations:
Action: "Convert time zone"
Base time: trigger time
Format string: Universal sortable date/time pattern [u]
Source: +10:00
Destination: UTC
Action: "Compose"
Expression: formatDateTime( body('Convert_time_zone'), 'dd')
Then for the start time UTC:
Action: Initialise variable
Name: Output Start Date Time
Type: String
Value: yyyy-MM-[Output from Compose*]"T"22:00:00.0000000Z')
* in that [Output from Compose] I made it an expression so I could subtract 1 day, using: sub( int(outputs('Compose')), 1)
This way I could always get my local date (dd) then manually shift the date, and set it as well as the time for start time.
Then I would use that local date for the End date time, and manually set the time.
Not the cleanest solution, but it works 100% of the time regardless of daylight savings or any time conversions. It is however not transferrable to other users without them editing the info.
Related
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.
I'm trying to figure out how Postgres handles DST in combination with intervals. Specifically I want to allow my users to create events with recurring dates - e.g. everyday at 16:00 local time.
For what I'm doing I need to store the first date in the user's local time, and then add a number of days to it, without changing the time of day in the user's local time. I was hoping that timestamptz with a full timezone name (so it knows when to apply DST?) combined with simple 1 day intervals would do the job - but it fails at my simple example:
Germany uses CET (+1:00) and switches to CEST (+2:00) at 2:00 in the morning on March 28.
Tunesia uses CET all year.
Thus I expected, that using a timestamptz on March 27 and adding 1 day to it, I'd see a different utc-offset in Berlin, and no change in Tunis - but they both changed the offset equally, as if Tunis was using DST:
select
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz as "tunis_before_dst",
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz + INTERVAL '1 day' as "tunis_after_dst",
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz as "berlin_before_dst",
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz + INTERVAL '1 day' as "berlin_after_dst"
results in:
tunis_before_dst: '2021-03-27 16:00:00+01'
tunis_after_dst: '2021-03-28 16:00:00+02'
berlin_before_dst: '2021-03-27 16:00:00+01'
berlin_after_dst: '2021-03-28 16:00:00+02'
Looking through pg_timezone_names, I can see that my Postgres instance is aware of Africa/Tunis not having DST - so I'm wondering why it's changing the UTC offset for it then.
I guess it's obvious that timezones and DST are very confusing to me, but am I doing something wrong handling them or is timezonetz not what I think it is?
Rant first. The concept of DST is breathtaking nonsense. Even the name is obvious BS. "Daylight Saving Time". No daylight has been saved. I can't believe the EU still did not manage to get rid of it, even though the overwhelming majority wants it gone, and it's been consensus to scrap it for a while now.
With that out of my system, the primary misunderstanding is this: you assume that the data type timestamp with time zone would store time zone information. It does not. Becomes obvious here:
Thus I expected, [...] I'd see a different utc-offset in Berlin, and no change in
Tunis - but they both changed the offset equally
The time zone offset you see in the output is the offset determined by the current timezone setting of your session. Time zone serves as input / output modifier / decorator. Postgres always stores UTC time internally. And no time zone information whatsoever.
The type name is a bit deceiving there. It has been known to fool the best:
Time zone storage in data type "timestamp with time zone"
Once you've grasped that concept, the rest should become obvious.
To preserve the local time (wall clock time of day), use the data type timestamp without time zone (timestamp), or even just time (never use the broken timetz), and store time zone information additionally - ideally the time zone name ('Europe/Berlin' like you have it), not a time zone abbreviation or a numeric offset.
timestamp with time zone (timestamptz) is the right choice to store unique points in time, independent of any time zones. The time zone offset is just an input modifier. Both of the following literals result in the same timestamptz value exactly, because both time zones happen to apply the same offset at this time of the year:
'2021-03-27 16:00:00 Africa/Tunis'::timestamptz
'2021-03-27 16:00:00 Europe/Berlin'::timestamptz
But these differ by one hour, because the German offset has changed according to the local DST regime:
'2021-03-28 16:00:00 Africa/Tunis'::timestamptz
'2021-03-28 16:00:00 Europe/Berlin'::timestamptz
Related:
Ignoring time zones altogether in Rails and PostgreSQL
Preserve timezone in PostgreSQL timestamptz type
Is there a standard for encoding a date as a timestamp? My thoughts:
This should be 12:00pm UTC in local time, eg 9:00am at T-3, therefore anyone consuming the timestamp, regardless of their -12/+12 offset, will recognize the same date, regardless of whether they parse at the UTC timezone
It could be 12:00pm at UTC
It could be the start of the day (12:00am) at UTC
It could be start of the day (12:00am UTC) in local time eg 9:00pm at T-3
Is there an official spec or standard to adhere to?
It would be easy to point to this document and say 'this is the standard' as opposed to being unaware and having to change our logic down the line.
There isn't a standard for this, because a date and a timestamp are logically two very different concepts.
A date covers the entire range of time on that day, not a specific point in time.
It may be a different date for a person in another time zone at any given point in time, but dates themselves do not have any association with time zones. Visualize a date as just a square on a calendar, not a point on a timeline.
Many APIs will use midnight (00:00) as the default time when a date-only value is assigned to a date+time value. However:
Whether it is UTC based or local-time based is very dependent on that particular API. There is no standard for this, nor is one answer necessarily better than the other.
Assigning a local-time midnight can be problematic for time zones with transitions near midnight. For example, in Santiago, Chile on 2019-09-08, the day started at 01:00 due to the start of DST. There was no 00:00 on that day.
Also, you tagged your question with momentjs. Since a Moment object is basically a timestamp (not a date), then Moment.js will generally assign the start of the day if provided a date-only value. The time zone involved is key to deciding which moment that actually is, which illustrates my prior points.
For example:
// Parsing as UTC
moment.utc('2019-09-08').format() //=> "2019-09-08T00:00:00Z"
// Parsing as Local Time (my local time zone is US Pacific Time)
moment('2019-09-08').format() //=> "2019-09-08T00:00:00-07:00"
// Parsing in a specific time zone (on a day without midnight)
moment.tz('2019-09-08', 'America/Santiago').format() //=> "2019-09-08T01:00:00-03:00"
Also keep in mind that sometimes APIs can be misnamed. The JavaScript Date object is not a date-only value, but actually a timestamp, just like a moment.
Im using this repo
https://github.com/remirobert/Tempo
Can someone help me understand how to grab the current time zone of the device, and then notify tempo? I am using the timeAgoNow() function of tempo to find display how long ago the post was made, but the timezone difference is messing it up. My datasource is using UTC time.
Cocoa uses UTC internally. for all of its date/time calculations.
If you create an NSDate for now:
NSDate()
You get a date that is the number of elapsed seconds since midnight, 1970 in UTC time.
Dates only have time zones when you display them.
By default logging a date to the console logs it in UTC, which can be confusing.
If I'm working on a project that does a lot of date/time calculations I'll create a debugging method that converts an NSDate to a date/time string in the current locale, which is easier to read/debug without having to mentally convert from UTC back to local time.
I have never used Tempo, so I don't know if it is using date strings, NSDate, or "internet dates" (which are also in UTC, but use a different "zero date" or "epoch date")
I am running mongodb on ubuntu server. The server time is
root# date
Thu Sep 13 21:15:58 BST 2012
But when I run the following command I get a different result
root# mongo
MongoDB shell version: 2.2.0
connecting to: test
> new Date()
ISODate("2012-09-13T20:15:58.670Z")
There is exactly one hour difference. When I update a documents updated_on field with php using MongoDate(), the value of the field is still 1 hour off.
[EDIT]
Actually I just checked my php error log and the time in the log file is 1 hour off as well
[13-Sep-2012 20:11:14 UTC] Log Message (Time should be 21:11:14)
Mongo tells you
2012-09-13T20:15:58.670Z
Z = Zulu time / Zero offset / UTC. You can also express the time in that TZ as 2012-09-13T20:15:58.670+00:00, as defined in the ISO8601 standard by the way.
BST is UTC+1. So, they are the same time but in different time zones.
You can resolve this issue by displaying the DateTime with ToLocalTime method.
MVC C# Example: #Model.StartDate.ToLocalTime()
This is due to the way MongoDB store datetime in BST format. So the daylight savings time or the time zone of the server will have an effect on the actual date time returned to the application. This simple code will be able to format as usual with ToString("dd MMMM yyyy hh:mm tt") or any other format based on your requirements.
Here you need to understand a concept in time setting in clocks called daylight saving time. In some countries around the world the clock is advanced by 1 or more hours to experience day light by one more hour. The difference between IST and GST is 5.30 hrs but the actual time difference is between New Delhi and London time is 6.30 hrs. See this article from 4GuysFromRolla for setting and using server time.
On windows change your timezone.
Controll Panel -> Date and Time -> Change on timezone -> (UTC) Coordinated universal time.
And then just change your time