Rails ActiveRecord Timezone for a single table - ruby-on-rails-3

Almost all of my tables uses UTC.
However some legacy table uses EST.
I live in EST. so I set
config.time_zone = 'Eastern Time (US & Canada)'
So far so good, data is stored in UTC and translated to EST
However, when I handle some legacy tables,
I want to have data stored in EST and translated as it is in EST, I can set;
config.active_record.time_zone = :local
but, applies to all models and it breaks a lot of tests.
Is there a way to set timezone for each model?

maybe if you set the active_record.time_zone to :local and use Time.zone for store the data in EST it will help you...

Related

Storing DATETIME with and without TIMEZONE

Databases often store a datetime without timezone as a separate type as a datetime with a timezone. As an example, I'll use BigQuery (though most databases store this the same):
DATETIME is a date/time that does not store timezone.
TIMESTAMP is a date/time that does store timezone.
I understand abstractly that "Dec 2 at 2:45 PM" is a different time in Japan as it is in New York, but I'm wondering why that even matters if all the dates are stored in UTC by the application. For example, if the value to be inserted is:
2021-12-02 14:45:00
Wouldn't that value be inserted as 2021-12-02 14:45:00 UTC in both data types? Or, would the 2:45PM be stored as "2:45 PM UTC" in the DATETIME type but would be stored as (if using EST) 2:45 PM EST --> 6:45 PM UTC in the TIMESTAMP type?
And if the value was:
2021-12-02 14:45:00 EST
Wouldn't that value also be inserted as 2021-12-02 18:45:00 UTC in both data types, and stored the same way? It seems to only 'timezone' is on the query-side, and can't that act either as a cursor-variable or some sort of metadata on the field (similar to a NULL check)? I guess I'm not following why the timezone-aware and no-timezone need to be stored as two different types if all date/times get stored as UTC anyways.
The SQL standard defines two types for a date with time-of-day:
TIMESTAMP (Also known more clearly in some databases such as Postgres as TIMESTAMP WITHOUT TIME ZONE)
TIMESTAMP WITH TIME ZONE
The first type is meant to purposely lack any context of time zone or offset-from-UTC. So noon on the 23rd of January next year, 2022-01-23 12:00, means noon anywhere and everywhere. It means noon on Tokyo Japan as well as noon on Toulouse France, and also noon in Toledo Ohio US. These are all obviously different moments, several hours apart. Therefore, this type cannot represent a moment, is not a specific point on the time line.
The second type does represent a moment, is a specific point on ne timeline. When you want to track actual moments, such as when a row was written to the database, or when a shipment arrived in a warehouse, use this type.
Unfortunately, the SQL spec says little about the various date-time types and behaviors. So the various database products vary widely in their support for these types and their interpretations of behavior.
In some databases such as Postgres, a value submitted to a column of the fist type (TIMESTAMP WITHOUT TIME ZONE) containing an indicator of zone or offset will have the date and time recorded as submitted. No adjustment is made. Any zone or offset input is ignored and discarded.
In some databases such as Postgres, a value submitted to a column of the second type (TIMESTAMP WITH TIME ZONE) containing an indicator of zone or offset will have its date and time adjusted to UTC before being written to the database. In such databases, this type is always in UTC, that is, represents a moment as seen with an offset of zero.
What is an offset? Merely a number of hours-minutes-seconds ahead of UTC (+) or behind UTC (-). A time zone, in contrast, is much more. A time zone has a name in Continent/Region format, and contains the history past, present, and future changes to the offset used by the people of a particular region as decided by their politicians.
So the TIMESTAMP WITH TIME ZONE type in databases such as Postgres is a misnomer. No time zone information is stored in the database. Any time zone or offset info submitted with the date and time is used to adjust to UTC. The zone/offset info is then discarded. So if remembering the zone originally submitted is important to you, you will need to store that yourself in a second column. Regarding the misnomer, you can think of the type as being TIMESTAMP WITH REGARD FOR SUBMITTED OFFSET OR TIME ZONE. But be clear, in databases such as Postgres your moment is stored in UTC, always UTC, and is retrieved as UTC, always UTC.
Unfortunately, there is a wrinkle here. All too commonly, tooling and middleware will inject a default time zone, adjusting the retrieved UTC moment into some time zone. While well-intentioned, this anti-feature creates the illusion that the value was stored with that time zone. But the values actually stored in UTC, at least with databases such as Postgres.
You asked:
2021-12-02 14:45:00 Wouldn't that value be inserted as 2021-12-02 14:45:00 UTC in both data types?
No.
In a column of a data type akin to the TIMESTAMP WITHOUT TIME ZONE, that date and time will be stored as submitted, a quarter to 3 PM on the second of December this year.
In a column of a data type akin to TIMESTAMP WITH TIME ZONE, the value stored may depend on the behavior of your particular database and your particular middleware, tooling, and driver. The behavior might simply assume you meant 2021-12-02 14:45:00 as seen in UTC, and store that. Or the behavior may assume you meant 2021-12-02 14:45:00 as seen in a particular time zone. And in databases such as Postgres, an adjustment to UTC would be applied before finally storing. You must study the documentation of your particular database, middleware, tooling, and driver to discover which behavior will be seen in your software. Be sure to conduct experiments to verify your understanding.
You asked:
2021-12-02 14:45:00 … Or, would the 2:45PM be stored as "2:45 PM UTC" in the DATETIME type but would be stored as (if using EST) 2:45 PM EST --> 6:45 PM UTC in the TIMESTAMP type?
”Likely yes”, for the first clause. But no EST involved at all. The date is stored as-is, 2021-12-02, along with the time-of-day as-is, 14:45:00. The EST part is ignored and discarded. (But experiment to verify this behavior in your particular tooling.)
And “maybe” for the second clause. As discussed in last bullet above, the behavior for TIMESTAMP WITH TIME ZONE may vary. Read docs, and conduct experiments.
You said:
though most databases store this the same
No, incorrect. That would be a very big “No”.
Databases vary widely in their support for date-time features, their kinds of date-time types, the names of their types, the technical details of their types, and the behaviors of the database server, middleware, drivers, and tooling.
Some older database systems have legacy data types supplanted by newer types, but all still supported, further complicating the picture.
You said:
I guess I'm not following why the timezone-aware and no-timezone need to be stored as two different types if all date/times get stored as UTC anyways.
You incorrectly assume that the “no-timezone” type stores in UTC. It does not.
That is what the “no-timezone” means: no regard for an offset or zone, no consideration for any offset or zone, no adjustment for any offset or zone, and no concept of offset or zone. The TIMESTAMP WITHOUT TIME ZONE type means simply, literally, a date, a time-of-day, and nothing more. Anything more than that is either (a) a figment of your imagination, or (b) interference by your middleware/tooling/drivers.

SQL Query to Update Existing DateTime field and values in DB to PST from GMT

I am looking for the best way to write a query that takes an existing DateTime field/value in a database (there are over 50K rows) and updates the DateTime value to a different time zone. What I want to do is change the existing date/time value from GMT time (the current server time) to PST (would be minus 7 hours I think). I want to change the values in the database then I will change the server time on the server to PST to reflect the time zone so that all new records show the PST time zone.
Any help on this would be appreciated, thanks in advance for your time.
You probably shouldn't do this at all. Keep your data in GMT (really, UTC) in the database, and convert it to pacific time in your application logic as needed. For example, if you connect to you SQL Server from a .NET application, query the UTC time from the database and convert it using TimeZoneInfo.ConvertTimeFromUtc
The biggest reason for this is that Pacific time uses daylight saving time. So while part of the year it follows PST (which is UTC-8), during the summer it follows PDT (which is UTC-7). That means the amount of time to adjust by is variable, and depends on the date itself.
Not only that, but a value that occurs during the fall-back transition is ambiguous in local time. For example, 2015-11-01 at 1:30 AM occurs twice in Pacific time. If you convert all your data to Pacific time, you'll potentially lose some information.
If you really need to convert between time zones directly in SQL Server, consider using my SQL Server Time Zone Support project. For example:
SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')
Unfortunately, SQL Server does not have any built-in timezone conversion functionality. However, this can be accomplished via SQLCLR. You can create a scalar User-Defined Functions using the DateTime.ToLocalTime() method.
Yes, technically you can use the TimeZoneInfo class which has methods such as ConvertTime(DateTime, TimeZoneInfo, TimeZoneInfo) and ConvertTimeFromUtc(DateTime, TimeZoneInfo), but the TimeZoneInfo class requires that the assembly have a PERMISSION_SET of UNSAFE whereas DateTime can run as SAFE. The only drawback to sticking with the DateTime methods is that they can only convert between UTC and the server's local time, as opposed to TimeZoneInfo which can convert between any two given time zones.
Since your data is already in GMT / UTC, it would be fairly easy to convert to Pacific* time using DateTime.ToLocalTime, but only if you change your server's time zone before updating the data, and not after (as is mentioned in the question). At which point you could do something like the following:
ALTER TABLE [SchemaName].[TableName]
ADD [IsConverted] BIT NULL; -- make sure we don't convert multiple times
WHILE (1 = 1)
BEGIN
UPDATE TOP (2000) tab
SET tab.DateField = dbo.ConvertTimeToLocalTime(tab.DateField),
tab.IsConverted = 1
FROM [SchemaName].[TableName] tab
WHERE tab.IsConverted IS NULL;
IF (##ROWCOUNT = 0)
BEGIN
BREAK;
END;
END;
-- Remove the temporary tracking field, but not until all rows are converted
IF (NOT EXISTS(
SELECT *
FROM [SchemaName].[TableName] tab
WHERE tab.IsConverted IS NULL
)
)
BEGIN
ALTER TABLE [SchemaName].[TableName]
DROP COLUMN [IsConverted];
END;
* Please note that I used "Pacific" time instead of PST because Pacific time can be either PST or PDT depending on Daylight Savings. Hence, trying to convert by simply doing DATEADD(HOUR, -7, [DateField]) would be correct for some rows, but not all rows.
Also, for those interested in this particular conversion function, it is available (though not for free) in the Full version of the SQL# library (which I am the author of). There are some blog posts around that show how to do it, though I have not seen one that follows best practices such that it will perform optimally. Of course, if this is a one-time conversion, then optimal performance for a price is admittedly less important than less-efficient-but-free :-).
You can change the database time zone by using the SET TIME_ZONE clause of the ALTER DATABASE statement. For example:
ALTER DATABASE SET TIME_ZONE='Europe/London';ALTER DATABASE SET TIME_ZONE='-05:00';
YOu can refer to the following link for further details.
http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Hibernate DST Date mapping issue for timestamps without time zone

I've a problem with storing java.util/sql.Date in PostgreSQL using hibernate: I'm storing dates as timestamp without time zone type. Now, in most cases everything is alright, as I'm mapping it in entities as java.util(sql).Date.
However, I encountered one problem I don't know how to overcome yet:
On March 30th there was a time change (Daylight Saving Time started). In my country it meant switching from local 2am to local 3am.
In my database I have few entries with times between 2 and 3 am, such as
"2014-03-30 02:15:55"
Now, what Java does is it takes this date and displays it as 3:15:55 am, because of the time change. However, I desperately need it to be 02:15:55, exactly how it's stored in the database (basically meaning don't use the +02:00 timezone BEFORE 3am, but use it AFTER). I'm afraid about 26th October as well, that's when the DST ends.
Is this possible using Hibernate and/or Spring? I'd love a global config for such case.
Best,
Marcin
You need to store dates in UTC timezone to ignore DST. See this question How to store date/time and timestamps in UTC time zone with JPA and Hibernate for solutions

Time difference issue in two different countries

There are two clients with same window application. One is in India and other one is in Belgium. Sql server and web service application is hosted at Belgium. In sql I am storing UTC date time.
Now issue is a time difference for this two clients. I want to show UTC time in history form that mean what is stored in Database I have to bind that data to gird. No any extra code because I suppose to bind UTC date-time. Event then I get time difference for this two client.
Blue header screen is of a Indian client and other one is of a Belgium client. In Belgium time is showing exactly as in Database but difference is for India. Am I missing anything in configuration or what?
You have to convert both time zone in standard UTC and save it. Used dateadd() function to manipulate datetime. such as
declare #IST_date datetime
declare #BE_date datetime
declare #UTC_date datetime
--Indian standard time is (GMT + 5:30 hrs)
-- Belgium standard time is ( GMT + 1 hour)
select #UTC_date = DATEADD(hh,5.30, #IST_date)
select #UTC_date = DATEADD(hh,1, #BE_date)
why not simply convert the value at display ?
DateTime MyDate = Data["ChangedDate"];
DateTime MyDateUTC = MyDate.ToUniversalTime();
tadaaaaa
Ditch System.DateTime and use Noda Time!
Getting Started with Noda Time
System.DateTime uses the system culture and time zone at unpredictable moments, where Noda Time works without any defaults. Takes a bit of work to understand, but you'll never look back.
Use Noda time on the client, and store all the values in UTC in the database. You may also wish to store the original time zone that the date time was entered in.

How to handle date/times in POST parameters?

By default, Rails' config.time_zone is UTC. So, fields like created_at, updated_at are saved in UTC.
Now, presume user's time zone is Pacific Standard Time (PST).
In order to show dates and times in the user's time zone, I have set Time.zone to PST.
Now, when a user enters a date (in user's local time zone, which is PST) in a form, that date is received in the controller according to the setting of Time.zone.
But, I want to save all times in the database in UTC.
So, how would I go about converting any date/time field in-place in the parameters hash to UTC? I'm wondering if there is a solution where Rails can do the conversion automatically?
I would like that any date/time received for any datetime field be converted to UTC, without having to write the conversion code in every controller.
UPDATE
I tested by setting Time.zone to Pacific Time (US & Canada) as well as to Eastern Time (US & Canada) and then saving a model. The created_at time in both cases was in UTC. So that confirms that setting Time.zone has no effect on what time is saved by Rails in the created_at field. That value is effected only by the config.time_zone (which if not defined in application.rb will default to UTC)
I'm so confused by this all!
In the database, all dates are always saved as UTC. The config.time_zone parameter is setting the default value for Time.zone, just for display. So you don't have to do anything to get the behavior you desire, just keep Time.zone set correctly for the user, and rails takes care of the rest.