I am on a Debian Stable 9 (stretch), the newly updated TZ database for Africa/Casablanca Table currently states isdst=0 and an offset from UTC of +01.
From the DST in Morocco Wiki page
https://en.wikipedia.org/wiki/Daylight_saving_time_in_Morocco
It is clear that a permanent offset of UTC +1:00 was added from October 2018 and daylight savings is now permanently observed.
But during Ramadan the offset has been traditionally reset to UTC 00:00. But the TZ database denotes that they add an offset of +01 and isdst is set to 1.
This issue is only applicable to Africa/Casablanca.
It seems to be an issue with Debian Stable. Any advice on fixing this issue is appreciated
zdump -v /usr/share/zoneinfo/Africa/Casablanca | grep 2019
/usr/share/zoneinfo/Africa/Casablanca Sun May 5 01:59:59 2019 UT = Sun May 5 02:59:59 2019 +01 isdst=0 gmtoff=3600
/usr/share/zoneinfo/Africa/Casablanca Sun May 5 02:00:00 2019 UT = Sun May 5 02:00:00 2019 +00 isdst=1 gmtoff=0
/usr/share/zoneinfo/Africa/Casablanca Sun Jun 9 01:59:59 2019 UT = Sun Jun 9 01:59:59 2019 +00 isdst=1 gmtoff=0
/usr/share/zoneinfo/Africa/Casablanca Sun Jun 9 02:00:00 2019 UT = Sun Jun 9 03:00:00 2019 +01 isdst=0 gmtoff=3600
From the tzdb 2018h release notes (emphasis mine):
Changes to future timestamps
Guess that Morocco will continue to fall back just before and spring
forward just after Ramadan, the practice since 2012. (Thanks to
Maamar Abdelkader.) This means Morocco will observe negative DST
during Ramadan in main and vanguard formats, and in rearguard format
it stays in the +00 timezone and observes ordinary DST in all months
other than Ramadan. As before, extend this guesswork to the year
2037. As a consequence, Morocco is scheduled to observe three DST transitions in some Gregorian years (e.g., 2033) due to the mismatch
between the Gregorian and Islamic calendars.
It was later confirmed that Morocco did indeed adjust its clocks for Ramadan as predicted. You can read the tz discussion thread, or the article about it on timeanddate.com.
Even the Wikipedia article you cited mentions this:
... An exception was made during the month of Ramadan during which clocks reverted to UTC+00:00 (standard time).
https://play.golang.org/p/82QgBdoI2G
package main
import "fmt"
import "time"
func main() {
fmt.Println(time.Now().Format("01-JAN-2006 15:04:00"))
}
The output should be like if date time today is 2016-03-03 08:00:00 +0000UTC
Output: 03-MAR-2016 08:00:00
Time should be in 24hr format.
Your layout is incorrect, it should show how the reference time is represented in the format you want, where the reference time is Mon Jan 2 15:04:05 -0700 MST 2006.
Your layout should be:
"02-Jan-2006 15:04:05"
Note the 05 for the seconds part. And since you specified the hours as 15, that is 24-hour format. 3 or 03 is for the 12-hour format.
fmt.Println(time.Now().Format("02-Jan-2006 15:04:05"))
For me it prints:
03-Mar-2016 13:03:10
Also note Jan for months, JAN is not recognized. If you want uppercased month, you may use strings.ToUpper():
fmt.Println(strings.ToUpper(time.Now().Format("02-Mar-2006 15:04:05")))
Output:
03-MAR-2016 13:03:10
Also note that on the Go Playground the time is always set to a constant when your application is started (which is 2009-11-10 23:00:00 +0000 UTC).
fmt.Println(time.Now().Format("02-Jan-2006 15:04:05"))
See Time package constants
The reference time used in the layouts is the specific time:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445. Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
I see that typing 100.days gives me [edit: seems to give me] a Fixnum 8640000:
> 100.days.equal?(8640000)
=> true
I would have thought those two values were interchangable, until I tried this:
x = Time.now.to_date
=> Wed, 31 Oct 2012
> [x + 100.days, x + 8640000]
=> [Fri, 08 Feb 2013, Mon, 07 May 25668]
Why, and how, does adding apparently equal values to equal dates give different results?
The above results are from the Rails console, using Rails version 3.1.3 and Ruby version 1.9.2p320. (I know, I should upgrade to the latest version...)
100.days doesn't return a Fixnum, it returns an ActiveSupport::Duration, which tries pretty hard to look like a integer under most operations.
Date#+ and Time#+ are overridden to detect whether a Duration is being added, and if so does the calculation properly rather than just adding the integer value (While Time.+ expects a number of seconds, i.e. + 86400 advances by 1 day, Date.+ expects a number of days, so +86400 advances by 86400 days).
In addition some special cases like adding a day on the day daylight savings comes into effect are covered. This also allow Time.now + 1.month to advance by 1 calendar month irrespective of the number of days in the current month.
Besides what Frederick's answer supplies, adding 8640000 to a Date isn't the same as adding 8640000 to a Time, nor is 100.days the correct designation for 100 days.
Think of 100.days meaning "give me the number of seconds in 100 days", not "This value represents days". Rails used to return the number of seconds, but got fancy/smarter and changed it to a duration so the date math could do the right thing - mostly. That fancier/smarter thing causes problems like you encountered by masking what's really going on, and makes it harder to debug until you do know.
Date math assumes day values, not seconds, whereas Time wants seconds. So, working with 100 * 24 * 60 * 60 = 8640000:
100 * 24 * 60 * 60 => 8640000
date = Date.parse('31 Oct 2012') => Wed, 31 Oct 2012
time = Time.new(2012, 10, 31) => 2012-10-31 00:00:00 -0700
date + 8640000 => Mon, 07 May 25668
time + 8640000 => 2013-02-08 00:00:00 -0700
date + 100 => Fri, 08 Feb 2013
It's a pain sometimes dealing with Times and Dates, and you're sure to encounter bugs in code you've written where you forget. That's where the ActiveSupport::Duration part helps, by handling some of the date/time offsets for you. The best tactic is to use either Date/DateTime or Time, and not mix them unless absolutely necessary. If you do have to mix them, then bottleneck the code into methods so you have a single place to look if a problem crops up.
I use Date and DateTime if I need to handle larger ranges than Time can handle, plus DateTime has some other useful features, otherwise I use Time because it's more closely coupled to the OS and C. (And I revealed some of my roots there.)
The user inputs a date range let's say from yesterday with any timezone.
from_datetime = "10/01/2012 00:00 +0545"
I get purchased time for the book like below:
purchased_at = Book.where("created_at > #{from_date}").purchased_at
=> Fri, 08 Jun 2012 09:44:26 UTC +00:00
The problem is this gives me UTC time but I want to show the purchased_at time in the requested time_zone which can vary.
I can't use in_time_zone as the input from_date only has time offset, can I ?
purchased_at.in_time_zone("Kathmandu")
=> Fri, 08 Jun 2012 15:29:26 NPT +05:45
Is there any way around?
Give an offset, you can get a timezone name from ActiveSupport::TimeZone:
> ActiveSupport::TimeZone[5.hours + 45.minutes]
=> (GMT+05:45) Kathmandu
Then you can hand that to in_time_zone:
> Time.now.in_time_zone(ActiveSupport::TimeZone[5.hours + 45.minutes])
=> Thu, 18 Oct 2012 12:33:12 NPT +05:45
You can pull the offset out of the from_datetime with a bit of simple wrangling if you know the incoming format.
There are issues with this approach:
The mapping from offset to name isn't unique.
DST could be a problem if ActiveSupport::TimeZone[] gives you the wrong name.
Depending on your needs, you could just apply the offset manually and ignore the timezone when formatting the timestamp:
> (Time.now.utc + 5.hours + 45.minutes).strftime('%Y-%m-%d %H:%M:%S +0545')
=> "2012-10-18 12:40:12 +0545"
I need somehow to determine whether some TDateTime value is within the Daylight Saving Time range for my timezone or not (in C# the same thing does the DateTime.IsDaylightSavingTime() method).
I know in Delphi there's no similar function, because Delphi TDateTime contains no information about timezone, but I suppose there's some way how to do this using Win32 API.
I've looked at Win32 API GetTimeZoneInformation and GetTimeZoneInformationForYear functions, but I don't quite understand how to use them, so I'd like to ask you for help. Thanks in advance for any tips.
Edit:
Example:
In my timezone (Central European) Daylight Saving Time started this year on
28th March at 2 am and ends on 31st October 2010 at 3 am.
I need a function with header:
function IsDaylightSavingTime(input: TDateTime): boolean;
that will return true if the input date is between 28th March 2010 2:00 and 31st October 2010 3:00 and false if not.
(The example is just for year 2010, but I'd need it to work for all years.)
Once again, I know that information saved in TDateTime alone is not enough, but I think that with some Win32 API function I should be able to get e.g. information about current timezone from Windows settings.
It is not as easy as it sounds, because:
1) The switch-over date between DST and standard time is not the same for all countries
2) The switch-over date between DST and standard time is not the same algorithm for the same country for all years (for example in Central Europe it was previously first Sunday in April, IIRC, now it is last Sunday in March). The USA changed from first Sunday in April to the second Sunday in March from 2007 and on.
So - a simple date is not enough, you'll also need a geographical location.
But, if you can live with the fact, that you limit yourself to the switch-over dates that can be calculated from the CURRENT algorithm for the CURRENT year for the CURRENT locale (country) and that this may be wrong for dates both in the future and in the past, then you can use the information in TIME_ZONE_INFORMATION to calculate the switch-over dates:
USES Windows,SysUtils,DateUtils;
FUNCTION GetDaylightSavingsSwitchOverDates(Year : Cardinal ; VAR Start,Stop : TDateTime) : BOOLEAN;
VAR
TZ : TTimeZoneInformation;
FUNCTION DecodeSwitchOverDate(Year : Cardinal ; CONST Time : TSystemTime) : TDateTime;
VAR
I : Cardinal;
BEGIN
Result:=EncodeDateTime(Year,Time.wMonth,1,Time.wHour,Time.wMinute,Time.wSecond,0);
IF Time.wDay=5 THEN BEGIN
Result:=DateOf(EndOfTheMonth(Result))+TimeOf(Result);
WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO
Result:=IncDay(Result,-1)
END
ELSE BEGIN
WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO Result:=IncDay(Result);
FOR I:=1 TO PRED(Time.wDay) DO Result:=IncWeek(Result)
END
END;
BEGIN
IF GetTimeZoneInformation(TZ)=TIME_ZONE_ID_UNKNOWN THEN
Result:=FALSE
ELSE BEGIN
Start:=DecodeSwitchOverDate(Year,TZ.DaylightDate);
Stop:=DecodeSwitchOverDate(Year,TZ.StandardDate);
Result:=TRUE
END
END;
FUNCTION StartOfDST(Year : Cardinal) : TDateTime;
VAR
Stop : TDateTime;
BEGIN
IF NOT GetDaylightSavingsSwitchOverDates(Year,Result,Stop) THEN Result:=0
END;
FUNCTION EndOfDST(Year : Cardinal) : TDateTime;
VAR
Start : TDateTime;
BEGIN
IF NOT GetDaylightSavingsSwitchOverDates(Year,Start,Result) THEN Result:=0
END;
Looping through the years 2000 to 2020 on my PC (Central Europe Time Zone), I get the following dates:
DST in 2000: Sun 26 Mar 2000 02:00:00 through Sun 29 Oct 2000 03:00:00
DST in 2001: Sun 25 Mar 2001 02:00:00 through Sun 28 Oct 2001 03:00:00
DST in 2002: Sun 31 Mar 2002 02:00:00 through Sun 27 Oct 2002 03:00:00
DST in 2003: Sun 30 Mar 2003 02:00:00 through Sun 26 Oct 2003 03:00:00
DST in 2004: Sun 28 Mar 2004 02:00:00 through Sun 31 Oct 2004 03:00:00
DST in 2005: Sun 27 Mar 2005 02:00:00 through Sun 30 Oct 2005 03:00:00
DST in 2006: Sun 26 Mar 2006 02:00:00 through Sun 29 Oct 2006 03:00:00
DST in 2007: Sun 25 Mar 2007 02:00:00 through Sun 28 Oct 2007 03:00:00
DST in 2008: Sun 30 Mar 2008 02:00:00 through Sun 26 Oct 2008 03:00:00
DST in 2009: Sun 29 Mar 2009 02:00:00 through Sun 25 Oct 2009 03:00:00
DST in 2010: Sun 28 Mar 2010 02:00:00 through Sun 31 Oct 2010 03:00:00
DST in 2011: Sun 27 Mar 2011 02:00:00 through Sun 30 Oct 2011 03:00:00
DST in 2012: Sun 25 Mar 2012 02:00:00 through Sun 28 Oct 2012 03:00:00
DST in 2013: Sun 31 Mar 2013 02:00:00 through Sun 27 Oct 2013 03:00:00
DST in 2014: Sun 30 Mar 2014 02:00:00 through Sun 26 Oct 2014 03:00:00
DST in 2015: Sun 29 Mar 2015 02:00:00 through Sun 25 Oct 2015 03:00:00
DST in 2016: Sun 27 Mar 2016 02:00:00 through Sun 30 Oct 2016 03:00:00
DST in 2017: Sun 26 Mar 2017 02:00:00 through Sun 29 Oct 2017 03:00:00
DST in 2018: Sun 25 Mar 2018 02:00:00 through Sun 28 Oct 2018 03:00:00
DST in 2019: Sun 31 Mar 2019 02:00:00 through Sun 27 Oct 2019 03:00:00
DST in 2020: Sun 29 Mar 2020 02:00:00 through Sun 25 Oct 2020 03:00:00
but at least some of these years are incorrect due to the algorithm having changed from my locale in the years listed.
Your function would then be:
FUNCTION IsDaylightSavingTime(Input : TDateTime) : BOOLEAN;
VAR
Start,Stop : TDateTime;
BEGIN
Result:=GetDaylightSavingsSwitchOverDates(YearOf(Input),Start,Stop) AND (Input>=Start) AND (Input<Stop)
END;
Maybe it is overkill for your specific application, but the open source project "Olson Time Zone Database for Delphi" allows to access all time zones supported by the tz database project. The tz database gets regular updates with newest daylight savings changes or fixes.
TZDB can be compiled on Delphi 6, 7,
8, 9, 10, 2007, 2009, 2010 and XE or
FreePascal 2.0 and higher. TZDB is
best used with Delphi XE which
introduces TTimeZone class into RTL.
Ondra C. -
Yes, you're correct. You need to:
Set a Delphi TDateTime variable to the date/time you wish
Convert it to Windows SystemTime
Call GetTimeZoneInformation() to get TTimeZoneInformation
Call GetTimeZoneInformationForYear(), with your TTimeZoneInformation struct, to get DST info for your timezone (I'm not sure where you'd get TTimeZoneInformation for some arbitrary timezone - but you should be able to find it on MSDN).
Do the arithmetic to see whether your System time occurs AFTER TTZI.StandardDate (in which case it's standard time), or AFTER TTZI.DaylightDate (in which case it's DST).
Alternatively ...
Perhaps you could just convert this into a Delphi table:
http://www.twinsun.com/tz/tz-link.htm
For any datetime in, any timezone, just see if the given datetime falls within DST, or outside of it. Voila! No Microsoft APIs - just a simple table lookup or if/else case block!
'Hope that helps .. pSM
As you say yourself, that information is not kept bundled with datetimes in Delphi, so you can't simply port this. Every routine that proceduces a tdatetime would have to add this info, which is not the case in Delphi.
Maybe you should explain more what you are really trying to do, and describe the problem less by analogy
I used .net reflector to view the implementation of this function in .net. It is defined as follows, maybe you can convert the math into Delphi? If you need to dig further into this, I suggest opening Reflector for yourself. I think it will help you!
public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes)
{
return (CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero);
}
internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes)
{
if (daylightTimes != null)
{
DateTime time4;
DateTime time5;
if (time.Kind == DateTimeKind.Utc)
{
return TimeSpan.Zero;
}
DateTime time2 = daylightTimes.Start + daylightTimes.Delta;
DateTime end = daylightTimes.End;
if (daylightTimes.Delta.Ticks > 0L)
{
time4 = end - daylightTimes.Delta;
time5 = end;
}
else
{
time4 = time2;
time5 = time2 - daylightTimes.Delta;
}
bool flag = false;
if (time2 > end)
{
if ((time >= time2) || (time < end))
{
flag = true;
}
}
else if ((time >= time2) && (time < end))
{
flag = true;
}
if ((flag && (time >= time4)) && (time < time5))
{
flag = time.IsAmbiguousDaylightSavingTime();
}
if (flag)
{
return daylightTimes.Delta;
}
}
return TimeSpan.Zero;
}
You can find an example of this in the JEDI Code Library (Open Source) in the JclDateTime.pas unit, in the LocalDateTimeToDateTime function. The Daylight Savings time info is retrieved and used to convert to and from UTC time.
I know this is not an answer to your question, but you might be interested in the following two functions: SystemTimeToTzSpecificLocalTime() and TzSpecificLocalTimeToSystemTime(). The first one converts universal time to a corresponding time for the time zone specified (where nil means your local time zone). The other one works the other way round, but is included only in Windows XP and above, as Borland Help says. If you are going to do a time-zone dependent time conversion only, they should be OK for you. And it is good to know that they check whether the UTC time given is a DST or Standard time. I didn't read it anywhere, however. I just checked it by myself, so please correct me if I'm wrong.
Please, see the following function. I'm not sure whether I would use it anywhere, for I'm not sure about its correctness, but it might be worth seeing. And please, don't tell me it's a stupid method, for I know that :-).
function IsDaylightSavingTime(lLocalTime: TDateTime): boolean;
var
lUniversalSystemTime: TSystemTime;
lLocalSystemTime: TSystemTime;
lTimeZoneInfo: TTimeZoneInformation;
begin
case GetTimeZoneInformation(lTimeZoneInfo) of
TIME_ZONE_ID_UNKNOWN:
begin
Result := False;
Exit;
end;
TIME_ZONE_ID_STANDARD,
TIME_ZONE_ID_DAYLIGHT: ;
else
//TIME_ZONE_ID_INVALID:
RaiseLastOSError();
end;
DateTimeToSystemTime(lLocalTime, lLocalSystemTime);
if not TzSpecificLocalTimeToSystemTime(nil, lLocalSystemTime, lUniversalSystemTime) then
RaiseLastOSError();
Result := SameTime(SystemTimeToDateTime(lUniversalSystemTime),
IncMinute(lLocalTime, lTimeZoneInfo.DaylightBias + lTimeZoneInfo.Bias));
end;