what does this npgsql Time datatype documentation mean? - npgsql

We are considering PostgreSQL because of its adherence to ISO 8601 3.5.2, specifically in regard to the standalone (uncombined) Time datatype (e.g. 18:50) as distinct from combined DateTime (Timestamp) type. See the relevant PostgreSQL 9.2. docs. The Time of Day datatype is based on the 24-hour clock, disassociated from the calendar, and runs from 00:00 to 24:00.
We have Windows desktops and are contemplating using the npgsql provider for .NET. But I am being thrown by the documentation of the npgsql provider for npgsqlTime type; the Hours instance property reads as follows:
Gets the number of whole hours held in the instance. Note that the
time 24:00:00 can be stored for roundtrip compatibility. Any
calculations on such a value will normalised [sic] it to 00:00:00. [my emphasis]
"Such a value" refers to "24:00:00". Do I understand this documentation correctly, that if one were to create two instances of npgsqlTime as follows:
NpgsqlTime midnightTonight = new NpgsqlTime(24,0,0);
NpgsqlTime midnightThisMorning = new NpgsqlTime(0,0,0);
and then invoke the Subtract method:
midnightTonight.Subtract(midnightThisMorning);
midnightTonight will be normalized (silently by the provider) to 00:00, and the result of the subtraction method will be an interval not of 24 hours but of 0 hours?
Or that if we instantiated two Times so:
NpgsqlTime midnightTonight = new NpgsqlTime(24,0,0);
NpgsqlTime lateAfternoon = new NpgsqlTime(16,0,0);
and then subtracted:
midnightTonight.Subtract(lateAfternoon);
we would get an interval of 16 hours rather than 8?
EDIT: Now that PostgreSQL and Npgsql are both installed and I can test, it seems the silent "normalisation" of 24:00 to 00:00 does not occur on Subtract() but it does occur on Add().
EDIT: So Subtract() is OK. We want the answer to be 8, and it is 8.
I am happy that it does not occur on Subtract() and I wish it did not occur by default on Add(), especially since there is a Normalize() method in the public API.

Related

Date from angular(timezone) to server (utc) then utc to Timezone

I have an issue with getting dates back in the right timezone.
Firs at all in the local machine, everything works fine but not in the server: the server is hosted in USA and the clients mostly are in Australia.
So a date is sent from angular app("12/23/2015 11:00:00 AM") to the server, the server store a date as utc in the database, until this point everything is working(I checked and the date is stored in the right utc)
book.StartDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(book.StartDateTime.Value, DateTimeKind.Unspecified), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(locationDetails.TimeZone)); // book.CreatedDate.Value.ToUniversalTime();
The issue is:
When a client request some dates stored in the database. The date stored in the database is return back to the client like this:
bookview.StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(bookli.StartDateTime.Value, DateTimeKind.Utc), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
I checked and at this point the date is "12/23/2015 11:00:00 AM" -> the conversion is right in the server(in the server side I put a log),
But in the angular is shown as "12/23/2015 10:00:00 PM"
So apparently the issue is when the date is transferred by the api to the client, maybe when is converted to JSON
I have tried different ways nothing work, I have removed "DateTime.SpecifyKind", I convert the date to string then back to datetime format and nothing seems to work.
What could I do?
A few things:
Your example is not complete, so I can only speculate on some areas. It would be better to show both sides, including how you load and parse the data in Angular, and what the data looks like over the wire.
You shouldn't be sending dates back and forth in a locale-specific format like "12/23/2015 11:00:00 AM". You might use those in your UI, but they're not appropriate over the wire (in your JSON). Instead, you should be using ISO8601/RFC3339, such as "2015-12-23T11:00:00Z". (You probably are already doing this if you're using WebAPI.)
A DateTime object when serialized to ISO8601 format is coupled with the associated DateTimeKind in the Kind property.
If the Kind is Utc, then the ISO8601 string will end with a Z.
If the Kind is Local, then the ISO8601 string will end with the machine-local offset for that timestamp, such as -08:00.
If the Kind is Unspecified, then the ISO8601 string will not have either Z or offset - which means that it cannot unambiguously represent a specific moment in time.
This is ultimately the cause of the error. You're converting the DateTime to another time zone, which leaves it with Unspecified kind, which then gets serialized without an offset - so on the client side that gets interpreted (probably) in the local time zone of the browser.
A better approach is to use DateTimeOffset instead of DateTime. Then you don't have to worry about Kind, and the offset is always present. If you change your bookview.StartDateTime to a DateTimeOffset type, you can do something like this to fix the problem:
DateTimeOffset dto = new DateTimeOffset(bookli.StartDateTime.Value, TimeSpan.Zero);
bookView.StartDTO = TimeZoneInfo.ConvertTime(dto, ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
That will ensure that the offset gets persisted in the data.
On the client side, pay careful attention to how the ISO string gets parsed. if it's loaded into a Date object, then it will indeed be converted to the client's time zone. Instead, you might take a look at moment.js for client-side time formatting. In particular, use moment.parseZone to keep the value in the same offset that it was presented. For example:
var s = moment.parseZone("2015-12-31T11:00:00+00:00").format("L LT"); // "12/31/2015 11:00 AM"
In commented code, you also showed a call to DateTime.ToUniversalTime - be very careful with that. If the source kind is Unspecified, it is treated as Local. Therefore, the local time zone of the computer is reflected in the converted value. It's better to avoid ToUniversalTime and ToLocalTime entirely. Use only the conversion methods on TimeZoneInfo.
ToolsHelper.OlsonTimeZoneToTimeZoneInfo isn't something we know about either. But I'll assume it does a CLDR mapping similar to this one. However, if you're working with Olson time zones anyway, the better approach would be to not use TimeZoneInfo at all. Instead, use Noda Time, with it's native support for tzdb time zones.

Why the WinForms DateTimePicker's supports maximum of 12/31/9998 23:59:59, instead of 12/31/9999 23:59:59

Does anyone knows why the control does not support higher values like 12/31/9999? I am looking for the particular reason for this.
From the DateTimePicker.cs source code file, as visible at the ReferenceSource site:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly DateTime MaxDateTime = new DateTime(9998, 12, 31);
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static readonly DateTime MinDateTime = new DateTime(1753, 1, 1);
These limits are checked in the Value property setter before it pinvokes the native control to set the date.
Not 100% sure where these limits came from. They do follow the common pattern, a programmer taking a shortcut to avoid dealing with an awkward problem. Some common examples:
COM dates can't go lower than 1900. That was a shortcut taken by a Lotus programmer, working on the once dominant spreadsheet program called "123". He didn't deal with the year 1900 not being a leap year. Microsoft had to copy the bug in Excel to keep it compatible with Lotus spreadsheets.
The year 1753, as used in DateTimePicker.MinDateTime was a shortcut taken by a Sybase programmer, the company that started SQL Server. That's the year that England switched from the Julian to the Gregorian calender. Which caused 15 days to get lost, the amount by which Julian dates drifted by not properly handling leap years. Not having to deal with invalid dates was obviously desirable. Putting that limit into DTP avoids data-binding problems.
DateTime.MinDate being the year 0 is a shortcut for not having to deal with negative DateTime.Tick values.
DateTime.MaxDate being the year 10,000 was a shortcut around a problem with TimeSpan.TotalMilliseconds. Which returns a double, a value type that has up to 15 significant digits. Going beyond 10,000 requires more digits.
Which inspires an explanation for the year 9998, there are plenty of icky problems getting close to DateTime.MaxDate. For example, SQL Server conks out at 3 milliseconds before midnight, .NET at 100 nanoseconds before midnight. DateTimePicker uses local time which can cause MaxDate to be exceeded in various timezones throughout the day of December 31st. So the Microsoft programmer did what most any other programmer did before him, he took a shortcut:
public static DateTime MaximumDateTime {
get {
DateTime maxSupportedDateTime = CultureInfo.CurrentCulture.Calendar.MaxSupportedDateTime;
if (maxSupportedDateTime.Year > MaxDateTime.Year)
{
return MaxDateTime;
}
return maxSupportedDateTime;
}
}
This is of course never a real problem, it is not meaningful to handle dates that far into the future. Use the MaximumDateTime property if you need some kind of validity check in your own code.
This is another non-answer, but maybe it'll be useful to someone. Following the WINAPI route in my comment, SYSTEMTIME is limited to dates between 1601 and 30827 because it is based on a FILETIME structure, which stores time as a 64-bit count of 100ns ticks since #1/1/1601#. It further only allows values less than 0x8000000000000000, which results in the year 30827 upper limit.
The .NET DateTimePicker control is based on the WINAPI Date and Time Picker control, so it makes sense that it would have at least these limits. The documentation mentions the switch from the Julian to the Gregorian calendar in 1753, which may explain the #1/1/1753# limit coded into the .NET control.
That may help to explain the lower limit, but still doesn't explain the upper limit. Unless someone from the development team chimes in, the only answer to "why?" may be "because it's hardcoded that way".
{Edit: the justification for the 1601 date for SYSTEMTIME appears to be that it was the previous start of a 400-year cycle in the proleptic Gregorian calendar. Still doesn't help explain #12/31/9998#.}
This isn't a proper answer (though I'm still trying to dig). Looking at the source there is a field defined in the DateTimePicker:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static readonly DateTime MaxDateTime = new DateTime(9998, 12, 31);
In your question you ask why it is "12/31/9998 23:59:59" when in fact it is only "12/31/9998 00:00:00", which is almost more peculiar.
I then searched for usages of this field. It seems to be used only as an absolute bound for the date time picker and nothing else. In fact, the MaximumDateTime property looks like this:
public static DateTime MaximumDateTime
{
get
{
DateTime supportedDateTime = CultureInfo.CurrentCulture.Calendar.MaxSupportedDateTime;
if (supportedDateTime.Year > DateTimePicker.MaxDateTime.Year)
return DateTimePicker.MaxDateTime;
else
return supportedDateTime;
}
}
So the maximum date is actually defined by either the current culture's defined maximum supported date, or the absolute maximum supported by the DateTimePicker, whichever is less in terms of the year part. For the "en-US" culture the maximum supported date is equal to DateTime.MaxValue, so the MaxDateTime field is used insteam.
Again, this isn't meant to answer why the particular value is used, but to give more insight on how it is being used within the DateTimePicker itself.

Check if DST is on according to UTC time in sql

I am working in MVC4 project where i am facing problem with time.Actually my project requirement is to show all time used in application according to Brazil time.So i have used GETUTCDATE() for saving time in application.
Now i want to check if DST is on according to time i saved..i mean central time. How do i check this.
I have search on net and found one solution
DECLARE #IsDST BIT;
SET #IsDST = CASE WHEN DateDiff(hour, GetDate(), GetUTCDate()) = 4 THEN 'True'
ELSE 'False' END;
SELECT GETDATE() AS GETDATE,
GETUTCDATE() AS GETUTCDATE,
#IsDST;
But when i try to run this script,it return false ??
But as per DST calculation,it always starts from 2nd Sunday of March and ends on 1st Sunday of November.
Then it should return true ,that DST is on.
Am i doing right or is there another better approach to check if DST is on central time,so that i can show brazil time according to DST
Well, this particular code doesn't work for detecting DST in Brazil, because it just measures the difference right now between local time and UTC, checking for 4 hours difference or not.
Most of Brazil is 3 hours behind UTC in the standard time, and 2 hours behind UTC in the daylight time. So this code probably won't work for you. You can read more in this Wikipedia article.
Daylight Saving Time is very different all over the world, so if you intend to use this approach then you will have to modify your code to match the time zone of the server that it's running on.
Personally, I would recommend not doing this in SQL at all. Time zone conversions aren't really the realm of the database. They work much better in application code. You should work with UTC in your database, and convert it to Brazil or whatever time zone you require in your application.
Since you said this was an ASP.Net MVC4 application, I recommend you either use the .net TimeZoneInfo class, or use the excellent Noda Time library to do your conversions in your .Net code.
Using TimeZoneInfo:
DateTime utcDT = // ... your UTC value returned from the database
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(
"E. South America Standard Time"); // Brazil
DateTime brazilDT = TimeZoneInfo.ConvertTimeFromUtc(utcDT, tz);
Using Noda Time:
DateTime utcDT = // ... your UTC value returned from the database
Instant instant = Instant.FromDateTimeUtc(utcDT);
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/Sao_Paulo"]; // Brazil
ZonedDateTime brazilZDT = instant.InZone(tz);
DateTime brazilDT = brazilZDT.ToDateTimeUnspecified();

Why doesn't appengine auto-convert datetime to UTC when calling put()

Here's what I'm trying to do: the user submits a time in pacific, once submitted I use .replace to set the timezone to Pacific.
Pacific = time.USTimeZone(-8, "Pacific", "PST", "PDT")
addEvent.date = addEvent.date.replace(tzinfo=Pacific)
Once i've set the tzinfo, I'm doing a put. According to the python documentation of google appengine it says:
"If the datetime value has a tzinfo attribute, it will be converted to the UTC time zone for storage. Values come back from the datastore as UTC, with a tzinfo of None. An application that needs date and time values to be in a particular time zone must set tzinfo correctly when updating the value, and convert values to the timezone when accessing the value."
However, when I do a put(), i get the following error:
WARNING 2012-10-06 21:10:14,579 tasklets.py:399] initial generator _put_tasklet(context.py:264) raised NotImplementedError(DatetimeProperty date can only support UTC. Please derive a new Property to support alternative timezones.)
WARNING 2012-10-06 21:10:14,579 tasklets.py:399] suspended generator put(context.py:703) raised NotImplementedError(DatetimeProperty date can only support UTC. Please derive a new Property to support alternative timezones.)
Please note I am using NDB
Ok, so after doing that I assumed that maybe NDB doesn't automatically convert it into UTC. So then I tried to convert it to UTC using the following code:
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return str("UTC")
def dst(self, dt):
return timedelta(0)
and now I still get the same error even after I convert the pacific time to UTC and set the tzinfo name as "UTC".
Could really use a ton of help here...
thanks!
The solution is to remove the tzinfo completely from the time after converting to UTC.
timestamp = timestamp.replace(tzinfo=None)
Here my working example:
if my_date.utcoffset():
my_date = (my_date - my_date.utcoffset()).replace(tzinfo=None)
I ran into this using http://hnrss.org/. This was my solution.
import datetime
from dateutil import parser
your_date_string = 'Mon, 24 Oct 2016 16:49:47 +0000'
your_date = parser.parse(your_date_string)
# your_date is now a datetime.datetime object
your_date_minus_tz = your_date.replace(tzinfo=None)
Now try your put() and you should see 2016-10-24 16:49:47 in the Cloud datastore.

Saving duration info to SQL Server

I have a WPF app, where one of the fields has a numeric input box for length of a phone call, called ActivityDuration.
Previously this has been saved as an Integer value that respresents minutes. However, the client now wishes to record meetings using the same table, but meetings can last for 4-5 hours so entering 240 minutes doesn't seem very user friendly.
I'm currently considering my options, whether to change ActivityDuration to a time value in SQL 2008 and try to use a time mask input box, or keep it as an integer and present the client with 2 numeric input boxes, one for hours and one for minutes and then do the calculation to save it in SQL Server 2008 as integer minutes.
I'm open to comments and suggestions. One further consideration is that I will need to be able to calculate total time based upon the ActivityDuration so the field DataType should allow it to be summed easy.
The new time datatype only supports 24 hours, so if you need more you'll have to use datetime.
So if sum 7 x 4 hour meetings, you'll get "4 hours" back
How the DB stores it is also different to how you present and capture the data.
Why not hh:nn type display and convert in the client and store as datetime?
Track the start and end time, no need to mask out the date, since the duration will just be a calculation off of the two dates. You can even do this in "sessions" such that one meeting can have multiple sessions (i.e. one meeting that spans across lunch, that shouldn't be counted toward the duration...).
The data type, then is either datetime or smalldatetime.
Then to get the "total duration" it's just a query using
Select sum(datediff(mm, startdate, enddate)) from table where meetingID = 1

Resources