How to get timezone offset for historic daylight saving time local date? - sql-server-2014

There’s an SQL Server who uses GETDATE() in UTC+1, so it’s prone to different local times. In summer, it yields +02:00 (CEST), while in winter, it’s +01:00 (CET).
Getting the timezone offset of the current time is easy. How to get the UTC offset for historic dates?
The date actually speaks for itself—if it’s between 1 o’clock UTC on the last Sundays in March and October respectively, it’s DST, otherwise it’s not. But that’s a cumbersome heuristic to apply to each query.
I can’t seem to use SYSDATETIMEOFFSET()/TODATETIMEOFFSET/SWITCHOFFSET/DATENAME(TZOFFSET,…) because I’d need to already know the offset. In this very instance, AT TIME ZONE/sys.time_zone_info drop out, because it’s an older 2014 SQL server.
That’s got to be a standard issue. Aside from using UTC dates or storing the offset to each date, isn’t there a reasonable way to get the DST offset from any date in a specific (geographical) timezone?

I adapted this answer to find the relevant Sundays, and used a hard-wired decision for CET/CEST to have this workaround:
DECLARE #Date DATETIME2(3) = '2008-09-15 13:38:42.951'
DECLARE #DST_Start DATETIME2(0) = (SELECT DATEADD(HOUR,1,DATEADD(DAY,DATEDIFF(DAY,'19000107',DATEADD(MONTH,DATEDIFF(MONTH,0,CONCAT(YEAR(#Date),'-03-01')),30))/7*7,'19000107')));
DECLARE #DST_End DATETIME2(0) = (SELECT DATEADD(HOUR,1,DATEADD(DAY,DATEDIFF(DAY,'19000107',DATEADD(MONTH,DATEDIFF(MONTH,0,CONCAT(YEAR(#Date),'-10-01')),30))/7*7,'19000107')));
SELECT CONVERT(VARCHAR(29),TODATETIMEOFFSET(#Date,CASE WHEN #Date BETWEEN #DST_Start AND #DST_End THEN '+02:00' ELSE '+01:00' END),126);
-- ⇒ 2008-09-15T13:38:42.951+02:00
This specific organic approach doesn’t account for the fuzziness within the adjustment periods, but that’s a crux anyhow.

Related

Why do two MySQL dates in 1582 appear to be the same but comparison result is false?

I know that Gregorian calendar started on Oct 15th 1582, and during the transition from Julian calendar, 10 days had been dropped.
When I'm doing this query:
SELECT STR_TO_DATE('1582-10-05', '%Y-%m-%d')
I'm getting this result:
1582-10-15 (the 10 days difference).
But when I'm trying to match between such dates I'm getting the original date (Oct 5th and not 15th).
For example:
SELECT STR_TO_DATE('1582-10-05', '%Y-%m-%d') = STR_TO_DATE('1582-10-15', '%Y-%m-%d')
I'm getting a false response, although you would have expected to get a true since Oct 5th actually count as Oct 15th, as we saw in the first example.
Anyone can explain what's going on here?
On documentation it is stated that, TO_DAYS and FROM_DAYS functions must be called cautiously because of the transformation you noticed. Additionally, when I inspect the source codes of MySQL, I realized that STR_TO_DATE uses similar methodology with these functions. As I understand, cutover dates are completely unsafe to store or apply operations. Documentation says; "Dates during a cutover are nonexistent.", too.
Also for the inconsistency between different servers I may have an explanation. I have 2 different machines which have MySQL installed in Istanbul, Turkey and Frankfurt, Germany. They have same setup excluding localisation settings. First one shows 1, the other one shows 11 for the date substraction query. It means (in my humble opinion) there is unexplained sections about calendar cutover & localisation on official documentation.
Please see the following results:
SELECT STR_TO_DATE('1582-10-05', '%Y-%m-%d');
# Result #1: 1582-10-15
SELECT DATE_FORMAT(STR_TO_DATE('1582-10-05', '%Y-%m-%d'), '%Y-%m-%d');
# Result #2: 1582-10-05
SELECT DATE_FORMAT(STR_TO_DATE('1582-10-15', '%Y-%m-%d'), '%Y-%m-%d');
# Result #3: 1582-10-15
SQL fiddle demo.
These indicate the problem lies in the way the 1582-10-05 date is displayed rather than how it is stored. Result #2 shows that if the DATE_FORMAT function is used instead to explicitly convert the date into the same string format then the input date is displayed. This also explains why the comparison query in the question returns false: Behind the scenes, the two stored dates are different.
As you've discovered, this quirk occurs for all dates between 1582-10-05 and 1582-10-14 inclusive, i.e. the range of dates that don't really exist: The implicit conversion to text for all of these gives a date 10 days after. So if for some reason there is a need to display dates in this range (perhaps questionable), a simple workaround is to always use the DATE_FORMAT function.

Timezone conversion in a Google spreadsheet

I know this looks simple.
In a Google spreadsheet, I have a column where I enter time in one timezone (GMT)
And another column should automatically get time in another time zone(Pacific Time)
GMT | PT
----------|------------
5:00 AM | 9:00 PM
As of now I am using
=$C$3-time(8,0,0)
The problem here is, I want to change the time formula for Daylight savings.
Is there any function or script available which can take the daylight saving into calculation automatically.
Short answer
There is no built-in function but you could build a custom function.
Example
/**
* Converts a datetime string to a datetime string in a targe timezone.
*
*#param {"October 29, 2016 1:00 PM CDT"} datetimeString Date, time and timezone.
*#param {"Timezone"} timeZone Target timezone
*#param {"YYYY-MM-dd hh:mm a z"} Datetime format
*#customfunction
*/
function formatDate(datetimeString,timeZone,format) {
var moment = new Date(datetimeString);
if(moment instanceof Date && !isNaN(moment)){
return Utilities.formatDate(moment, timeZone, format)
} else {
throw 'datetimeString can not be parsed as a JavaScript Date object'
}
}
NOTE:
new Date(string) / Date.parse(string) implementation in Google Apps Script doesn't support some timezones abbreviations.
From https://tc39.es/ecma262/#sec-date-time-string-format
There exists no international standard that specifies abbreviations for civil time zones like CET, EST, etc. and sometimes the same abbreviation is even used for two very different time zones.
Related
Get UTC offset from timezone abbreviations
Explanation
In order to consider daylight saving time zones the input argument for of the value to be converted should include the date, no only the time of the day. You could set a default date and time zone to build the datetimeString by concatenating it before calling the formula.
=formatDate("October 29, 2016 "&A2&" GMT","PDT","hh:mm a")
For the target timezone besides the three letter abbreviation we could use TZ database names like America/Los_Angeles, example:
=formatDate("October 29, 2016 "&A2&" GMT","America/Los_Angeles","HH:mm")
If timezone abbreviation and TZ name fails for the datetimeString use time offsets (i.e. UTC-4).
See also
Calculating year, month, days between dates in google apps script
References
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date
This tutorial was amazingly helpful: https://davidkeen.com/blog/2017/01/time-zone-conversion-in-google-sheets/
Google Sheets does not have a built in way of converting time zone data but by using the power of Moment.js and Google’s script editor we can add time zone functionality to any sheet.
These are the files I copied into my script project:
https://momentjs.com/downloads/moment-with-locales.js saved as moment.js
https://momentjs.com/downloads/moment-timezone-with-data.js saved as moment-timezone.js
Make sure you add the moment.js script first and have it above the moment-timezone.js script because moment-timezone.js depends on it.
Then in your other script project, your Code.gs file can look like this:
var DT_FORMAT = 'YYYY-MM-DD HH:mm:ss';
/**
https://stackoverflow.com/questions/34946815/timezone-conversion-in-a-google-spreadsheet/40324587
*/
function toUtc(dateTime, timeZone) {
var from = m.moment.tz(dateTime, DT_FORMAT, timeZone);//https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
return from.utc().format(DT_FORMAT);
}
/**
https://stackoverflow.com/questions/34946815/timezone-conversion-in-a-google-spreadsheet/40324587
*/
function fromUtc(dateTime, timeZone) {
var from = m.moment.utc(dateTime, DT_FORMAT);//https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
return from.tz(timeZone).format(DT_FORMAT);
}
The easiest method is using a simple calculation.
Use =NOW() command in sheets and subtract it with the time difference divided by 24.
Example:
IST to Colombia
=NOW()-(10.5/24)
The time difference from India to Colombia is 10hours and 50min, we need to subtract it from the "Now" time and divide it by 24.
If the time zone is ahead of your place, then you need to add it.
Example:
IST to JAPAN:
=NOW()+(3.5/24)
=Now is set to US time by default, you can change it under general in settings.
enter image description here
enter image description here
I had the same problem (convert Manila Time to Sydney Time and automatically adjust for daylight saving time) when I found this page.
I didn't want to have a custom function but I found that, in Sydney, AEST (Australian Eastern Standard Time) starts on the first Sunday of April and AEDT (Australian Eastern Daylight Time) starts on the first Sunday of October.
So I thought, if I could find a formula that detects whether a date falls between the first Sunday of April and first Sunday of October (Standard Time) then I can automatically add 1 hour to the usual 2 hours to Manila time during Daylight Saving Time (dates falling outside the two dates) to have Sydney Time.
These two Excel solutions worked fine in Google Sheets:
How You Can Determine the First Sunday in a Month in Excel
How to determine if a date falls between two dates or on weekend in Excel
First Sunday of April this year (A1):
=CONCATENATE("4/1/",Year(today()))+CHOOSE(WEEKDAY(CONCATENATE("4/1/",Year(today())),1),7,6,5,4,3,2,1)
First Sunday of October this year (A2):
=CONCATENATE("10/1/",year(today()))+CHOOSE(WEEKDAY(CONCATENATE("10/1/",year(today())),1),7,6,5,4,3,2,1)
DST detector (A3) — if a date falls outside these two dates, it's DST in Sydney:
=IF(AND(today()>A1,today()<A2),"AEST","AEDT")
Time in Sydney (A4):
=NOW()+TIME(IF(A3="AEDT",3,2),0,0)
NOW() can be changed to any time format for tabulation:
I'm a new contributor and a novice, but I stumbled upon a function that had not been mentioned despite many hours of searching on the Sheets/Time Zone issue. Hoping this relatively simple solution will help.
For my sheet, I just want to add a row and automatically populate the local sheet date and time in the first two cells.
The .getTimezoneOffset() returns the difference in minutes between the client TZ and GMT, which in NY during Daylight Savings Time is 240. The function returns a positive number for the zones with "GMT-x", and vice versa for zones with "GMT+x". Hence the need to divide by -60 to get the correct hour and sign.
function addRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
sheet.insertRows(2, 1);
rightNow = new Date();
var tzOffset = "GMT" + rightNow.getTimezoneOffset() / -60;
var fDate = Utilities.formatDate(rightNow, tzOffset, "MM-dd-yyyy");
var fTime = Utilities.formatDate(rightNow, tzOffset, "HH:mm");
sheet.getRange('A2').setValue(fDate);
sheet.getRange('B2').setValue(fTime);
sheet.getRange('C2').setValue(tzOffset);
}
I've since found that I'm not the first person to respond to the GMT correction connundrum mentioning .getTimezoneOffset(). However, this thread has the most views on this topic, so I figured this option deserves a mention.
DST ends here on November 7th, 2021, so I'll report back if it doesn't adjust as expected to "GMT-5"
.getTimezoneOffset()
That can also be done without macros. Just using functions and data manipulation will suffice. Explaning the whole process here would be a bit cumbersome. Just do your research on how the various time functions work and use your creativity.
Hint: Use =NOW() if you want both current date and time. You'll actually need that if you need to find out the precise diff in time between to different dates.
Use =NOW()-INT(NOW()) when you only want the time (with date truncated if both times fall on the same date). Then format the corresponding cell or cells for time (i.e. 4:30 PM), not for date-time (3/25/2019 17:00:00). The latter is the format you'd use when you want to show both date and time... like when you use NOW().
Also search online for the Daylight Saving Time offset for the various standard time zones (PT, MT, CT, ET, AT) with respect to the Coordinated Universal Time (UTC). For example, in 2019 the offset for Pacific Time is UTC-7 when DST is observed starting on March 10 at 2 AM (Pacific) until November 3 at 2 AM. That means that the difference in time from UTC to Pacific is 7 hours. During the rest of the year is 8 hours (UTC-8). During DST observance starting sometime in March (the 10th this yr) it goes from PST to PDT by moving clocks forward 1 hr, or what we know as UTC-7 (that's summer time). After DST observance it goes from PDT to PST by moving clocks back 1 hr again, or what we know as UTC-8 (or winter time). Remember that the clock is advanced one hour in March to make better use of time. That's what we call DST, or Daylight Saving Time. So after March 8 at 2 AM (this year in 2019) we are in UTC-7. In November, we do the opposite. In Nov 3 at 2 AM the clock is taken back one hour as the winter kicks in. At that point we are back in Standard Time. Seems a bit confusing but it's really not.
So, basically, for folks in PT they go from PST to PDT in March and from PDT to PST in November. The exact same process goes on with Mountain Time, Central Time and Eastern Time. But they have different UTC time offsets. MT is either UTC-6 or UTC-7. CT is either UTC-5 or UTC-6. And ET is either UTC-4 or UTC-5. All depending on whether we are in summer time when Daylight Saving is observed to make better use of daylight and working hours, or in winter time (AKA, Standard Time).
Study these thoroughly and understand how they work, and play around with the various time functions in Excel or Google Sheets like the TIME(#,#,#) and NOW() functions and such, and believe me, soon you'll be able to do about anything like a pro with plain functions without having to use VBA Google Apps Script. You can also use the TEXT() function, though, with tricks like =TEXT(L4,"DDD, MMM D")&" | "&TEXT(L4,"h:mm AM/PM"), where L4 contains you date-timestamp, to display time and date formats. The VALUE() function also comes in handy every now and then. You can even design a numerical countdown timer without the use of macros. You'd need to create a circular reference and set iterations to 1, and time display to say every 1 min, in your spreadsheet settings for that.
The official timeanddate dot com website is a good source of info for all to know about time zones and how daylight time is handled. They have all UTC offsets there too.
Create your own Timezone Converter in Google Sheets:
Step 1: Create your table for the timezone converter.
Step 2: Enter the times for your time zones in a column.
Note: Ensure that you select date/time format(Select Cell(s) -> Format -> Number -> Time/Date)
Step 3: Write a formula to convert timezone using the following functions
Google Sheet Functions
=HOUR(A8)+(B3*C3) converts the hours.
=MINUTE(A8)+(B3*C3) converts the minutes.
Step 4: Convert back to time format using TIME(h,m,s) function
=TIME(HOUR(A8)+(B3*C3), MINUTE(A8)+(B3*C3), SECOND(A8))
This is a simple way to convert timezones.
However, if you want to implement an accurate timezone converter that takes care of the previous day, next day, and beyond 24 hours, beyond 60 minutes, please use MOD operations and handle all the cases.
Visit(or Use) this google sheet for reference:
https://docs.google.com/spreadsheets/d/1tfz5AtU3pddb46PG93HFlzpE8zqy421N0MKxHBCSqpo/edit?usp=sharing
just use the TZData format to "pull" a sync from UTC and display your choice.
Example in order to "change" the display of your cell to Berlin local time
=fromUTC(N82, "Europe/Berlin")
or for Tokyo
=fromUTC(N82, "Asia/Tokyo")
or San Francisco
=fromUTC(N82, "America/Inuvik")
point of reference for Time Zones is here >>
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Date/Time stored as floating point, which algorithm is used?

I'm have access to a 3rd party application's database, and I see a field called "date" which stores date/time values as floating point numbers, but I'm not sure how this floating point number is mapped to a date/time. There is no documentation for this database.
Here is some sample data:
date-field actual-date-time
253507382.168744 1/12/09 6:43 PM PST
253507480.136126 1/12/09 6:44 PM PST
253508091.838982 1/12/09 6:54 PM PST
256703604.015055 2/18/09 6:33 PM PST
256704413.484674 2/18/09 6:46 PM PST
Note: I had to enter these values manually so there's a slight chance they may be off a bit. If you would like to see more data, let me know and I'll add more.
I'm hoping someone is familiar with storing dates in this format and can let me know how to get a date/time given a floating point number.
If you look at the change in the numbers over the 10 and 13 minute intervals, you'll see that it's about 60. Therefore I conclude that it's a count of the number of seconds from a base date.
I think the base date is 1/1/2000 or 1/1/2001.
Edit: The base date appears to be 1/1/2001, and the time appears to be adjusted as well - it's probably UTC with your local time offset added.
If you subtract any of the two points you'll see that the values represent the number of seconds, at microsecond accuracy. It should be easy to work out the base date where the clock "started". On Unix and related systems this is January 1st, 1970.
The timestamps are 'number of seconds elapsed since 00:00 on January 1st 2001'. It's not a common date format but at least it should be easy to work with now you know what it represents!

Access data conversion issue

I'm using Access 2003. Have a table with some date values in a text data column like this;
May-97
Jun-99
Jun-00
Sep-02
Jan-04
I need to convert them to proper date format and into another Date/time column, So create a new Date/Time columns and just updated the values from the Text column into this new column. At first it looked fine, except for years after the year 2000. The new columns converted the dates as follows;
May-97 > 01/05/1997
Jun-99 > 01/06/1999
Jun-00 > 01/06/2000
Sep-02 > 01/09/2010
Jan-04 > 01/01/2010
As you can see any data with year after 2000 get converted to 2010. The same thing happens if I query the data using FORMAT(dateString, "dd/mm/yyyy").
Any ideas why this is so? Do I have to split the month and year and combine them again?
Thanks
Access/Jet/ACE (and many other Windows components) use a window for interpreting 2-digit years. For 00 to 29, it's assumed to be 2000-2029, and for 30-99, 1930-1999. This was put in place to address Y2K compatibility issues sometime in the 1997-98 time frame.
I do not allow 2-digit year input anywhere in any of my apps. Because of that, I don't have to have any code to interpret what is intended by the user (which could conceivably make mistakes).
This also points up the issue of the independence of display format and data storage with Jet/ACE date values. The storage is as a double, with the integer part indicating the day since 12/30/1899 and the decimal part the time portion within the day. Any date you enter is going to be stored as only one number.
If you input an incomplete date (i.e., with no century explicitly indicated for the year), your application has to make an assumption as to what the user intends. The 2029 window is one solution to the 2-digit year problem, but in my opinion, it's entirely inappropriate to depend on it because the user can change it in their Control Panel Regional Settings. I don't write any complicated code to verify dates, I just require 4-digit year entry and avoid the problem entirely. I have been doing this since c. 1998 as a matter of course, and everybody is completely accustomed to it. A few users squawked back then, and I had the "it's because of Y2K" as the excuse that shut them down. Once they got used it, it became a non-issue.
The date is ambiguous, so it is seeing 02 as the day number. Depending on your locale, something like this may suit:
cdate("01-" & Field)
However, it may be best to convert to four digit year, month, day format, which is always unambiguous.
Access seems to be get conduced between MM-YYYY format and MM-DD format. Don't know why it is doing it for dates after the year 2000, but solved it by converting the original string date to full date (01-May-01). Now Access converts the year into 2001 instead of 2010.
If you don't supply a year and the two sets of digits entered into a date field could be a day and month then Access assumes the current year. So your first three dates definitely have a year in them. But the last two don't.
Note that this isn't Access but actually the operating system doing the work. You get the same results in Excel. I had an interesting conversattion with some Microsoft employees on this issue and it's actually OLEAUT32.DLL.

Get the next xx:30 time (the next half-past hour) after a specified time

I'm trying to find a MySQL query which will obtain the time that is the next half-past-the-hour from a specified datetime. I explicitly mean the next half-past-the-hour, not the next half-hourly point.
So for instance:
If the datetime was "2009-10-27
08:15:24", the answer would be
"2009-10-27 08:30:00"
If the
datetime was "2009-10-27 08:49:02",
the answer would be "2009-10-27
09:30:00".
I came across this page which refers to SQL Server, and towards the end of that thread there is a similar sort of problem. But it's not quite the same, and it relies on a function that MySQL doesn't have.
Here is a fuller list of examples and expected return values:
2009-10-27 08:15:24 should return 2009-10-27 08:30:00
2009-10-27 08:49:02 should return 2009-10-27 09:30:00
2009-10-27 23:49:10 should return 2009-10-28 00:30:00
2009-10-27 10:30:00(.000001) should return 2009-10-27 11:30:00
(Note how, in the fourth example, because the exact half-past (10:30:00.0000000) has already gone, the next half-past-the-hour point is found.)
I tried using this kind of thing:
SELECT IF( (MINUTE(NOW()) < 30), HOUR(NOW()), (HOUR(NOW()) + 1) )
(after which addition of a CONCATed string would take place), but it would fail because of the changeover to another day, and it feels inherently 'hacky'.
Can anyone suggest a suitable sort of algorithm? I wouldn't expect a full answer (though that would be nice!), but suggestions as to the kind of algorithm would be helpful. I've been drawing over bits of paper for two hours now! I have a hunch that using modulo might be useful but I'm not sufficiently familiar with using it.
The answer will be fed to a PHP class later, but I'd rather implement this at SQL level if possible, as the rest of query also performs other date comparison functions efficiently.
This is a little messy, but works:
select from_unixtime( floor((unix_timestamp(MY_DATE)+(30*60))/(60*60))*(60*60) + (30*60) )
It pushes the time forward 30 minutes, then truncates to the top of the hour, then adds 30 minutes to it. Because it's working unix timestamps (seconds since 1970), you don't have to worry about the boundaries of days, months, years, etc.
I can't help but notice that this would be much easier at the PHP level :-) That said, here's what you can do:
Add 30 minutes to your datetime using DATE_ADD(); this will move to the next hour if it's already past half-hour
Create a new datetime value by extracting date / hour and hard coding minutes / seconds. CONVERT(), ADDTIME() and MAKETIME() all help.
The end result is:
select ADDTIME(
CONVERT(DATE(DATE_ADD(yourDateTime, INTERVAL 30 MINUTE)), DATETIME), # date part
MAKETIME(HOUR(DATE_ADD(yourDateTime, INTERVAL 30 MINUTE)), 30, 0) # hour + :30:00
) from ...
Use the MAKETIME(hour,minute,second) function to construct the desired value.