File Coverage

blib/lib/Astro/FITS/HdrTrans/UKIRTDB.pm
Criterion Covered Total %
statement 130 204 63.7
branch 34 90 37.7
condition 10 48 20.8
subroutine 24 28 85.7
pod 19 19 100.0
total 217 389 55.7


line stmt bran cond sub pod time code
1             package Astro::FITS::HdrTrans::UKIRTDB;
2              
3             =head1 NAME
4              
5             Astro::FITS::HdrTrans::UKIRTDB - UKIRT Database Table translations
6              
7             =head1 SYNOPSIS
8              
9             %generic_headers = translate_from_FITS(\%FITS_headers, \@header_array);
10              
11             %FITS_headers = transate_to_FITS(\%generic_headers, \@header_array);
12              
13             =head1 DESCRIPTION
14              
15             Converts information contained in UKIRTDB FITS headers to and from
16             generic headers. See Astro::FITS::HdrTrans for a list of generic
17             headers.
18              
19             =cut
20              
21 10     10   7532175 use 5.006;
  10         45  
22 10     10   58 use warnings;
  10         26  
  10         354  
23 10     10   67 use strict;
  10         25  
  10         273  
24 10     10   71 use Carp;
  10         20  
  10         792  
25              
26 10     10   745 use Time::Piece;
  10         12468  
  10         125  
27              
28             # Inherit from Base
29 10     10   856 use base qw/ Astro::FITS::HdrTrans::JAC /;
  10         25  
  10         1759  
30              
31 10     10   70 use vars qw/ $VERSION /;
  10         49  
  10         26187  
32              
33             # Note that we use %02 not %03 because of historical reasons
34             $VERSION = "1.63";
35              
36             # for a constant mapping, there is no FITS header, just a generic
37             # header that is constant
38             my %CONST_MAP = (
39             COORDINATE_UNITS => 'degrees',
40             );
41              
42             # NULL mappings used to override base class implementations
43             my @NULL_MAP = ();
44              
45             # unit mapping implies that the value propogates directly
46             # to the output with only a keyword name change
47              
48             my %UNIT_MAP = (
49             AIRMASS_START => "AMSTART",
50             AIRMASS_END => "AMEND",
51             CAMERA => "CAMLENS",
52             CAMERA_NUMBER => "CAMNUM",
53             CONFIGURATION_INDEX => "CNFINDEX",
54             DEC_BASE => "DECBASE",
55             DEC_SCALE => "PIXELSIZ",
56             DEC_TELESCOPE_OFFSET => "DECOFF",
57             DETECTOR_READ_TYPE => "MODE",
58             DR_GROUP => "GRPNUM",
59             DR_RECIPE => "RECIPE",
60             EQUINOX => "EQUINOX",
61             FILTER => "FILTER",
62             FILENAME => "FILENAME",
63             GAIN => "DEPERDN",
64             GRATING_DISPERSION => "GDISP",
65             GRATING_ORDER => "GORDER",
66             INSTRUMENT => "INSTRUME",
67             NUMBER_OF_COADDS => 'NEXP',
68             NUMBER_OF_EXPOSURES => "NEXP",
69             OBJECT => "OBJECT",
70             OBSERVATION_MODE => "INSTMODE",
71             OBSERVATION_NUMBER => "RUN",
72             OBSERVATION_TYPE => "OBSTYPE",
73             PROJECT => "PROJECT",
74             RA_SCALE => "PIXELSIZ",
75             RA_TELESCOPE_OFFSET => "RAOFF",
76             TELESCOPE => "TELESCOP",
77             WAVEPLATE_ANGLE => "WPLANGLE",
78             Y_BASE => "DECBASE",
79             X_DIM => "DCOLUMNS",
80             Y_DIM => "DROWS",
81             X_OFFSET => "RAOFF",
82             Y_OFFSET => "DECOFF",
83             X_SCALE => "PIXELSIZ",
84             Y_SCALE => "PIXELSIZ",
85             X_LOWER_BOUND => "RDOUT_X1",
86             X_UPPER_BOUND => "RDOUT_X2",
87             Y_LOWER_BOUND => "RDOUT_Y1",
88             Y_UPPER_BOUND => "RDOUT_Y2"
89             );
90              
91              
92             # Create the translation methods
93             __PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP, \@NULL_MAP );
94              
95              
96             =head1 METHODS
97              
98             =over 4
99              
100             =item B<can_translate>
101              
102             Determine if this class can handle the translation. Returns true
103             if the TELESCOP is "UKIRT" and there is a "FILENAME" key and
104             a "RAJ2000" key. These keywords allow the DB results to be disambiguated
105             from the actual file headers.
106              
107             $cando = $class->can_translate( \%hdrs );
108              
109             =cut
110              
111             sub can_translate {
112 20     20 1 64 my $self = shift;
113 20         47 my $FITS_headers = shift;
114 20 50 100     143 if (exists $FITS_headers->{TELESCOP}
      100        
      66        
115             && $FITS_headers->{TELESCOP} =~ /UKIRT/
116             && exists $FITS_headers->{FILENAME}
117             && exists $FITS_headers->{RAJ2000}) {
118 1         6 return 1;
119             }
120             }
121              
122             =back
123              
124             =head1 COMPLEX CONVERSIONS
125              
126             These methods are more complicated than a simple mapping. We have to
127             provide both from- and to-FITS conversions All these routines are
128             methods and the to_ routines all take a reference to a hash and return
129             the translated value (a many-to-one mapping) The from_ methods take a
130             reference to a generic hash and return a translated hash (sometimes
131             these are many-to-many)
132              
133             =over 4
134              
135             =item B<to_INST_DHS>
136              
137             Sets the INST_DHS header.
138              
139             =cut
140              
141             sub to_INST_DHS {
142 1     1 1 4 my $self = shift;
143 1         4 my $FITS_headers = shift;
144 1         2 my $return;
145              
146 1 50       12 if ( exists( $FITS_headers->{DHSVER} ) ) {
147 0         0 $FITS_headers->{DHSVER} =~ /^(\w+)/;
148 0         0 my $dhs = uc($1);
149 0         0 $return = $FITS_headers->{INSTRUME} . "_$dhs";
150             } else {
151 1         11 my $dhs = "UKDHS";
152 1         5 $return = $FITS_headers->{INSTRUME} . "_$dhs";
153             }
154              
155 1         4 return $return;
156              
157             }
158              
159             =item B<to_EXPOSURE_TIME>
160              
161             Converts either the C<EXPOSED> or C<DEXPTIME> FITS header into
162             the C<EXPOSURE_TIME> generic header.
163              
164             =cut
165              
166             sub to_EXPOSURE_TIME {
167 1     1 1 2 my $self = shift;
168 1         2 my $FITS_headers = shift;
169 1         3 my $return;
170              
171 1 50 33     8 if ( exists( $FITS_headers->{'EXPOSED'} ) && defined( $FITS_headers->{'EXPOSED'} ) ) {
    0 0        
    0 0        
172 1         3 $return = $FITS_headers->{'EXPOSED'};
173             } elsif ( exists( $FITS_headers->{'DEXPTIME'} ) && defined( $FITS_headers->{'DEXPTIME'} ) ) {
174 0         0 $return = $FITS_headers->{'DEXPTIME'};
175             } elsif ( exists( $FITS_headers->{'EXP_TIME'} ) && defined( $FITS_headers->{'EXP_TIME'} ) ) {
176 0         0 $return = $FITS_headers->{'EXP_TIME'};
177             }
178 1         4 return $return;
179             }
180              
181             =item B<to_COORDINATE_TYPE>
182              
183             Converts the C<EQUINOX> FITS header into B1950 or J2000, depending
184             on equinox value, and sets the C<COORDINATE_TYPE> generic header.
185              
186             =cut
187              
188             sub to_COORDINATE_TYPE {
189 1     1 1 2 my $self = shift;
190 1         2 my $FITS_headers = shift;
191 1         2 my $return;
192 1 50       5 if (exists($FITS_headers->{EQUINOX})) {
193 1 50       12 if ($FITS_headers->{EQUINOX} =~ /1950/) {
    50          
194 0         0 $return = "B1950";
195             } elsif ($FITS_headers->{EQUINOX} =~ /2000/) {
196 1         13 $return = "J2000";
197             }
198             }
199 1         6 return $return;
200             }
201              
202             =item B<to_GRATING_NAME>
203              
204             =cut
205              
206             sub to_GRATING_NAME {
207 1     1 1 3 my $self = shift;
208 1         2 my $FITS_headers = shift;
209 1         1 my $return;
210 1 50       17 if (exists($FITS_headers->{GRATING})) {
    50          
211 0         0 $return = $FITS_headers->{GRATING};
212             } elsif (exists($FITS_headers->{GRISM})) {
213 1         3 $return = $FITS_headers->{GRISM};
214             }
215 1         4 return $return;
216             }
217              
218             =item B<to_GRATING_WAVELENGTH>
219              
220             =cut
221              
222             sub to_GRATING_WAVELENGTH {
223 1     1 1 3 my $self = shift;
224 1         2 my $FITS_headers = shift;
225 1         3 my $return;
226 1 50       4 if (exists($FITS_headers->{GLAMBDA})) {
    50          
227 0         0 $return = $FITS_headers->{GLAMBDA};
228             } elsif (exists($FITS_headers->{CENWAVL})) {
229 0         0 $return = $FITS_headers->{CENWAVL};
230             }
231 1         4 return $return;
232             }
233              
234             =item B<to_SLIT_ANGLE>
235              
236             Converts either the C<SANGLE> or the C<SLIT_PA> header into the C<SLIT_ANGLE>
237             generic header.
238              
239             =cut
240              
241             sub to_SLIT_ANGLE {
242 1     1 1 2 my $self = shift;
243 1         2 my $FITS_headers = shift;
244 1         2 my $return;
245 1 50       7 if (exists($FITS_headers->{'SANGLE'})) {
    50          
246 0         0 $return = $FITS_headers->{'SANGLE'};
247             } elsif (exists($FITS_headers->{'SLIT_PA'} )) {
248 0         0 $return = $FITS_headers->{'SLIT_PA'};
249             }
250 1         3 return $return;
251              
252             }
253              
254             =item B<to_SLIT_NAME>
255              
256             Converts either the C<SLIT> or the C<SLITNAME> header into the C<SLIT_NAME>
257             generic header.
258              
259             =cut
260              
261             sub to_SLIT_NAME {
262 1     1 1 2 my $self = shift;
263 1         2 my $FITS_headers = shift;
264 1         3 my $return;
265 1 50       5 if (exists($FITS_headers->{'SLIT'})) {
    50          
266 0         0 $return = $FITS_headers->{'SLIT'};
267             } elsif (exists($FITS_headers->{'SLITNAME'} )) {
268 1         3 $return = $FITS_headers->{'SLITNAME'};
269             }
270 1         2 return $return;
271              
272             }
273              
274             =item B<to_SPEED_GAIN>
275              
276             =cut
277              
278             sub to_SPEED_GAIN {
279 1     1 1 2 my $self = shift;
280 1         3 my $FITS_headers = shift;
281 1         8 my $return;
282              
283 1 50       6 if ( exists( $FITS_headers->{'SPD_GAIN'} ) ) {
    50          
284 0         0 $return = $FITS_headers->{'SPD_GAIN'};
285             } elsif ( exists( $FITS_headers->{'WAVEFORM'} ) ) {
286 1 50       7 if ( $FITS_headers->{'WAVEFORM'} =~ /thermal/i ) {
287 0         0 $return = 'thermal';
288             } else {
289 1         3 $return = 'normal';
290             }
291             }
292 1         2 return $return;
293             }
294              
295             =item B<to_STANDARD>
296              
297             Converts either the C<STANDARD> header (if it exists) or uses the
298             C<OBJECT> or C<RECIPE> headers to determine if an observation is of a
299             standard. If the C<OBJECT> header starts with either B<BS> or B<FS>,
300             I<or> the DR recipe contains the word STANDARD, it is assumed to be a
301             standard.
302              
303             =cut
304              
305             sub to_STANDARD {
306 1     1 1 3 my $self = shift;
307 1         2 my $FITS_headers = shift;
308              
309             # Set false as default so we do not have to repeat this in the logic
310             # below (could just use undef == false)
311 1         10 my $return = 0; # default false
312              
313 1 50 33     9 if ( exists( $FITS_headers->{'STANDARD'} ) &&
    0 0        
      0        
      0        
314             length( $FITS_headers->{'STANDARD'} . "") > 0 ) {
315              
316 1 50       9 if ($FITS_headers->{'STANDARD'} =~ /^[tf]$/i) {
    50          
317             # Raw header read from FITS header
318 0         0 $return = (uc($FITS_headers->{'STANDARD'}) eq 'T');
319             } elsif ($FITS_headers->{'STANDARD'} =~ /^[01]$/) {
320             # Translated header either so a true logical
321 1         4 $return = $FITS_headers->{'STANDARD'};
322             }
323              
324             } elsif ( ( exists $FITS_headers->{OBJECT} &&
325             $FITS_headers->{'OBJECT'} =~ /^[bf]s/i ) ||
326             ( exists( $FITS_headers->{'RECIPE'} ) &&
327             $FITS_headers->{'RECIPE'} =~ /^standard/i
328             )) {
329             # Either we have an object with name prefix of BS or FS or
330             # our recipe looks suspiciously like a standard.
331 0         0 $return = 1;
332              
333             }
334              
335 1         3 return $return;
336              
337             }
338              
339             =item B<to_UTDATE>
340              
341             =cut
342              
343             sub to_UTDATE {
344 1     1 1 3 my $self = shift;
345 1         3 my $FITS_headers = shift;
346 1         2 my $return;
347              
348 1 50       4 if ( exists( $FITS_headers->{'UT_DATE'} ) ) {
349 1         3 my $datestr = $FITS_headers->{'UT_DATE'};
350 1         6 $return = _parse_date($datestr);
351 1 50       18 die "Error parsing date \"$datestr\"" unless defined $return;
352 1         7 $return = $return->strftime('%Y%m%d');
353             }
354              
355 1         43 return $return;
356              
357             }
358              
359             =item B<to_UTSTART>
360              
361             Strips the optional 'Z' from the C<DATE-OBS> header, or if that header does
362             not exist, combines the C<UT_DATE> and C<RUTSTART> headers into a unified
363             C<UTSTART> header.
364              
365             =cut
366              
367             sub to_UTSTART {
368 2     2 1 5 my $self = shift;
369 2         4 my $FITS_headers = shift;
370 2         2 my $return;
371              
372 2 50 0     7 if ( exists( $FITS_headers->{'DATE_OBS'} ) ) {
    0 0        
      0        
373 2         6 my $dateobs = $FITS_headers->{'DATE_OBS'};
374 2         6 $return = $self->_parse_iso_date( $dateobs );
375             } elsif (exists($FITS_headers->{'UT_DATE'}) && defined($FITS_headers->{'UT_DATE'}) &&
376             exists($FITS_headers->{'RUTSTART'}) && defined( $FITS_headers->{'RUTSTART'} ) ) {
377             # Use the default UTDATE translation but insert "-" for ISO parsing
378 0         0 my $ut = $self->to_UTDATE($FITS_headers);
379 0         0 $ut = join("-", substr($ut,0,4), substr($ut,4,2), substr($ut,6,2));
380 0         0 my $hour = int($FITS_headers->{'RUTSTART'});
381 0         0 my $minute = int( ( $FITS_headers->{'RUTSTART'} - $hour ) * 60 );
382 0         0 my $second = int( ( ( ( $FITS_headers->{'RUTSTART'} - $hour ) * 60) - $minute ) * 60 );
383 0         0 $return = $self->_parse_iso_date( $ut . "T$hour:$minute:$second" );
384             }
385              
386 2         5 return $return;
387             }
388              
389             =item B<from_UTSTART>
390              
391             Converts the C<UTSTART> generic header into C<UT_DATE>, C<RUTSTART>,
392             and C<DATE-OBS> database headers.
393              
394             =cut
395              
396             sub from_UTSTART {
397 0     0 1 0 my $self = shift;
398 0         0 my $generic_headers = shift;
399 0         0 my %return_hash;
400 0 0       0 if (exists($generic_headers->{UTSTART})) {
401 0         0 my $t = _parse_date( $generic_headers->{'UTSTART'} );
402 0         0 my $month = $t->month;
403 0         0 $month =~ /^(.{3})/;
404 0         0 $month = $1;
405 0         0 $return_hash{'UT_DATE'} = $month . " " . $t->mday . " " . $t->year;
406 0         0 $return_hash{'RUTSTART'} = $t->hour + ($t->min / 60) + ($t->sec / 3600);
407 0         0 $return_hash{'DATE_OBS'} = $generic_headers->{'UTSTART'};
408             }
409 0         0 return %return_hash;
410             }
411              
412             =item B<to_UTEND>
413              
414             Strips the optional 'Z' from the C<DATE-END> header, or if that header does
415             not exist, combines the C<UT_DATE> and C<RUTEND> headers into a unified
416             C<UTEND> header.
417              
418             =cut
419              
420             sub to_UTEND {
421 1     1 1 3 my $self = shift;
422 1         1 my $FITS_headers = shift;
423 1         2 my $return;
424              
425 1 50 0     3 if ( exists( $FITS_headers->{'DATE_END'} ) ) {
    0 0        
      0        
426 1         3 my $dateend = $FITS_headers->{'DATE_END'};
427 1         4 $return = $self->_parse_iso_date( $dateend );
428             } elsif (exists($FITS_headers->{'UT_DATE'}) && defined($FITS_headers->{'UT_DATE'}) &&
429             exists($FITS_headers->{'RUTEND'}) && defined( $FITS_headers->{'RUTEND'} ) ) {
430             # Use the default UTDATE translation but insert "-" for ISO parsing
431 0         0 my $ut = $self->to_UTDATE($FITS_headers);
432 0         0 $ut = join("-", substr($ut,0,4), substr($ut,4,2), substr($ut,6,2));
433 0         0 my $hour = int($FITS_headers->{'RUTEND'});
434 0         0 my $minute = int( ( $FITS_headers->{'RUTEND'} - $hour ) * 60 );
435 0         0 my $second = int( ( ( ( $FITS_headers->{'RUTEND'} - $hour ) * 60) - $minute ) * 60 );
436 0         0 $return = $self->_parse_iso_date( $ut . "T$hour:$minute:$second" );
437             }
438              
439 1         4 return $return;
440             }
441              
442             =item B<from_UTEND>
443              
444             Converts the C<UTEND> generic header into C<UT_DATE>, C<RUTEND>
445             and C<DATE-END> database headers.
446              
447             =cut
448              
449             sub from_UTEND {
450 0     0 1 0 my $self = shift;
451 0         0 my $generic_headers = shift;
452 0         0 my %return_hash;
453 0 0       0 if (exists($generic_headers->{UTEND})) {
454 0         0 my $t = _parse_date( $generic_headers->{'UTEND'} );
455 0         0 my $month = $t->month;
456 0         0 $month =~ /^(.{3})/;
457 0         0 $month = $1;
458 0         0 $return_hash{'UT_DATE'} = $month . " " . $t->mday . " " . $t->year;
459 0         0 $return_hash{'RUTEND'} = $t->hour + ($t->min / 60) + ($t->sec / 3600);
460 0         0 $return_hash{'DATE_END'} = $generic_headers->{'UTEND'};
461             }
462 0         0 return %return_hash;
463             }
464              
465             =item B<to_X_BASE>
466              
467             Converts the decimal hours in the FITS header C<RABASE> into
468             decimal degrees for the generic header C<X_BASE>.
469              
470             =cut
471              
472             sub to_X_BASE {
473 1     1 1 3 my $self = shift;
474 1         2 my $FITS_headers = shift;
475 1         3 my $return;
476 1 50       4 if (exists($FITS_headers->{RABASE})) {
477 1         3 $return = $FITS_headers->{RABASE} * 15;
478             }
479 1         3 return $return;
480             }
481              
482             =item B<from_X_BASE>
483              
484             Converts the decimal degrees in the generic header C<X_BASE>
485             into decimal hours for the FITS header C<RABASE>.
486              
487             =cut
488              
489             sub from_X_BASE {
490 0     0 1 0 my $self = shift;
491 0         0 my $generic_headers = shift;
492 0         0 my %return_hash;
493 0 0       0 if (exists($generic_headers->{X_BASE})) {
494 0         0 $return_hash{'RABASE'} = $generic_headers->{X_BASE} / 15;
495             }
496 0         0 return %return_hash;
497             }
498              
499             =item B<to_RA_BASE>
500              
501             Converts the decimal hours in the FITS header C<RABASE> into
502             decimal degrees for the generic header C<RA_BASE>.
503              
504             =cut
505              
506             sub to_RA_BASE {
507 1     1 1 2 my $self = shift;
508 1         2 my $FITS_headers = shift;
509 1         2 my $return;
510 1 50       4 if (exists($FITS_headers->{RABASE})) {
511 1         9 $return = $FITS_headers->{RABASE} * 15;
512             }
513 1         26 return $return;
514             }
515              
516             =item B<from_RA_BASE>
517              
518             Converts the decimal degrees in the generic header C<RA_BASE>
519             into decimal hours for the FITS header C<RABASE>.
520              
521             =cut
522              
523             sub from_RA_BASE {
524 0     0 1 0 my $self = shift;
525 0         0 my $generic_headers = shift;
526 0         0 my %return_hash;
527 0 0       0 if (exists($generic_headers->{RA_BASE})) {
528 0         0 $return_hash{'RABASE'} = $generic_headers->{RA_BASE} / 15;
529             }
530 0         0 return %return_hash;
531             }
532              
533             =back
534              
535             =head1 INTERNAL METHODS
536              
537             =over 4
538              
539             =item B<_fix_dates>
540              
541             Handle the case where DATE_OBS and/or DATE_END are given, and convert
542             them into DATE-OBS and/or DATE-END.
543              
544             =cut
545              
546             sub _fix_dates {
547 1     1   5 my ( $class, $FITS_headers ) = @_;
548              
549 1 50       5 if( defined( $FITS_headers->{'DATE_OBS'} ) ) {
550 1         12 $FITS_headers->{'DATE-OBS'} = $class->_parse_iso_date( $FITS_headers->{'DATE_OBS'} );
551             }
552 1 50       5 if( defined( $FITS_headers->{'DATE_END'} ) ) {
553 1         4 $FITS_headers->{'DATE-END'} = $class->_parse_iso_date( $FITS_headers->{'DATE_END'} );
554             }
555              
556             }
557              
558             =item B<_parse_date>
559              
560             Parses a string as a date. Returns a C<Time::Piece> object.
561              
562             $time = _parse_date( $date );
563              
564             Returns C<undef> if the time could not be parsed.
565             Returns the object unchanged if the argument is already a C<Time::Piece>.
566              
567             It will also recognize a MySQL style date: '2002-03-15 07:04:00'
568             and a simple YYYYMMDD.
569              
570             The date is assumed to be in UT.
571              
572             =cut
573              
574             sub _parse_date {
575 1     1   2 my $date = shift;
576              
577             # If we already have a Time::Piece return
578 1 50       7 return bless $date, "Time::Piece"
579             if UNIVERSAL::isa( $date, "Time::Piece");
580              
581             # We can use Time::Piece->strptime but it requires an exact
582             # format rather than working it out from context (and we don't
583             # want an additional requirement on Date::Manip or something
584             # since Time::Piece is exactly what we want for Astro::Coords)
585             # Need to fudge a little
586              
587 1         3 my $format;
588              
589             # Need to disambiguate ISO date from MySQL date
590 1 50       11 if ($date =~ /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/) {
    50          
    50          
591             # MySQL
592 0         0 $format = '%Y-%m-%d %T';
593              
594             } elsif ($date =~ /\d\d\d\d-\d\d-\d\d/) {
595             # ISO
596              
597             # All arguments should have a day, month and year
598 0         0 $format = "%Y-%m-%d";
599              
600             # Now check for time
601 0 0       0 if ($date =~ /T/) {
602             # Date and time
603             # Now format depends on the number of colons
604 0         0 my $n = ( $date =~ tr/:/:/ );
605 0 0       0 $format .= "T" . ($n == 2 ? "%T" : "%R");
606             }
607             } elsif ($date =~ /^\d\d\d\d\d\d\d\d\b/) {
608             # YYYYMMDD format
609 0         0 $format = "%Y%m%d";
610             } else {
611             # Allow Sybase date for compatability.
612             # Mar 15 2002 7:04AM
613 1         4 $format = "%b %d %Y %I:%M%p";
614              
615             }
616              
617             # Now parse
618             # Note that this time is treated as "local" rather than "gm"
619 1         2 my $time = eval { Time::Piece->strptime( $date, $format ); };
  1         5  
620 1 50       53 if ($@) {
621 0         0 return undef;
622             } else {
623             # Note that the above constructor actually assumes the date
624             # to be parsed is a local time not UTC. To switch to UTC
625             # simply get the epoch seconds and the timezone offset
626             # and run gmtime
627             # Sometime around v1.07 of Time::Piece the behaviour changed
628             # to return UTC rather than localtime from strptime!
629             # The joys of backwards compatibility.
630 1 50       4 if ($time->[Time::Piece::c_islocal]) {
631 0         0 my $tzoffset = $time->tzoffset;
632 0         0 my $epoch = $time->epoch;
633 0         0 $time = gmtime( $epoch + $tzoffset->seconds );
634             }
635              
636             }
637              
638 1         3 return $time;
639             }
640              
641             =back
642              
643             =head1 SEE ALSO
644              
645             C<Astro::FITS::HdrTrans>, C<Astro::FITS::HdrTrans::UKIRT>,
646             C<Astro::FITS::HdrTrans::Base>.
647              
648             =head1 AUTHORS
649              
650             Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt>,
651             Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>
652              
653             =head1 COPYRIGHT
654              
655             Copyright (C) 2007-2008 Science and Technology Facilities Council.
656             Copyright (C) 2002-2005 Particle Physics and Astronomy Research Council.
657             All Rights Reserved.
658              
659             This program is free software; you can redistribute it and/or modify it under
660             the terms of the GNU General Public License as published by the Free Software
661             Foundation; either version 2 of the License, or (at your option) any later
662             version.
663              
664             This program is distributed in the hope that it will be useful,but WITHOUT ANY
665             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
666             PARTICULAR PURPOSE. See the GNU General Public License for more details.
667              
668             You should have received a copy of the GNU General Public License along with
669             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
670             Place,Suite 330, Boston, MA 02111-1307, USA
671              
672             =cut
673              
674             1;