NodaTime timezone parse: Etc/GMT+3 offset in negative - nodatime

This is a little code sample to illustrate the issue:
enter code here
var offset1 = DateTimeZoneProviders.Tzdb.GetZoneOrNull("Europe/Moscow")
.GetUtcOffset(Instant.FromDateTimeUtc(DateTime.UtcNow));
var offset2 = DateTimeZoneProviders.Tzdb.GetZoneOrNull("Etc/GMT+3")
.GetUtcOffset(Instant.FromDateTimeUtc(DateTime.UtcNow));
Console.WriteLine(offset1 + " vs " + offset2);
Results in "{+03} vs {-03}"

Yes, that's correct. That's because the "Etc/GMT+X" zone IDs are confusing. They match POSIX TZ strings, which for some reason represent "the offset of UTC from local time" instead of the normal "offset of local time from UTC". See the Wikipedia list of tz database time zone for confirmation of that.
From the etcetra file:
# Be consistent with POSIX TZ settings in the Zone names,
# even though this is the opposite of what many people expect.
# POSIX has positive signs west of Greenwich, but many people expect
# positive signs east of Greenwich. For example, TZ='Etc/GMT+4' uses
# the abbreviation "-04" and corresponds to 4 hours behind UT
# (i.e. west of Greenwich) even though many people would expect it to
# mean 4 hours ahead of UT (i.e. east of Greenwich).
If you want to create a time zone representing a fixed offset, you can either use an identifier of "UTC+X" or use DateTimeZone.ForOffset.

Related

Spamassassin - score by time of day sent

Is there a way to assign a score for mail sent between certain hours. I find a lot of spam is sent in the middle of the night so would like to give anything between say 2am and 5am a score of 2 or 3.
You can use SpamAssassin to penalize mail received within certain hours, but it's messy.
Before we start, verify that SA's primary defenses are properly set up:
DNS Blocklists, including DNSBLs & URIBLs, are a necessity; set them up before all else
Bayes in SpamAssassin is another must-have, though it requires training
Use Razor for fuzzy matching (see also Installing Razor) if the license works for you.
If that's insufficient, then you can address the sort of issue you're keying on. Try:
The RelayCountry plugin to penalize countries you never converse with
The TextCat plugin to discriminate against the languages you never converse in
If all that doesn't help enough, then (and only then) you can consider what you proposed. Read on.
Don't forget about time zones. You can't use the Date header for this reason. This type of rule is not safe for deployments that have conversations that span too many time zones, and you must ensure the MX record servers are all consistent and on the same time zone. Be aware that daylight savings (aka “summer time”) can be annoying here.
Identify a relay that your receiving infrastructure adds but is added before SpamAssassin runs (so SA can see it). This will manifest as a Received header near the top of your email. Again, make sure it's actually visible to SpamAssassin; the Received header added by your IMAP server will not be visible.
It is possible that you have SpamAssassin configured to run before any internal relay is stamped into the message. If this is the case, do not proceed further as you cannot reliably determine the local time.
Okay, all caveats aside, here's an example Received header:
Received: from external-host.example.com
(external-host.example.com [198.51.100.25])
by mx.mydomain with ESMTPS id ABC123DEF456;
Fri, 13 Mar 2020 12:34:56 -0400 (EDT)
This must be a header one of your systems adds or else it could have a different time zone, clock skew, or even a forged timestamp.
Match that in a rule that clearly denotes you as the author (by convention, start it with your initials):
header CL_RCVD_WEE_HOURS Received =~ /\sby\smx\.mydomain\swith\s[^:]{9,64}+:(?<=[0 ][2-4]:)[0-9:]{5}\s-0[45]00\s/
describe CL_RCVD_WEE_HOURS Received by our mx.mydomain relay between 2a and 5a EST/EDT
score CL_RCVD_WEE_HOURS 0.500
A walk through that regex (see also an interactive explanation at Regex101):
First, you need to verify that it's your relay, matched by name: by mx.mydomain with
Then, skip ahead 9-64 non-colon characters (quickly, with no backtracking, thus the + sign). You'll need to verify your server doesn't have any colons here
The real meat is in a look-behind (since we actually skipped over the hour for speed purposes), which seeks the leading zero (or else a space) and then the 2, 3, or 4 (not 5 since we don't want to match a time like 05:59:59)
Finally, there's a sanity check to ensure we're looking at the right time zone. I assumed you're in the US on the east coast, which is -0400 or -0500 depending on whether daylight savings is in effect
So you'll need to change the server name, review whether the colon trick works with your relay, and possibly adjust the time zone regex.
I also gave this a lower score than you desired. Start low and slowly raise it as needed. 3.000 is a really high value.

Wrong DST offset in pytz for a given zone

Mexico City's DST (Daylight Saving Time) ended on OCT28 2018 at 3:00 AM local time, that is the exact moment when clocks where moved backward one hour in time to start at 2:00 again.
When creating the following aware datetime:
>>>mex = pytz.timezone('America/Mexico_City')
>>>mex_dt = mex.localize(datetime(2018,10,28,1,30))
I get this result:
>>>print(mex_dt)
2018-10-28 01:30:00-06:00
While the correct result should be:
2018-10-28 01:30:00-05:00
because the created 01:30AM time still belongs to DST and should have an offset of -05:00. It seems like pytz supposes all countries move their clocks backwards at 2:00, but this is not the case in Mexico, where this happens at 3:00
Does anybody know how I can correct for this? suggestions?
I see you asked the same question on the IANA time zone mailing list.
As Paul Eggert replied there, your information appears to be incorrect. Mexico's official time policy indeed sets the time of transition at 2:00. If you have information to the contrary, please share your source.
You can also see this change visualized here, or read about time in Mexico on Wikipedia.

Open trip planner script slower on days other than today

I'm making use of open trip planner using the jython scripting method explained here: http://docs.opentripplanner.org/en/latest/Scripting/
(specifically 'Using OTP as a library') and am using a script very similar to their example script
For testing purposes I have two csv files containing 40 locations each. The locations are inside the Netherlands and I have loaded both the dutch gtfs and map. The strange thing is that the code that calculates the public transport trip times (line 32 in the example script: res = spt.eval(colleges), using modes WALK,TRANSIT) takes longer when I specify a day other than today.
An example:
req.setDateTime(2018, 12, 8, 16, 00, 00) # today
spt.eval(my_data) # -> takes ~7 - 10 seconds
req.setDateTime(2018, 12, 7, 16, 00, 00) # yesterday
spt.eval(my_data) # -> takes ~30 - 40 seconds
When not setting req.setDateTime(), spt.eval() is even faster. Note that I ran the script on the 6th, for the 6th, as well, and it was fast then too, so it's certainly related to "today" and not specifically the 8th.
Of course my primary question is, how do I make it fast for days other than today? (my main interest is actually tomorrow)
Is it related to when the OTP instance is started or is it some internal optimization? I don't think it's related to the building of the graph because that was built a couple of days ago. I was looking into providing a day or datetime setting when initializing OTP but am unable to find that in the docs.
(I haven't tried messing with my system time yet, but that's also an option I'm not very fond of). Any ideas or comments are welcome. If necessary I will provide a reproducible sample tomorrow.
This problem was actually caused because of how I used req.setDateTime() in combination with req.setMaxTimeSec().
Basically, setMaxTimeSec() uses the date set by setDateTime() as a starting point, and defines a worstTime (aka the last possible time) to that date time + the maxTimeSec. However, if setDateTime() was not yet set when calling setMaxTimeSec(), the current date time is used instead. This will consequently cause problems when you happen to call setDateTime() AFTERWARDS. Example:
setMaxTimeSec(60*60) # Sets worst time to now + 1 hour
setDateTime(yesterday) # Sets departure time to yesterday
This example has a very long time window to search for solutions! Instead of only looking within an hour time, we are now looking in a window of 25 hours!
Anyway, a simple solution is to first call setDateTime(), and then setMaxTimeSec():
setDateTime(yesterday) # Sets departure time to yesterday
setMaxTimeSec(60*60) # Sets worst time to yesterday + 1 hour
Alternatively, if, for some reason, you can't switch these methods, you can always correct the setMaxTimeSec() with the time difference between now and your setDateTime()-value:
date = datetime.strptime('2019-01-08 21:00', '%Y-%m-%d %H:%M')
date_seconds = time.mktime(date.timetuple())
now_seconds = time.mktime(datetime.now().timetuple())
date_diff_seconds = int(round(date_seconds - now_seconds))
req.setMaxTimeSec(60*60 + date_diff_seconds)
req.setDateTime(date.year, date.month, date.day, date.hour, date.minute, 00)

Node JS new Date one hour mismatch

When I print this:
const from = new Date();
console.log(from);
I get this: 2017-02-13T22:55:01.395Z
By the way it's 23:55. Why is there a one-hour mismatch?
When I do this:
console.log(from.getHours())
I get it right (23) which is fine. What is happening?
The string representation of from printed in your question is expressed in UTC, which you can tell by the trailing Z.
However, from is also capable of expressing the point in time it represents internally in local time, according to your time zone: from.toLocaleString()
Similarly, .getHours() returns the time-of-day hour in local time.
The implication is that your local time is 1 hour ahead of UTC, such as in Western Europe, for instance (while DST isn't in effect).
In other words: The output you're getting is as designed.

NodaTime supports relative time display?

I have a situation where the relative time is more important to a user than an absolute time. So it's more important to be able to quickly say "event happened 5 days and 5 hours ago" than "event happened at 1 PM CDT and it's 5 PM CST 5 days later now."
We store our dates in UTC and convert to display for the user:
pDateTime = DateTime.SpecifyKind(pDateTime, DateTimeKind.Utc);
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[pCurrentUser.PreferredTimezone];
return Instant.FromDateTimeUtc(pDateTime).InZone(dateTimeZone).ToString("HH':'mm':'ss' 'MM'/'dd'/'yy' 'x", CultureInfo.InvariantCulture);
We'll be using NodaTime 1.2 when it's fully out and just used vanilla ToString before.
However, times using this pattern end up using the daylight status of the time as opposed to the current daylight status. This means that times look something like: 16:15:32 10/25/13 CDT even though we have now transitioned to CST.
It is an absolute measure of the time. This forces the user to do the logic: "How long ago was that? Is it daylight saving time now? If so, the difference is x. If not, I have to add or subtract an hour? That makes the difference y."
Meanwhile, a relative measure of the time would display 15:15:32 10/25/13 CST in the absence of DST. This forces the user to do no conversions and allows them to compute what that time means in context much easier.
In a display that has numerous dates, it can get tricky to do the absolute time logic over the entire set. Doing it once is tricky to get right. However, a friendly relative string like "posted 5 hours ago" also forces them to resolve both the date and time themselves - that information is still important.
A compromise might be to do the posted blank hours/minutes ago for the first 24 hours or to include both the friendly string and absolute time - these are both patterns I've seen done.
But ignoring those, is there a way in NodaTime to imbue a time with a specific daylight status in order to get times displaying in a relative context?
However, times using this pattern end up using the daylight status of the time as opposed to the current daylight status. This means that times look something like: 16:15:32 10/25/13 CDT even though we have now transitioned to CST.
Yes, and it should. Displaying a date/time with CST despite that date/time occurring in CDT would be very odd, IMO.
So it's more important to be able to quickly say "event happened 5 days and 5 hours ago" than "event happened at 1 PM CDT and it's 5 PM CST 5 days later now."
In that case you shouldn't be displaying a date/time at all, in my view. Convert both ZonedDateTime values to Instant, take the Duration between them, and then you can see that it's 5 days and 5 hours ago. (I can't remember how much help we provide with that - you may need to manually take the number of ticks and divide by NodaConstants.TicksPerStandardDay etc. Look at DurationPattern to see if it helps though.)
Alternatively, if you really want to display a date and time, but still easily be able to extract the difference between them mentally, two options suggest themselves:
Use OffsetDateTime instead; there you could force the offsets to be the same, although I still think it would be odd to display an offset which wasn't actually the current offset in the zone you were observing the time in. Or you could just display the relevant offset at the time, so -5 for CST and -4 for CDT.
Just display everything in UTC, so that daylight saving transitions are irrelevant.
Note that you can't get months between the two ZonedDateTime values, as we're dealing with an elapsed time (a duration) rather than calendar-logical arithmetic (a period).

Resources